[インデックス 1917] ファイルの概要
このコミットは、Go言語の標準ライブラリであるlog
パッケージにおける小さなバグ修正です。具体的には、log.Stderr
関数が意図せず標準出力(stdout)にログを出力していた問題を修正し、正しく標準エラー出力(stderr)にログが出力されるように変更されました。これは、ログの出力先に関する基本的な期待値を満たすための重要な修正であり、デバッグやシステム監視においてログが正しいストリームに送られることを保証します。
コミット
commit 79b55e226a72cc995fef58f683d6c8e77d181c87
Author: David Symonds <dsymonds@golang.org>
Date: Mon Mar 30 19:01:59 2009 -0700
log.Stderr should actually go to stderr.
R=r
APPROVED=r
DELTA=1 (0 added, 0 deleted, 1 changed)
OCL=26926
CL=26928
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/79b55e226a72cc995fef58f683d6c8e77d181c87
元コミット内容
log.Stderr should actually go to stderr.
(log.Stderr
は実際にstderrに出力されるべきである。)
変更の背景
Go言語のlog
パッケージは、プログラムの実行中に情報を記録するための基本的な機能を提供します。このパッケージには、標準出力(stdout)や標準エラー出力(stderr)など、異なる出力先にログを送信するためのヘルパー関数が用意されています。
このコミットが行われる前、log
パッケージ内のStderr
関数には論理的な誤りがありました。関数の名前がStderr
であるにもかかわらず、その内部実装ではログメッセージを標準出力(stdout
)に書き込んでいました。これは、開発者がlog.Stderr
を呼び出した際に期待する動作(エラーメッセージがエラー出力ストリームに送られること)と実際の動作が異なるという、潜在的なバグでした。
ログの出力先は、特にUNIX系のシステムにおいて非常に重要です。標準出力は通常のプログラム出力に使用され、標準エラー出力はエラーメッセージや診断情報に使用されるのが慣例です。これにより、シェルスクリプトなどでプログラムの出力をリダイレクトする際に、通常の出力とエラー出力を分離して処理することが可能になります。例えば、command > output.txt 2> error.txt
のようにすることで、標準出力はoutput.txt
に、標準エラー出力はerror.txt
にそれぞれリダイレクトされます。
log.Stderr
が誤ってstdout
に書き込んでいた場合、エラーメッセージが通常の出力に混ざってしまい、エラーの監視やログの解析が困難になる可能性がありました。このコミットは、この不整合を解消し、log.Stderr
がその名前の通りに機能するようにするための修正です。
前提知識の解説
Go言語のlog
パッケージ
Go言語の標準ライブラリには、log
パッケージが含まれています。これは、シンプルなロギング機能を提供し、プログラムの実行中に情報を記録するために使用されます。log
パッケージは、ログメッセージのフォーマット、出力先の指定(ファイル、標準出力、標準エラー出力など)、およびログレベルの制御(ただし、より高度なロギングライブラリほどではない)を可能にします。
標準出力(stdout)と標準エラー出力(stderr)
UNIX系オペレーティングシステムでは、各プロセスはデフォルトで3つの標準I/Oストリームを持っています。
- 標準入力(stdin): プロセスへの入力データが供給されるストリーム。通常はキーボードに接続されています。
- 標準出力(stdout): プロセスからの通常の出力データが書き込まれるストリーム。通常はターミナル画面に接続されています。
- 標準エラー出力(stderr): プロセスからのエラーメッセージや診断情報が書き込まれるストリーム。通常はターミナル画面に接続されていますが、stdoutとは独立してリダイレクトできます。
これらのストリームは、ファイルディスクリプタという数値で識別されます。stdinは0、stdoutは1、stderrは2です。
log.Output
メソッド
log
パッケージの内部では、ログメッセージの実際の書き込みはLogger
型のOutput
メソッドによって行われます。このメソッドは、ログメッセージをフォーマットし、指定された出力先(io.Writer
インターフェースを実装するオブジェクト)に書き込む役割を担います。
log
パッケージには、デフォルトで設定されたロガーがいくつか存在します。
log.Print
,log.Printf
,log.Println
などは、デフォルトのロガー(通常はos.Stderr
に出力)を使用します。log.Stdout
,log.Stderr
は、それぞれ特定の出力先(os.Stdout
またはos.Stderr
)に特化したヘルパー関数です。
このコミットの文脈では、log.Stderr
関数が内部でstdout.Output
を呼び出していたことが問題でした。ここでいうstdout
は、log
パッケージ内で定義されている、os.Stdout
に紐付けられた内部的なロガーインスタンスを指します。同様に、stderr
はos.Stderr
に紐付けられた内部的なロガーインスタンスを指します。
技術的詳細
このコミットは、src/lib/log.go
ファイル内のStderr
関数の実装を修正しています。
修正前のコードは以下のようになっていました。
// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr).
func Stderr(v ...) {
stdout.Output(2, fmt.Sprintln(v))
}
このコードでは、Stderr
関数が呼び出された際に、fmt.Sprintln(v)
でフォーマットされた文字列をstdout.Output
メソッドに渡していました。前述の通り、stdout
はos.Stdout
(標準出力)に紐付けられたロガーインスタンスです。したがって、log.Stderr
を呼び出すと、メッセージは標準エラー出力ではなく、標準出力に書き込まれていました。
修正後のコードは以下の通りです。
// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr).
func Stderr(v ...) {
stderr.Output(2, fmt.Sprintln(v))
}
この変更では、stdout.Output
の呼び出しがstderr.Output
に置き換えられています。stderr
はos.Stderr
(標準エラー出力)に紐付けられたロガーインスタンスであるため、この修正によりlog.Stderr
関数は期待通りにメッセージを標準エラー出力に書き込むようになりました。
Output(2, ...)
の最初の引数2
は、呼び出し元のスタックフレームをスキップする深さを示しています。これは、ログメッセージにファイル名と行番号を含める際に、log.Output
を直接呼び出している場所ではなく、そのさらに上位の呼び出し元(この場合はlog.Stderr
を呼び出したユーザーコード)の情報を表示するために使用されます。この引数は今回の修正とは直接関係ありませんが、log
パッケージの内部動作を理解する上で重要です。
この修正は非常にシンプルですが、ロギングのセマンティクスを正しく保つ上で不可欠なものです。これにより、Goプログラムが生成するエラーログが、システムや他のツールによって適切に処理されることが保証されます。
コアとなるコードの変更箇所
--- a/src/lib/log.go
+++ b/src/lib/log.go
@@ -160,7 +160,7 @@ func Stdout(v ...) {
// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr).
func Stderr(v ...) {
- stdout.Output(2, fmt.Sprintln(v))
+ stderr.Output(2, fmt.Sprintln(v))
}
// Stdoutf is a helper functions for easy formatted logging to stdout. It is analogous to Printf().
コアとなるコードの解説
変更はsrc/lib/log.go
ファイルのStderr
関数内の一行のみです。
- stdout.Output(2, fmt.Sprintln(v))
- この行は修正前のコードで、
log.Stderr
関数がログメッセージをstdout
(標準出力)に書き込む原因となっていました。
- この行は修正前のコードで、
+ stderr.Output(2, fmt.Sprintln(v))
- この行は修正後のコードで、
stdout
をstderr
に置き換えることで、ログメッセージが正しくstderr
(標準エラー出力)に書き込まれるようになりました。
- この行は修正後のコードで、
この変更により、log.Stderr
関数は、その名前が示す通り、エラー関連のメッセージを標準エラー出力ストリームに送信するという、期待される動作をするようになりました。これは、Go言語のロギング機能の正確性と信頼性を向上させるための、根本的かつ重要な修正です。
関連リンク
- Go言語
log
パッケージのドキュメント: https://pkg.go.dev/log - Go言語
os
パッケージのドキュメント (os.Stdout, os.Stderrについて): https://pkg.go.dev/os
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- UNIXの標準I/Oストリームに関する一般的な知識
- Go言語のロギングに関する一般的な情報