Compare commits

...

1 Commits

Author SHA1 Message Date
grandwizard28
958ecae3a5 refactor(instrumentation): flatten code source into code.filepath, code.function, code.lineno
Replace the nested slog.Source group with flat OTel semantic convention
keys by adding a Source log handler wrapper that extracts source info
from record.PC. Remove AddSource from the JSON handler since the wrapper
handles it directly.
2026-03-22 01:50:11 +05:30
3 changed files with 67 additions and 7 deletions

View File

@@ -13,13 +13,7 @@ type zapToSlogConverter struct{}
func NewLogger(config Config) *slog.Logger {
logger := slog.New(
loghandler.New(
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: config.Logs.Level, AddSource: true, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// This is more in line with OpenTelemetry semantic conventions
if a.Key == slog.SourceKey {
a.Key = "code"
return a
}
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: config.Logs.Level, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
a.Key = "timestamp"
return a
@@ -27,6 +21,7 @@ func NewLogger(config Config) *slog.Logger {
return a
}}),
loghandler.NewSource(),
loghandler.NewCorrelation(),
loghandler.NewFiltering(),
loghandler.NewException(),

View File

@@ -0,0 +1,28 @@
package loghandler
import (
"context"
"log/slog"
"runtime"
)
type source struct{}
func NewSource() *source {
return &source{}
}
func (h *source) Wrap(next LogHandler) LogHandler {
return LogHandlerFunc(func(ctx context.Context, record slog.Record) error {
if record.PC != 0 {
frame, _ := runtime.CallersFrames([]uintptr{record.PC}).Next()
record.AddAttrs(
slog.String("code.filepath", frame.File),
slog.String("code.function", frame.Function),
slog.Int("code.lineno", frame.Line),
)
}
return next.Handle(ctx, record)
})
}

View File

@@ -0,0 +1,37 @@
package loghandler
import (
"bytes"
"context"
"encoding/json"
"log/slog"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSource(t *testing.T) {
src := NewSource()
buf := bytes.NewBuffer(nil)
logger := slog.New(&handler{base: slog.NewJSONHandler(buf, &slog.HandlerOptions{Level: slog.LevelDebug}), wrappers: []Wrapper{src}})
logger.InfoContext(context.Background(), "test")
m := make(map[string]any)
err := json.Unmarshal(buf.Bytes(), &m)
require.NoError(t, err)
assert.Contains(t, m, "code.filepath")
assert.Contains(t, m, "code.function")
assert.Contains(t, m, "code.lineno")
assert.Contains(t, m["code.filepath"], "source_test.go")
assert.Contains(t, m["code.function"], "TestSource")
assert.NotZero(t, m["code.lineno"])
// Ensure the nested "source" key is not present.
assert.NotContains(t, m, "source")
assert.NotContains(t, m, "code")
}