Skip to content

Commit e58e174

Browse files
authored
Fix custom attribute storage (#5)
* chore: go mod tidy * Store attribute in a sync.Map instead of context. Fixes #4
1 parent ffb2bc3 commit e58e174

File tree

3 files changed

+30
-22
lines changed

3 files changed

+30
-22
lines changed

example/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
github.com/google/uuid v1.6.0 // indirect
1414
github.com/samber/lo v1.47.0 // indirect
1515
github.com/samber/slog-multi v1.0.0 // indirect
16-
go.opentelemetry.io/otel v1.19.0 // indirect
17-
go.opentelemetry.io/otel/trace v1.19.0 // indirect
16+
go.opentelemetry.io/otel v1.29.0 // indirect
17+
go.opentelemetry.io/otel/trace v1.29.0 // indirect
1818
golang.org/x/text v0.16.0 // indirect
1919
)

example/go.sum

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
4-
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
3+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
55
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
66
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
77
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -12,12 +12,12 @@ github.com/samber/slog-formatter v1.0.0 h1:ULxHV+jNqi6aFP8xtzGHl2ejFRMl2+jI2UhCp
1212
github.com/samber/slog-formatter v1.0.0/go.mod h1:c7pRfwhCfZQNzJz+XirmTveElxXln7M0Y8Pq781uxlo=
1313
github.com/samber/slog-multi v1.0.0 h1:snvP/P5GLQ8TQh5WSqdRaxDANW8AAA3egwEoytLsqvc=
1414
github.com/samber/slog-multi v1.0.0/go.mod h1:uLAvHpGqbYgX4FSL0p1ZwoLuveIAJvBECtE07XmYvFo=
15-
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
16-
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
17-
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
18-
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
19-
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
20-
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
15+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
16+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
17+
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
18+
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
19+
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
20+
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
2121
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
2222
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
2323
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

middleware.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net/http"
66
"strconv"
77
"strings"
8+
"sync"
89
"time"
910

1011
"log/slog"
@@ -130,6 +131,11 @@ func NewWithConfig(logger *slog.Logger, config Config) func(http.Handler) http.H
130131
// dump response body
131132
bw := newBodyWriter(w, ResponseBodyMaxSize, config.WithResponseBody)
132133

134+
// Make sure we create a map only once per request (in case we have multiple middleware instances)
135+
if v := r.Context().Value(customAttributesCtxKey); v == nil {
136+
r = r.WithContext(context.WithValue(r.Context(), customAttributesCtxKey, &sync.Map{}))
137+
}
138+
133139
defer func() {
134140
// Pass thru filters and skip early the code below, to prevent unnecessary processing.
135141
for _, filter := range config.Filters {
@@ -232,9 +238,11 @@ func NewWithConfig(logger *slog.Logger, config Config) func(http.Handler) http.H
232238

233239
// custom context values
234240
if v := r.Context().Value(customAttributesCtxKey); v != nil {
235-
switch attrs := v.(type) {
236-
case []slog.Attr:
237-
attributes = append(attributes, attrs...)
241+
if m, ok := v.(*sync.Map); ok {
242+
m.Range(func(key, value any) bool {
243+
attributes = append(attributes, slog.Attr{Key: key.(string), Value: value.(slog.Value)})
244+
return true
245+
})
238246
}
239247
}
240248

@@ -268,17 +276,17 @@ func GetRequestIDFromContext(ctx context.Context) string {
268276
return ""
269277
}
270278

271-
// AddCustomAttributes adds custom attributes to the request context.
279+
// AddCustomAttributes adds custom attributes to the request context. This func can be called from any handler or middleware, as long as the slog-http middleware is already mounted.
272280
func AddCustomAttributes(r *http.Request, attr slog.Attr) {
273-
v := r.Context().Value(customAttributesCtxKey)
274-
if v == nil {
275-
*r = *r.WithContext(context.WithValue(r.Context(), customAttributesCtxKey, []slog.Attr{attr}))
276-
return
277-
}
281+
AddContextAttributes(r.Context(), attr)
282+
}
278283

279-
switch attrs := v.(type) {
280-
case []slog.Attr:
281-
*r = *r.WithContext(context.WithValue(r.Context(), customAttributesCtxKey, append(attrs, attr)))
284+
// AddContextAttributes is the same as AddCustomAttributes, but it doesn't need access to the request struct.
285+
func AddContextAttributes(ctx context.Context, attr slog.Attr) {
286+
if v := ctx.Value(customAttributesCtxKey); v != nil {
287+
if m, ok := v.(*sync.Map); ok {
288+
m.Store(attr.Key, attr.Value)
289+
}
282290
}
283291
}
284292

0 commit comments

Comments
 (0)