go-mssqldbによるログと診断

go-mssqldbドライバーは接続問題のトラブルシューティング、クエリ問題、パフォーマンス分析のための設定可能なログ機能を提供します。 この記事では利用可能なログフラグとカスタムロガーの使い方について説明します。

ログフラグ

log接続パラメータを使って診断出力を有効にしてください。 ログフラグはビットマスク値であり、整数の値を加えて組み合わせることができます:

フラグ値 Category Description
1 Errors エラーメッセージを記録してください。
2 Messages サーバーからの情報メッセージを記録してください。
4 Rows 行データをログに記録する。
8 SQL サーバーに送信されたSQL文をログに記録します。
16 パラメーター パラメータ名と値をログに記録します。
32 Transactions トランザクション開始、コミット、ロールバックイベントを記録します。
64 Debug 低レベルのプロトコルとTDSの詳細を記録してください。
128 再試行 接続再試行の記録をつけてください。

フラグ 4 (行)と 16 (パラメータ)は、アプリケーションデータ、秘密、または個人を識別可能な情報を公開することができます。 これらは通常の本番設定ではなく、一時的な診断フラグとして扱ってください。

Examples

ログエラーのみ:

sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=1

ログエラー、SQL文、パラメータ:

sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=25

Note

251 + 8 + 16 (エラー+SQL+パラメータ)として計算されます。

すべて記録する:

sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&log=255

Warning

高いログフラグ値(64128255)は冗長な出力を生み出し、パフォーマンスに影響を与えることがあります。 デバッグ専用に使ってください。

デフォルトのロガー

デフォルトでは、ドライバーはGoの標準の log パッケージにログを書き、 os.Stderrに書き込みます。 出力にはタイムスタンプとログカテゴリが含まれます:

2026/03/28 10:15:30 mssql: login successful
2026/03/28 10:15:30 mssql: SQL: SELECT 1

SetLoggerを使ったカスタムロガー

mssql.SetLoggerを使ってドライバーのログ出力をカスタムロガーにリダイレクトします。 ロガーは mssql.Logger インターフェースを実装しなければなりません:

import "github.com/microsoft/go-mssqldb"

type myLogger struct{}

func (l *myLogger) Printf(format string, v ...interface{}) {
    // Write to your preferred logging system
    fmt.Printf("[MSSQL] "+format+"\n", v...)
}

func (l *myLogger) Println(v ...interface{}) {
    fmt.Println(append([]interface{}{"[MSSQL]"}, v...)...)
}

func main() {
    mssql.SetLogger(&myLogger{})
    // ... open connection
}

SetContextLoggerを用いたコンテキスト認識ロガー

mssql.SetContextLoggerを使ってコンテキスト認識型ロガーを提供します。 ロガーはコンテキスト、ログカテゴリ、メッセージ文字列を受信する単一のmssql.ContextLoggerメソッドを持つLogインターフェースを実装しなければなりません。 この方法により、ドライバーログとリクエストスコープ型トレースデータ(例えばトレースID)を相関させることができます。

import (
    "context"
    "log/slog"
    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
)

type contextLogger struct{}

func (l *contextLogger) Log(ctx context.Context, category msdsn.Log, msg string) {
    slog.InfoContext(ctx, msg, "category", category)
}

func main() {
    mssql.SetContextLogger(&contextLogger{})
    // ... open connection
}

診断チェックリスト

接続やクエリの問題のトラブルシューティング時:

  1. エラー log=1 から始め、サーバーメッセージも必要なら log=3 してください。
  2. 問題を再現し、エラーテキストを確認してから、さらに多くのカテゴリを有効にしてください。
  3. どのSQL文やプロシージャコールが送信されたか確認が必要な場合は 8 を追加してください。
  4. パラメータ値や返された行を機密データを漏らさずに記録できる安全な環境でのみ、 16 または 4 を追加してください。
  5. 問題がプロトコルレベルや再試行関連と思われる場合は、 log=64 に増やすか、再試行診断のための 128 を追加してください。
  6. 問題が解決したらログを削除または減らしてください。

log/slogを用いた構造化ログ

Go 1.21では構造化ログ用の log/slog が導入されました。 SetContextLoggerを使ってドライバー出力をslog経由にルーティングしてください:

import (
    "context"
    "log/slog"
    "os"

    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
)

type slogLogger struct {
    logger *slog.Logger
}

func (l *slogLogger) Log(ctx context.Context, category msdsn.Log, msg string) {
    level := slog.LevelInfo
    if category == msdsn.LogErrors {
        level = slog.LevelError
    }
    if category == msdsn.LogDebug {
        level = slog.LevelDebug
    }

    l.logger.LogAttrs(ctx, level, msg,
        slog.Int("category", int(category)),
    )
}

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))

    mssql.SetContextLogger(&slogLogger{logger: logger})

    // Connection logging now produces structured JSON.
}

ゼロログとの統合

ゼロログ はゼロ割り当て構造ロガーです。 ドライバーログをゼロログ経由でルーティングする:

import (
    "context"
    "os"

    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
    "github.com/rs/zerolog"
)

type zerologAdapter struct {
    logger zerolog.Logger
}

func (l *zerologAdapter) Log(ctx context.Context, category msdsn.Log, msg string) {
    event := l.logger.Info()
    if category == msdsn.LogErrors {
        event = l.logger.Error()
    }
    event.Int("category", int(category)).Msg(msg)
}

func main() {
    logger := zerolog.New(os.Stdout).With().Timestamp().Logger()
    mssql.SetContextLogger(&zerologAdapter{logger: logger})
}

Zapとの統合

ZAP は高性能な構造化ロガーです。 ドライバーログをZAP経由でルーティングする:

import (
    "context"

    "github.com/microsoft/go-mssqldb"
    "github.com/microsoft/go-mssqldb/msdsn"
    "go.uber.org/zap"
)

type zapAdapter struct {
    logger *zap.Logger
}

func (l *zapAdapter) Log(ctx context.Context, category msdsn.Log, msg string) {
    if category == msdsn.LogErrors {
        l.logger.Error(msg, zap.Int("category", int(category)))
    } else {
        l.logger.Info(msg, zap.Int("category", int(category)))
    }
}

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync()

    mssql.SetContextLogger(&zapAdapter{logger: logger})
}

相関ID伝播

分散システムでは、相関IDをコンテキスト内で伝播させ、ドライバーログをトリガーしたリクエストと相関させます。

type correlationKey struct{}

func WithCorrelationID(ctx context.Context, id string) context.Context {
    return context.WithValue(ctx, correlationKey{}, id)
}

func CorrelationID(ctx context.Context) string {
    if id, ok := ctx.Value(correlationKey{}).(string); ok {
        return id
    }
    return "unknown"
}

type correlatedLogger struct {
    logger *slog.Logger
}

func (l *correlatedLogger) Log(ctx context.Context, category msdsn.Log, msg string) {
    l.logger.LogAttrs(ctx, slog.LevelInfo, msg,
        slog.String("correlation_id", CorrelationID(ctx)),
        slog.Int("category", int(category)),
    )
}

次に、リクエストコンテキストに相関IDを渡します:

func handleRequest(w http.ResponseWriter, r *http.Request) {
    correlationID := r.Header.Get("X-Correlation-ID")
    if correlationID == "" {
        correlationID = uuid.NewString()
    }

    ctx := WithCorrelationID(r.Context(), correlationID)

    // All database operations using this context will include the correlation ID.
    rows, err := db.QueryContext(ctx, "SELECT TOP (10) ProductID, Name FROM Production.Product")
    // ...
}

相関IDの実務における使い方

ログ、再試行、データベース呼び出しにまたがる追跡キーとして、相関 ID を使用してください:

  1. リクエスト境界で相関IDを生成または受け入れます。
  2. リクエストコンテキストに保存し、アプリケーションログやドライバーログに含めてください。
  3. SQL側のトラブルシューティングは、セッションコンテキスト内で sp_set_session_context を設定し、 SESSION_CONTEXTで読み込むと良いでしょう。

相関IDはSQL Serverのテーブルに自動的に保存されるわけではありません。 SESSION_CONTEXT セッションスコープが設定されているため、値は現在の接続にのみ適用され、セッション終了後も持続しません。

耐久性のある履歴が必要な場合は、相関IDを監査テーブルやビジネステーブル(例:AuditLog、タイムスタンプ、オペレーション、ステータスを含むcorrelation_idテーブル)に明示的に書き込んでください。

本番ログ構成

本番環境では、パフォーマンスオーバーヘッドを避け、機密データがログに現れるのを防ぐために最小限のログングを有効にしてください:

環境 推奨 log キャプチャされる内容
Development 63 (エラー+メッセージ+行+SQL + パラメータ+トランザクション) デバッグのための完全な可視性。
Staging 3 (エラー+メッセージ) クエリの詳細なしのエラーやサーバーメッセージ。
生産 1 (エラー)または 0 (オフ) エラーだけ、またはドライバーログを完全に無効にするだけです。

注意事項

Parametersフラグ(16)は、個人識別情報(PII)、パスワード、その他の機密データを含む実際のパラメータ値を記録します。 このフラグは本番環境で絶対に有効にしないでください。 各フラグの完全なリスク評価については、 セキュリティのベストプラクティスをご覧ください。

生産における伐採抑制

環境変数を使って、各デプロイステージごとのログレベルを制御します:

if os.Getenv("APP_ENV") == "production" {
    // Use only error-level logging in production.
    connString += "&log=1"
} else {
    // Full logging in development.
    connString += "&log=63"
}