Skip to content

Commit e3fc90c

Browse files
committed
added handling of OpenTracing LogFields in the same way as Jaeger by JSON encoding them
1 parent 9a72b9a commit e3fc90c

File tree

5 files changed

+244
-17
lines changed

5 files changed

+244
-17
lines changed

events/event_nettrace.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
package events
22

33
import (
4-
"github.com/opentracing/basictracer-go"
4+
"github.com/openzipkin/zipkin-go-opentracing"
55
"golang.org/x/net/trace"
66
)
77

8-
// NetTraceIntegrator can be passed into a basictracer as NewSpanEventListener
8+
// NetTraceIntegrator can be passed into a zipkintracer as NewSpanEventListener
99
// and causes all traces to be registered with the net/trace endpoint.
10-
var NetTraceIntegrator = func() func(basictracer.SpanEvent) {
10+
var NetTraceIntegrator = func() func(zipkintracer.SpanEvent) {
1111
var tr trace.Trace
12-
return func(e basictracer.SpanEvent) {
12+
return func(e zipkintracer.SpanEvent) {
1313
switch t := e.(type) {
14-
case basictracer.EventCreate:
14+
case zipkintracer.EventCreate:
1515
tr = trace.New("tracing", t.OperationName)
16-
case basictracer.EventFinish:
16+
case zipkintracer.EventFinish:
1717
tr.Finish()
18-
case basictracer.EventLog:
18+
case zipkintracer.EventLog:
1919
if t.Payload != nil {
2020
tr.LazyPrintf("%s (payload %v)", t.Event, t.Payload)
2121
} else {

json.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) 2016 Uber Technologies, Inc.
2+
// Copyright (c) 2016 Bas van Beek
3+
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy
5+
// of this software and associated documentation files (the "Software"), to deal
6+
// in the Software without restriction, including without limitation the rights
7+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
// copies of the Software, and to permit persons to whom the Software is
9+
// furnished to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in
12+
// all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
// THE SOFTWARE.
21+
22+
package zipkintracer
23+
24+
import (
25+
"encoding/json"
26+
"fmt"
27+
28+
"github.com/opentracing/opentracing-go/log"
29+
)
30+
31+
type fieldsAsMap map[string]string
32+
33+
// MaterializeWithJSON converts log Fields into JSON string
34+
func MaterializeWithJSON(logFields []log.Field) ([]byte, error) {
35+
fields := fieldsAsMap(make(map[string]string, len(logFields)))
36+
for _, field := range logFields {
37+
field.Marshal(fields)
38+
}
39+
// if we only have an event log Field we do not create a json serialization of
40+
// the key-value pairs contained within the log Fields, but simply return the
41+
// payload of the event log Field.
42+
if len(fields) == 1 {
43+
if event, ok := fields["event"]; ok {
44+
return []byte(event), nil
45+
}
46+
}
47+
return json.Marshal(fields)
48+
}
49+
50+
func (ml fieldsAsMap) EmitString(key, value string) {
51+
ml[key] = value
52+
}
53+
54+
func (ml fieldsAsMap) EmitBool(key string, value bool) {
55+
ml[key] = fmt.Sprintf("%t", value)
56+
}
57+
58+
func (ml fieldsAsMap) EmitInt(key string, value int) {
59+
ml[key] = fmt.Sprintf("%d", value)
60+
}
61+
62+
func (ml fieldsAsMap) EmitInt32(key string, value int32) {
63+
ml[key] = fmt.Sprintf("%d", value)
64+
}
65+
66+
func (ml fieldsAsMap) EmitInt64(key string, value int64) {
67+
ml[key] = fmt.Sprintf("%d", value)
68+
}
69+
70+
func (ml fieldsAsMap) EmitUint32(key string, value uint32) {
71+
ml[key] = fmt.Sprintf("%d", value)
72+
}
73+
74+
func (ml fieldsAsMap) EmitUint64(key string, value uint64) {
75+
ml[key] = fmt.Sprintf("%d", value)
76+
}
77+
78+
func (ml fieldsAsMap) EmitFloat32(key string, value float32) {
79+
ml[key] = fmt.Sprintf("%f", value)
80+
}
81+
82+
func (ml fieldsAsMap) EmitFloat64(key string, value float64) {
83+
ml[key] = fmt.Sprintf("%f", value)
84+
}
85+
86+
func (ml fieldsAsMap) EmitObject(key string, value interface{}) {
87+
ml[key] = fmt.Sprintf("%+v", value)
88+
}
89+
90+
func (ml fieldsAsMap) EmitLazyLogger(value log.LazyLogger) {
91+
value(ml)
92+
}

span_test.go

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package zipkintracer
22

33
import (
4+
"reflect"
45
"testing"
56

67
"github.com/opentracing/opentracing-go"
78
"github.com/opentracing/opentracing-go/ext"
9+
"github.com/opentracing/opentracing-go/log"
810
"github.com/stretchr/testify/assert"
911
)
1012

@@ -101,16 +103,25 @@ func TestSpan_SingleLoggedTaggedSpan(t *testing.T) {
101103

102104
span := tracer.StartSpan("x")
103105
span.LogEventWithPayload("event", "payload")
106+
span.LogFields(log.String("key_str", "value"), log.Uint32("32bit", 4294967295))
104107
span.SetTag("tag", "value")
105108
span.Finish()
106109
spans := recorder.GetSpans()
107110
assert.Equal(t, 1, len(spans))
108111
assert.Equal(t, "x", spans[0].Operation)
109-
assert.Equal(t, 1, len(spans[0].Logs))
112+
assert.Equal(t, 2, len(spans[0].Logs))
110113
// XXX: broken tests
111114
// assert.Equal(t, "event", spans[0].Logs[0].Event)
112115
// assert.Equal(t, "payload", spans[0].Logs[0].Payload)
113116
assert.Equal(t, opentracing.Tags{"tag": "value"}, spans[0].Tags)
117+
fv := NewLogFieldValidator(t, spans[0].Logs[0].Fields)
118+
fv.
119+
ExpectNextFieldEquals("event", reflect.String, "event").
120+
ExpectNextFieldEquals("payload", reflect.Interface, "payload")
121+
fv = NewLogFieldValidator(t, spans[0].Logs[1].Fields)
122+
fv.
123+
ExpectNextFieldEquals("key_str", reflect.String, "value").
124+
ExpectNextFieldEquals("32bit", reflect.Uint32, "4294967295")
114125
}
115126

116127
func TestSpan_TrimUnsampledSpans(t *testing.T) {
@@ -126,16 +137,17 @@ func TestSpan_TrimUnsampledSpans(t *testing.T) {
126137
}
127138

128139
span := tracer.StartSpan("x")
129-
span.LogEventWithPayload("event", "payload")
140+
span.LogFields(log.String("key_str", "value"), log.Uint32("32bit", 4294967295))
130141
span.SetTag("tag", "value")
131142
span.Finish()
132143
spans := recorder.GetSpans()
133144
assert.Equal(t, 1, len(spans))
134145
assert.Equal(t, 1, len(spans[0].Logs))
135-
// XXX: broken tests
136-
// assert.Equal(t, "event", spans[0].Logs[0].Event)
137-
// assert.Equal(t, "payload", spans[0].Logs[0].Payload)
138146
assert.Equal(t, opentracing.Tags{"tag": "value"}, spans[0].Tags)
147+
fv := NewLogFieldValidator(t, spans[0].Logs[0].Fields)
148+
fv.
149+
ExpectNextFieldEquals("key_str", reflect.String, "value").
150+
ExpectNextFieldEquals("32bit", reflect.Uint32, "4294967295")
139151

140152
recorder.Reset()
141153
// Tracer that trims only unsampled and never samples
@@ -149,7 +161,7 @@ func TestSpan_TrimUnsampledSpans(t *testing.T) {
149161
}
150162

151163
span = tracer.StartSpan("x")
152-
span.LogEventWithPayload("event", "payload")
164+
span.LogFields(log.String("key_str", "value"), log.Uint32("32bit", 4294967295))
153165
span.SetTag("tag", "value")
154166
span.Finish()
155167
spans = recorder.GetSpans()
@@ -171,7 +183,7 @@ func TestSpan_DropAllLogs(t *testing.T) {
171183
}
172184

173185
span := tracer.StartSpan("x")
174-
span.LogEventWithPayload("event", "payload")
186+
span.LogFields(log.String("key_str", "value"), log.Uint32("32bit", 4294967295))
175187
span.SetTag("tag", "value")
176188
span.Finish()
177189
spans := recorder.GetSpans()

testutil_test.go

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package zipkintracer
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"testing"
7+
8+
"github.com/opentracing/opentracing-go/log"
9+
)
10+
11+
// LogFieldValidator facilitates testing of Span.Log*() implementations.
12+
//
13+
// Usage:
14+
//
15+
// fv := log.NewLogFieldValidator(t, someLogStructure.Fields)
16+
// fv.
17+
// ExpectNextFieldEquals("key1", reflect.String, "some string value").
18+
// ExpectNextFieldEquals("key2", reflect.Uint32, "4294967295")
19+
//
20+
// LogFieldValidator satisfies the log.Encoder interface and thus is able to
21+
// marshal log.Field instances (which it takes advantage of internally).
22+
type LogFieldValidator struct {
23+
t *testing.T
24+
fieldIdx int
25+
fields []log.Field
26+
nextKey string
27+
nextKind reflect.Kind
28+
nextValAsString string
29+
}
30+
31+
// NewLogFieldValidator returns a new validator that will test the contents of
32+
// `fields`.
33+
func NewLogFieldValidator(t *testing.T, fields []log.Field) *LogFieldValidator {
34+
return &LogFieldValidator{
35+
t: t,
36+
fields: fields,
37+
}
38+
}
39+
40+
// ExpectNextFieldEquals facilitates a fluent way of testing the contents
41+
// []Field slices.
42+
func (fv *LogFieldValidator) ExpectNextFieldEquals(key string, kind reflect.Kind, valAsString string) *LogFieldValidator {
43+
if len(fv.fields) < fv.fieldIdx {
44+
fv.t.Errorf("Expecting more than the %v Fields we have", len(fv.fields))
45+
}
46+
fv.nextKey = key
47+
fv.nextKind = kind
48+
fv.nextValAsString = valAsString
49+
fv.fields[fv.fieldIdx].Marshal(fv)
50+
fv.fieldIdx++
51+
return fv
52+
}
53+
54+
// EmitString satisfies the Encoder interface
55+
func (fv *LogFieldValidator) EmitString(key, value string) {
56+
fv.validateNextField(key, reflect.String, value)
57+
}
58+
59+
// EmitBool satisfies the Encoder interface
60+
func (fv *LogFieldValidator) EmitBool(key string, value bool) {
61+
fv.validateNextField(key, reflect.Bool, value)
62+
}
63+
64+
// EmitInt satisfies the Encoder interface
65+
func (fv *LogFieldValidator) EmitInt(key string, value int) {
66+
fv.validateNextField(key, reflect.Int, value)
67+
}
68+
69+
// EmitInt32 satisfies the Encoder interface
70+
func (fv *LogFieldValidator) EmitInt32(key string, value int32) {
71+
fv.validateNextField(key, reflect.Int32, value)
72+
}
73+
74+
// EmitInt64 satisfies the Encoder interface
75+
func (fv *LogFieldValidator) EmitInt64(key string, value int64) {
76+
fv.validateNextField(key, reflect.Int64, value)
77+
}
78+
79+
// EmitUint32 satisfies the Encoder interface
80+
func (fv *LogFieldValidator) EmitUint32(key string, value uint32) {
81+
fv.validateNextField(key, reflect.Uint32, value)
82+
}
83+
84+
// EmitUint64 satisfies the Encoder interface
85+
func (fv *LogFieldValidator) EmitUint64(key string, value uint64) {
86+
fv.validateNextField(key, reflect.Uint64, value)
87+
}
88+
89+
// EmitFloat32 satisfies the Encoder interface
90+
func (fv *LogFieldValidator) EmitFloat32(key string, value float32) {
91+
fv.validateNextField(key, reflect.Float32, value)
92+
}
93+
94+
// EmitFloat64 satisfies the Encoder interface
95+
func (fv *LogFieldValidator) EmitFloat64(key string, value float64) {
96+
fv.validateNextField(key, reflect.Float64, value)
97+
}
98+
99+
// EmitObject satisfies the Encoder interface
100+
func (fv *LogFieldValidator) EmitObject(key string, value interface{}) {
101+
fv.validateNextField(key, reflect.Interface, value)
102+
}
103+
104+
// EmitLazyLogger satisfies the Encoder interface
105+
func (fv *LogFieldValidator) EmitLazyLogger(value log.LazyLogger) {
106+
fv.t.Error("Test infrastructure does not support EmitLazyLogger yet")
107+
}
108+
109+
func (fv *LogFieldValidator) validateNextField(key string, actualKind reflect.Kind, value interface{}) {
110+
if fv.nextKey != key {
111+
fv.t.Errorf("Bad key: expected %q, found %q", fv.nextKey, key)
112+
}
113+
if fv.nextKind != actualKind {
114+
fv.t.Errorf("Bad reflect.Kind: expected %v, found %v", fv.nextKind, actualKind)
115+
return
116+
}
117+
if fv.nextValAsString != fmt.Sprint(value) {
118+
fv.t.Errorf("Bad value: expected %q, found %q", fv.nextValAsString, fmt.Sprint(value))
119+
}
120+
// All good.
121+
}

zipkin-recorder.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,11 @@ func (r *Recorder) RecordSpan(sp RawSpan) {
117117
}
118118

119119
for _, spLog := range sp.Logs {
120-
// XXX: decide how to represent spLog.Fields as an annotation
121-
var annotationValue string
122-
annotate(span, spLog.Timestamp, annotationValue, r.endpoint)
120+
if logs, err := MaterializeWithJSON(spLog.Fields); err != nil {
121+
fmt.Printf("JSON serialization of OpenTracing LogFields failed: %+v", err)
122+
} else {
123+
annotate(span, spLog.Timestamp, string(logs), r.endpoint)
124+
}
123125
}
124126
_ = r.collector.Collect(span)
125127
}

0 commit comments

Comments
 (0)