diff --git a/CHANGELOG.md b/CHANGELOG.md index 7938fd0d625..9b7242c3c5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Removed ### Fixed +- Adds `trace.Link`'s own MarshalJSON method to marshal all fields, not just fields under embedded struct `trace.SpanContext` (#1820) ### Security diff --git a/trace/trace.go b/trace/trace.go index d372e7d9d72..7e4427a7c15 100644 --- a/trace/trace.go +++ b/trace/trace.go @@ -176,7 +176,7 @@ func decodeHex(h string, b []byte) error { // // Trace state must be valid according to the W3C Trace Context specification at all times. All // mutating operations validate their input and, in case of valid parameters, return a new TraceState. -type TraceState struct { //nolint:golint +type TraceState struct { // nolint:golint // TODO @matej-g: Consider implementing this as attribute.Set, see // comment https://github.com/open-telemetry/opentelemetry-go/pull/1340#discussion_r540599226 kvs []attribute.KeyValue @@ -278,7 +278,7 @@ func (ts TraceState) copyKVsAndDeleteEntry(key attribute.Key) []attribute.KeyVal // TraceStateFromKeyValues is a convenience method to create a new TraceState from // provided key/value pairs. -func TraceStateFromKeyValues(kvs ...attribute.KeyValue) (TraceState, error) { //nolint:golint +func TraceStateFromKeyValues(kvs ...attribute.KeyValue) (TraceState, error) { // nolint:golint if len(kvs) == 0 { return TraceState{}, nil } @@ -314,7 +314,7 @@ func isTraceStateKeyValueValid(kv attribute.KeyValue) bool { } // TraceFlags contains flags that can be set on a SpanContext -type TraceFlags byte //nolint:golint +type TraceFlags byte // nolint:golint // IsSampled returns if the sampling bit is set in the TraceFlags. func (tf TraceFlags) IsSampled() bool { @@ -582,6 +582,19 @@ type Link struct { DroppedAttributeCount int } +// Implements MarshalJSON to marshal all fields of link not just SpanContext +func (l Link) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + SpanContext SpanContext + Attributes []attribute.KeyValue + DroppedAttributeCount int + }{ + SpanContext: l.SpanContext, + Attributes: l.Attributes, + DroppedAttributeCount: l.DroppedAttributeCount, + }) +} + // SpanKind is the role a Span plays in a Trace. type SpanKind int diff --git a/trace/trace_test.go b/trace/trace_test.go index dc8afe47e50..0f3f78890ad 100644 --- a/trace/trace_test.go +++ b/trace/trace_test.go @@ -15,6 +15,8 @@ package trace import ( + "bytes" + "encoding/json" "fmt" "testing" @@ -128,7 +130,7 @@ func TestHasTraceID(t *testing.T) { }, } { t.Run(testcase.name, func(t *testing.T) { - //proto: func (sc SpanContext) HasTraceID() bool{} + // proto: func (sc SpanContext) HasTraceID() bool{} sc := SpanContext{traceID: testcase.tid} have := sc.HasTraceID() if have != testcase.want { @@ -155,7 +157,7 @@ func TestHasSpanID(t *testing.T) { }, } { t.Run(testcase.name, func(t *testing.T) { - //proto: func (sc SpanContext) HasSpanID() bool {} + // proto: func (sc SpanContext) HasSpanID() bool {} have := testcase.sc.HasSpanID() if have != testcase.want { t.Errorf("Want: %v, but have: %v", testcase.want, have) @@ -254,7 +256,7 @@ func TestStringTraceID(t *testing.T) { }, } { t.Run(testcase.name, func(t *testing.T) { - //proto: func (t TraceID) String() string {} + // proto: func (t TraceID) String() string {} have := testcase.tid.String() if have != testcase.want { t.Errorf("Want: %s, but have: %s", testcase.want, have) @@ -281,7 +283,7 @@ func TestStringSpanID(t *testing.T) { }, } { t.Run(testcase.name, func(t *testing.T) { - //proto: func (t TraceID) String() string {} + // proto: func (t TraceID) String() string {} have := testcase.sid.String() if have != testcase.want { t.Errorf("Want: %s, but have: %s", testcase.want, have) @@ -1023,3 +1025,29 @@ func TestSpanContextDerivation(t *testing.T) { t.Fatalf("WithTraceState: Unexpected context created: %s", cmp.Diff(modified, to)) } } + +func TestTraceLinkMarshalJSON(t *testing.T) { + link := Link{ + SpanContext: SpanContext{ + traceID: TraceID([16]byte{1}), + spanID: SpanID([8]byte{42}), + traceFlags: 0x0, + traceState: TraceState{}, + }, + Attributes: []attribute.KeyValue{ + attribute.String("foo", "8"), + attribute.String("bar", "22"), + }, + DroppedAttributeCount: 1, + } + + expected := "{\"SpanContext\":{\"TraceID\":\"01000000000000000000000000000000\",\"SpanID\":\"2a00000000000000\",\"TraceFlags\":\"00\",\"TraceState\":null,\"Remote\":false},\"Attributes\":[{\"Key\":\"foo\",\"Value\":{\"Type\":\"STRING\",\"Value\":\"8\"}},{\"Key\":\"bar\",\"Value\":{\"Type\":\"STRING\",\"Value\":\"22\"}}],\"DroppedAttributeCount\":1}" + + m, err := json.Marshal(link) + if err != nil { + t.Fatalf("Link MarshalJSON() err: %v", err) + } + if !bytes.Equal(m, []byte(expected)) { + t.Errorf("Link MarshalJSON() expected %s, got %s", expected, string(m)) + } +}