Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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に紐付けられた内部的なロガーインスタンスを指します。同様に、stderros.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メソッドに渡していました。前述の通り、stdoutos.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に置き換えられています。stderros.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))
    • この行は修正後のコードで、stdoutstderrに置き換えることで、ログメッセージが正しくstderr(標準エラー出力)に書き込まれるようになりました。

この変更により、log.Stderr関数は、その名前が示す通り、エラー関連のメッセージを標準エラー出力ストリームに送信するという、期待される動作をするようになりました。これは、Go言語のロギング機能の正確性と信頼性を向上させるための、根本的かつ重要な修正です。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (GitHub): https://github.com/golang/go
  • UNIXの標準I/Oストリームに関する一般的な知識
  • Go言語のロギングに関する一般的な情報