Splunk Observability Cloud のログと Go トレースデータを接続する 🔗
Splunk OTel Go インストルメンテーションによって自動的に提供されるトレース属性を含めるように、ロギングライブラリを設定できます。トレースメタデータを使用して、トレースとログイベントを関連付け、Splunk でログを調査します。
以下のステップでは、トレースメタデータを抽出し、ログフィールドとしてデータを含むようにロギングライブラリを設定する方法を説明します。
コンテキストからトレースメタデータを抽出する 🔗
OpenTelemetry Trace API の SpanContextFromContext
関数は、トレースメタデータを context.Context
から抽出し、SpanContext
の形式で返します:
spanContext := trace.SpanContextFromContext(ctx)
if !spanContext.IsValid() {
// ctx does not contain a valid span.
// There is no trace metadata to add.
return
}
SpanContext
構造体は、トレースとスパンID、サンプリング情報を含むトレースフラグ、および状態情報を含みます。このメタデータをログイベントに追加することで、そのコンテキストを充実させ、トレースとログを相関させることができます。
ログイベントに注釈を付ける 🔗
SpanContext
でメタデータを収集した後、それを使ってログイベントに注釈を付けることができます。
構造化ログ 🔗
構造化ロガーを使用している場合、トレースメタデータをフィールドとして追加します。次の例は、zap
ロギングライブラリを使用してログイベントに注釈を付ける方法を示しています:
logger, _ := zap.NewProduction()
defer logger.Sync()
logger = logger.With(
zap.String("trace_id", spanContext.TraceID().String()),
zap.String("span_id", spanContext.SpanID().String()),
zap.String("trace_flags", spanContext.TraceFlags().String()),
)
logger.Info("Failed to fetch URL", zap.String("URL", url))
非構造化ログ 🔗
非構造化ロギングを使用している場合、ログメッセージの一部としてトレースメタデータを追加することができます。以下の例は、標準ライブラリ log
パッケージを使用してトレースメタデータを追加する方法を示しています:
// Add the metadata following an order you can parse later on
log.Printf(
"(trace_id: %s, span_id: %s, trace_flags: %s): failed to fetch URL: %s",
spanContext.TraceID().String(),
spanContext.SpanID().String(),
spanContext.TraceFlags().String(),
url,
)
ログ注釈の例 🔗
以下は、トレースメタデータを抽出し、処理されたリクエストのログメッセージに注釈を付ける chi
サーバーの完全な例です:
package main
import (
"context"
"net/http"
"github.com/go-chi/chi"
"github.com/signalfx/splunk-otel-go/instrumentation/github.com/go-chi/chi/splunkchi"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)
func withTraceMetadata(ctx context.Context, logger *zap.Logger) *zap.Logger {
spanContext := trace.SpanContextFromContext(ctx)
if !spanContext.IsValid() {
// ctx does not contain a valid span.
// There is no trace metadata to add.
return logger
}
return logger.With(
zap.String("trace_id", spanContext.TraceID().String()),
zap.String("span_id", spanContext.SpanID().String()),
zap.String("trace_flags", spanContext.TraceFlags().String()),
)
}
func helloHandler(logger *zap.Logger) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := withTraceMetadata(r.Context(), logger)
n, err := w.Write([]byte("Hello World!\n"))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
l.Error("failed to write request response", zap.Error(err))
} else {
l.Info("request handled", zap.Int("response_bytes", n))
}
}
}
func main() {
logger, err := zap.NewProduction()
if err != nil {
panic(err)
}
defer logger.Sync()
router := chi.NewRouter()
router.Use(splunkchi.Middleware())
router.Get("/hello", helloHandler(logger))
if err := http.ListenAndServe(":8080", router); err != nil {
panic(err)
}
}