diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5c995c081..301e53afdc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm For example, `[]string{"foo", "bar"}` attribute value is now transformed to `log.SliceValue(log.StringValue("foo"), log.StringValue("bar"))` instead of `log.String("[foo bar"])`. (#6254) - Add the `WithSource` option to the `go.opentelemetry.io/contrib/bridges/otelslog` log bridge to set the `code.*` attributes in the log record that includes the source location where the record was emitted. (#6253) - Add `ContextWithStartTime` and `StartTimeFromContext` to `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`, which allows setting the start time using go context. (#6137) +- Set the `code.*` attributes in `go.opentelemetry.io/contrib/bridges/otelzap` if the `zap.Logger` was created with the `AddCaller` or `AddStacktrace` option. (#6268) ### Fixed diff --git a/bridges/otelzap/core.go b/bridges/otelzap/core.go index 4e222e37a6a..f2c25569152 100644 --- a/bridges/otelzap/core.go +++ b/bridges/otelzap/core.go @@ -41,6 +41,7 @@ import ( "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" ) type config struct { @@ -200,6 +201,16 @@ func (o *Core) Write(ent zapcore.Entry, fields []zapcore.Field) error { r.SetSeverityText(ent.Level.String()) r.AddAttributes(o.attr...) + if ent.Caller.Defined { + r.AddAttributes( + log.String(string(semconv.CodeFilepathKey), ent.Caller.File), + log.Int(string(semconv.CodeLineNumberKey), ent.Caller.Line), + log.String(string(semconv.CodeFunctionKey), ent.Caller.Function), + ) + } + if ent.Stack != "" { + r.AddAttributes(log.String(string(semconv.CodeStacktraceKey), ent.Stack)) + } if len(fields) > 0 { ctx, attrbuf := convertField(fields) if ctx != nil { diff --git a/bridges/otelzap/core_test.go b/bridges/otelzap/core_test.go index 0d00794b409..bcd7130b714 100644 --- a/bridges/otelzap/core_test.go +++ b/bridges/otelzap/core_test.go @@ -17,6 +17,7 @@ import ( "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/log/logtest" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" ) var ( @@ -164,6 +165,50 @@ func TestCoreEnabled(t *testing.T) { assert.Equal(t, zap.InfoLevel.String(), got.SeverityText()) } +func TestCoreWithCaller(t *testing.T) { + rec := logtest.NewRecorder() + zc := NewCore(loggerName, WithLoggerProvider(rec)) + logger := zap.New(zc, zap.AddCaller()) + + logger.Info(testMessage) + got := rec.Result()[0].Records[0] + assert.Equal(t, testMessage, got.Body().AsString()) + assert.Equal(t, log.SeverityInfo, got.Severity()) + assert.Equal(t, zap.InfoLevel.String(), got.SeverityText()) + assert.Equal(t, 3, got.AttributesLen()) + got.WalkAttributes(func(kv log.KeyValue) bool { + switch kv.Key { + case string(semconv.CodeFilepathKey): + assert.Contains(t, kv.Value.AsString(), "core_test.go") + case string(semconv.CodeLineNumberKey): + assert.Positive(t, kv.Value.AsInt64()) + case string(semconv.CodeFunctionKey): + assert.Contains(t, kv.Value.AsString(), "TestCoreWithCaller") + default: + assert.Fail(t, "unexpected attribute key", kv.Key) + } + return true + }) +} + +func TestCoreWithStacktrace(t *testing.T) { + rec := logtest.NewRecorder() + zc := NewCore(loggerName, WithLoggerProvider(rec)) + logger := zap.New(zc, zap.AddStacktrace(zapcore.ErrorLevel)) + + logger.Error(testMessage) + got := rec.Result()[0].Records[0] + assert.Equal(t, testMessage, got.Body().AsString()) + assert.Equal(t, log.SeverityError, got.Severity()) + assert.Equal(t, zap.ErrorLevel.String(), got.SeverityText()) + assert.Equal(t, 1, got.AttributesLen()) + got.WalkAttributes(func(kv log.KeyValue) bool { + assert.Equal(t, string(semconv.CodeStacktraceKey), kv.Key) + assert.NotEmpty(t, kv.Value.AsString()) + return true + }) +} + func TestNewCoreConfiguration(t *testing.T) { t.Run("Default", func(t *testing.T) { r := logtest.NewRecorder() diff --git a/bridges/otelzap/go.mod b/bridges/otelzap/go.mod index abea363927a..68da34e7733 100644 --- a/bridges/otelzap/go.mod +++ b/bridges/otelzap/go.mod @@ -4,6 +4,7 @@ go 1.22 require ( github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/log v0.7.0 go.uber.org/zap v1.27.0 ) @@ -13,7 +14,6 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.opentelemetry.io/otel v1.31.0 // indirect go.opentelemetry.io/otel/metric v1.31.0 // indirect go.opentelemetry.io/otel/trace v1.31.0 // indirect go.uber.org/multierr v1.11.0 // indirect