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

Export resource attributes from zipkin exporter #2589

Merged
merged 5 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Changed

- Jaeger exporter takes into additional 70 bytes overhead into consideration when sending UDP packets (#2489, #2512)
- Zipkin exporter exports `Resource` attributes as the `Tags` field. (#2589)

### Deprecated

Expand Down
47 changes: 29 additions & 18 deletions exporters/zipkin/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,27 @@ func attributesToJSONMapString(attributes []attribute.KeyValue) string {
return (string)(jsonBytes)
}

// attributeToStringPair serializes each attribute to a string pair
func attributeToStringPair(kv attribute.KeyValue) (string, string) {
switch kv.Value.Type() {
// For slice attributes, serialize as JSON list string.
case attribute.BOOLSLICE:
json, _ := json.Marshal(kv.Value.AsBoolSlice())
return (string)(kv.Key), (string)(json)
case attribute.INT64SLICE:
json, _ := json.Marshal(kv.Value.AsInt64Slice())
return (string)(kv.Key), (string)(json)
case attribute.FLOAT64SLICE:
json, _ := json.Marshal(kv.Value.AsFloat64Slice())
return (string)(kv.Key), (string)(json)
case attribute.STRINGSLICE:
json, _ := json.Marshal(kv.Value.AsStringSlice())
return (string)(kv.Key), (string)(json)
default:
return (string)(kv.Key), kv.Value.Emit()
}
}

// extraZipkinTags are those that may be added to every outgoing span
var extraZipkinTags = []string{
"otel.status_code",
Expand All @@ -177,25 +198,15 @@ var extraZipkinTags = []string{

func toZipkinTags(data tracesdk.ReadOnlySpan) map[string]string {
attr := data.Attributes()
m := make(map[string]string, len(attr)+len(extraZipkinTags))
resourceAttr := data.Resource().Attributes()
m := make(map[string]string, len(attr)+len(resourceAttr)+len(extraZipkinTags))
for _, kv := range attr {
switch kv.Value.Type() {
// For slice attributes, serialize as JSON list string.
case attribute.BOOLSLICE:
json, _ := json.Marshal(kv.Value.AsBoolSlice())
m[(string)(kv.Key)] = (string)(json)
case attribute.INT64SLICE:
json, _ := json.Marshal(kv.Value.AsInt64Slice())
m[(string)(kv.Key)] = (string)(json)
case attribute.FLOAT64SLICE:
json, _ := json.Marshal(kv.Value.AsFloat64Slice())
m[(string)(kv.Key)] = (string)(json)
case attribute.STRINGSLICE:
json, _ := json.Marshal(kv.Value.AsStringSlice())
m[(string)(kv.Key)] = (string)(json)
default:
m[(string)(kv.Key)] = kv.Value.Emit()
}
k, v := attributeToStringPair(kv)
m[k] = v
}
for _, kv := range resourceAttr {
k, v := attributeToStringPair(kv)
m[k] = v
}

if data.Status().Code != codes.Unset {
Expand Down
42 changes: 41 additions & 1 deletion exporters/zipkin/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import (
func TestModelConversion(t *testing.T) {
resource := resource.NewSchemaless(
semconv.ServiceNameKey.String("model-test"),
semconv.ServiceVersionKey.String("0.1.0"),
attribute.Int64("resource-attr1", 42),
attribute.IntSlice("resource-attr2", []int{0, 1, 2}),
)

inputBatch := tracetest.SpanStubs{
Expand Down Expand Up @@ -408,6 +411,10 @@ func TestModelConversion(t *testing.T) {
"attr3": "[0,1,2]",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data with no parent
Expand Down Expand Up @@ -447,6 +454,10 @@ func TestModelConversion(t *testing.T) {
"attr2": "bar",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data of unspecified kind
Expand Down Expand Up @@ -486,6 +497,10 @@ func TestModelConversion(t *testing.T) {
"attr2": "bar",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data of internal kind
Expand Down Expand Up @@ -525,6 +540,10 @@ func TestModelConversion(t *testing.T) {
"attr2": "bar",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data of client kind
Expand Down Expand Up @@ -570,6 +589,10 @@ func TestModelConversion(t *testing.T) {
"peer.hostname": "test-peer-hostname",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data of producer kind
Expand Down Expand Up @@ -609,6 +632,10 @@ func TestModelConversion(t *testing.T) {
"attr2": "bar",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data of consumer kind
Expand Down Expand Up @@ -648,6 +675,10 @@ func TestModelConversion(t *testing.T) {
"attr2": "bar",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data with no events
Expand Down Expand Up @@ -678,6 +709,10 @@ func TestModelConversion(t *testing.T) {
"attr2": "bar",
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
},
},
// model for span data with an "error" attribute set to "false"
Expand Down Expand Up @@ -712,7 +747,12 @@ func TestModelConversion(t *testing.T) {
Value: "ev2",
},
},
Tags: nil, // should be omitted
Tags: map[string]string{
"service.name": "model-test",
"service.version": "0.1.0",
"resource-attr1": "42",
"resource-attr2": "[0,1,2]",
}, // only resource tags should be included
},
}
gottenOutputBatch := SpanModels(inputBatch)
Expand Down
5 changes: 5 additions & 0 deletions exporters/zipkin/zipkin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ func logStoreLogger(s *logStore) *log.Logger {
func TestExportSpans(t *testing.T) {
resource := resource.NewSchemaless(
semconv.ServiceNameKey.String("exporter-test"),
semconv.ServiceVersionKey.String("0.1.0"),
)

spans := tracetest.SpanStubs{
Expand Down Expand Up @@ -271,6 +272,8 @@ func TestExportSpans(t *testing.T) {
Tags: map[string]string{
"otel.status_code": "Error",
"error": "404, file not found",
"service.name": "exporter-test",
"service.version": "0.1.0",
},
},
// model of child
Expand Down Expand Up @@ -299,6 +302,8 @@ func TestExportSpans(t *testing.T) {
Tags: map[string]string{
"otel.status_code": "Error",
"error": "403, forbidden",
"service.name": "exporter-test",
"service.version": "0.1.0",
},
},
}
Expand Down