Skip to content

Commit 3cc8455

Browse files
m1x0naldas
authored andcommitted
Added possibility to customize span operationName
Extended TracerConfig with OperationNameFunc which allows to override default operation name during span creation. If there is no OperationNameFunc provided then fallback will be used which covers most of the use-cases.
1 parent ed09afe commit 3cc8455

File tree

2 files changed

+78
-7
lines changed

2 files changed

+78
-7
lines changed

jaegertracing/jaegertracing.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ type (
6262
// http body limit size (in bytes)
6363
// NOTE: don't specify values larger than 60000 as jaeger can't handle values in span.LogKV larger than 60000 bytes
6464
LimitSize int
65+
66+
// OperationNameFunc composes operation name based on context. Can be used to override default naming
67+
OperationNameFunc func(ctx echo.Context) string
6568
}
6669
)
6770

@@ -72,8 +75,9 @@ var (
7275
ComponentName: defaultComponentName,
7376
IsBodyDump: false,
7477

75-
LimitHTTPBody: true,
76-
LimitSize: 60_000,
78+
LimitHTTPBody: true,
79+
LimitSize: 60_000,
80+
OperationNameFunc: defaultOperationName,
7781
}
7882
)
7983

@@ -130,6 +134,9 @@ func TraceWithConfig(config TraceConfig) echo.MiddlewareFunc {
130134
if config.ComponentName == "" {
131135
config.ComponentName = defaultComponentName
132136
}
137+
if config.OperationNameFunc == nil {
138+
config.OperationNameFunc = defaultOperationName
139+
}
133140

134141
return func(next echo.HandlerFunc) echo.HandlerFunc {
135142
return func(c echo.Context) error {
@@ -138,7 +145,7 @@ func TraceWithConfig(config TraceConfig) echo.MiddlewareFunc {
138145
}
139146

140147
req := c.Request()
141-
opname := "HTTP " + req.Method + " URL: " + c.Path()
148+
opname := config.OperationNameFunc(c)
142149
realIP := c.RealIP()
143150
requestID := getRequestID(c) // request-id generated by reverse-proxy
144151

@@ -218,10 +225,10 @@ func TraceWithConfig(config TraceConfig) echo.MiddlewareFunc {
218225

219226
func limitString(str string, size int) string {
220227
if len(str) > size {
221-
return str[:size/2] + "\n---- skipped ----\n" + str[len(str)-size/2:]
228+
return str[:size/2] + "\n---- skipped ----\n" + str[len(str)-size/2:]
222229
}
223230

224-
return str
231+
return str
225232
}
226233

227234
func logError(span opentracing.Span, err error) {
@@ -248,6 +255,11 @@ func generateToken() string {
248255
return fmt.Sprintf("%x", b)
249256
}
250257

258+
func defaultOperationName(ctx echo.Context) string {
259+
req := ctx.Request()
260+
return "HTTP " + req.Method + " URL: " + ctx.Path()
261+
}
262+
251263
// TraceFunction wraps funtion with opentracing span adding tags for the function name and caller details
252264
func TraceFunction(ctx echo.Context, fn interface{}, params ...interface{}) (result []reflect.Value) {
253265
// Get function name

jaegertracing/jaegertracing_test.go

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"net/http"
88
"net/http/httptest"
9+
"strings"
910
"testing"
1011

1112
"github.com/labstack/echo/v4"
@@ -101,11 +102,14 @@ func (tr *mockTracer) currentSpan() *mockSpan {
101102
}
102103

103104
func (tr *mockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
104-
tr.hasStartSpanWithOption = (len(opts) > 0)
105+
tr.hasStartSpanWithOption = len(opts) > 0
105106
if tr.span != nil {
107+
tr.span.opName = operationName
106108
return tr.span
107109
}
108-
return createSpan(tr)
110+
span := createSpan(tr)
111+
span.opName = operationName
112+
return span
109113
}
110114

111115
func (tr *mockTracer) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error {
@@ -325,3 +329,58 @@ func TestTraceWithoutLimitHTTPBody(t *testing.T) {
325329
assert.Equal(t, "123456789012345678901234567890", tracer.currentSpan().getLog("http.req.body"))
326330
assert.Equal(t, "Hi 123456789012345678901234567890", tracer.currentSpan().getLog("http.resp.body"))
327331
}
332+
333+
func TestTraceWithDefaultOperationName(t *testing.T) {
334+
tracer := createMockTracer()
335+
336+
e := echo.New()
337+
e.Use(Trace(tracer))
338+
339+
e.GET("/trace", func(c echo.Context) error {
340+
return c.String(http.StatusOK, "Hi")
341+
})
342+
343+
req := httptest.NewRequest(http.MethodGet, "/trace", nil)
344+
rec := httptest.NewRecorder()
345+
e.ServeHTTP(rec, req)
346+
347+
assert.Equal(t, "HTTP GET URL: /trace", tracer.currentSpan().getOpName())
348+
}
349+
350+
func TestTraceWithCustomOperationName(t *testing.T) {
351+
tracer := createMockTracer()
352+
353+
e := echo.New()
354+
e.Use(TraceWithConfig(TraceConfig{
355+
Tracer: tracer,
356+
ComponentName: "EchoTracer",
357+
OperationNameFunc: func(ctx echo.Context) string {
358+
// This is an example of operation name customization
359+
// In most cases default formatting is more than enough
360+
req := ctx.Request()
361+
opName := "HTTP " + req.Method
362+
363+
path := ctx.Path()
364+
paramNames := ctx.ParamNames()
365+
366+
for _, name := range paramNames {
367+
from := ":" + name
368+
to := "{" + name + "}"
369+
path = strings.ReplaceAll(path, from, to)
370+
}
371+
372+
return opName + " " + path
373+
},
374+
}))
375+
376+
e.GET("/trace/:traceID/spans/:spanID", func(c echo.Context) error {
377+
return c.String(http.StatusOK, "Hi")
378+
})
379+
380+
req := httptest.NewRequest(http.MethodGet, "/trace/123456/spans/123", nil)
381+
rec := httptest.NewRecorder()
382+
e.ServeHTTP(rec, req)
383+
384+
assert.Equal(t, true, tracer.currentSpan().isFinished())
385+
assert.Equal(t, "HTTP GET /trace/{traceID}/spans/{spanID}", tracer.currentSpan().getOpName())
386+
}

0 commit comments

Comments
 (0)