メインコンテンツまでスキップ

ロギング

このセクションでは、Go-Sailにおけるログの取得方法について紹介します。

はじめに

ロギングはほぼ全てのシステムで行われているタスクです。その理由は単純で、ログを記録することでイベントの追跡や、問題の原因特定・トラブルシュートが容易になるからです。

Go-Sailは、アプリケーション内部で発生する事象をより良く把握できるよう、頑健なロギングサービスを提供しており、ファイルへのメッセージ記録やエクスポーター経由での外部出力も対応しています。

設定

Go-Sailのロギングコンポーネントは uber/zap ロギングライブラリを基盤としており、サービス起動時に自動で有効化されます。設定によって挙動を指定できます。

main.go
// ※コメント・変数名等以外、元のサンプルコードは省略しますが、日本語化が必要な部分があれば日本語化してください。

上記のサンプルコードでは、ログレベルを warn に設定し、出力先をカレントディレクトリの running.log ファイルに指定、ログファイルの最大サイズは100MBとしています。Go-Sailのロギングコンポーネントは、ログのローテーションにも対応しているため、ログファイルが無制限に肥大化してディスクを圧迫したり、多数生成される心配はありません。MaxBackups 設定により最新10個までのファイルの保存、古いログファイルは自動で圧縮され、ディスク容量も節約できます。

レベル

ログレベルは、記録されるログの詳細度を決定します。これはロギング関数の呼び出し方法とは独立しており、設定段階でのみ有効です。 ログレベルは低い順に次の通りです:

  • debug
  • info
  • warn
  • error

呼び出し時には緊急度や重要度に応じて使い分けましょう。例えば、単純に情報を出力したい場合は Info() メソッドを使用します。

main.go
import (
"github.com/keepchen/go-sail/v3/sail"
)

func main() {
sail.GetLogger().Info("何かをログ出力します…")
}

設定されたロギングレベルが info より高い場合、上記のコードで出力されたログは記録されません。これによりグローバルな制御が可能です。

ConsoleOutput

デフォルトでは、ログはファイルにのみ記録されます。しかし、開発者が確認しやすいよう、端末にも同時に出力したい場合があります。その場合は、設定で ConsoleOutput: true としてください。

main.go
// 設定例(主要部分のみ抜粋)
LoggerConf: logger.Conf{
Level: "warn",
Filename: "running.log",
MaxSize: 100,
MaxBackups: 10,
Compress: true,
ConsoleOutput: true,
}

モジュール(Modules)

モジュールリストは、呼び出し元ニーズに応じてログを分類し、特定のファイルに記録することを目的とします。例えば、スケジュールされたタスク(定期処理)のみを schedule という専用ファイルに記録したい場合に適しています。これにより、重要度の高い定期実行のエラーなどの確認が簡単になります。

設定でモジュール名を宣言するだけで、特定モジュールへのログ記録が可能となります。

main.go
// 設定例(主要部分のみ抜粋)
LoggerConf: logger.Conf{
Level: "warn",
Filename: "running.log",
MaxSize: 100,
MaxBackups: 10,
Compress: true,
ConsoleOutput: true,
Modules: []string{"schedule"},
}

モジュール名を指定してログを呼び出す例:

sail.GetLogger("schedule").Info("何かをログ出力します…")
ヒント

存在しないモジュールを呼び出した場合、デフォルトのファイルに記録されます。

以下二行のログ出力は同一場所に保存されます。

sail.GetLogger().Info("…")
sail.GetLogger("no-existent-module").Info("…")

エクスポーター(Exporter)

通常、Go-Sailのログはローカルファイルに出力されますが、開発環境では問題なくても、複数台構成やマイクロサービスの本番環境では「ローカル」は非永続的・安全でないストレージとなることが多く、ログを中央倉庫やELK等へ転送する必要が生じます。そこでGo-Sailのエクスポーター機能が役立ちます。

エクスポーターは、ログをメッセージキュー等のミドルウェアへ同期し、他のコンシューマーが参照・処理できる仕組みです。現在サポートされるミドルウェアは次の通りです:

  • Redis(スタンドアロン)
  • Redis(クラスタ)
  • Nats
  • Kafka
ヒント

データ損失防止のため、RedisエクスポーターはPub/Subは使わず、List型で RPush による書き込み、消費側は LPop で取得してください。

エクスポーターの利用時、RedisやNATS・Kafkaなどの接続情報は、別途個別に設定が必要です。

また、サービスの安定性を担保するため、ログ出力用エクスポーターの設定は、コアサービス構成とは設計上切り離されています。厳密な要件がなければ、同一設定としても構いません。

分散トレーシング(Distributed Tracing)

使い方

Webアプリケーションでは、リクエスト~処理~レスポンスまで、一連の処理をログ・トレースすることが非常に一般的です。

この全工程で発生した重要なイベントやエラー等を追跡しやすくするため、分散トレーシング機能が役立ちます。 リンクトレースはサービス起動時に自動で有効化され、各ルートハンドラでも利用可能です。

main.go
// ハンドラ内での例
if err := c.ShouldBind(&loginRequest); err != nil {
sail.LogTrace(c).Warn("リクエストパラメータのバインドに失敗", zap.Error(err))
sail.Response(c).Wrap(sailConstants.ErrRequestParamsInvalid, nil).Send()
return
}

コンポーネントの引き渡し

リンクトレーシングを有効にするには、コンテキストからロギングコンポーネントを取得してログ記録用途に使うことを推奨します。また、このコンポーネントをさらに深い階層の関数に引数として渡すことも可能です。

main.go
var (
loginRequest LoginRequest
loggerSvc = sail.LogTrace(c).GetLogger()
)
if err := c.ShouldBind(&loginRequest); err != nil {
loggerSvc.Warn("リクエストパラメータのバインドに失敗", zap.Error(err))
sail.Response(c).Wrap(sailConstants.ErrRequestParamsInvalid, nil).Send()
return
}

toDoSomething(loggerSvc)

サービス間連携

マイクロサービス環境では、複数サービスが相互にHTTPリクエストを送る場面が頻繁にあります。この全リクエストチェーンをトレースするには、リクエストIDをヘッダーで連携します。すべてGo-Sail製のサービスなら自動連携されます。

独自のHTTPクライアント実装例:

main.go
headers := map[string]string{
"X-Request-Id": sail.LogTrace(c).RequestID(),
}
sail.Utils().HttpClient().SendRequest("POST", "https://....", nil, headers)
ヒント

現時点でGo-SailはHTTPリクエストのみ処理します。他プロトコル(gRPC等)を利用する場合は手動対応が必要ですが、原理は同じです。