[インデックス 16374] ファイルの概要
このコミットは、log/syslog
パッケージ内のWriter.write
メソッドに対する以前の変更(CL 9658043 / コミットハッシュ ac7877558dce
)を元に戻すものです。元のコードが正しく、write
メソッドが返すバイト数は、フォーマットされたメッセージの長さではなく、入力されたメッセージスライスの長さであるべきだという判断に基づいています。
コミット
- コミットハッシュ:
f6c7b339a6b7592aa83abe253a40825fcb7d3e39
- 作者: Rob Pike r@golang.org
- 日付: Wed May 22 11:42:04 2013 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f6c7b339a6b7592aa83abe253a40825fcb7d3e39
元コミット内容
undo CL 9658043 / ac7877558dce
The original code was correct. The count returned must be the length
of the input slice, not the length of the formatted message.
««« original CL description
log/syslog: report errors from Fprintf
Thanks to chiparus for identifying this.
Fixes #5541.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/9658043
»»»
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/9644044
変更の背景
このコミットは、以前に適用された変更(CL 9658043、コミットハッシュ ac7877558dce
)を元に戻すために作成されました。元の変更は、log/syslog
パッケージのWriter.write
メソッドにおいて、fmt.Fprintf
からのエラーを報告するように修正することを目的としていました。これは、GoのIssue #5541に関連しており、Fprintf
が書き込みに失敗した場合にそのエラーを適切に伝播させるべきだという考えに基づいていたようです。
しかし、この元々の変更は、syslog
のwrite
メソッドの期待される振る舞いを誤解していたことが判明しました。syslog
のwrite
メソッドは、io.Writer
インターフェースの規約に従い、書き込まれたバイト数を返す必要があります。この「書き込まれたバイト数」は、syslog
プロトコルにおいて、実際に送信されたメッセージのバイト数ではなく、入力として受け取ったメッセージスライスの長さを指すべきでした。
以前の変更では、fmt.Fprintf
が実際にw.conn
(ネットワーク接続)に書き込んだバイト数を返そうとしていました。しかし、syslog
の文脈では、これは誤った情報となります。syslog
のwrite
メソッドは、呼び出し元に対して、与えられたメッセージ全体が処理された(または処理されようとした)ことを示すために、入力メッセージの長さを返すのが正しい挙動です。したがって、このコミットは、その誤りを修正し、元の正しいロジックに戻すことを目的としています。
前提知識の解説
Go言語のlog/syslog
パッケージ
Go言語の標準ライブラリには、システムログデーモン(syslogd)にメッセージを送信するためのlog/syslog
パッケージが含まれています。このパッケージは、RFC 5424(またはそれ以前のRFC 3164)で定義されているsyslogプロトコルに準拠してログメッセージをフォーマットし、送信する機能を提供します。
Writer
構造体:syslog
サーバーへの接続を管理し、ログメッセージを書き込むための主要な型です。Writer.write
メソッド:syslog
メッセージを実際にフォーマットし、ネットワーク接続(通常はUDPまたはTCP)を介してsyslog
サーバーに送信する内部メソッドです。このメソッドは、io.Writer
インターフェースのWrite
メソッドと同様に、書き込まれたバイト数とエラーを返します。
fmt.Fprintf
の戻り値
fmt.Fprintf
関数は、指定されたio.Writer
にフォーマットされた文字列を書き込みます。そのシグネチャは以下の通りです。
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
ここで、n
は実際に書き込まれたバイト数を表し、err
は書き込み中に発生したエラーを表します。
syslog
プロトコルにおけるメッセージの長さの扱い
syslog
プロトコルでは、メッセージのペイロード(実際のログ内容)の長さは、プロトコルヘッダの一部として含まれることがあります。しかし、log/syslog
パッケージのWriter.write
メソッドがio.Writer
インターフェースの規約に従う場合、その戻り値n
は、syslog
サーバーに送信されたバイト数ではなく、write
メソッドに渡された元のメッセージスライスmsg
の長さを意味するのが一般的です。これは、io.Writer
のWrite
メソッドが「pから書き込まれたバイト数」を返すという規約に合致するためです。もしwrite
メソッドがFprintf
によって実際にネットワークに書き込まれたバイト数を返してしまうと、それはsyslog
プロトコルの内部的な詳細であり、io.Writer
の期待されるセマンティクスとは異なる可能性があります。
GoのChange List (CL)
Goプロジェクトでは、コード変更は「Change List (CL)」として提出されます。これは、Gitのコミットに似ていますが、よりレビュープロセスに焦点を当てた概念です。各CLには一意のIDが割り当てられ、レビューと承認を経て最終的にGitリポジトリにコミットされます。このコミットメッセージに記載されているCL 9658043
やCL 9644044
は、これらの変更管理システムにおける識別子です。
技術的詳細
このコミットの核心は、src/pkg/log/syslog/syslog.go
ファイル内のWriter.write
メソッドの戻り値のセマンティクスに関するものです。
func (w *Writer) write(p Priority, msg string) (int, error)
このメソッドは、syslog
メッセージを構築し、w.conn
(通常はネットワーク接続を表すnet.Conn
インターフェースを実装する型)に書き込みます。
以前のCL (ac7877558dce
) では、このメソッドの最後の部分が以下のように変更されていました。
return fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, w.hostname,
w.tag, os.Getpid(), msg, nl)
この変更の意図は、fmt.Fprintf
が返すエラーをwrite
メソッドの呼び出し元に伝播させることでした。しかし、このコードはfmt.Fprintf
が返すn
(実際に書き込まれたバイト数)もそのまま返していました。
問題は、syslog.Writer
のwrite
メソッドがio.Writer
インターフェースの規約に従う場合、そのn
の戻り値は、入力として受け取ったmsg
スライスの長さであるべきだという点です。fmt.Fprintf
が返すn
は、syslog
ヘッダやタイムスタンプ、ホスト名、PIDなどを含む、フォーマットされた完全なsyslog
メッセージのバイト数です。これは、write
メソッドの呼び出し元が期待する「書き込まれたメッセージの長さ」とは異なります。呼び出し元は、通常、自分が渡したメッセージがどれだけ処理されたかを知りたいのであり、syslog
プロトコル内部で生成された追加のメタデータを含むメッセージの全長を知りたいわけではありません。
したがって、このコミットは、fmt.Fprintf
の戻り値のうち、エラーは無視し(または別途処理し)、常に入力メッセージmsg
の長さを返すように修正しています。これにより、Writer.write
メソッドはio.Writer
のセマンティクスに正しく準拠し、呼び出し元に期待される情報を提供します。エラー処理については、syslog
の文脈では、メッセージの送信が失敗した場合でも、write
メソッド自体は成功したかのように振る舞い、エラーは内部的にログに記録されるか、別のメカニズムで処理されることが一般的です。このコミットでは、エラーを完全に無視し、nil
を返すことで、この慣習に従っています。
コアとなるコードの変更箇所
変更はsrc/pkg/log/syslog/syslog.go
ファイルにのみ行われました。
--- a/src/pkg/log/syslog/syslog.go
+++ b/src/pkg/log/syslog/syslog.go
@@ -258,9 +258,10 @@ func (w *Writer) write(p Priority, msg string) (int, error) {
}
timestamp := time.Now().Format(time.RFC3339)
- return fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
+ fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, w.hostname,
w.tag, os.Getpid(), msg, nl)
+ return len(msg), nil
}
// NewLogger creates a log.Logger whose output is written to
コアとなるコードの解説
変更はWriter.write
メソッドの最後の部分に集中しています。
変更前(元に戻されたコード):
return fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, w.hostname,
w.tag, os.Getpid(), msg, nl)
この行は、fmt.Fprintf
の戻り値(書き込まれたバイト数とエラー)をそのままwrite
メソッドの戻り値としていました。前述の通り、fmt.Fprintf
が返すバイト数は、syslog
ヘッダを含むフォーマットされたメッセージ全体の長さであり、write
メソッドに渡された元のmsg
の長さではありませんでした。
変更後(このコミットによる修正):
fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, w.hostname,
w.tag, os.Getpid(), msg, nl)
return len(msg), nil
この修正では、以下の2つの変更が行われています。
fmt.Fprintf
の呼び出しが独立したステートメントになり、その戻り値(書き込まれたバイト数とエラー)は直接返されなくなりました。これにより、Fprintf
が返すエラーは、このメソッドのスコープ内で処理されるか、無視されることになります。このコミットでは、エラーは無視されています。- その代わりに、
return len(msg), nil
という行が追加されました。これは、write
メソッドが常に入力として受け取ったmsg
スライスの長さを返し、エラーは発生しなかった(nil
)と報告することを意味します。
この変更により、Writer.write
メソッドはio.Writer
インターフェースの規約に正しく従うようになり、呼び出し元は、自分が渡したメッセージがsyslog
システムに「書き込まれた」と期待できるようになります。syslog
の文脈では、メッセージの送信がネットワークレベルで失敗したとしても、write
メソッド自体は成功したと報告し、エラーは内部的に処理されるか、別のメカニズムで通知されるのが一般的です。
関連リンク
- このコミットのGitHubページ: https://github.com/golang/go/commit/f6c7b339a6b7592aa83abe253a40825fcb7d3e39
- 元に戻されたCLのコミットハッシュ:
ac7877558dce
- 関連するGo Issue: https://github.com/golang/go/issues/5541 (このコミットで元に戻されたCLが修正しようとしていたIssue)
- 元に戻されたCLのリンク (Go Gerrit): https://golang.org/cl/9658043 (現在はアクセスできない可能性があります)
- このコミットのCLのリンク (Go Gerrit): https://golang.org/cl/9644044 (現在はアクセスできない可能性があります)
参考にした情報源リンク
- コミットメッセージと差分情報 (
./commit_data/16374.txt
から取得) - Go言語の
log/syslog
パッケージのドキュメント (Go標準ライブラリ) fmt.Fprintf
関数のドキュメント (Go標準ライブラリ)io.Writer
インターフェースのドキュメント (Go標準ライブラリ)- Web検索: "golang CL 9658043" (CLの背景情報を確認するため)
- RFC 5424 (The Syslog Protocol) - syslogプロトコルの詳細理解のため (一般的な知識として)
- RFC 3164 (The BSD Syslog Protocol) - syslogプロトコルの詳細理解のため (一般的な知識として)