Skip to content

コンテキスト統合

DD は Go 標準ライブラリ context.Context との統合をサポートし、トレーシング情報の自動伝播とコンテキストフィールドの抽出が可能です。

ContextKey 型

ContextKeystring をベースとしたカスタムキー型で、他パッケージのコンテキストキーとの競合を回避します。

go
type ContextKey string

TraceID / SpanID / RequestID に対応する 3 つのキー定数が定義済みです:

定数
ContextKeyTraceIDContextKey"trace_id"
ContextKeySpanIDContextKey"span_id"
ContextKeyRequestIDContextKey"request_id"

注入と読み取り

関数シグネチャ説明
WithTraceID(ctx context.Context, traceID string) context.ContextTraceID を注入
WithSpanID(ctx context.Context, spanID string) context.ContextSpanID を注入
WithRequestID(ctx context.Context, requestID string) context.ContextRequestID を注入
GetTraceID(ctx context.Context) stringTraceID を読み取り(欠損時は "" を返す)
GetSpanID(ctx context.Context) stringSpanID を読み取り(欠損時は "" を返す)
GetRequestID(ctx context.Context) stringRequestID を読み取り(欠損時は "" を返す)

With* 関数は context.WithValue に基づいて新しい ctx を派生し(キーは対応する ContextKey 定数)、Get* 関数は ctx から string 値を取り出します;キーが存在しない、または値が string でない場合は、一律に空文字列を返します。

使用例

go
func handleRequest(ctx context.Context) {
    // トレーシング情報を注入
    ctx = dd.WithTraceID(ctx, "trace-abc123")
    ctx = dd.WithSpanID(ctx, "span-def456")
    ctx = dd.WithRequestID(ctx, "req-789")

    // コンテキストフィールドを手動抽出してログに渡す
    logger.InfoWith("リクエスト処理",
        dd.String("trace_id", dd.GetTraceID(ctx)),
        dd.String("span_id", dd.GetSpanID(ctx)),
        dd.String("request_id", dd.GetRequestID(ctx)),
    )
}

一括抽出

手動 Get* は単発の場面に適しています。グローバル/静的フィールド(サービス名やホスト名など)を毎ログに自動付与するには、ContextExtractor を Logger に登録します。抽出器は毎回 *With 呼び出し時に実行されます。注意:抽出器が受け取るのは context.Background() であり、リクエストスコープの TraceID を自動取得できません(下記の制限事項を参照)。

ContextExtractor

ContextExtractorcontext.Context からフィールドを自動抽出する関数型で、OpenTelemetry、Jaeger などのトレーシングフレームワークとの連携に便利です。

go
type ContextExtractor func(ctx context.Context) []Field

エクストラクタは Logger 内部が保持するスレッドセーフなレジストリ(contextExtractorRegistryプライベートで外部非公開)で管理されます:追加順に実行され、読み取りは atomic.Pointer によるロックフリー高速パスを通ります;いずれかのエクストラクタが panic した場合は recover されて stderr に記録され、アプリケーションをクラッシュさせることはありません。

エクストラクタの登録

エクストラクタ自体はこのファイルで型のみ定義されます;登録/管理 API は Logger 上にあります(core ドメイン):

go
// エクストラクタを追加(error を返す、nil エクストラクタは拒否されます)
err := logger.AddContextExtractor(func(ctx context.Context) []dd.Field {
    return []dd.Field{
        dd.String("trace_id", dd.GetTraceID(ctx)),
        dd.String("request_id", dd.GetRequestID(ctx)),
    }
})

// 全エクストラクタを一括置き換え
_ = logger.SetContextExtractors(extractor1, extractor2)

// 現在登録済みのエクストラクタのスナップショットを取得
extractors := logger.GetContextExtractors()

コンテキストの制限(重要)

ログメソッド(Info/InfoWith 等)は context.Context を受け取らず、ContextExtractor は内部で context.Background() で呼ばれるため、リクエストスコープの TraceID/SpanID を自動抽出できません。下記の OTel 例はグローバル span が存在する場合のみフィールドを出力します。リクエストごとのトレース ID を付与するには WithFields() で手動渡してください(分散トレーシング統合 を参照)。

OpenTelemetry 例

go
// OTel span の trace_id / span_id を各ログに注入
otelExtractor := dd.ContextExtractor(func(ctx context.Context) []dd.Field {
    span := trace.SpanFromContext(ctx)
    if !span.SpanContext().IsValid() {
        return nil
    }
    return []dd.Field{
        dd.String("trace_id", span.SpanContext().TraceID().String()),
        dd.String("span_id", span.SpanContext().SpanID().String()),
    }
})
_ = logger.AddContextExtractor(otelExtractor)

完全な例

HTTP ミドルウェア

go
func tracingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        traceID := r.Header.Get("X-Trace-ID")
        if traceID == "" {
            traceID = generateTraceID()
        }
        ctx := dd.WithTraceID(r.Context(), traceID)
        ctx = dd.WithRequestID(ctx, generateRequestID())
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

gRPC インターセプター

go
func loggingInterceptor(
    ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (interface{}, error) {
    md, _ := metadata.FromIncomingContext(ctx)
    ctx = dd.WithTraceID(ctx, md.Get("trace-id")[0])
    ctx = dd.WithRequestID(ctx, md.Get("request-id")[0])

    dd.InfoWith("gRPC リクエスト",
        dd.String("method", info.FullMethod),
        dd.String("trace_id", dd.GetTraceID(ctx)),
        dd.String("request_id", dd.GetRequestID(ctx)),
    )
    return handler(ctx, req)
}

次のステップ

  • Logger -- AddContextExtractor / SetContextExtractors / GetContextExtractors
  • 構造化フィールド -- Field コンストラクタとフィールド検証
  • 設定 -- Config.ContextExtractors