Goのslogパッケージを体験する
Min-jun Kim
Dev Intern · Leapcell

Preface
Goバージョン1.21.0で、構造化ロギング機能を提供する新しいパッケージlog/slog
が導入されました。従来のロギングと比較して、構造化ロギングは可読性が高く、処理、分析、検索において大きな利点があるため、より一般的です。
The slog Package
slogパッケージは構造化ログを提供し、各ログエントリにはメッセージ、重要度レベル、およびその他のさまざまな属性がキーと値のペアとして表示されます。
slogパッケージの主な機能は次のとおりです。
- 構造化ロギング
- ログ重要度レベル
- カスタムログハンドラー
- ログのグルーピング
First Experience
package main import ( "context" "log/slog" ) func main() { slog.Info("slog msg", "greeting", "hello slog") // Carrying context slog.InfoContext(context.Background(), "slog msg with context", "greeting", "hello slog") }
上記の例では、パッケージ関数slog.Info
を呼び出すことによって、infoレベルのログを直接出力しています。内部的には、この関数はデフォルトのLogger
インスタンスを使用してロギング操作を実行します。さらに、slog.InfoContext
を使用して、関連するコンテキストを持つログを出力できます。
Info()
とInfoContext()
以外にも、さまざまなレベルでログを記録するためのDebug()
、Warn()
、Error()
のような関数もあります。
上記のプログラムを実行すると、次の出力が生成されます。
2025/06/18 21:08:08 INFO slog msg greeting="hello slog"
2025/06/18 21:08:08 INFO slog msg with context greeting="hello slog"
Creating a Logger
デフォルトでは、slogパッケージ関数を使用してログを出力する場合、形式はplain textです。JSONまたはkey=value形式で出力する場合は、slog.New()
を使用してLoggerインスタンスを作成する必要があります。この関数を使用する場合、slog.Handler
の実装を渡す必要がありあります。slogパッケージには、TextHandler
とJsonHandler
の2つの実装があります。
TextHandler
TextHandlerは、ログレコードをio.Writer
に一連のキーと値のペアとして書き込むログハンドラーです。各キーと値のペアは、key=valueの形式で表され、スペースで区切られます。
package main import ( "context" "log/slog" "os" ) func main() { textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil)) textLogger.InfoContext(context.Background(), "TextHandler", "Name", "Leapcell") }
上記の例では、slog.NewTextHandler
を使用してログハンドラーを作成しています。最初のパラメータであるos.Stdout
は、ログがコンソールに出力されることを示します。次に、ハンドラーをパラメーターとしてslog.New
に渡し、Loggerインスタンスを作成し、ロギング操作に使用します。
プログラムの出力は次のとおりです。
time=2025-06-18T21:09:03.912+00:00 level=INFO msg=TextHandler Name=Leapcell
JsonHandler
JsonHandlerは、ログレコードをJSON形式でio.Writer
に書き込むログハンドラーです。
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) jsonLogger.InfoContext(context.Background(), "JsonHandler", "name", "Leapcell") }
上記の例では、slog.NewJsonHandler
を使用してJSONログハンドラーを作成しています。最初のパラメータであるos.Stdout
は、コンソールへ出力することを示します。次に、ハンドラーをslog.New
に渡してLoggerインスタンスを作成し、ロギング操作に使用します。
プログラムの出力は次のとおりです。
{ "time": "2025-06-18T21:09:22.614686104+00:00", "level": "INFO", "msg": "JsonHandler", "name": "Leapcell" }
Global Logger Instance
slogにはデフォルトのLoggerインスタンスがあります。デフォルトのLoggerを取得する場合は、次のコードを参照してください。
logger := slog.Default()
これまでの例では、常に特定のLoggerインスタンスを使用してログを出力していました。ただし、毎回特定のLoggerインスタンスを介してログを記録するのではなく、グローバルに操作したい場合は、slog.SetDefault
関数を使用して、デフォルトのLoggerインスタンスを設定および置き換えることができます。これにより、ロギングがより便利で柔軟になります。
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) slog.SetDefault(jsonLogger) slog.InfoContext(context.Background(), "JsonHandler", "name", "Leapcell") //{"time":"2025-06-18T21:11:22.41760604+00:00","level":"INFO","msg":"JsonHandler","name":"Leapcell"} }
Grouping
グルーピングとは、ログレコード内の関連する属性(キーと値のペア)をグループ化することを指します。次に例を示します。
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)).WithGroup("information") jsonLogger.InfoContext(context.Background(), "json-log", slog.String("name", "Leapcell"), slog.Int("phone", 1234567890)) textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil)).WithGroup("information") textLogger.InfoContext(context.Background(), "json-log", slog.String("name", "Leapcell"), slog.Int("phone", 1234567890)) }
このプログラムを実行した結果は次のとおりです。
{"time":"2025-06-18T21:12:23.124255258+00:00","level":"INFO","msg":"json-log","information":{"name":"Leapcell","phone":1234567890}}
time=2025-06-18T21:12:23.127+00:00 level=INFO msg=json-log information.name=Leapcell information.phone=1234567890
出力によると、LoggerインスタンスをJsonHandler
でグループ化すると、グループ名がキーになり、値はすべてのキーと値のペアで構成されるJSONオブジェクトになります。
TextHandler
でLoggerをグループ化すると、グループ名がすべてのキーと値のペアのキーと組み合わされ、最終的にgroupName.key=value
として表示されます。
Efficient Logging with LogAttrs
頻繁にログを記録する必要がある場合は、以前の例と比較すると、slog.LogAttrs
関数をslog.Attr
型と一緒に使用する方が効率的です。これは、型解析のプロセスが削減されるためです。
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) jsonLogger.LogAttrs(context.Background(), slog.LevelInfo, "Efficient log output", slog.String("Name", "Leapcell"), slog.Int("Contact", 12345678901)) }
上記の例では、LogAttrs
メソッドを使用してログエントリを出力します。このメソッドのシグネチャは次のとおりです。
func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr)
シグネチャに基づいて、最初のパラメータはcontext.Context
、2番目のパラメータはLevel
(slogパッケージで定義されたログ重要度レベル)、3番目のパラメータはAttr
キーと値のペアです。
Info
のような他のメソッドを使用してログを出力する場合、キーと値のペアは内部的にAttr
型に変換されます。LogAttrs
メソッドを使用すると、Attr
型を直接指定できるため、変換プロセスが削減され、ロギングがより効率的になります。
With: Setting Common Attributes
すべてのログに同じキーと値のペアを含める必要がある場合は、共通の属性を設定することを検討できます。
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) logger := jsonLogger.With("systemID", "s1") logger.LogAttrs(context.Background(), slog.LevelInfo, "json-log", slog.String("k1", "v1")) logger.LogAttrs(context.Background(), slog.LevelInfo, "json-log", slog.String("k2", "v2")) }
With
メソッドを使用して、1つまたは複数の固定属性を追加し、新しいLoggerインスタンスを返すことができます。この新しいインスタンスによって出力されるログには、追加された固定属性が含まれるため、すべてのログステートメントに同じキーと値のペアを追加する必要がなくなります。
このプログラムの出力は次のとおりです。
{"time":"2025-06-18T21:19:51.338328238+00:00","level":"INFO","msg":"json-log","systemID":"s1","k1":"v1"} {"time":"2025-06-18T21:19:51.338604943+00:00","level":"INFO","msg":"json-log","systemID":"s1","k2":"v2"}
HandlerOptions: Configuration Options for Log Handlers
注意深い読者は、以前の例で、NewJSONHandler
またはNewTextHandler
を使用しているかどうかにかかわらず、2番目のパラメータがnil
に設定されていたことに気付いたかもしれません。これは、デフォルト構成が使用されることを意味します。
このパラメータは*HandlerOptions
型です。これを使用すると、ログステートメントのソースコードの場所を表示するかどうか、最小ログ出力レベル、およびキーと値のペア属性を書き換える方法を構成できます。
package main import ( "context" "log/slog" "os" "time" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelError, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { if t, ok := a.Value.Any().(time.Time); ok { a.Value = slog.StringValue(t.Format(time.DateTime)) } } return a }, })) jsonLogger.InfoContext(context.Background(), "json-log", slog.String("name", "Leapcell")) jsonLogger.ErrorContext(context.Background(), "json-log", slog.String("name", "Leapcell")) }
この例では、JsonHandler
でLoggerインスタンスを作成します。JsonHandler
を作成するときに、HandlerOptions
パラメータを使用して次の構成が指定されています。
- ログステートメントのソースコード(ソース情報)を出力します
- 最小ログレベルをErrorに設定します
- キー
"time"
を持つ属性の形式を"2006-01-02 15:04:05"
に書き換えます
このプログラムの出力は次のとおりです。
{ "time": "2025-06-18 21:21:31", "level": "ERROR", "source": { "function": "main.main", "file": "D:/goproject/src/gocode/play/main.go", "line": 24 }, "msg": "json-log", "name": "Leapcell" }
出力は期待どおりです。INFOレベルのログは出力されず、ソース情報が含まれ、"time"
キーの値が書き換えられています。
Customizing the Value in Key-Value Pairs
以前の例では、HandlerOptions
構成を使用してキーと値のペアの値を変更しました。この方法に加えて、slogパッケージは値を変更する別の方法もサポートしています。
package main import ( "context" "log/slog" ) type Password string func (Password) LogValue() slog.Value { return slog.StringValue("REDACTED_PASSWORD") } func main() { slog.LogAttrs(context.Background(), slog.LevelInfo, "Sensitive Data", slog.Any("password", Password("1234567890"))) }
上記の例では、slog.LogValuer
インターフェース(LogValue() slog.Value
メソッドを型に追加することにより)を実装しています。これにより、キーと値のペアの値を上書きできます。ログを記録すると、値はLogValue
メソッドの戻り値に置き換えられます。
このプログラムの出力は次のとおりです。
2025/06/18 21:37:11 INFO Sensitive Data password=REDACTED_PASSWORD
予想どおり、password
の値が変更されました。
Summary
この記事では、Goのslogパッケージについて、基本的な使い方、Loggerインスタンスの作成、効率的なロギング、ログ情報のカスタマイズなど、詳しく紹介しました。
この記事を読むことで、slogパッケージについてより深く理解し、より効果的にそれを使用してログを管理および記録できるようになるはずです。
We are Leapcell, your top choice for hosting Go projects.
Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:
Multi-Language Support
- Develop with Node.js, Python, Go, or Rust.
Deploy unlimited projects for free
- pay only for usage — no requests, no charges.
Unbeatable Cost Efficiency
- Pay-as-you-go with no idle charges.
- Example: $25 supports 6.94M requests at a 60ms average response time.
Streamlined Developer Experience
- Intuitive UI for effortless setup.
- Fully automated CI/CD pipelines and GitOps integration.
- Real-time metrics and logging for actionable insights.
Effortless Scalability and High Performance
- Auto-scaling to handle high concurrency with ease.
- Zero operational overhead — just focus on building.
Explore more in the Documentation!
Follow us on X: @LeapcellHQ