Skip to content

Commit

Permalink
feat(otelzap): add extra fields option
Browse files Browse the repository at this point in the history
  • Loading branch information
tamj0rd2 committed Aug 22, 2022
1 parent a41206e commit aa0e1a0
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 24 deletions.
2 changes: 2 additions & 0 deletions otelzap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ couple of [options](https://pkg.go.dev/github.com/uptrace/opentelemetry-go-extra
number, and function name of the caller. Enabled by default.
- `otelzap.WithStackTrace(true)` configures the logger to capture logs with a stack trace. Disabled
by default.
- `otelzap.WithExtraFields(true)` configures the logger to add the given fields to structured log
messages and to span log events.
- `otelzap.WithTraceIDField(true)` configures the logger to add `trace_id` field to structured log
messages. This option is only useful with backends that don't support OTLP and instead parse log
messages to extract structured information.
8 changes: 8 additions & 0 deletions otelzap/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ func WithStackTrace(on bool) Option {
}
}

// WithExtraFields configures the logger to add the given extra fields to structured log messages
// and the span
func WithExtraFields(fields ...zapcore.Field) Option {
return func(l *Logger) {
l.extraFields = append(l.extraFields, fields...)
}
}

// WithTraceIDField configures the logger to add `trace_id` field to structured log messages.
//
// This option is only useful with backends that don't support OTLP and instead parse log
Expand Down
48 changes: 24 additions & 24 deletions otelzap/otelzap.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ func (l *Logger) FatalContext(ctx context.Context, msg string, fields ...zapcore
func (l *Logger) logFields(
ctx context.Context, lvl zapcore.Level, msg string, fields []zapcore.Field,
) []zapcore.Field {
if len(l.extraFields) > 0 {
fields = append(fields, l.extraFields...)
}

if lvl < l.minLevel {
return fields
}
Expand All @@ -148,7 +152,7 @@ func (l *Logger) logFields(
return fields
}

attrs := make([]attribute.KeyValue, 0, numAttr+len(fields)+len(l.extraFields))
attrs := make([]attribute.KeyValue, 0, numAttr+len(fields))

for _, f := range fields {
if f.Type == zapcore.NamespaceType {
Expand All @@ -158,14 +162,6 @@ func (l *Logger) logFields(
attrs = appendField(attrs, f)
}

for _, f := range l.extraFields {
if f.Type == zapcore.NamespaceType {
// should this be a prefix?
continue
}
attrs = appendField(attrs, f)
}

l.log(span, lvl, msg, attrs)

if l.withTraceID {
Expand Down Expand Up @@ -355,21 +351,24 @@ func (s *SugaredLogger) Desugar() *Logger {
// and the second as the field value.
//
// For example,
// sugaredLogger.With(
// "hello", "world",
// "failure", errors.New("oh no"),
// Stack(),
// "count", 42,
// "user", User{Name: "alice"},
// )
//
// sugaredLogger.With(
// "hello", "world",
// "failure", errors.New("oh no"),
// Stack(),
// "count", 42,
// "user", User{Name: "alice"},
// )
//
// is the equivalent of
// unsugared.With(
// String("hello", "world"),
// String("failure", "oh no"),
// Stack(),
// Int("count", 42),
// Object("user", User{Name: "alice"}),
// )
//
// unsugared.With(
// String("hello", "world"),
// String("failure", "oh no"),
// Stack(),
// Int("count", 42),
// Object("user", User{Name: "alice"}),
// )
//
// Note that the keys in key-value pairs should be strings. In development,
// passing a non-string key panics. In production, the logger is more
Expand Down Expand Up @@ -606,7 +605,8 @@ func (s SugaredLoggerWithCtx) Fatalf(template string, args ...interface{}) {
// pairs are treated as they are in With.
//
// When debug-level logging is disabled, this is much faster than
// s.With(keysAndValues).Debug(msg)
//
// s.With(keysAndValues).Debug(msg)
func (s SugaredLoggerWithCtx) Debugw(msg string, keysAndValues ...interface{}) {
keysAndValues = s.s.logKVs(s.ctx, zap.DebugLevel, msg, keysAndValues)
s.s.skipCaller.Debugw(msg, keysAndValues...)
Expand Down
47 changes: 47 additions & 0 deletions otelzap/otelzap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"testing"
"time"

"go.uber.org/zap/zaptest/observer"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/attribute"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
Expand Down Expand Up @@ -346,6 +348,51 @@ func TestOtelZap(t *testing.T) {
test.require(t, event)
})
}

t.Run("providing extra fields to be recorded on the span, and logged", func(t *testing.T) {
sr := tracetest.NewSpanRecorder()
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr))
tracer := provider.Tracer("test")

ctx := context.Background()
ctx, span := tracer.Start(ctx, "main")

core, observedLogs := observer.New(zap.InfoLevel)
logger := New(zap.New(core), WithMinLevel(zap.InfoLevel))
loggerWithCtx := logger.Ctx(ctx).Clone(WithExtraFields(
zap.String("foo", "bar"),
zap.String("MyTraceIDKey", span.SpanContext().TraceID().String()),
))
loggerWithCtx.Info("hello")

span.End()

spans := sr.Ended()
require.Equal(t, 1, len(spans))

events := spans[0].Events()
require.Equal(t, 1, len(events))

event := events[0]
require.Equal(t, "log", event.Name)

m := attrMap(event.Attributes)
foo, ok := m["foo"]
require.True(t, ok)
require.Equal(t, "bar", foo.AsString())

_, ok = m["MyTraceIDKey"]
require.True(t, ok)
requireCodeAttrs(t, m)

require.Equal(t, 1, observedLogs.Len())
require.Equal(t, "hello", observedLogs.All()[0].Message)
require.Equal(t, zap.InfoLevel, observedLogs.All()[0].Level)

contextMap := observedLogs.All()[0].ContextMap()
require.Equal(t, "bar", contextMap["foo"])
require.Equal(t, span.SpanContext().TraceID().String(), contextMap["MyTraceIDKey"])
})
}

func requireCodeAttrs(t *testing.T, m map[attribute.Key]attribute.Value) {
Expand Down

0 comments on commit aa0e1a0

Please sign in to comment.