Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to dynamically add custom span attribute #1

Merged
merged 4 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion foxtrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@ func (t *Tracer) Trace(next fox.HandlerFunc) fox.HandlerFunc {

ctx := t.cfg.propagator.Extract(req.Context(), t.cfg.carrier(req))

attributes := httpconv.ServerRequest(t.service, req)
if t.cfg.attrsFn != nil {
attributes = append(attributes, t.cfg.attrsFn(req)...)
}

opts := []trace.SpanStartOption{
trace.WithAttributes(httpconv.ServerRequest(t.service, req)...),
trace.WithAttributes(attributes...),
trace.WithSpanKind(trace.SpanKindServer),
}

Expand Down
33 changes: 33 additions & 0 deletions foxtrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/tigerwill90/fox"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
"net/http"
Expand Down Expand Up @@ -86,3 +87,35 @@ func TestPropagationWithCustomPropagators(t *testing.T) {
require.NoError(t, err)
router.ServeHTTP(w, r)
}

func TestWithSpanAttributes(t *testing.T) {
provider := trace.NewNoopTracerProvider()
otel.SetTextMapPropagator(b3prop.New())

r := httptest.NewRequest("GET", "/user/123?foo=bar", nil)
w := httptest.NewRecorder()

ctx := context.Background()
sc := trace.NewSpanContext(trace.SpanContextConfig{
TraceID: trace.TraceID{0x01},
SpanID: trace.SpanID{0x01},
})
ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
ctx, _ = provider.Tracer(tracerName).Start(ctx, "test")
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header))

router := fox.New()
mw := New("foobar", WithTracerProvider(provider), WithSpanAttributes(func(r *http.Request) []attribute.KeyValue {
attrs := make([]attribute.KeyValue, 1)
attrs[0] = attribute.String("http.target", r.URL.String())
return attrs
}))
err := router.Handle(http.MethodGet, "/user/{id}", mw.Trace(func(c fox.Context) {
span := trace.SpanFromContext(c.Request().Context())
assert.Equal(t, sc.TraceID(), span.SpanContext().TraceID())
assert.Equal(t, sc.SpanID(), span.SpanContext().SpanID())
}))

require.NoError(t, err)
router.ServeHTTP(w, r)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.19

require (
github.com/stretchr/testify v1.8.3
github.com/tigerwill90/fox v0.9.1
github.com/tigerwill90/fox v0.9.4
go.opentelemetry.io/contrib/propagators/b3 v1.17.0
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tigerwill90/fox v0.9.1 h1:RKRpQhv5uNyqgNu+9e2EnOs9XEBrk8AvVccsmHUQBs4=
github.com/tigerwill90/fox v0.9.1/go.mod h1:frBRK49d4TXcELy3911opGU3RZQSUq++BHQUwhykpQw=
github.com/tigerwill90/fox v0.9.4 h1:uqZqwSDbso1gs1U7kw4Y6srXOLFNgMjKQUNIZWr5kak=
github.com/tigerwill90/fox v0.9.4/go.mod h1:frBRK49d4TXcELy3911opGU3RZQSUq++BHQUwhykpQw=
go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo=
go.opentelemetry.io/contrib/propagators/b3 v1.17.0/go.mod h1:IkfUfMpKWmynvvE0264trz0sf32NRTZL4nuAN9AbWRc=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
Expand Down
21 changes: 21 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package otelfox

import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
"net/http"
Expand All @@ -17,16 +18,26 @@ func (o optionFunc) apply(c *config) {
o(c)
}

// Filter is a function that determines whether a given HTTP request should be traced.
// It returns true to indicate the request should be traced or false otherwise.
type Filter func(r *http.Request) bool

// SpanNameFormatter is a function that formats the span name given the HTTP request.
// This allows for dynamic naming of spans based on attributes of the request.
type SpanNameFormatter func(r *http.Request) string

// SpanAttributesFunc is a function type that can be used to dynamically
// generate span attributes for a given HTTP request. It is used in
// conjunction with the WithSpanAttributes middleware option.
type SpanAttributesFunc func(r *http.Request) []attribute.KeyValue

type config struct {
provider trace.TracerProvider
propagator propagation.TextMapPropagator
carrier func(r *http.Request) propagation.TextMapCarrier
spanFmt SpanNameFormatter
filters []Filter
attrsFn SpanAttributesFunc
}

func defaultConfig() *config {
Expand Down Expand Up @@ -87,3 +98,13 @@ func WithFilter(f ...Filter) Option {
c.filters = append(c.filters, f...)
})
}

// WithSpanAttributes specifies a function for generating span attributes.
// The function will be invoked for each request, and its return attributes
// will be added to the span. For example, you can use this option to add
// the http.target attribute to the span.
func WithSpanAttributes(fn SpanAttributesFunc) Option {
return optionFunc(func(c *config) {
c.attrsFn = fn
})
}