[インデックス 17519] ファイルの概要
このコミットは、Go言語の標準ライブラリ log/syslog
パッケージにおいて、ローカルのsyslogデーモンへのログ出力形式を修正するものです。特に、ネットワーク経由でのsyslog出力とローカルソケット経由でのsyslog出力で異なる形式を使用するように変更し、ローカルsyslogデーモンが期待する形式に合わせることを目的としています。
コミット
commit 87a6d75012986fb8867b746afcd42f742c119945
Author: Russ Cox <rsc@golang.org>
Date: Mon Sep 9 16:17:44 2013 -0400
log/syslog: use alternate format for logging to local syslog daemon
Fixes #5803.
Is it correct behavior? Who knows.
R=golang-dev, bradfitz, jgc
CC=golang-dev, jgc
https://golang.org/cl/13248048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/87a6d75012986fb8867b746afcd42f742c119945
元コミット内容
log/syslog: use alternate format for logging to local syslog daemon
Fixes #5803.
Is it correct behavior? Who knows.
このコミットは、ローカルのsyslogデーモンへのログ出力に代替フォーマットを使用することを目的としています。Issue #5803を修正するものであり、その動作が「正しい」かどうかは不明であるというコメントが付されています。
変更の背景
このコミットの背景には、Go言語の log/syslog
パッケージがローカルのsyslogデーモンと通信する際に、特定の環境(特にUnix系システム)でログメッセージが正しく解釈されない問題がありました。コミットメッセージにある Fixes #5803
が示すように、この変更は特定のバグ報告に対応するものです。
syslogプロトコルにはRFC 3164(BSD syslogプロトコル)とRFC 5424(syslogプロトコル)という主要な標準が存在し、それぞれメッセージのフォーマットに違いがあります。特に、ローカルのsyslogデーモン(例: syslogd
や rsyslogd
、systemd-journald
など)は、ネットワーク経由で受信するログと、ローカルソケット(通常 /dev/log
や /var/run/syslog
)経由で受信するログで、期待するフォーマットが異なる場合があります。
従来の log/syslog
パッケージは、ネットワーク経由での出力とローカルソケット経由での出力で同じフォーマットを使用しており、これが一部のローカルsyslogデーモンで問題を引き起こしていました。具体的には、ローカルデーモンはホスト名を含まない、より簡潔な形式を期待することがあります。また、タイムスタンプの形式も異なる場合があります。
コミットメッセージの「Is it correct behavior? Who knows.」という記述は、syslogの実装がOSやバージョンによって多様であり、すべての環境で「正しい」とされる単一のフォーマットを特定することが困難であるという当時の状況を反映しています。この変更は、少なくとも特定の一般的なローカルsyslogデーモンとの互換性を向上させるための試みでした。
前提知識の解説
syslogとは
syslogは、システムやアプリケーションからのログメッセージを収集、保存、管理するための標準的なプロトコルです。Unix系OSで広く利用されており、カーネルメッセージ、システムサービス、ユーザーアプリケーションなど、様々なソースからのログを一元的に扱うことができます。
syslogメッセージは通常、以下の要素を含みます。
- Priority (PRI): メッセージの重要度とファシリティ(メッセージの発生源)を示す数値。
- Header: タイムスタンプとホスト名が含まれます。
- Message: ログの本体。
syslogの通信方法
syslogデーモンとの通信には主に以下の方法があります。
- UDP/TCP: ネットワーク経由でログを送信します。リモートのsyslogサーバーにログを集約する際に使用されます。
- Unixドメインソケット: ローカルホスト内でログを送信する際に使用されます。通常
/dev/log
や/var/run/syslog
といったパスにソケットファイルが存在します。この方法は、ネットワークオーバーヘッドがなく、ローカルシステム内での高速な通信に適しています。
syslogメッセージフォーマットのバリエーション
syslogメッセージのフォーマットには、主に以下の2つのRFC(Request for Comments)で定義されたものがあります。
-
RFC 3164 (BSD syslog protocol): 伝統的なsyslogフォーマットで、以下のような形式です。
<PRI>Timestamp Hostname Tag: Message
例:<34>Oct 11 22:14:15 myhost myapp: This is a log message.
タイムスタンプはMMM DD HH:MM:SS
形式(例:Oct 11 22:14:15
)です。 -
RFC 5424 (syslog protocol): RFC 3164を拡張した新しいフォーマットで、より構造化されたデータや高精度なタイムスタンプをサポートします。
<PRI>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID [STRUCTURED-DATA] MSG
例:<1>1 2003-10-11T22:14:15.003Z myhost myapp - ID47 [exampleSDID@32473 iut=\"3\" eventSource=\"Application\"] This is a log message.
タイムスタンプはISO 8601形式(例:2003-10-11T22:14:15.003Z
)です。
ローカルのsyslogデーモンは、Unixドメインソケット経由で受信するメッセージに対して、RFC 3164のような簡潔な形式、特にホスト名を含まない形式を期待することがあります。これは、ローカルで発生したログであるため、ホスト名は自明であるという前提があるためです。
Go言語の log/syslog
パッケージ
Go言語の log/syslog
パッケージは、Goアプリケーションからsyslogデーモンにログを送信するためのインターフェースを提供します。syslog.New()
関数を使って *syslog.Writer
を作成し、Writer
の Crit()
, Err()
, Warning()
, Info()
, Debug()
などのメソッドを使ってログを書き込みます。
技術的詳細
このコミットの主要な技術的変更点は、log/syslog
パッケージがローカルのsyslogデーモンに接続しているかどうかを識別し、その場合にのみ異なるログメッセージフォーマットを使用するようにしたことです。
-
netConn
構造体の変更:netConn
構造体にlocal bool
フィールドが追加されました。このフィールドは、現在のネットワーク接続がローカルのsyslogデーモンへのものであるかどうかを示すフラグとして機能します。type netConn struct { local bool // 追加 conn net.Conn }
-
Writer.connect()
メソッドの変更:Writer.connect()
メソッド内で、net.Dial
を使って接続が確立された際に、w.conn
に&netConn{conn: c}
を代入するように変更されました。これにより、w.conn
はnetConn
の値ではなく、*netConn
型のポインタを保持するようになります。これは、後述のwriteString
メソッドのレシーバ型変更と関連しています。 -
unixSyslog()
関数の変更:syslog_unix.go
内のunixSyslog()
関数は、Unixドメインソケット経由でローカルのsyslogデーモンに接続する際に呼び出されます。この関数内でnetConn
を返す際に、新しく追加されたlocal
フィールドをtrue
に設定するように変更されました。// 変更前: return netConn{conn}, nil // 変更後: return &netConn{conn: conn, local: true}, nil
これにより、ローカル接続であることが明示的にマークされます。
-
netConn.writeString()
メソッドの変更と条件付きフォーマット: 最も重要な変更はnetConn.writeString()
メソッドです。- まず、このメソッドのレシーバが
netConn
から*netConn
に変更されました。これにより、writeString
メソッド内でnetConn
インスタンスのフィールド(local
など)を変更できるようになります(ただし、このコミットではlocal
フィールド自体は変更していません)。 - メソッドの内部に
if n.local { ... }
という条件分岐が追加されました。 n.local
がtrue
の場合(つまりローカル接続の場合)、以下の変更が適用されます。- タイムスタンプのフォーマットが
time.RFC3339
からtime.Stamp
に変更されます。time.Stamp
はJan _2 15:04:05
のような形式(例:Sep 9 16:17:44
)です。これはRFC 3164のタイムスタンプ形式に近いです。 fmt.Fprintf
のフォーマット文字列からhostname
フィールドが削除されます。これにより、ローカルsyslogデーモンに送信されるメッセージにはホスト名が含まれなくなります。
- タイムスタンプのフォーマットが
// 変更前: func (n netConn) writeString(...) // 変更後: func (n *netConn) writeString(...) func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error { if n.local { // ローカル接続の場合 // ネットワーク形式と比較して、変更点は以下: // 1. time.RFC3339 の代わりに time.Stamp を使用。 // 2. Fprintf からホスト名フィールドを削除。 timestamp := time.Now().Format(time.Stamp) // time.Stamp フォーマット _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s", // ホスト名なし p, timestamp, tag, os.Getpid(), msg, nl) return err } // ネットワーク接続の場合 (変更なし) timestamp := time.Now().Format(time.RFC3339) _, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s", p, timestamp, hostname, tag, os.Getpid(), msg, nl) return err }
- まず、このメソッドのレシーバが
-
netConn.close()
メソッドのレシーバ変更:netConn.close()
メソッドのレシーバもnetConn
から*netConn
に変更されました。これはwriteString
と同様に、netConn
がポインタとして扱われるようになったことによる整合性のための変更です。
これらの変更により、log/syslog
パッケージは、ローカルのsyslogデーモンに対してはホスト名を含まず、time.Stamp
形式のタイムスタンプを使用する、より簡潔なメッセージフォーマットでログを送信するようになります。これにより、一部のシステムでローカルsyslogがログを正しく処理できるようになることが期待されます。
コアとなるコードの変更箇所
src/pkg/log/syslog/syslog.go
--- a/src/pkg/log/syslog/syslog.go
+++ b/src/pkg/log/syslog/syslog.go
@@ -103,7 +103,8 @@ type serverConn interface {
}
type netConn struct {
- conn net.Conn
+ local bool // 追加
+ conn net.Conn
}
// New establishes a new connection to the system log daemon. Each
@@ -163,7 +164,7 @@ func (w *Writer) connect() (err error) {
var c net.Conn
c, err = net.Dial(w.network, w.raddr)
if err == nil {
- w.conn = netConn{c}
+ w.conn = &netConn{conn: c} // ポインタに変更
if w.hostname == "" {
w.hostname = c.LocalAddr().String()
}
@@ -282,7 +283,17 @@ func (w *Writer) write(p Priority, msg string) (int, error) {
return len(msg), nil
}
-func (n netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
+func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error { // レシーバをポインタに変更
+ if n.local { // ローカル接続の場合の条件分岐
+ // Compared to the network form below, the changes are:
+ // 1. Use time.Stamp instead of time.RFC3339.
+ // 2. Drop the hostname field from the Fprintf.
+ timestamp := time.Now().Format(time.Stamp) // time.Stamp フォーマット
+ _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s", // ホスト名なし
+ p, timestamp,
+ tag, os.Getpid(), msg, nl)
+ return err
+ }
timestamp := time.Now().Format(time.RFC3339)
_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, hostname,
@@ -290,7 +311,7 @@ func (n netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
return err
}
-func (n netConn) close() error {
+func (n *netConn) close() error { // レシーバをポインタに変更
return n.conn.Close()
}
src/pkg/log/syslog/syslog_unix.go
--- a/src/pkg/log/syslog/syslog_unix.go
+++ b/src/pkg/log/syslog/syslog_unix.go
@@ -23,7 +23,7 @@ func unixSyslog() (conn serverConn, err error) {
if err != nil {
continue
} else {
- return netConn{conn}, nil
+ return &netConn{conn: conn, local: true}, nil // local: true を設定
}
}
}
コアとなるコードの解説
このコミットの核心は、netConn
構造体に local
フラグを追加し、このフラグに基づいて writeString
メソッドが異なるログフォーマットを選択するようにした点です。
-
netConn
構造体へのlocal
フィールド追加:netConn
は、syslogデーモンとの実際のネットワーク接続(またはUnixドメインソケット接続)をラップする構造体です。ここにlocal bool
フィールドが追加されたことで、この接続がローカルのsyslogデーモンに対するものか、それともリモートのsyslogサーバーに対するものかを区別できるようになりました。 -
unixSyslog()
でのlocal
フラグの設定:syslog_unix.go
にあるunixSyslog()
関数は、Unix系システムでローカルのsyslogデーモンへの接続を確立する際に使用されます。この関数がnetConn
インスタンスを返す際に、local: true
を明示的に設定するようになりました。これにより、ローカルソケット経由の接続が正しく識別されます。 -
netConn.writeString()
の条件付きロジック:writeString
メソッドは、実際にログメッセージをsyslogデーモンに書き込む役割を担います。このメソッド内でif n.local { ... }
という条件分岐が導入されました。n.local
がtrue
の場合(ローカル接続)、タイムスタンプはtime.Stamp
フォーマット(例:Sep 9 16:17:44
)で整形され、fmt.Fprintf
のフォーマット文字列からホスト名 (%s
) が削除されます。これにより、メッセージは<PRI>Timestamp Tag[PID]: Message
の形式になります。これは、多くのローカルsyslogデーモンが期待する、より簡潔な形式です。n.local
がfalse
の場合(ネットワーク接続)、従来のtime.RFC3339
フォーマット(例:2013-09-09T16:17:44-04:00
)とホスト名を含むフォーマットが維持されます。これは、RFC 5424に準拠した、より詳細なネットワークログに適しています。
-
レシーバのポインタ化:
netConn.writeString()
とnetConn.close()
のレシーバが値レシーバ (netConn
) からポインタレシーバ (*netConn
) に変更されました。これは、Writer.connect()
でw.conn
に&netConn{conn: c}
のようにポインタが代入されるようになったことと整合性を取るためです。Goでは、メソッドがレシーバのフィールドを変更する場合や、レシーバが大きな構造体である場合にポインタレシーバを使用するのが一般的です。この変更により、writeString
メソッドがnetConn
インスタンスのlocal
フィールドを参照できるようになります。
これらの変更により、Goの log/syslog
パッケージは、接続先のsyslogデーモンの種類(ローカルかリモートか)に応じて、適切なログメッセージフォーマットを動的に選択できるようになり、互換性の問題が解決されることが期待されます。
関連リンク
- Go Issue 5803: https://github.com/golang/go/issues/5803
- Go CL 13248048: https://golang.org/cl/13248048
参考にした情報源リンク
- Go Issue 59229 (macOS Monterey/Venturaでのsyslog問題): https://github.com/golang/go/issues/59229
- RFC 3164 - The BSD syslog Protocol: https://datatracker.ietf.org/doc/html/rfc3164
- RFC 5424 - The Syslog Protocol: https://datatracker.ietf.org/doc/html/rfc5424
- Go
time
パッケージのFormat
メソッドとレイアウト文字列: https://pkg.go.dev/time#Time.Format