package otelx import ( "bytes" "context" "log/slog" "strings" "testing" "go.opentelemetry.io/otel/trace" ) func newLogger(buf *bytes.Buffer) *slog.Logger { return slog.New(traceHandler{slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelInfo})}) } func ctxWithSpan(traceHex, spanHex string) context.Context { tid, _ := trace.TraceIDFromHex(traceHex) sid, _ := trace.SpanIDFromHex(spanHex) sc := trace.NewSpanContext(trace.SpanContextConfig{TraceID: tid, SpanID: sid}) return trace.ContextWithSpanContext(context.Background(), sc) } func TestTraceHandlerInjectsIDs(t *testing.T) { var buf bytes.Buffer logger := newLogger(&buf) ctx := ctxWithSpan("0123456789abcdef0123456789abcdef", "0123456789abcdef") logger.InfoContext(ctx, "hello") out := buf.String() if !strings.Contains(out, `"trace_id":"0123456789abcdef0123456789abcdef"`) { t.Fatalf("expected trace_id in log, got: %s", out) } if !strings.Contains(out, `"span_id":"0123456789abcdef"`) { t.Fatalf("expected span_id in log, got: %s", out) } } func TestTraceHandlerNoSpanNoIDs(t *testing.T) { var buf bytes.Buffer logger := newLogger(&buf) logger.InfoContext(context.Background(), "hello") if strings.Contains(buf.String(), "trace_id") { t.Fatalf("did not expect trace_id without a span, got: %s", buf.String()) } } func TestTraceHandlerPreservesWithAttrs(t *testing.T) { var buf bytes.Buffer logger := newLogger(&buf).With("service", "test-svc") ctx := ctxWithSpan("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbb") logger.InfoContext(ctx, "hello") out := buf.String() // With(...) 后 trace 注入仍生效(WithAttrs 返回包装后的 handler)。 if !strings.Contains(out, `"service":"test-svc"`) || !strings.Contains(out, "trace_id") { t.Fatalf("expected both service and trace_id after With(), got: %s", out) } } func TestParseLevel(t *testing.T) { cases := map[string]slog.Level{ "debug": slog.LevelDebug, "info": slog.LevelInfo, "warn": slog.LevelWarn, "error": slog.LevelError, "": slog.LevelInfo, "bogus": slog.LevelInfo, } for in, want := range cases { if got := parseLevel(in); got != want { t.Errorf("parseLevel(%q)=%v want %v", in, got, want) } } }