mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-03-05 15:05:51 +02:00
othttp: add WithSpanFormatter option (#617)
* othttp: add WithSpanFormatter option * plugin/othttp: add span formatter test * remove typo * preserve operation && change option name * nil check * fix comment typo Co-Authored-By: Rahul Patel <rghetia@yahoo.com> * add default formatter test case Co-authored-by: Rahul Patel <rghetia@yahoo.com>
This commit is contained in:
parent
367635b740
commit
6489b07bf5
@ -28,6 +28,7 @@ import (
|
||||
type MockSpan struct {
|
||||
sc core.SpanContext
|
||||
tracer apitrace.Tracer
|
||||
Name string
|
||||
}
|
||||
|
||||
var _ apitrace.Span = (*MockSpan)(nil)
|
||||
@ -66,8 +67,9 @@ func (ms *MockSpan) End(options ...apitrace.EndOption) {
|
||||
func (ms *MockSpan) RecordError(ctx context.Context, err error, opts ...apitrace.ErrorOption) {
|
||||
}
|
||||
|
||||
// SetName does nothing.
|
||||
// SetName sets the span name.
|
||||
func (ms *MockSpan) SetName(name string) {
|
||||
ms.Name = name
|
||||
}
|
||||
|
||||
// Tracer returns MockTracer implementation of Tracer.
|
||||
|
@ -37,6 +37,9 @@ type MockTracer struct {
|
||||
|
||||
// Sampled specifies if the new span should be sampled or not.
|
||||
Sampled bool
|
||||
|
||||
// OnSpanStarted is called every time a new trace span is started
|
||||
OnSpanStarted func(span *MockSpan)
|
||||
}
|
||||
|
||||
var _ apitrace.Tracer = (*MockTracer)(nil)
|
||||
@ -77,6 +80,10 @@ func (mt *MockTracer) Start(ctx context.Context, name string, o ...apitrace.Star
|
||||
span = &MockSpan{
|
||||
sc: sc,
|
||||
tracer: mt,
|
||||
Name: name,
|
||||
}
|
||||
if mt.OnSpanStarted != nil {
|
||||
mt.OnSpanStarted(span)
|
||||
}
|
||||
|
||||
return apitrace.ContextWithSpan(ctx, span), span
|
||||
|
@ -53,12 +53,13 @@ type Handler struct {
|
||||
operation string
|
||||
handler http.Handler
|
||||
|
||||
tracer trace.Tracer
|
||||
props propagation.Propagators
|
||||
spanStartOptions []trace.StartOption
|
||||
readEvent bool
|
||||
writeEvent bool
|
||||
filters []Filter
|
||||
tracer trace.Tracer
|
||||
props propagation.Propagators
|
||||
spanStartOptions []trace.StartOption
|
||||
readEvent bool
|
||||
writeEvent bool
|
||||
filters []Filter
|
||||
spanNameFormatter func(string, *http.Request) string
|
||||
}
|
||||
|
||||
// Option function used for setting *optional* Handler properties
|
||||
@ -140,10 +141,27 @@ func WithMessageEvents(events ...event) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithSpanNameFormatter takes a function that will be called on every
|
||||
// incoming request and the returned string will become the Span Name
|
||||
func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option {
|
||||
return func(h *Handler) {
|
||||
h.spanNameFormatter = f
|
||||
}
|
||||
}
|
||||
|
||||
func defaultFormatter(operation string, _ *http.Request) string {
|
||||
return operation
|
||||
}
|
||||
|
||||
// NewHandler wraps the passed handler, functioning like middleware, in a span
|
||||
// named after the operation and with any provided HandlerOptions.
|
||||
func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler {
|
||||
h := Handler{handler: handler, operation: operation}
|
||||
h := Handler{
|
||||
handler: handler,
|
||||
operation: operation,
|
||||
spanNameFormatter: defaultFormatter,
|
||||
}
|
||||
|
||||
defaultOpts := []Option{
|
||||
WithTracer(global.Tracer("go.opentelemetry.io/plugin/othttp")),
|
||||
WithPropagators(global.Propagators()),
|
||||
@ -169,7 +187,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
opts := append([]trace.StartOption{}, h.spanStartOptions...) // start with the configured options
|
||||
|
||||
ctx := propagation.ExtractHTTP(r.Context(), h.props, r.Header)
|
||||
ctx, span := h.tracer.Start(ctx, h.operation, opts...)
|
||||
ctx, span := h.tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...)
|
||||
defer span.End()
|
||||
|
||||
readRecordFunc := func(int64) {}
|
||||
|
@ -100,3 +100,63 @@ func TestBasicFilter(t *testing.T) {
|
||||
t.Fatalf("got %q, expected %q", got, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpanNameFormatter(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
name string
|
||||
formatter func(s string, r *http.Request) string
|
||||
operation string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "default formatter",
|
||||
formatter: defaultFormatter,
|
||||
operation: "test_operation",
|
||||
expected: "test_operation",
|
||||
},
|
||||
{
|
||||
name: "custom formatter",
|
||||
formatter: func(s string, r *http.Request) string {
|
||||
return r.URL.Path
|
||||
},
|
||||
operation: "",
|
||||
expected: "/hello",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
var id uint64
|
||||
var spanName string
|
||||
tracer := mocktrace.MockTracer{
|
||||
StartSpanID: &id,
|
||||
OnSpanStarted: func(span *mocktrace.MockSpan) {
|
||||
spanName = span.Name
|
||||
},
|
||||
}
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := io.WriteString(w, "hello world"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
h := NewHandler(
|
||||
handler,
|
||||
tc.operation,
|
||||
WithTracer(&tracer),
|
||||
WithSpanNameFormatter(tc.formatter),
|
||||
)
|
||||
r, err := http.NewRequest(http.MethodGet, "http://localhost/hello", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h.ServeHTTP(rr, r)
|
||||
if got, expected := rr.Result().StatusCode, http.StatusOK; got != expected {
|
||||
t.Fatalf("got %d, expected %d", got, expected)
|
||||
}
|
||||
if got, expected := spanName, tc.expected; got != expected {
|
||||
t.Fatalf("got %q, expected %q", got, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user