From 3dd4e816a6b2803da6cfda6812e09803f2c5d324 Mon Sep 17 00:00:00 2001 From: ReStartercc Date: Fri, 28 Oct 2022 12:22:18 +0800 Subject: [PATCH] fix wrong representation of status value in zipkin exporter (#3340) * export status codes to upper case in zipkin exporter * add test cases for status UNSET and ERROR * Update exporters/zipkin/model.go correct the usage of case-sensitive terms Co-authored-by: Tyler Yahn * change unit test cases to test exact behavior * move this PR to unrelease section in the CHANGELOG.md Co-authored-by: Tyler Yahn --- CHANGELOG.md | 1 + exporters/zipkin/model.go | 5 +- exporters/zipkin/model_test.go | 220 ++++++++++++++++++++++++++++++-- exporters/zipkin/zipkin_test.go | 4 +- 4 files changed, 215 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da030b9193f..a75865c5ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The `go.opentelemetry.io/otel/exporters/prometheus` exporter fixes duplicated `_total` suffixes. (#3369) - Cumulative metrics from the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) are defined as monotonic sums, instead of non-monotonic. (#3389) - Asynchronous counters (`Counter` and `UpDownCounter`) from the metric SDK now produce delta sums when configured with delta temporality. (#3398) +- Exported `Status` codes in the `go.opentelemetry.io/otel/exporters/zipkin` exporter are now exported as all upper case values. (#3340) ## [1.11.1/0.33.0] 2022-10-19 diff --git a/exporters/zipkin/model.go b/exporters/zipkin/model.go index 9bec6676a57..88596717f9e 100644 --- a/exporters/zipkin/model.go +++ b/exporters/zipkin/model.go @@ -20,6 +20,7 @@ import ( "fmt" "net" "strconv" + "strings" zkmodel "github.com/openzipkin/zipkin-go/model" @@ -209,7 +210,9 @@ func toZipkinTags(data tracesdk.ReadOnlySpan) map[string]string { } if data.Status().Code != codes.Unset { - m["otel.status_code"] = data.Status().Code.String() + // Zipkin expect to receive uppercase status values + // rather than default capitalized ones. + m["otel.status_code"] = strings.ToUpper(data.Status().Code.String()) } if data.Status().Code == codes.Error { diff --git a/exporters/zipkin/model_test.go b/exporters/zipkin/model_test.go index 454d18c495b..7c96712d38d 100644 --- a/exporters/zipkin/model_test.go +++ b/exporters/zipkin/model_test.go @@ -45,7 +45,85 @@ func TestModelConversion(t *testing.T) { ) inputBatch := tracetest.SpanStubs{ - // typical span data + // typical span data with UNSET status + { + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, + SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, + }), + Parent: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, + SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, + }), + SpanKind: trace.SpanKindServer, + Name: "foo", + StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), + EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), + Attributes: []attribute.KeyValue{ + attribute.Int64("attr1", 42), + attribute.String("attr2", "bar"), + attribute.IntSlice("attr3", []int{0, 1, 2}), + }, + Events: []tracesdk.Event{ + { + Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), + Name: "ev1", + Attributes: []attribute.KeyValue{ + attribute.Int64("eventattr1", 123), + }, + }, + { + Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), + Name: "ev2", + Attributes: nil, + }, + }, + Status: tracesdk.Status{ + Code: codes.Unset, + Description: "", + }, + Resource: res, + }, + // typical span data with OK status + { + SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, + SpanID: trace.SpanID{0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}, + }), + Parent: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, + SpanID: trace.SpanID{0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38}, + }), + SpanKind: trace.SpanKindServer, + Name: "foo", + StartTime: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), + EndTime: time.Date(2020, time.March, 11, 19, 25, 0, 0, time.UTC), + Attributes: []attribute.KeyValue{ + attribute.Int64("attr1", 42), + attribute.String("attr2", "bar"), + attribute.IntSlice("attr3", []int{0, 1, 2}), + }, + Events: []tracesdk.Event{ + { + Time: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), + Name: "ev1", + Attributes: []attribute.KeyValue{ + attribute.Int64("eventattr1", 123), + }, + }, + { + Time: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), + Name: "ev2", + Attributes: nil, + }, + }, + Status: tracesdk.Status{ + Code: codes.Ok, + Description: "", + }, + Resource: res, + }, + // typical span data with ERROR status { SpanContext: trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}, @@ -373,7 +451,49 @@ func TestModelConversion(t *testing.T) { }.Snapshots() expectedOutputBatch := []zkmodel.SpanModel{ - // model for typical span data + // model for typical span data with UNSET status + { + SpanContext: zkmodel.SpanContext{ + TraceID: zkmodel.TraceID{ + High: 0x001020304050607, + Low: 0x8090a0b0c0d0e0f, + }, + ID: zkmodel.ID(0xfffefdfcfbfaf9f8), + ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), + Debug: false, + Sampled: nil, + Err: nil, + }, + Name: "foo", + Kind: "SERVER", + Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), + Duration: time.Minute, + Shared: false, + LocalEndpoint: &zkmodel.Endpoint{ + ServiceName: "model-test", + }, + RemoteEndpoint: nil, + Annotations: []zkmodel.Annotation{ + { + Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), + Value: `ev1: {"eventattr1":123}`, + }, + { + Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), + Value: "ev2", + }, + }, + Tags: map[string]string{ + "attr1": "42", + "attr2": "bar", + "attr3": "[0,1,2]", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", + }, + }, + // model for typical span data with OK status { SpanContext: zkmodel.SpanContext{ TraceID: zkmodel.TraceID{ @@ -409,7 +529,50 @@ func TestModelConversion(t *testing.T) { "attr1": "42", "attr2": "bar", "attr3": "[0,1,2]", - "otel.status_code": "Error", + "otel.status_code": "OK", + "service.name": "model-test", + "service.version": "0.1.0", + "resource-attr1": "42", + "resource-attr2": "[0,1,2]", + }, + }, + // model for typical span data with ERROR status + { + SpanContext: zkmodel.SpanContext{ + TraceID: zkmodel.TraceID{ + High: 0x001020304050607, + Low: 0x8090a0b0c0d0e0f, + }, + ID: zkmodel.ID(0xfffefdfcfbfaf9f8), + ParentID: zkmodelIDPtr(0x3f3e3d3c3b3a3938), + Debug: false, + Sampled: nil, + Err: nil, + }, + Name: "foo", + Kind: "SERVER", + Timestamp: time.Date(2020, time.March, 11, 19, 24, 0, 0, time.UTC), + Duration: time.Minute, + Shared: false, + LocalEndpoint: &zkmodel.Endpoint{ + ServiceName: "model-test", + }, + RemoteEndpoint: nil, + Annotations: []zkmodel.Annotation{ + { + Timestamp: time.Date(2020, time.March, 11, 19, 24, 30, 0, time.UTC), + Value: `ev1: {"eventattr1":123}`, + }, + { + Timestamp: time.Date(2020, time.March, 11, 19, 24, 45, 0, time.UTC), + Value: "ev2", + }, + }, + Tags: map[string]string{ + "attr1": "42", + "attr2": "bar", + "attr3": "[0,1,2]", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -452,7 +615,7 @@ func TestModelConversion(t *testing.T) { Tags: map[string]string{ "attr1": "42", "attr2": "bar", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -495,7 +658,7 @@ func TestModelConversion(t *testing.T) { Tags: map[string]string{ "attr1": "42", "attr2": "bar", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -538,7 +701,7 @@ func TestModelConversion(t *testing.T) { Tags: map[string]string{ "attr1": "42", "attr2": "bar", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -587,7 +750,7 @@ func TestModelConversion(t *testing.T) { "net.peer.ip": "1.2.3.4", "net.peer.port": "9876", "peer.hostname": "test-peer-hostname", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -630,7 +793,7 @@ func TestModelConversion(t *testing.T) { Tags: map[string]string{ "attr1": "42", "attr2": "bar", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -673,7 +836,7 @@ func TestModelConversion(t *testing.T) { Tags: map[string]string{ "attr1": "42", "attr2": "bar", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -707,7 +870,7 @@ func TestModelConversion(t *testing.T) { Tags: map[string]string{ "attr1": "42", "attr2": "bar", - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "model-test", "service.version": "0.1.0", @@ -809,7 +972,40 @@ func TestTagsTransformation(t *testing.T) { want: nil, }, { - name: "statusCode", + name: "statusCode UNSET", + data: tracetest.SpanStub{ + Attributes: []attribute.KeyValue{ + attribute.String("key", keyValue), + }, + Status: tracesdk.Status{ + Code: codes.Unset, + Description: "", + }, + }, + want: map[string]string{ + "key": keyValue, + }, + }, + { + name: "statusCode OK", + data: tracetest.SpanStub{ + Attributes: []attribute.KeyValue{ + attribute.String("key", keyValue), + attribute.Bool("ok", true), + }, + Status: tracesdk.Status{ + Code: codes.Ok, + Description: "", + }, + }, + want: map[string]string{ + "key": keyValue, + "ok": "true", + "otel.status_code": "OK", + }, + }, + { + name: "statusCode ERROR", data: tracetest.SpanStub{ Attributes: []attribute.KeyValue{ attribute.String("key", keyValue), @@ -823,7 +1019,7 @@ func TestTagsTransformation(t *testing.T) { want: map[string]string{ "error": statusMessage, "key": keyValue, - "otel.status_code": codes.Error.String(), + "otel.status_code": "ERROR", }, }, { diff --git a/exporters/zipkin/zipkin_test.go b/exporters/zipkin/zipkin_test.go index 16fb8bd98da..c5a4a6ac8f9 100644 --- a/exporters/zipkin/zipkin_test.go +++ b/exporters/zipkin/zipkin_test.go @@ -270,7 +270,7 @@ func TestExportSpans(t *testing.T) { RemoteEndpoint: nil, Annotations: nil, Tags: map[string]string{ - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "404, file not found", "service.name": "exporter-test", "service.version": "0.1.0", @@ -300,7 +300,7 @@ func TestExportSpans(t *testing.T) { RemoteEndpoint: nil, Annotations: nil, Tags: map[string]string{ - "otel.status_code": "Error", + "otel.status_code": "ERROR", "error": "403, forbidden", "service.name": "exporter-test", "service.version": "0.1.0",