[インデックス 16379] ファイルの概要
このコミットは、Go言語の標準ライブラリである log/syslog
パッケージにおける Writer
型の write
メソッドの修正に関するものです。具体的には、syslog
への書き込み処理において、fmt.Fprintf
から返されるエラーを適切に処理するように変更されています。
コミット
commit e17a6d4b9d613918004991ee74ea95a29fc291e7
Author: Rob Pike <r@golang.org>
Date: Wed May 22 12:45:52 2013 -0700
log/syslog: report errors from write
Fixes #5541.
This time for sure.
R=golang-dev, minux.ma, iant
CC=golang-dev
https://golang.org/cl/9668043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e17a6d4b9d613918004991ee74ea95a29fc291e7
元コミット内容
log/syslog: report errors from write
Fixes #5541.
This time for sure.
R=golang-dev, minux.ma, iant
CC=golang-dev
https://golang.org/cl/9668043
変更の背景
このコミットの背景には、log/syslog
パッケージの Writer
が syslog
メッセージを書き込む際に発生する可能性のあるエラーが適切に報告されていなかったという問題があります。コミットメッセージにある Fixes #5541.
は、このコミットがGoのIssue 5541を修正するものであることを示唆しています。また、This time for sure.
という記述から、過去にも同様の問題に対する修正が試みられたが、完全には解決していなかった可能性が伺えます。
io.Writer
インターフェースを実装する型は、書き込み操作が成功したバイト数と、発生したエラーを返すことが期待されます。しかし、以前の実装では、fmt.Fprintf
の戻り値であるエラーが無視されており、syslog
への書き込みが失敗した場合でも、呼び出し元にはそのエラーが伝播しないという問題がありました。これにより、アプリケーションが syslog
へのログ出力の失敗を検知できず、重要なログが失われる可能性がありました。
前提知識の解説
Go言語の log/syslog
パッケージ
log/syslog
パッケージは、GoプログラムからUnix系のシステムで広く利用されているロギングプロトコルである syslog
にメッセージを送信するための機能を提供します。syslog
は、システムイベント、アプリケーションログ、セキュリティ情報などを一元的に管理するための標準的な方法です。
io.Writer
インターフェース
Go言語の io
パッケージには、データの書き込み操作を抽象化するための Writer
インターフェースが定義されています。
type Writer interface {
Write(p []byte) (n int, err error)
}
Write
メソッドは、バイトスライス p
を書き込み、書き込まれたバイト数 n
と、発生したエラー err
を返します。このインターフェースを実装する型は、エラーが発生した場合にはそのエラーを返すことが期待されます。
fmt.Fprintf
関数
fmt
パッケージの Fprintf
関数は、指定された io.Writer
にフォーマットされた文字列を書き込みます。そのシグネチャは以下の通りです。
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
Fprintf
は、書き込まれたバイト数 n
と、書き込み中に発生したエラー err
を返します。
syslog
プロトコル
syslog
プロトコルは、メッセージのフォーマットと転送方法を定義します。メッセージは通常、優先度(ファシリティと重要度)、タイムスタンプ、ホスト名、アプリケーション名、プロセスID、メッセージ本文などの情報を含みます。
技術的詳細
このコミットの技術的な核心は、log/syslog
パッケージの Writer
型が io.Writer
インターフェースを適切に実装し、syslog
への書き込みエラーを正確に報告するようにすることです。
以前の実装では、fmt.Fprintf
の戻り値であるエラーがアンダースコア _
で破棄されていました。
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
このコードでは、fmt.Fprintf
がエラーを返しても、常に nil
エラーが返されていました。これは、io.Writer
の契約に違反し、syslog
への書き込みが失敗した場合でも、呼び出し元がその事実を知ることができないという問題を引き起こしていました。
今回の修正では、fmt.Fprintf
の戻り値であるエラーを err
変数にキャプチャし、その err
が nil
でない場合に、そのエラーを呼び出し元に返すように変更されています。
_, err := fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, w.hostname,
w.tag, os.Getpid(), msg, nl)
if err != nil {
return 0, err
}
// Note: return the length of the input, not the number of
// bytes printed by Fprintf, because this must behave like
// an io.Writer.
return len(msg), nil
ここで重要なのは、io.Writer
の Write
メソッドが返す n
(書き込まれたバイト数) の扱いです。fmt.Fprintf
が返す n
は、フォーマットされた文字列のバイト数であり、元の入力メッセージ msg
の長さとは異なります。しかし、io.Writer
の契約では、Write
メソッドは入力バイトスライスの長さ(または実際に書き込まれたバイト数)を返すことが期待されます。このコミットでは、コメントで明示されているように、fmt.Fprintf
が書き込んだバイト数ではなく、元の入力メッセージ msg
の長さ len(msg)
を返すことで、io.Writer
のセマンティクスに準拠しています。エラーが発生した場合は 0
とエラーを返します。
コアとなるコードの変更箇所
変更は src/pkg/log/syslog/syslog.go
ファイルの Writer
型の write
メソッドに集中しています。
--- a/src/pkg/log/syslog/syslog.go
+++ b/src/pkg/log/syslog/syslog.go
@@ -258,9 +258,15 @@ func (w *Writer) write(p Priority, msg string) (int, error) {
}\n
timestamp := time.Now().Format(time.RFC3339)\n-\tfmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",\n+\t_, err := fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",\n \t\tp, timestamp, w.hostname,\n \t\tw.tag, os.Getpid(), msg, nl)\n+\tif err != nil {\n+\t\treturn 0, err\n+\t}\n+\t// Note: return the length of the input, not the number of\n+\t// bytes printed by Fprintf, because this must behave like\n+\t// an io.Writer.\n \treturn len(msg), nil
}\n
コアとなるコードの解説
変更前は、fmt.Fprintf
の戻り値であるエラーが破棄されていました。
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 // エラーは常にnil
変更後は、fmt.Fprintf
の戻り値であるエラーを err
変数で受け取り、そのエラーをチェックしています。
_, err := fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s",
p, timestamp, w.hostname,
w.tag, os.Getpid(), msg, nl)
if err != nil {
return 0, err // エラーがあれば、0バイトとエラーを返す
}
// Note: return the length of the input, not the number of
// bytes printed by Fprintf, because this must behave like
// an io.Writer.
return len(msg), nil // エラーがなければ、入力メッセージの長さを返す
この変更により、syslog
への書き込み中にネットワークエラーやその他の問題が発生した場合、fmt.Fprintf
が返すエラーが捕捉され、write
メソッドの呼び出し元に適切に伝播されるようになります。これにより、log/syslog
パッケージの利用者は、ログ出力の失敗を検知し、適切なエラーハンドリングを行うことができるようになります。
コメントで示されているように、io.Writer
のセマンティクスに従うために、fmt.Fprintf
が実際に書き込んだバイト数ではなく、元の入力メッセージ msg
の長さ len(msg)
を返す点が重要です。これは、io.Writer
の Write
メソッドが、引数として受け取ったバイトスライスのうち、何バイトを処理したか(または処理しようとしたか)を返すという一般的な期待に合致するためです。
関連リンク
- Go Issue #5541: Web検索では直接的な情報を見つけることができませんでした。Goの公式Issueトラッカーでこの番号のIssueが存在した可能性がありますが、現在はアクセスできないか、別のリポジトリに移動している可能性があります。
- Go Change List 9668043: Web検索では直接的な情報を見つけることができませんでした。Goのコードレビューシステム(Gerrit)の古いCL番号である可能性がありますが、現在はアクセスできないか、情報が公開されていない可能性があります。
参考にした情報源リンク
- Go言語の公式ドキュメント:
log/syslog
パッケージ、io
パッケージ、fmt
パッケージに関する公式ドキュメントは、これらの機能の理解に不可欠です。 syslog
プロトコルに関する一般的な情報源:syslog
の動作原理を理解するために、RFC 5424などの標準ドキュメントや、関連する技術記事が参考になります。- Go言語のコミット履歴: GoのGitHubリポジトリのコミット履歴を辿ることで、関連する変更や議論の文脈を理解することができます。