log/slog - Golang learning step 6-1
- 公開日
- カテゴリ:Logging
- タグ:Golang,roadmap.sh,学習メモ

roadmap.sh > Go > Logging > log/slog の学習を進めていきます。
※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。
contents
開発環境
- チップ: Apple M2 Pro
- OS: macOS Sonoma
- go version: go1.23.2 darwin/arm64
参考 URL
- [official] Documentation: log
- [official] Documentation: slog
- [official] Go Blog: Structured Logging with slog
- [article] Go by Example: Logging
- [feed] Explore top posts about Logging
log パッケージ
log パッケージは、Go の標準ライブラリに含まれるシンプルなログ出力機能を提供するパッケージです。プログラムの動作確認やエラーの記録に便利です。
主な特徴
- シンプルなログ出力
- 標準出力や標準エラー出力にログメッセージを出力する
- デフォルトで標準エラー出力に出力
- 使い方がシンプル
- 初心者でもすぐに利用できる
- 日時が自動的に付与される
- 高度な機能はなし
- 基本的なログ出力のみで、構造化ログや高度なカスタマイズはできない
- ログレベル(INFO、ERRORなど)の概念がない
主な関数
log.Println
: シンプルにログを出力します。log.Printf
: 書式付きのログを出力します。log.Fatal
: メッセージを出力した後にプログラムを終了します。log.Panic
: メッセージを出力した後にパニックを発生させます。
基本的なログ出力
package main
import "log"
func main() {
// 基本的なログ出力
log.Println("アプリケーションを開始します") // 2024/12/09 10:30:45 アプリケーションを開始します
// フォーマット付きのログ出力
count := 3
log.Printf("現在の数値: %d", count) // 2024/12/09 10:30:45 現在の数値: 3
}
出力:
2024/12/09 22:42:23 アプリケーションを開始します
2024/12/09 22:42:23 現在の数値: 3
プレフィックスの設定
ログメッセージの前に固定の文字列を追加できます
package main
import "log"
func main() {
// プレフィックスの設定
log.SetPrefix("【INFO】")
log.Println("システムチェックを開始") // 【INFO】2024/12/09 10:30:45 システムチェックを開始
// プレフィックスの変更
log.SetPrefix("【ERROR】")
log.Println("エラーが発生しました") // 【ERROR】2024/12/09 10:30:45 エラーが発生しました
}
出力:
【INFO】2024/12/09 22:44:06 システムチェックを開始
【ERROR】2024/12/09 22:44:06 エラーが発生しました
出力先の変更
ログの出力先をファイルに変更できます
package main
import (
"log"
"os"
)
func main() {
// ログファイルを作成
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal("ログファイルを開けません:", err)
}
defer file.Close()
// 出力先をファイルに設定
log.SetOutput(file)
log.Println("このメッセージはファイルに書き込まれます")
}
出力: app.log
2024/12/09 22:46:28 このメッセージはファイルに書き込まれます
フラグによる出力形式の制御
ログの出力形式をカスタマイズできます
package main
import (
"log"
)
func main() {
// 日時のみを表示
log.SetFlags(log.Ldate)
log.Println("日付のみ") // 2024/12/09 メッセージ
// 時刻のみを表示
log.SetFlags(log.Ltime)
log.Println("時刻のみ") // 10:30:45 メッセージ
// 日時とファイル名、行番号を表示
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("詳細情報付き") // 2024/12/09 10:30:45 main.go:15: メッセージ
}
出力:
2024/12/09 日付のみ
22:48:20 時刻のみ
2024/12/09 22:48:20 main.go:57: 詳細情報付き
カスタムロガーの作成
独自のロガーインスタンスを作成できます
package main
import (
"log"
"os"
)
func main() {
// 情報ログ用のロガー
infoLog := log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime)
// エラーログ用のロガー
errorLog := log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
infoLog.Println("これは情報ログです")
// INFO: 2024/12/09 10:30:45 これは情報ログです
errorLog.Println("これはエラーログです")
// ERROR: 2024/12/09 10:30:45 main.go:14: これはエラーログです
}
出力:
INFO: 2024/12/09 22:49:34 これは情報ログです
ERROR: 2024/12/09 22:49:34 main.go:70: これはエラーログです
Fatal と Panic の使用
プログラムを停止させる重大なエラーの場合に使用します
package main
import "log"
func main() {
// Fatal - ログを出力して os.Exit(1) を呼び出す
if somethingBadHappens() {
log.Fatal("致命的なエラーが発生しました")
}
// Panic - ログを出力してパニックを発生させる
if unexpectedCondition() {
log.Panic("予期せぬエラーが発生しました")
}
}
func somethingBadHappens() bool {
return true
}
func unexpectedCondition() bool {
return false
}
出力:
# Fatal
2024/12/09 22:51:01 致命的なエラーが発生しました
exit status 1
# Panic
2024/12/09 22:51:26 予期せぬエラーが発生しました
panic: 予期せぬエラーが発生しました
goroutine 1 [running]:
.
.
.
実践的な使用例
エラーハンドリングとログ出力を組み合わせた例
package main
import (
"log"
"os"
)
func main() {
// ログの設定
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
// ファイル操作の例
file, err := os.Open("存在しないファイル.txt")
if err != nil {
log.Printf("ファイルオープンエラー: %v", err)
return
}
defer file.Close()
// 処理続行...
log.Println("ファイルの処理を開始します")
}
出力:
2024/12/09 22:54:36 main.go:99: ファイルオープンエラー: open 存在しないファイル.txt: no such file or directory
log/slog パッケージ
slog パッケージは、Go 1.21 から標準ライブラリに追加された、構造化ログをサポートするパッケージです。従来の log よりも機能が豊富で、クラウドや分散システム向けに最適化されています。
主な特徴
- 構造化ログのサポート
- メッセージだけでなく、キーと値のペア形式でログ情報を記録します。
- 柔軟なカスタマイズ
- ログのフォーマット(JSON形式など)や出力先を自由に設定できます。
- パフォーマンス最適化
- 効率的にログを記録し、高負荷のアプリケーションにも対応。
- ロガーの階層構造
- コンテキストごとに異なるロガーを設定可能です。
- ログレベル
- ログレベル(Debug、Info、Warn、Error)をサポート
主な構成要素
Logger
: ログを記録する中心的なオブジェクト。Handler
: ログの出力形式や出力先を制御する部分。
基本的なログ出力
package main
import "log/slog"
func main() {
// 基本的なログ出力
slog.Info("アプリケーションを開始します")
// 属性(キーと値のペア)を持つログ出力
slog.Info("ユーザーがログイン",
"username", "taro",
"ip", "192.168.1.1",
"loginTime", time.Now(),
)
// 異なるログレベルの使用
slog.Debug("デバッグ情報です")
slog.Info("通常の情報です")
slog.Warn("警告です")
slog.Error("エラーが発生しました")
}
出力:
2024/12/09 22:57:57 INFO アプリケーションを開始します
2024/12/09 22:57:57 INFO ユーザーがログイン username=taro ip=192.168.1.1 loginTime=2024-12-09T22:57:57.388+09:00
2024/12/09 22:57:57 INFO 通常の情報です
2024/12/09 22:57:57 WARN 警告です
2024/12/09 22:57:57 ERROR エラーが発生しました
ロガーの設定とハンドラーの使用
異なる出力形式を設定できます
package main
import (
"log/slog"
"os"
)
func main() {
// JSONハンドラーの設定
jsonHandler := slog.NewJSONHandler(os.Stdout, nil)
logger := slog.New(jsonHandler)
slog.SetDefault(logger)
// JSON形式でログが出力されます
slog.Info("サーバー起動",
"port", 8080,
"env", "production",
)
// テキストハンドラーの設定
textHandler := slog.NewTextHandler(os.Stdout, nil)
logger = slog.New(textHandler)
slog.SetDefault(logger)
// テキスト形式でログが出力されます
slog.Info("サーバー起動",
"port", 8080,
"env", "production",
)
}
出力:
{"time":"2024-12-09T22:58:37.947658+09:00","level":"INFO","msg":"サーバー起動","port":8080,"env":"production"}
time=2024-12-09T22:58:37.947+09:00 level=INFO msg=サーバー起動 port=8080 env=production
カスタムハンドラーのオプション設定
package main
import (
"log/slog"
"os"
)
func main() {
opts := &slog.HandlerOptions{
Level: slog.LevelDebug, // ログレベルの設定
AddSource: true, // ソースコードの位置情報を追加
}
handler := slog.NewJSONHandler(os.Stdout, opts)
logger := slog.New(handler)
slog.SetDefault(logger)
slog.Debug("デバッグ情報が表示されます")
slog.Info("ファイルの処理を開始", "filename", "test.txt")
}
出力:
{"time":"2024-12-09T22:59:54.517853+09:00","level":"DEBUG","source":{"function":"main.main0203","file":"/path/to/main.go","line":163},"msg":"デバッグ情報が表示されます"}
{"time":"2024-12-09T22:59:54.518227+09:00","level":"INFO","source":{"function":"main.main0203","file":"/path/to/main.go","line":164},"msg":"ファイルの処理を開始","filename":"test.txt"}
構造化された属性グループの使用
package main
import "log/slog"
func main() {
// 属性グループを使用したログ出力
slog.Info("データベース接続",
slog.Group("database",
slog.String("host", "localhost"),
slog.Int("port", 5432),
slog.String("user", "admin"),
),
slog.Group("settings",
slog.Bool("ssl", true),
slog.Int("timeout", 30),
),
)
}
出力:
2024/12/09 23:01:21 INFO データベース接続 database.host=localhost database.port=5432 database.user=admin settings.ssl=true settings.timeout=30
エラーハンドリングとの統合
package main
import (
"errors"
"log/slog"
)
func main() {
err := doSomething()
if err != nil {
slog.Error("操作に失敗しました",
"error", err,
"operation", "doSomething",
"retry", false,
)
}
}
func doSomething() error {
return errors.New("何か問題が発生しました")
}
出力:
2024/12/09 23:02:31 ERROR 操作に失敗しました error=何か問題が発生しました operation=doSomething retry=false
ロガーのコンテキスト付き使用
package main
import (
"log/slog"
"context"
)
func main() {
// コンテキストを持つロガーの作成
ctx := context.Background()
logger := slog.With(
"service", "user-api",
"version", "1.0.0",
)
// このロガーを使用する全てのログに上記の属性が付加されます
logger.InfoContext(ctx, "リクエストを受信",
"method", "GET",
"path", "/users",
)
}
出力:
2024/12/09 23:03:46 INFO リクエストを受信 service=user-api version=1.0.0 method=GET path=/users
レベル別ロギングの実践的な例
package main
import (
"log/slog"
"os"
)
func main() {
// 開発環境用の設定
opts := &slog.HandlerOptions{
Level: slog.LevelDebug,
}
if os.Getenv("ENV") == "production" {
// 本番環境ではInfoレベル以上のみ出力
opts.Level = slog.LevelInfo
}
handler := slog.NewJSONHandler(os.Stdout, opts)
logger := slog.New(handler)
slog.SetDefault(logger)
// 異なるレベルでのログ出力
slog.Debug("詳細なデバッグ情報") // 開発環境でのみ表示
slog.Info("通常の処理情報") // 常に表示
slog.Warn("警告メッセージ") // 常に表示
slog.Error("エラー情報") // 常に表示
}
出力:
{"time":"2024-12-09T23:04:45.567226+09:00","level":"DEBUG","msg":"詳細なデバッグ情報"}
{"time":"2024-12-09T23:04:45.567471+09:00","level":"INFO","msg":"通常の処理情報"}
{"time":"2024-12-09T23:04:45.567476+09:00","level":"WARN","msg":"警告メッセージ"}
{"time":"2024-12-09T23:04:45.567479+09:00","level":"ERROR","msg":"エラー情報"}
ファイル出力との組み合わせ
package main
import (
"log/slog"
"os"
)
func main() {
// ログファイルを開く
file, err := os.OpenFile(
"app.log",
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
0666,
)
if err != nil {
slog.Error("ログファイルを開けません", "error", err)
return
}
defer file.Close()
// ファイルへのJSON形式での出力を設定
handler := slog.NewJSONHandler(file, nil)
logger := slog.New(handler)
slog.SetDefault(logger)
// アプリケーションのログ出力
slog.Info("アプリケーション開始",
"env", os.Getenv("ENV"),
"pid", os.Getpid(),
)
}
log と log/slog の違い
特徴 | log | log/slog |
---|---|---|
提供時期 | 初期(Go 1.0から) | Go 1.21から |
ログ形式 | テキスト形式のみ | 構造化ログ(JSON形式など対応) |
カスタマイズ性 | 限定的 | 柔軟で高度なカスタマイズが可能 |
構造化ログ | 非対応 | 対応 |
推奨用途 | 小規模なアプリケーションやデバッグ | 大規模アプリケーションやクラウド環境 |
パフォーマンス | 基本的 | 最適化済み |
ログレベル | なし | Debug/Info/Warn/Error |
エラーハンドリング | 基本的 | 構造化されたエラー情報 |
slog パッケージは、モダンなアプリケーションやクラウド環境での利用を目的としており、複雑な要件に適しています。小規模なプロジェクトでは log で十分な場合もありますが、構造化ログが必要な場面では slog を検討する価値があります。
まとめ
log
パッケージとlog/slog
パッケージは、それぞれ異なる用途と特徴を持つGoの標準ログパッケージlog
パッケージは、シンプルで基本的なログ出力機能に特化log/slog
パッケージは、構造化ログと高度なカスタマイズ機能を提供log
パッケージの主な特徴- シンプルな使用方法
- 基本的なログ出力
- プレフィックスやフラグによる簡易的なカスタマイズ機能
- 小規模アプリケーションに最適
log/slog
パッケージの主な特徴- 構造化ログのサポート
- JSON形式での出力対応
- 複数のログレベル(Debug/Info/Warn/Error)
- コンテキストとの統合
- 大規模アプリケーションやクラウド環境に最適
- 使い分けのポイント
- シンプルな用途は
log
パッケージ - 高度な要件や大規模システムは
log/slog
パッケージ - 構造化ログが必要な場合は
log/slog
パッケージ
- シンプルな用途は
[Next] Step 6-2: Zap
[Prev] Step 5-4: Gorilla