Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Span flags are not decoded #11267

Closed
MrAlias opened this issue Sep 24, 2024 · 1 comment · Fixed by #11275
Closed

Span flags are not decoded #11267

MrAlias opened this issue Sep 24, 2024 · 1 comment · Fixed by #11275

Comments

@MrAlias
Copy link
Contributor

MrAlias commented Sep 24, 2024

When decoding OTLP data encoded as JSON, the Span.Flags field is not decoded.

$ cat go.mod
module testing/flag

go 1.23.1

require (
	github.com/stretchr/testify v1.9.0
	go.opentelemetry.io/collector/pdata v1.16.0
)

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/kr/text v0.2.0 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	go.uber.org/multierr v1.11.0 // indirect
	golang.org/x/net v0.26.0 // indirect
	golang.org/x/sys v0.21.0 // indirect
	golang.org/x/text v0.16.0 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
	google.golang.org/grpc v1.66.2 // indirect
	google.golang.org/protobuf v1.34.2 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)
package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/pdata/ptrace"
)

var (
	pSpanA = func() ptrace.Span {
		s := ptrace.NewSpan()
		s.SetTraceID(pcommon.TraceID([16]byte{0x1}))
		s.SetSpanID(pcommon.SpanID([8]byte{0x2}))
		s.SetFlags(1)
		return s
	}()

	pScope = func() pcommon.InstrumentationScope {
		s := pcommon.NewInstrumentationScope()
		s.SetName("test")
		s.SetVersion("v0.0.1")
		return s
	}()

	pScopeSpans = func() ptrace.ScopeSpans {
		s := ptrace.NewScopeSpans()
		pSpanA.CopyTo(s.Spans().AppendEmpty())
		pScope.CopyTo(s.Scope())
		return s
	}()

	pResSpans = func() ptrace.ResourceSpans {
		rs := ptrace.NewResourceSpans()
		pScopeSpans.CopyTo(rs.ScopeSpans().AppendEmpty())
		return rs
	}()

	pTraces = func() ptrace.Traces {
		traces := ptrace.NewTraces()
		pResSpans.CopyTo(traces.ResourceSpans().AppendEmpty())
		return traces
	}()
)

func TestDecode(t *testing.T) {
	var enc ptrace.JSONMarshaler
	b, err := enc.MarshalTraces(pTraces)
	require.NoError(t, err)

	t.Log(string(b))
	assert.Equal(t, data, b)
}

//	{
//	 "resourceSpans": [
//	   {
//	     "resource": {},
//	     "scopeSpans": [
//	       {
//	         "scope": {
//	           "name": "test",
//	           "version": "v0.0.1"
//	         },
//	         "spans": [
//	           {
//	             "traceId": "01000000000000000000000000000000",
//	             "spanId": "0200000000000000",
//	             "flags": 1,
//	           }
//	         ]
//	       }
//	     ]
//	   }
//	 ]
//	}
var data = []byte(`{"resourceSpans":[{"resource":{},"scopeSpans":[{"scope":{"name":"test","version":"v0.0.1"},"spans":[{"traceId":"01000000000000000000000000000000","spanId":"0200000000000000","parentSpanId":"","flags":1,"status":{}}]}]}]}`)

func TestEncode(t *testing.T) {
	var dec ptrace.JSONUnmarshaler
	got, err := dec.UnmarshalTraces(data)
	require.NoError(t, err)
	assert.Equal(t, pTraces, got)
}
$ go test -v .
=== RUN   TestDecode
    bug_test.go:53: {"resourceSpans":[{"resource":{},"scopeSpans":[{"scope":{"name":"test","version":"v0.0.1"},"spans":[{"traceId":"01000000000000000000000000000000","spanId":"0200000000000000","parentSpanId":"","flags":1,"status":{}}]}]}]}
--- PASS: TestDecode (0.00s)
=== RUN   TestEncode
    bug_test.go:85:
        	Error Trace:	/home/tyler/go/src/testing/flag/bug_test.go:85
        	Error:      	Not equal:
        	            	expected: ptrace.Traces{orig:(*v1.ExportTraceServiceRequest)(0xc000012b28), state:(*internal.State)(0xc000015758)}
        	            	actual  : ptrace.Traces{orig:(*v1.ExportTraceServiceRequest)(0xc000012de0), state:(*internal.State)(0xc000015f88)}
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -29,3 +29,3 @@
        	            	         },
        	            	-        Flags: (uint32) 1,
        	            	+        Flags: (uint32) 0,
        	            	         Name: (string) "",
        	Test:       	TestEncode
--- FAIL: TestEncode (0.00s)
FAIL
FAIL	testing/flag	0.005s
FAIL

It looks to be a missing case statement here:

func (dest Span) unmarshalJsoniter(iter *jsoniter.Iterator) {
iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool {
switch f {
case "traceId", "trace_id":
if err := dest.orig.TraceId.UnmarshalJSON([]byte(iter.ReadString())); err != nil {
iter.ReportError("readSpan.traceId", fmt.Sprintf("parse trace_id:%v", err))
}
case "spanId", "span_id":
if err := dest.orig.SpanId.UnmarshalJSON([]byte(iter.ReadString())); err != nil {
iter.ReportError("readSpan.spanId", fmt.Sprintf("parse span_id:%v", err))
}
case "traceState", "trace_state":
dest.TraceState().FromRaw(iter.ReadString())
case "parentSpanId", "parent_span_id":
if err := dest.orig.ParentSpanId.UnmarshalJSON([]byte(iter.ReadString())); err != nil {
iter.ReportError("readSpan.parentSpanId", fmt.Sprintf("parse parent_span_id:%v", err))
}
case "name":
dest.orig.Name = iter.ReadString()
case "kind":
dest.orig.Kind = otlptrace.Span_SpanKind(json.ReadEnumValue(iter, otlptrace.Span_SpanKind_value))
case "startTimeUnixNano", "start_time_unix_nano":
dest.orig.StartTimeUnixNano = json.ReadUint64(iter)
case "endTimeUnixNano", "end_time_unix_nano":
dest.orig.EndTimeUnixNano = json.ReadUint64(iter)
case "attributes":
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
dest.orig.Attributes = append(dest.orig.Attributes, json.ReadAttribute(iter))
return true
})
case "droppedAttributesCount", "dropped_attributes_count":
dest.orig.DroppedAttributesCount = json.ReadUint32(iter)
case "events":
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
dest.Events().AppendEmpty().unmarshalJsoniter(iter)
return true
})
case "droppedEventsCount", "dropped_events_count":
dest.orig.DroppedEventsCount = json.ReadUint32(iter)
case "links":
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
dest.Links().AppendEmpty().unmarshalJsoniter(iter)
return true
})
case "droppedLinksCount", "dropped_links_count":
dest.orig.DroppedLinksCount = json.ReadUint32(iter)
case "status":
dest.Status().unmarshalJsoniter(iter)
default:
iter.Skip()
}
return true
})
}

@MrAlias
Copy link
Contributor Author

MrAlias commented Sep 24, 2024

The same omission looks to exist for the SpanLink type as well:

switch f {
case "traceId", "trace_id":
if err := dest.orig.TraceId.UnmarshalJSON([]byte(iter.ReadString())); err != nil {
iter.ReportError("readSpanLink", fmt.Sprintf("parse trace_id:%v", err))
}
case "spanId", "span_id":
if err := dest.orig.SpanId.UnmarshalJSON([]byte(iter.ReadString())); err != nil {
iter.ReportError("readSpanLink", fmt.Sprintf("parse span_id:%v", err))
}
case "traceState", "trace_state":
dest.orig.TraceState = iter.ReadString()
case "attributes":
iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool {
dest.orig.Attributes = append(dest.orig.Attributes, json.ReadAttribute(iter))
return true
})
case "droppedAttributesCount", "dropped_attributes_count":
dest.orig.DroppedAttributesCount = json.ReadUint32(iter)
default:
iter.Skip()
}

MrAlias added a commit to MrAlias/opentelemetry-collector that referenced this issue Sep 25, 2024
For both the `Span` and `SpanLink`, the current unmarshalJsoniter
methods do not unmarshal the `flags` field. This updates the
functionality to correctly decode these values into the destination.

Fix open-telemetry#11267
MrAlias added a commit to MrAlias/opentelemetry-collector that referenced this issue Sep 25, 2024
For both the `Span` and `SpanLink`, the current unmarshalJsoniter
methods do not unmarshal the `flags` field. This updates the
functionality to correctly decode these values into the destination.

Fix open-telemetry#11267

Signed-off-by: Tyler Yahn <codingalias@gmail.com>
MrAlias added a commit to MrAlias/opentelemetry-collector that referenced this issue Sep 25, 2024
For both the `Span` and `SpanLink`, the current unmarshalJsoniter
methods do not unmarshal the `flags` field. This updates the
functionality to correctly decode these values into the destination.

Fix open-telemetry#11267

Signed-off-by: Tyler Yahn <codingalias@gmail.com>
jackgopack4 pushed a commit to jackgopack4/opentelemetry-collector that referenced this issue Oct 8, 2024
For both the `Span` and `SpanLink`, the current unmarshalJsoniter
methods do not unmarshal the `flags` field. This updates the
functionality to correctly decode these values into the destination.

Fix open-telemetry#11267

Signed-off-by: Tyler Yahn <codingalias@gmail.com>

---------

Signed-off-by: Tyler Yahn <codingalias@gmail.com>
Co-authored-by: Bogdan Drutu <bogdandrutu@gmail.com>
Co-authored-by: Alex Boten <223565+codeboten@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant