diff --git a/contrib/go.uber.org/zap/example_test.go b/contrib/go.uber.org/zap/example_test.go new file mode 100644 index 0000000000..3593fe4682 --- /dev/null +++ b/contrib/go.uber.org/zap/example_test.go @@ -0,0 +1,29 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package zap_test + +import ( + "context" + "go.uber.org/zap" + zaptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/go.uber.org/zap" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" +) + +func ExampleWithTraceFields() { + // start the DataDog tracer + tracer.Start() + defer tracer.Stop() + + // create the application logger + logger, _ := zap.NewProduction() + + // start a new span + span, ctx := tracer.StartSpanFromContext(context.Background(), "ExampleWithTraceCorrelation") + defer span.Finish() + + // log a message using the context containing span information + zaptrace.WithTraceFields(ctx, logger).Info("this is a log with tracing information") +} diff --git a/contrib/go.uber.org/zap/zap.go b/contrib/go.uber.org/zap/zap.go new file mode 100644 index 0000000000..2917d54cbc --- /dev/null +++ b/contrib/go.uber.org/zap/zap.go @@ -0,0 +1,40 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +// Package zap provides functions to correlate logs and traces using go.uber.org/zap package (https://github.com/uber-go/zap). +package zap // import "gopkg.in/DataDog/dd-trace-go.v1/contrib/go.uber.org/zap" + +import ( + "context" + "go.uber.org/zap" + "strconv" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry" +) + +const componentName = "go.uber.org/zap" + +func init() { + telemetry.LoadIntegration(componentName) + tracer.MarkIntegrationImported(componentName) +} + +// WithTraceFields looks for an active span in the provided context and if found, it returns a new *zap.Logger with +// traceID and spanID keys set. +func WithTraceFields(ctx context.Context, logger *zap.Logger) *zap.Logger { + span, ok := tracer.SpanFromContext(ctx) + if ok { + traceID := strconv.FormatUint(span.Context().TraceID(), 10) + spanID := strconv.FormatUint(span.Context().SpanID(), 10) + + return logger.With( + zap.String(ext.LogKeyTraceID, traceID), + zap.String(ext.LogKeySpanID, spanID), + ) + } + return logger +} diff --git a/contrib/go.uber.org/zap/zap_test.go b/contrib/go.uber.org/zap/zap_test.go new file mode 100644 index 0000000000..685b4acfae --- /dev/null +++ b/contrib/go.uber.org/zap/zap_test.go @@ -0,0 +1,48 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +package zap + +import ( + "context" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest/observer" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + internallog "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +func TestWithTraceFields(t *testing.T) { + tracer.Start(tracer.WithLogger(internallog.DiscardLogger{})) + defer tracer.Stop() + + // start a new span + span, ctx := tracer.StartSpanFromContext(context.Background(), "test") + defer span.Finish() + + observed, logs := observer.New(zapcore.InfoLevel) + + logger := zap.New(observed) + logger = WithTraceFields(ctx, logger) + logger.Info("some message") + + traceID := strconv.FormatUint(span.Context().TraceID(), 10) + spanID := strconv.FormatUint(span.Context().SpanID(), 10) + + require.Equal(t, 1, logs.Len()) + infoLog := logs.All()[0] + + require.Len(t, infoLog.Context, 2) + assert.Equal(t, "dd.trace_id", infoLog.Context[0].Key) + assert.Equal(t, traceID, infoLog.Context[0].String) + assert.Equal(t, "dd.span_id", infoLog.Context[1].Key) + assert.Equal(t, spanID, infoLog.Context[1].String) +} diff --git a/ddtrace/tracer/option.go b/ddtrace/tracer/option.go index 9175c20efe..e0049e1603 100644 --- a/ddtrace/tracer/option.go +++ b/ddtrace/tracer/option.go @@ -98,6 +98,7 @@ var contribIntegrations = map[string]struct { "github.com/zenazn/goji": {"Goji", false}, "log/slog": {"log/slog", false}, "github.com/uptrace/bun": {"Bun", false}, + "go.uber.org/zap": {"go.uber.org/zap", false}, } var ( diff --git a/go.mod b/go.mod index cfa9c77d8d..d5a2bfe08d 100644 --- a/go.mod +++ b/go.mod @@ -94,6 +94,7 @@ require ( go.opentelemetry.io/otel v1.20.0 go.opentelemetry.io/otel/trace v1.20.0 go.uber.org/atomic v1.11.0 + go.uber.org/zap v1.17.0 golang.org/x/mod v0.18.0 golang.org/x/oauth2 v0.9.0 golang.org/x/sys v0.21.0 @@ -255,6 +256,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/metric v1.20.0 // indirect + go.uber.org/multierr v1.6.0 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.24.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect diff --git a/go.sum b/go.sum index 4e64b997d1..984ebb6127 100644 --- a/go.sum +++ b/go.sum @@ -2182,8 +2182,10 @@ go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=