diff --git a/go.mod b/go.mod index fd38e0cb372..13940d67c63 100644 --- a/go.mod +++ b/go.mod @@ -55,10 +55,13 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // 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 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.12.1 // indirect diff --git a/go.sum b/go.sum index 3e53ede136f..96feefd9ce6 100644 --- a/go.sum +++ b/go.sum @@ -87,6 +87,7 @@ github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuD github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -303,6 +304,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -368,9 +370,11 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mostynb/go-grpc-compression v1.1.16 h1:D9tGUINmcII049pxOj9dl32Fzhp26TrDVQXECoKJqQg= github.com/mostynb/go-grpc-compression v1.1.16/go.mod h1:xxa6UoYynYS2h+5HB/Hglu81iYAp87ARaNmhhwi0s1s= diff --git a/model/go.mod b/model/go.mod index a870112b66b..dd60cea3391 100644 --- a/model/go.mod +++ b/model/go.mod @@ -4,14 +4,17 @@ go 1.17 require ( github.com/gogo/protobuf v1.3.2 + github.com/json-iterator/go v1.1.12 github.com/stretchr/testify v1.7.0 google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect diff --git a/model/go.sum b/model/go.sum index a912e82f0da..87584e36c13 100644 --- a/model/go.sum +++ b/model/go.sum @@ -11,8 +11,9 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -44,15 +45,23 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= diff --git a/model/otlp/json_test.go b/model/otlp/json_test.go index cde75064a37..b394cf128c3 100644 --- a/model/otlp/json_test.go +++ b/model/otlp/json_test.go @@ -16,6 +16,7 @@ package otlp import ( "testing" + "time" "github.com/stretchr/testify/assert" @@ -235,3 +236,112 @@ func TestMetricsNil(t *testing.T) { assert.EqualValues(t, pdata.Metrics{}, got) } + +var tracesOTLPFull = func() pdata.Traces { + traceID := pdata.NewTraceID([16]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}) + spanID := pdata.NewSpanID([8]byte{0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}) + td := pdata.NewTraces() + // Add ResourceSpans. + rs := td.ResourceSpans().AppendEmpty() + rs.SetSchemaUrl("schemaURL") + // Add resource. + rs.Resource().Attributes().UpsertString("host.name", "testHost") + rs.Resource().Attributes().UpsertString("service.name", "testService") + rs.Resource().SetDroppedAttributesCount(1) + // Add InstrumentationLibrarySpans. + il := rs.InstrumentationLibrarySpans().AppendEmpty() + il.InstrumentationLibrary().SetName("instrumentation name") + il.InstrumentationLibrary().SetVersion("instrumentation version") + il.SetSchemaUrl("schemaURL") + // Add spans. + sp := il.Spans().AppendEmpty() + sp.SetName("testSpan") + sp.SetKind(pdata.SpanKindClient) + sp.SetDroppedAttributesCount(1) + sp.SetStartTimestamp(pdata.NewTimestampFromTime(time.Now())) + sp.SetTraceID(traceID) + sp.SetSpanID(spanID) + sp.SetDroppedEventsCount(1) + sp.SetDroppedLinksCount(1) + sp.SetEndTimestamp(pdata.NewTimestampFromTime(time.Now())) + sp.SetParentSpanID(spanID) + sp.SetTraceState("state") + sp.Status().SetCode(pdata.StatusCodeOk) + sp.Status().SetMessage("message") + // Add attributes. + sp.Attributes().UpsertString("string", "value") + sp.Attributes().UpsertBool("bool", true) + sp.Attributes().UpsertInt("int", 1) + sp.Attributes().UpsertDouble("double", 1.1) + sp.Attributes().UpsertBytes("bytes", []byte("foo")) + // Add events. + event := sp.Events().AppendEmpty() + event.SetName("eventName") + event.SetTimestamp(pdata.NewTimestampFromTime(time.Now())) + event.SetDroppedAttributesCount(1) + event.Attributes().UpsertString("string", "value") + event.Attributes().UpsertBool("bool", true) + event.Attributes().UpsertInt("int", 1) + event.Attributes().UpsertDouble("double", 1.1) + event.Attributes().UpsertBytes("bytes", []byte("foo")) + // Add links. + link := sp.Links().AppendEmpty() + link.SetTraceState("state") + link.SetTraceID(traceID) + link.SetSpanID(spanID) + link.SetDroppedAttributesCount(1) + link.Attributes().UpsertString("string", "value") + link.Attributes().UpsertBool("bool", true) + link.Attributes().UpsertInt("int", 1) + link.Attributes().UpsertDouble("double", 1.1) + link.Attributes().UpsertBytes("bytes", []byte("foo")) + // Add another span. + sp2 := il.Spans().AppendEmpty() + sp2.SetName("testSpan2") + return td +}() + +func TestTracesJSONiter(t *testing.T) { + encoder := NewJSONTracesMarshaler() + jsonBuf, err := encoder.MarshalTraces(tracesOTLPFull) + assert.NoError(t, err) + + decoder := newJSONiterUnmarshaler() + got, err := decoder.UnmarshalTraces(jsonBuf) + assert.NoError(t, err) + assert.EqualValues(t, tracesOTLPFull, got) +} + +func BenchmarkTracesJSONUnmarshal(b *testing.B) { + b.ReportAllocs() + + encoder := NewJSONTracesMarshaler() + jsonBuf, err := encoder.MarshalTraces(tracesOTLPFull) + assert.NoError(b, err) + decoder := newJSONUnmarshaler() + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, err := decoder.UnmarshalTraces(jsonBuf) + assert.NoError(b, err) + } + }) +} + +func BenchmarkTracesJSONiterUnmarshal(b *testing.B) { + b.ReportAllocs() + + encoder := NewJSONTracesMarshaler() + jsonBuf, err := encoder.MarshalTraces(tracesOTLPFull) + assert.NoError(b, err) + decoder := newJSONiterUnmarshaler() + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _, err := decoder.UnmarshalTraces(jsonBuf) + assert.NoError(b, err) + } + }) +} \ No newline at end of file diff --git a/model/otlp/json_unmarshaler.go b/model/otlp/json_unmarshaler.go index 1291d3f8f19..3778f5ea388 100644 --- a/model/otlp/json_unmarshaler.go +++ b/model/otlp/json_unmarshaler.go @@ -16,9 +16,14 @@ package otlp // import "go.opentelemetry.io/collector/model/otlp" import ( "bytes" + "encoding/base64" + "encoding/json" + "errors" "github.com/gogo/protobuf/jsonpb" + jsoniter "github.com/json-iterator/go" + otlpcommon "go.opentelemetry.io/collector/model/internal/data/protogen/common/v1" otlplogs "go.opentelemetry.io/collector/model/internal/data/protogen/logs/v1" otlpmetrics "go.opentelemetry.io/collector/model/internal/data/protogen/metrics/v1" otlptrace "go.opentelemetry.io/collector/model/internal/data/protogen/trace/v1" @@ -72,3 +77,322 @@ func (d *jsonUnmarshaler) UnmarshalTraces(buf []byte) (pdata.Traces, error) { } return ipdata.TracesFromOtlp(td), nil } + +type jsoniterUnmarshaler struct { +} + +func newJSONiterUnmarshaler() *jsoniterUnmarshaler { + return &jsoniterUnmarshaler{} +} + +func (d *jsoniterUnmarshaler) UnmarshalLogs(buf []byte) (pdata.Logs, error) { + ld := &otlplogs.LogsData{} + return ipdata.LogsFromOtlp(ld), errors.New("unimplemented") +} + +func (d *jsoniterUnmarshaler) UnmarshalMetrics(buf []byte) (pdata.Metrics, error) { + md := &otlpmetrics.MetricsData{} + return ipdata.MetricsFromOtlp(md), errors.New("unimplemented") +} + +func (d *jsoniterUnmarshaler) UnmarshalTraces(buf []byte) (pdata.Traces, error) { + td := &otlptrace.TracesData{} + iter := jsoniter.ConfigFastest.BorrowIterator(buf) + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "resourceSpans", "resource_spans": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + rs := &otlptrace.ResourceSpans{} + + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "resource": + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "attributes": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + attr, ok := readAttribute(iter) + if ok { + rs.Resource.Attributes = append(rs.Resource.Attributes, attr) + } + return true + }) + case "droppedAttributesCount", "dropped_attributes_count": + rs.Resource.DroppedAttributesCount = iter.ReadUint32() + default: + iter.Skip() + } + return true + }) + case "instrumentationLibrarySpans", "instrumentation_library_spans": + + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + ils := &otlptrace.InstrumentationLibrarySpans{} + + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "instrumentationLibrary", "instrumentation_library": + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "name": + ils.InstrumentationLibrary.Name = iter.ReadString() + case "version": + ils.InstrumentationLibrary.Version = iter.ReadString() + default: + iter.Skip() + } + return true + }) + case "spans": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + ils.Spans = append(ils.Spans, readSpan(iter)) + return true + }) + case "schemaUrl": + ils.SchemaUrl = iter.ReadString() + default: + iter.Skip() + } + return true + }) + + rs.InstrumentationLibrarySpans = append(rs.InstrumentationLibrarySpans, ils) + return true + }) + case "schemaUrl", "schema_url": + rs.SchemaUrl = iter.ReadString() + default: + iter.Skip() + + } + return true + }) + + td.ResourceSpans = append(td.ResourceSpans, rs) + return true + }) + default: + iter.Skip() + } + return true + }) + err := iter.Error + jsoniter.ConfigFastest.ReturnIterator(iter) + return ipdata.TracesFromOtlp(td), err +} + +func readSpan(iter *jsoniter.Iterator) *otlptrace.Span { + sp := &otlptrace.Span{} + + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "traceId", "trace_id": + if err := sp.TraceId.UnmarshalJSON([]byte(iter.ReadString())); err != nil { + iter.Error = err + return false + } + case "spanId", "span_id": + if err := sp.SpanId.UnmarshalJSON([]byte(iter.ReadString())); err != nil { + iter.Error = err + return false + } + case "traceState", "trace_state": + sp.TraceState = iter.ReadString() + case "parentSpanId", "parent_span_id": + if err := sp.ParentSpanId.UnmarshalJSON([]byte(iter.ReadString())); err != nil { + iter.Error = err + return false + } + case "name": + sp.Name = iter.ReadString() + case "kind": + sp.Kind = otlptrace.Span_SpanKind(otlptrace.Span_SpanKind_value[iter.ReadString()]) + case "startTimeUnixNano", "start_time_unix_nano": + v, err := json.Number(iter.ReadString()).Int64() + if err != nil { + iter.Error = err + return false + } + sp.StartTimeUnixNano = uint64(v) + case "endTimeUnixNano", "end_time_unix_nano": + v, err := json.Number(iter.ReadString()).Int64() + if err != nil { + iter.Error = err + return false + } + sp.EndTimeUnixNano = uint64(v) + case "attributes": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + if attr, ok := readAttribute(iter); ok { + sp.Attributes = append(sp.Attributes, attr) + } + return true + }) + case "droppedAttributesCount", "dropped_attributes_count": + sp.DroppedAttributesCount = iter.ReadUint32() + case "events": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + sp.Events = append(sp.Events, readSpanEvent(iter)) + return true + }) + case "droppedEventsCount", "dropped_events_count": + sp.DroppedEventsCount = iter.ReadUint32() + case "links": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + sp.Links = append(sp.Links, readSpanLink(iter)) + return true + }) + case "droppedLinksCount", "dropped_links_count": + sp.DroppedLinksCount = iter.ReadUint32() + case "status": + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "message": + sp.Status.Message = iter.ReadString() + case "code": + sp.Status.Code = otlptrace.Status_StatusCode(otlptrace.Status_StatusCode_value[iter.ReadString()]) + default: + iter.Skip() + } + return true + }) + default: + iter.Skip() + } + return true + }) + return sp +} + +func readSpanLink(iter *jsoniter.Iterator) *otlptrace.Span_Link { + link := &otlptrace.Span_Link{} + + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "traceId", "trace_id": + iter.Error = link.TraceId.UnmarshalJSON([]byte(iter.ReadString())) + case "spanId", "span_id": + iter.Error = link.SpanId.UnmarshalJSON([]byte(iter.ReadString())) + case "traceState", "trace_state": + link.TraceState = iter.ReadString() + case "attributes": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + if attr, ok := readAttribute(iter); ok { + link.Attributes = append(link.Attributes, attr) + } + return true + }) + case "droppedAttributesCount": + link.DroppedAttributesCount = iter.ReadUint32() + default: + iter.Skip() + } + return true + }) + return link +} + +func readSpanEvent(iter *jsoniter.Iterator) *otlptrace.Span_Event { + event := &otlptrace.Span_Event{} + + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "timeUnixNano", "time_unix_nano": + v, err := json.Number(iter.ReadString()).Int64() + if err != nil { + iter.Error = err + return false + } + event.TimeUnixNano = uint64(v) + case "name": + event.Name = iter.ReadString() + case "attributes": + iter.ReadArrayCB(func(iter *jsoniter.Iterator) bool { + if attr, ok := readAttribute(iter); ok { + event.Attributes = append(event.Attributes, attr) + } + return true + }) + case "droppedAttributesCount", "dropped_attributes_count": + event.DroppedAttributesCount = iter.ReadUint32() + default: + iter.Skip() + } + return true + }) + return event +} + +func readAttribute(iter *jsoniter.Iterator) (otlpcommon.KeyValue, bool) { + var ( + key string + value otlpcommon.AnyValue + ok bool + ) + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + switch f { + case "key": + key = iter.ReadString() + case "value": + iter.ReadObjectCB(func(iter *jsoniter.Iterator, f string) bool { + value, ok = readAnyValue(iter, f) + return true + }) + default: + iter.Skip() + } + return true + }) + return otlpcommon.KeyValue{ + Key: key, + Value: value, + }, ok +} + +func readAnyValue(iter *jsoniter.Iterator, f string) (otlpcommon.AnyValue, bool) { + switch f { + case "stringValue", "string_value": + return otlpcommon.AnyValue{ + Value: &otlpcommon.AnyValue_StringValue{ + StringValue: iter.ReadString(), + }, + }, true + case "boolValue", "bool_value": + return otlpcommon.AnyValue{ + Value: &otlpcommon.AnyValue_BoolValue{ + BoolValue: iter.ReadBool(), + }, + }, true + case "intValue", "int_value": + v, err := json.Number(iter.ReadString()).Int64() + if err != nil { + iter.Error = err + return otlpcommon.AnyValue{}, false + } + return otlpcommon.AnyValue{ + Value: &otlpcommon.AnyValue_IntValue{ + IntValue: v, + }, + }, true + case "doubleValue", "double_value": + return otlpcommon.AnyValue{ + Value: &otlpcommon.AnyValue_DoubleValue{ + DoubleValue: iter.ReadFloat64(), + }, + }, true + case "bytesValue", "bytes_value": + v, err := base64.StdEncoding.DecodeString(iter.ReadString()) + if err != nil { + iter.Error = err + return otlpcommon.AnyValue{}, false + } + return otlpcommon.AnyValue{ + Value: &otlpcommon.AnyValue_BytesValue{ + BytesValue: v, + }, + }, true + default: + iter.Skip() + return otlpcommon.AnyValue{}, false + } +} \ No newline at end of file