はじめに
「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」9日目の記事は、Goのロギングの基礎についてです。
開発を始めたばかりの頃は、なにかとfmt.Printlnだけでデバッグを済ませがちです。
しかし、開発を進めていくと特に本番環境で問題が発生した際、fmt.Printlnだけではエラーの原因や処理の内容が追跡できなくなります。
この記事では、Go言語におけるロギングの基本的な考え方と実践方法について、解説します。
- これまで
fmt.Printlnやconsole.logだけでデバッグを済ませてきた方 - バックエンド開発におけるロギングのベストプラクティスの概要を知りたい方
- 本番環境でのアプリケーションの挙動をより深く理解したいと考えている方
Goの標準logパッケージや外部ライブラリを使って、APIの動作状況を適切に記録し、問題発生時の原因究明に役立つログを出力できるように、頑張りましょう!
ロギングの役割
アプリケーション開発において、ロギングは以下のような目的で活用されます。
開発時のデバッグ
開発中にプログラムが意図しない動作をした際、ログは問題の原因を特定するための手がかりとなります。
変数の値、関数の呼び出し順序、エラーメッセージなどをログに出力することで、コードのどこに問題があるのかを効率的に見つけ出すことができます。
本番運用時の監視
ログは、アプリケーションが本番環境で稼働している際、アプリケーションが正常に動作しているか、予期せぬエラーが発生していないか、パフォーマンスに問題がないかなどを把握するために役立ちます。
主に以下のような用途でログが役に立つことがあります。
- エラーの検知: 致命的なエラーが発生した場合、ログを通じて即座に検知し、対応することができます
- パフォーマンスの分析: 特定の処理にかかった時間などをログに記録することで、ボトルネックを特定し、改善に繋げることができます
- ユーザー行動の追跡: ユーザーがどのような操作を行ったかをログに記録することで、問題発生時の再現や、機能改善のヒントを得ることができます
そして本番環境では、大量のログが出力されるため、単にテキストを羅列するだけでなく、後から検索・分析しやすい形式でログを出力することが求められます。
Go標準ライブラリ log の使い方
Goには、標準でlogパッケージが用意されており、基本的なロギング機能を提供しています。
logパッケージを使用すると、タイムスタンプやファイル名、行番号などの情報を自動で付与してくれるので、難しいことを考えずに簡単に利用できます。
go mod initした状態でmain.goに以下のコードを記述してみます。
package main
import (
"log"
)
func main() {
log.Println("これは情報メッセージです。")
log.Printf("数値: %d, 文字列: %s", 123, "hello")
}
実行すると、ターミナルに以下のような出力が見られるかと思います。
2025/12/24 10:00:00 これは情報メッセージです。
2025/12/24 10:00:00 数値: 123, 文字列: hello
このように、log.Printlnやlog.Printfは、デフォルトで標準エラー出力にログを出力します。
ログレベル(Debug, Info, Error)の使い分け
ロギングにおいて、ログレベルを適切に使い分けると、さらに見やすいログを書くことができます。
ログレベルとは、出力するメッセージの重要度や緊急度を示す指標です。
- DEBUG: 開発時や問題発生時の詳細な情報、変数の値など
- INFO: アプリケーションの正常な動作を示す一般的な情報、ユーザーのログインなど
- WARN: 潜在的な問題や、将来的にエラーに繋がる可能性のある状況
- ERROR: アプリケーションの特定の機能が正常に動作しない問題が発生した場合
- FATAL: アプリケーションがこれ以上動作を継続できない重大なエラー
これらのログレベルを適切に使い分けることで、開発環境使用時は必要な情報に素早くアクセスできますし、本番環境使用時は本番環境での異常を効率的に検知・対応できるようになります。
【実践】zap を使用したログレベルごとのロギング
zapを使うと簡単にログレベルを実装できます。
まずは、zapをインストールしましょう。
go get go.uber.org/zap
ここでは、zap.NewDevelopment()で初期化したロガーを使って、各ログレベルのメッセージを出力する例を見てみましょう。
package main
import (
"fmt"
"log"
"go.uber.org/zap"
)
func main() {
// 開発用ロガー (人間が読みやすい形式) を初期化
logger, err := zap.NewDevelopment()
if err != nil {
log.Fatalf("zapを初期化できませんでした: %v", err)
}
defer logger.Sync() // バッファされたログをフラッシュ
// DEBUGレベル: 開発時のみ必要な詳細情報
// 例: リクエストの受信、変数の値、内部処理のステップなど
logger.Debug("DEBUGレベルのログです")
// INFOレベル: アプリケーションの正常な動作を示す一般的な情報
// 例: ユーザーのログイン、重要な処理の開始・終了など
logger.Info("INFOレベルのログです")
// WARNレベル: 潜在的な問題や、将来的にエラーに繋がる可能性のある状況
// 例: 設定ファイルが見つからないがデフォルト値で続行、非推奨APIの使用など
logger.Warn("WARNレベルのログです")
// ERRORレベル: アプリケーションの特定の機能が正常に動作しない、または予期せぬ問題が発生した場合
// 例: データベースへの書き込み失敗、外部API連携エラーなど
logger.Error("ERRORレベルのログです")
// FATALレベル: アプリケーションがこれ以上動作を継続できない重大なエラー
// 例: データベース接続失敗、必須設定の欠如など (log.Fatalと同様にプログラムを終了)
logger.Fatal("FATALレベルのログです")
fmt.Println("ログ出力の例が完了しました")
}
上記のコードを実行すると、実行時刻の隣に設定したログレベルと実行コード行が出力されていることがわかります。
これが標準ライブラリとのロギングの差です。
おわりに
本記事では、Goでのログ出力の基本について解説しました。Goの標準logパッケージからzapを使ったログレベルを振り分けるロギングまで、APIの動作状況を適切に記録する方法が理解できたかと思います。
これからはログ結果を外部ファイルに出力させたりなど、より高度なログ管理や監視の方法を覚えていきたいです。
最後までお読みいただき、誠にありがとうございました!
この「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」では、GoやJavaを使い、APIの作り方、データベースとの接続、テストやDockerといった気になったバックエンド技術の基本を振り返った学びを共有しています。
ぜひ他の記事もチェックして、筆者がこのひとりアドカレを完遂することができるか、確認してみてください(^^)
学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025
それでは、明日の「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」の記事もお楽しみに!
参考文献