github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/log/slog/doc.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Package slogは、メッセージ、重大度レベル、およびキー-値ペアとして表されるさまざまなその他の属性を含むログレコードを提供する構造化されたログを提供します。
     7  
     8  [Logger] という型を定義し、
     9  [Logger.Info] や [Logger.Error] などのいくつかのメソッドを提供して、
    10  興味深いイベントを報告するための構造化されたログを提供します。
    11  
    12  各Loggerは [Handler] に関連付けられています。
    13  Loggerの出力メソッドは、メソッド引数から [Record] を作成し、
    14  それを処理する方法を決定するHandlerに渡します。
    15  対応するLoggerメソッドを呼び出す [Info] や [Error] などのトップレベル関数を介してアクセス可能なデフォルトのLoggerがあります。
    16  
    17  ログレコードは、時刻、レベル、メッセージ、およびキー-値ペアのセットで構成されます。
    18  キーは文字列で、値は任意の型である場合があります。
    19  例として、
    20  
    21  	slog.Info("hello", "count", 3)
    22  
    23  呼び出しの時間、Infoレベル、メッセージ"hello"、および単一のキー"count"と値3を持つレコードを作成します。
    24  
    25  [Info] トップレベル関数は、デフォルトのLogger上の [Logger.Info] メソッドを呼び出します。
    26  [Logger.Info] に加えて、Debug、Warn、Errorレベルのメソッドがあります。
    27  これらの一般的なレベルのための便利なメソッドに加えて、
    28  [Logger.Log] メソッドがあり、レベルを引数として受け取ります。
    29  これらのメソッドのそれぞれに対応するトップレベル関数があり、
    30  デフォルトのロガーを使用します。
    31  
    32  デフォルトのハンドラは、ログレコードのメッセージ、時刻、レベル、および属性を
    33  文字列としてフォーマットし、 [log] パッケージに渡します。
    34  
    35  	2022/11/08 15:28:26 INFO hello count=3
    36  
    37  出力フォーマットをより細かく制御するには、別のハンドラを持つロガーを作成します。
    38  このステートメントでは、 [New] を使用して、 [TextHandler] で構造化されたレコードをテキスト形式で標準エラーに書き込む新しいロガーを作成しています。
    39  
    40  	logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
    41  
    42  [TextHandler] の出力は、キー=値のペアのシーケンスであり、機械によって簡単かつ曖昧に解析できます。
    43  この文:
    44  
    45  	logger.Info("hello", "count", 3)
    46  
    47  は、次の出力を生成します。
    48  
    49  	time=2022-11-08T15:28:26.000-05:00 level=INFO msg=hello count=3
    50  
    51  パッケージはまた、行区切りJSONで出力される [JSONHandler] を提供します。
    52  
    53  	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    54  	logger.Info("hello", "count", 3)
    55  
    56  次の出力を生成します:
    57  
    58  	{"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3}
    59  
    60  [TextHandler] と [JSONHandler] の両方は、 [HandlerOptions] で構成できます。
    61  最小レベルの設定(以下の [Levels] を参照)、
    62  ログ呼び出しのソースファイルと行の表示、
    63  およびログに記録される前に属性を変更するためのオプションがあります。
    64  
    65  デフォルトのロガーは以下のようにして変更できます。
    66  
    67  	slog.SetDefault(logger)
    68  
    69  [Info] のようなトップレベルの関数がloggerを使用するようになります。
    70  [SetDefault] は、 [log] パッケージが使用するデフォルトのロガーも更新します。
    71  これにより [log.Printf] などを使用する既存のアプリケーションが、
    72  書き換える必要なくログレコードをロガーのハンドラに送信できます。
    73  
    74  多くのログ呼び出しで共通の属性があります。
    75  たとえば、サーバーリクエストから生じるすべてのログイベントにURLやトレース識別子を含めたい場合があります。
    76  ログ呼び出しごとに属性を繰り返す代わりに、 [Logger.With] を使用して属性を含む新しいLoggerを構築できます。
    77  
    78  	logger2 := logger.With("url", r.URL)
    79  
    80  Withの引数は、 [Logger.Info] で使用されるキー-値ペアと同じです。
    81  結果は、元のハンドラと同じハンドラを持つ新しいLoggerですが、
    82  すべての呼び出しの出力に表示される追加の属性が含まれています。
    83  
    84  # Levels
    85  
    86  [Level] は、ログイベントの重要度または深刻度を表す整数です。
    87  レベルが高いほど、イベントはより深刻です。
    88  このパッケージは、最も一般的なレベルの定数を定義していますが、
    89  任意のintをレベルとして使用できます。
    90  
    91  アプリケーションでは、特定のレベル以上のメッセージのみをログに記録することが望ましい場合があります。
    92  一般的な構成の1つは、Infoレベル以上のメッセージをログに記録し、
    93  デバッグログを必要になるまで抑制することです。
    94  組み込みのハンドラは、 [HandlerOptions.Level] を設定することで、
    95  出力する最小レベルを構成できます。
    96  通常、プログラムの`main`関数がこれを行います。
    97  デフォルト値はLevelInfoです。
    98  
    99  [HandlerOptions.Level] フィールドを [Level] 値に設定すると、
   100  ハンドラの最小レベルがその寿命全体で固定されます。
   101  [LevelVar] に設定すると、レベルを動的に変化させることができます。
   102  LevelVarはLevelを保持し、複数のゴルーチンから読み書きすることができます。
   103  プログラム全体でレベルを動的に変化させるには、まずグローバルなLevelVarを初期化します。
   104  
   105  	var programLevel = new(slog.LevelVar) // Info by default
   106  
   107  次に、LevelVarを使用してハンドラを構築し、デフォルトにします。
   108  
   109  	h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})
   110  	slog.SetDefault(slog.New(h))
   111  
   112  プログラムは、単一のステートメントでログレベルを変更できるようになりました。
   113  
   114  	programLevel.Set(slog.LevelDebug)
   115  
   116  # Groups
   117  
   118  属性はグループに集めることができます。
   119  グループには、属性の名前に修飾子として使用される名前があります。
   120  この修飾子がどのように表示されるかは、ハンドラによって異なります。
   121  [TextHandler] は、グループと属性名をドットで区切ります。
   122  [JSONHandler] は、各グループを別々のJSONオブジェクトとして扱い、グループ名をキーとして扱います。
   123  
   124  [Group] を使用して、名前とキー値のリストからグループ属性を作成します。
   125  
   126  	slog.Group("request",
   127  	    "method", r.Method,
   128  	    "url", r.URL)
   129  
   130  [TextHandler] は、このグループを次のように表示します。
   131  
   132  	request.method=GET request.url=http://example.com
   133  
   134  [JSONHandler] は、次のように表示します。
   135  
   136  	"request":{"method":"GET","url":"http://example.com"}
   137  
   138  [Logger.WithGroup] を使用して、Loggerのすべての出力にグループ名を付けます。
   139  LoggerでWithGroupを呼び出すと、元のハンドラと同じハンドラを持つ新しいLoggerが生成されますが、すべての属性がグループ名で修飾されます。
   140  
   141  これにより、大規模なシステムで重複した属性キーを防止できます。
   142  サブシステムが同じキーを使用する可能性がある場合、異なるグループ名を持つ独自のLoggerを各サブシステムに渡して、潜在的な重複を修飾します。
   143  
   144  	logger := slog.Default().With("id", systemID)
   145  	parserLogger := logger.WithGroup("parser")
   146  	parseInput(input, parserLogger)
   147  
   148  parseInputがparserLoggerでログを記録する場合、そのキーは "parser"で修飾されるため、共通のキー "id"を使用していても、ログ行には異なるキーがあります。
   149  
   150  # Contexts
   151  
   152  一部のハンドラは、呼び出し元で利用可能な [context.Context] から情報を取得することを望む場合があります。
   153  トレースが有効になっている場合、現在のスパンの識別子などの情報が含まれます。
   154  
   155  [Logger.Log] と [Logger.LogAttrs] メソッドは、対応するトップレベル関数と同様に、最初の引数としてコンテキストを取ります。
   156  
   157  Loggerの便利なメソッド(Infoなど)と対応するトップレベル関数は、コンテキストを取りませんが、"Context"で終わる代替メソッドはコンテキストを取ります。例えば、
   158  
   159  	slog.InfoContext(ctx, "message")
   160  
   161  出力メソッドにコンテキストが利用可能な場合は、コンテキストを渡すことをお勧めします。
   162  
   163  # Attrs and Values
   164  
   165  [Attr] は、キーと値のペアです。Loggerの出力メソッドは、Attrsと交互にキーと値を受け入れます。以下の文を参照してください。
   166  
   167  	slog.Info("hello", slog.Int("count", 3))
   168  
   169  以下のように動作します。
   170  
   171  	slog.Info("hello", "count", 3)
   172  
   173  [Attr] には [Int] 、 [String] 、 [Bool] などの便利なコンストラクタがあり、一般的な型に対して、 [Any] 関数を使用して任意の型の [Attr] を構築することもできます。
   174  
   175  [Attr] の値部分は [Value] と呼ばれる型です。
   176  [any] のように、 [Value] は任意のGo値を保持できますが、
   177  すべての数値と文字列を含む一般的な値を、割り当てなしで表現できます。
   178  
   179  最も効率的なログ出力には、 [Logger.LogAttrs] を使用してください。
   180  これは [Logger.Log] に似ていますが、交互にキーと値を受け入れるのではなく、Attrsのみを受け入れるため、これも割り当てを回避できます。
   181  
   182  logger.LogAttrs(ctx, slog.LevelInfo, "hello", slog.Int("count", 3))
   183  
   184  は、以下と同じ出力を生成する最も効率的な方法です。
   185  
   186  slog.Info("hello", "count", 3)
   187  
   188  	slog.InfoContext(ctx, "hello", "count", 3)
   189  
   190  # Customizing a type's logging behavior
   191  
   192  タイプが [LogValuer] インターフェースを実装している場合、その [LogValue] メソッドから返される [Value] がログ出力に使用されます。
   193  これを使用して、タイプの値がログにどのように表示されるかを制御できます。
   194  例えば、パスワードのような秘密情報を伏せたり、構造体のフィールドをグループ化したりすることができます。
   195  詳細については、 [LogValuer] の例を参照してください。
   196  
   197  LogValueメソッドは、 [LogValuer] を実装している [Value] を返すことができます。
   198  [Value.Resolve] メソッドは、これらの場合に無限ループや無制限の再帰を回避するように注意して処理します。
   199  ハンドラの作者やその他の人々は、LogValueを直接呼び出す代わりに、[Value.Resolve] を使用したい場合があります。
   200  
   201  # Wrapping output methods
   202  
   203  ロガー関数は、呼び出し元のコールスタック上でリフレクションを使用して、アプリケーション内のログ呼び出しのファイル名と行番号を検索します。
   204  これは、slogをラップする関数に対して誤ったソース情報を生成する可能性があります。
   205  たとえば、mylog.goファイルでこの関数を定義する場合、以下のようになります。
   206  
   207  	func Infof(logger *slog.Logger, format string, args ...any) {
   208  	    logger.Info(fmt.Sprintf(format, args...))
   209  
   210  
   211  	}
   212  
   213  そして、main.goで次のように呼び出す場合、
   214  
   215  	Infof(slog.Default(), "hello, %s", "world")
   216  
   217  slogは、ソースファイルをmylog.goではなくmain.goとして報告しません。
   218  
   219  Infofの正しい実装は、ソースの場所(pc)を取得し、NewRecordに渡す必要があります。
   220  パッケージレベルの例である "wrapping" で示されているように、Infof関数の実装方法を示します。
   221  
   222  # Working with Records
   223  
   224  ハンドラが別のハンドラやバックエンドに渡す前に、レコードを変更する必要がある場合があります。
   225  レコードには、単純な公開フィールド(例: Time、Level、Message)と、状態(属性など)を間接的に参照する非公開フィールドが混在しています。
   226  これは、レコードの単純なコピーを変更する(例えば、属性を追加するために [Record.Add] または [Record.AddAttrs] を呼び出す)と、元のレコードに予期しない影響を与える可能性があることを意味します。
   227  レコードを変更する前に、 [Record.Clone] を使用して、元のレコードと状態を共有しないコピーを作成するか、 [NewRecord] で新しいレコードを作成し、 [Record.Attrs] を使用して古いレコードをトラバースしてそのAttrsを構築してください。
   228  
   229  # Performance considerations
   230  
   231  アプリケーションのプロファイリングによって、ログの記録にかかる時間がかなりあることが示された場合、以下の提案が役立つ場合があります。
   232  
   233  多くのログ行に共通の属性がある場合は、 [Logger.With] を使用して、その属性を持つLoggerを作成します。
   234  組み込みのハンドラは、 [Logger.With] の呼び出し時にその属性を1回だけフォーマットします。
   235  [Handler] インターフェースは、その最適化を許容するように設計されており、適切に書かれたHandlerはそれを活用するはずです。
   236  
   237  ログ呼び出しの引数は常に評価されます。たとえログイベントが破棄された場合でもです。
   238  可能であれば、値が実際にログに記録される場合にのみ計算が行われるように遅延させてください。
   239  たとえば、次の呼び出しを考えてみてください。
   240  
   241  	slog.Info("starting request", "url", r.URL.String())  // may compute String unnecessarily
   242  
   243  URL.Stringメソッドは、ロガーがInfoレベルのイベントを破棄する場合でも呼び出されます。
   244  代わりに、URLを直接渡してください。
   245  
   246  	slog.Info("starting request", "url", &r.URL) // calls URL.String only if needed
   247  
   248  組み込みの [TextHandler] は、そのStringメソッドを呼び出しますが、
   249  ログイベントが有効になっている場合にのみ呼び出します。
   250  Stringの呼び出しを回避することは、基礎となる値の構造を保持することもできます。
   251  例えば、 [JSONHandler] は解析されたURLのコンポーネントをJSONオブジェクトとして出力します。
   252  String呼び出しのコストを支払うことを避けたい場合、
   253  値の構造を検査する可能性のあるハンドラを引き起こすことなく、
   254  その値を隠すfmt.Stringer実装でラップしてください。
   255  
   256  [LogValuer] インターフェースを使用すると、無効なログ呼び出しで不必要な作業を回避できます。
   257  例えば、高価な値をログに記録する必要がある場合を考えてみましょう。
   258  
   259  	slog.Debug("frobbing", "value", computeExpensiveValue(arg))
   260  
   261  この行が無効になっていても、computeExpensiveValueが呼び出されます。
   262  これを回避するには、LogValuerを実装する型を定義します。
   263  
   264  	type expensive struct { arg int }
   265  
   266  	func (e expensive) LogValue() slog.Value {
   267  		return slog.AnyValue(computeExpensiveValue(e.arg))
   268  	}
   269  
   270  そして、ログ呼び出しでその型の値を使用します。
   271  
   272  	slog.Debug("frobbing", "value", expensive{arg})
   273  
   274  これで、行が有効になっている場合にのみcomputeExpensiveValueが呼び出されます。
   275  
   276  組み込みのハンドラは、[io.Writer.Write] を呼び出す前にロックを取得して、
   277  一度に正確に一つの [Record] が完全に書き込まれることを保証します。
   278  各ログレコードにはタイムスタンプがありますが、
   279  組み込みのハンドラはその時間を使用して書き込まれたレコードをソートしません。
   280  ユーザー定義のハンドラは、自身のロックとソートを担当します。
   281  
   282  # ハンドラの作成
   283  
   284  カスタムハンドラの作成方法についてのガイドについては、https://golang.org/s/slog-handler-guide を参照してください。
   285  */
   286  package slog