Skip to content

Commit f849b54

Browse files
authored
Add if the trace was sampled to the ctxTags (#184)
* we now will not add the traceId / spanId to ctxtags if it is not sampled * update to add sampled to the tags * code review * OT explicitly check value is true or false * OT not add tag if unknown value
1 parent 4832df0 commit f849b54

File tree

3 files changed

+69
-46
lines changed

3 files changed

+69
-46
lines changed

tracing/opentracing/id_extract.go

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,67 @@
1-
// Copyright 2017 Michal Witkowski. All Rights Reserved.
2-
// See LICENSE for licensing terms.
3-
41
package grpc_opentracing
52

63
import (
74
"strings"
85

9-
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
10-
"github.com/opentracing/opentracing-go"
6+
grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
7+
opentracing "github.com/opentracing/opentracing-go"
118
"google.golang.org/grpc/grpclog"
129
)
1310

1411
const (
15-
TagTraceId = "trace.traceid"
16-
TagSpanId = "trace.spanid"
12+
TagTraceId = "trace.traceid"
13+
TagSpanId = "trace.spanid"
14+
TagSampled = "trace.sampled"
15+
jaegerNotSampledFlag = "0"
1716
)
1817

19-
// hackyInjectOpentracingIdsToTags writes the given context to the ctxtags.
18+
// injectOpentracingIdsToTags writes trace data to ctxtags.
2019
// This is done in an incredibly hacky way, because the public-facing interface of opentracing doesn't give access to
2120
// the TraceId and SpanId of the SpanContext. Only the Tracer's Inject/Extract methods know what these are.
2221
// Most tracers have them encoded as keys with 'traceid' and 'spanid':
2322
// https://github.com/openzipkin/zipkin-go-opentracing/blob/594640b9ef7e5c994e8d9499359d693c032d738c/propagation_ot.go#L29
2423
// https://github.com/opentracing/basictracer-go/blob/1b32af207119a14b1b231d451df3ed04a72efebf/propagation_ot.go#L26
2524
// Jaeger from Uber use one-key schema with next format '{trace-id}:{span-id}:{parent-span-id}:{flags}'
2625
// https://www.jaegertracing.io/docs/client-libraries/#trace-span-identity
27-
func hackyInjectOpentracingIdsToTags(span opentracing.Span, tags grpc_ctxtags.Tags) {
28-
if err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, &hackyTagsCarrier{tags}); err != nil {
29-
grpclog.Printf("grpc_opentracing: failed extracting trace info into ctx %v", err)
26+
func injectOpentracingIdsToTags(span opentracing.Span, tags grpc_ctxtags.Tags) {
27+
if err := span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, &tagsCarrier{tags}); err != nil {
28+
grpclog.Infof("grpc_opentracing: failed extracting trace info into ctx %v", err)
3029
}
3130
}
3231

33-
// hackyTagsCarrier is a really hacky way of
34-
type hackyTagsCarrier struct {
32+
// tagsCarrier is a really hacky way of
33+
type tagsCarrier struct {
3534
grpc_ctxtags.Tags
3635
}
3736

38-
func (t *hackyTagsCarrier) Set(key, val string) {
39-
if strings.Contains(key, "traceid") || strings.Contains(strings.ToLower(key), "traceid") {
37+
func (t *tagsCarrier) Set(key, val string) {
38+
key = strings.ToLower(key)
39+
if strings.Contains(key, "traceid") {
4040
t.Tags.Set(TagTraceId, val) // this will most likely be base-16 (hex) encoded
41-
} else if (strings.Contains(key, "spanid") && !strings.Contains(key, "parent")) || (strings.Contains(strings.ToLower(key), "spanid") && !strings.Contains(strings.ToLower(key), "parent")) {
41+
}
42+
43+
if strings.Contains(key, "spanid") && !strings.Contains(strings.ToLower(key), "parent") {
4244
t.Tags.Set(TagSpanId, val) // this will most likely be base-16 (hex) encoded
43-
} else if key == "uber-trace-id" {
45+
}
46+
47+
if strings.Contains(key, "sampled") {
48+
switch val {
49+
case "true", "false":
50+
t.Tags.Set(TagSampled, val)
51+
}
52+
}
53+
54+
if key == "uber-trace-id" {
4455
parts := strings.Split(val, ":")
45-
if len(parts) >= 2 {
56+
if len(parts) == 4 {
4657
t.Tags.Set(TagTraceId, parts[0])
4758
t.Tags.Set(TagSpanId, parts[1])
59+
60+
if parts[3] != jaegerNotSampledFlag {
61+
t.Tags.Set(TagSampled, "true")
62+
} else {
63+
t.Tags.Set(TagSampled, "false")
64+
}
4865
}
4966
}
5067
}

tracing/opentracing/interceptors_test.go

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package grpc_opentracing_test
55

66
import (
7-
"encoding/json"
87
"errors"
98
"strconv"
109
"strings"
@@ -17,10 +16,10 @@ import (
1716

1817
"github.com/grpc-ecosystem/go-grpc-middleware"
1918
"github.com/grpc-ecosystem/go-grpc-middleware/tags"
20-
grpc_testing "github.com/grpc-ecosystem/go-grpc-middleware/testing"
19+
"github.com/grpc-ecosystem/go-grpc-middleware/testing"
2120
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
22-
grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
23-
opentracing "github.com/opentracing/opentracing-go"
21+
"github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
22+
"github.com/opentracing/opentracing-go"
2423
"github.com/opentracing/opentracing-go/mocktracer"
2524
"github.com/stretchr/testify/assert"
2625
"github.com/stretchr/testify/require"
@@ -36,20 +35,6 @@ var (
3635
fakeInboundSpanId = 999
3736
)
3837

39-
func tagsToJson(value map[string]interface{}) string {
40-
str, _ := json.Marshal(value)
41-
return string(str)
42-
}
43-
44-
func tagsFromJson(t *testing.T, jstring string) map[string]interface{} {
45-
var msgMapTemplate interface{}
46-
err := json.Unmarshal([]byte(jstring), &msgMapTemplate)
47-
if err != nil {
48-
t.Fatalf("failed unmarshaling tags from response %v", err)
49-
}
50-
return msgMapTemplate.(map[string]interface{})
51-
}
52-
5338
type tracingAssertService struct {
5439
pb_testproto.TestServiceServer
5540
T *testing.T
@@ -59,7 +44,9 @@ func (s *tracingAssertService) Ping(ctx context.Context, ping *pb_testproto.Ping
5944
assert.NotNil(s.T, opentracing.SpanFromContext(ctx), "handlers must have the spancontext in their context, otherwise propagation will fail")
6045
tags := grpc_ctxtags.Extract(ctx)
6146
assert.True(s.T, tags.Has(grpc_opentracing.TagTraceId), "tags must contain traceid")
62-
assert.True(s.T, tags.Has(grpc_opentracing.TagSpanId), "tags must contain traceid")
47+
assert.True(s.T, tags.Has(grpc_opentracing.TagSpanId), "tags must contain spanid")
48+
assert.True(s.T, tags.Has(grpc_opentracing.TagSampled), "tags must contain sampled")
49+
assert.Equal(s.T, tags.Values()[grpc_opentracing.TagSampled], "true", "sampled must be set to true")
6350
return s.TestServiceServer.Ping(ctx, ping)
6451
}
6552

@@ -72,12 +59,19 @@ func (s *tracingAssertService) PingList(ping *pb_testproto.PingRequest, stream p
7259
assert.NotNil(s.T, opentracing.SpanFromContext(stream.Context()), "handlers must have the spancontext in their context, otherwise propagation will fail")
7360
tags := grpc_ctxtags.Extract(stream.Context())
7461
assert.True(s.T, tags.Has(grpc_opentracing.TagTraceId), "tags must contain traceid")
75-
assert.True(s.T, tags.Has(grpc_opentracing.TagSpanId), "tags must contain traceid")
62+
assert.True(s.T, tags.Has(grpc_opentracing.TagSpanId), "tags must contain spanid")
63+
assert.True(s.T, tags.Has(grpc_opentracing.TagSampled), "tags must contain sampled")
64+
assert.Equal(s.T, tags.Values()[grpc_opentracing.TagSampled], "true", "sampled must be set to true")
7665
return s.TestServiceServer.PingList(ping, stream)
7766
}
7867

7968
func (s *tracingAssertService) PingEmpty(ctx context.Context, empty *pb_testproto.Empty) (*pb_testproto.PingResponse, error) {
8069
assert.NotNil(s.T, opentracing.SpanFromContext(ctx), "handlers must have the spancontext in their context, otherwise propagation will fail")
70+
tags := grpc_ctxtags.Extract(ctx)
71+
assert.True(s.T, tags.Has(grpc_opentracing.TagTraceId), "tags must contain traceid")
72+
assert.True(s.T, tags.Has(grpc_opentracing.TagSpanId), "tags must contain spanid")
73+
assert.True(s.T, tags.Has(grpc_opentracing.TagSampled), "tags must contain sampled")
74+
assert.Equal(s.T, tags.Values()[grpc_opentracing.TagSampled], "false", "sampled must be set to false")
8175
return s.TestServiceServer.PingEmpty(ctx, empty)
8276
}
8377

@@ -135,12 +129,17 @@ func (s *OpentracingSuite) SetupTest() {
135129
s.mockTracer.Reset()
136130
}
137131

138-
func (s *OpentracingSuite) createContextFromFakeHttpRequestParent(ctx context.Context) context.Context {
132+
func (s *OpentracingSuite) createContextFromFakeHttpRequestParent(ctx context.Context, sampled bool) context.Context {
133+
jFlag := 0
134+
if sampled {
135+
jFlag = 1
136+
}
137+
139138
hdr := http.Header{}
140-
hdr.Set("uber-trace-id", fmt.Sprintf("%d:%d:%d:1", fakeInboundTraceId, fakeInboundSpanId, fakeInboundSpanId))
139+
hdr.Set("uber-trace-id", fmt.Sprintf("%d:%d:%d:%d", fakeInboundTraceId, fakeInboundSpanId, fakeInboundSpanId, jFlag))
141140
hdr.Set("mockpfx-ids-traceid", fmt.Sprint(fakeInboundTraceId))
142141
hdr.Set("mockpfx-ids-spanid", fmt.Sprint(fakeInboundSpanId))
143-
hdr.Set("mockpfx-ids-sampled", fmt.Sprint(true))
142+
hdr.Set("mockpfx-ids-sampled", fmt.Sprint(sampled))
144143

145144
parentSpanContext, err := s.mockTracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(hdr))
146145
require.NoError(s.T(), err, "parsing a fake HTTP request headers shouldn't fail, ever")
@@ -179,7 +178,7 @@ func (s *OpentracingSuite) assertTracesCreated(methodName string) (clientSpan *m
179178
}
180179

181180
func (s *OpentracingSuite) TestPing_PropagatesTraces() {
182-
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx())
181+
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx(), true)
183182
_, err := s.Client.Ping(ctx, goodPing)
184183
require.NoError(s.T(), err, "there must be not be an on a successful call")
185184
s.assertTracesCreated("/mwitkow.testproto.TestService/Ping")
@@ -188,7 +187,7 @@ func (s *OpentracingSuite) TestPing_PropagatesTraces() {
188187
func (s *OpentracingSuite) TestPing_ClientContextTags() {
189188
const name = "opentracing.custom"
190189
ctx := grpc_opentracing.ClientAddContextTags(
191-
s.createContextFromFakeHttpRequestParent(s.SimpleCtx()),
190+
s.createContextFromFakeHttpRequestParent(s.SimpleCtx(), true),
192191
opentracing.Tags{name: ""},
193192
)
194193

@@ -206,7 +205,7 @@ func (s *OpentracingSuite) TestPing_ClientContextTags() {
206205
}
207206

208207
func (s *OpentracingSuite) TestPingList_PropagatesTraces() {
209-
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx())
208+
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx(), true)
210209
stream, err := s.Client.PingList(ctx, goodPing)
211210
require.NoError(s.T(), err, "should not fail on establishing the stream")
212211
for {
@@ -220,7 +219,7 @@ func (s *OpentracingSuite) TestPingList_PropagatesTraces() {
220219
}
221220

222221
func (s *OpentracingSuite) TestPingError_PropagatesTraces() {
223-
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx())
222+
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx(), true)
224223
erroringPing := &pb_testproto.PingRequest{Value: "something", ErrorCodeReturned: uint32(codes.OutOfRange)}
225224
_, err := s.Client.PingError(ctx, erroringPing)
226225
require.Error(s.T(), err, "there must be an error returned here")
@@ -229,6 +228,12 @@ func (s *OpentracingSuite) TestPingError_PropagatesTraces() {
229228
assert.Equal(s.T(), true, serverSpan.Tag("error"), "server span needs to be marked as an error")
230229
}
231230

231+
func (s *OpentracingSuite) TestPingEmpty_NotSampleTraces() {
232+
ctx := s.createContextFromFakeHttpRequestParent(s.SimpleCtx(), false)
233+
_, err := s.Client.PingEmpty(ctx, &pb_testproto.Empty{})
234+
require.NoError(s.T(), err, "there must be not be an on a successful call")
235+
}
236+
232237
type jaegerFormatInjector struct{}
233238

234239
func (jaegerFormatInjector) Inject(ctx mocktracer.MockSpanContext, carrier interface{}) error {

tracing/opentracing/server_interceptors.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ func newServerSpanFromInbound(ctx context.Context, tracer opentracing.Tracer, fu
6262
ext.RPCServerOption(parentSpanContext),
6363
grpcTag,
6464
)
65-
hackyInjectOpentracingIdsToTags(serverSpan, grpc_ctxtags.Extract(ctx))
65+
66+
injectOpentracingIdsToTags(serverSpan, grpc_ctxtags.Extract(ctx))
6667
return opentracing.ContextWithSpan(ctx, serverSpan), serverSpan
6768
}
6869

0 commit comments

Comments
 (0)