From 54063ce7052cd12722529112151456c6ea3ef5d2 Mon Sep 17 00:00:00 2001 From: Nabil Salah Date: Fri, 14 Feb 2025 17:31:55 +0200 Subject: [PATCH] Migrate converter thrift jaeger (#37823) #### Description Remove jaeger model/converter/thrift/jaeger dependency #### Link to tracking issue Resolves https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/37820 Part of https://github.com/jaegertracing/jaeger/issues/6409 #### Testing #### Documentation --------- Signed-off-by: nabil salah --- .../migrating_converter_thrift_jaeger.yaml | 27 +++ pkg/translator/jaeger/go.mod | 6 +- pkg/translator/jaeger/go.sum | 5 + .../fixtures/domain_01.json | 102 +++++++++ .../fixtures/domain_02.json | 45 ++++ .../fixtures/domain_03.json | 198 ++++++++++++++++++ .../fixtures/thrift_batch_01.json | 96 +++++++++ .../fixtures/thrift_batch_02.json | 38 ++++ .../jaegerthriftcoverter/from_domain.go | 144 +++++++++++++ .../jaegerthriftcoverter/from_domain_test.go | 58 +++++ .../sampling_from_domain.go | 91 ++++++++ .../sampling_from_domain_test.go | 145 +++++++++++++ .../sampling_to_domain.go | 74 +++++++ .../sampling_to_domain_test.go | 110 ++++++++++ .../jaeger/jaegerthriftcoverter/to_domain.go | 149 +++++++++++++ .../jaegerthriftcoverter/to_domain_test.go | 106 ++++++++++ receiver/jaegerreceiver/jaeger_agent_test.go | 2 +- 17 files changed, 1394 insertions(+), 2 deletions(-) create mode 100644 .chloggen/migrating_converter_thrift_jaeger.yaml create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_01.json create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_02.json create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_03.json create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_01.json create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_02.json create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/from_domain.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/from_domain_test.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain_test.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain_test.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/to_domain.go create mode 100644 pkg/translator/jaeger/jaegerthriftcoverter/to_domain_test.go diff --git a/.chloggen/migrating_converter_thrift_jaeger.yaml b/.chloggen/migrating_converter_thrift_jaeger.yaml new file mode 100644 index 000000000000..766b4155bb95 --- /dev/null +++ b/.chloggen/migrating_converter_thrift_jaeger.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/translator/jaeger/internal/jaeger + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Remove jaeger model/converter/thrift/jaeger dependency + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [37820] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/pkg/translator/jaeger/go.mod b/pkg/translator/jaeger/go.mod index b57150d19f0f..6f838252b472 100644 --- a/pkg/translator/jaeger/go.mod +++ b/pkg/translator/jaeger/go.mod @@ -3,7 +3,9 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/ go 1.23.0 require ( + github.com/gogo/protobuf v1.3.2 github.com/jaegertracing/jaeger-idl v0.5.0 + github.com/kr/pretty v0.3.1 github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.119.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils v0.119.0 github.com/stretchr/testify v1.10.0 @@ -15,11 +17,13 @@ require ( require ( github.com/apache/thrift v0.21.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/gogo/protobuf v1.3.2 // indirect + github.com/gogo/googleapis v1.4.1 // 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.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect go.opentelemetry.io/collector/pdata/pprofile v0.119.1-0.20250210123122-44b3eeda354c // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.34.0 // indirect diff --git a/pkg/translator/jaeger/go.sum b/pkg/translator/jaeger/go.sum index 64500ae8e973..bc3560361f9c 100644 --- a/pkg/translator/jaeger/go.sum +++ b/pkg/translator/jaeger/go.sum @@ -1,5 +1,6 @@ github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -8,6 +9,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -32,9 +35,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_01.json b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_01.json new file mode 100644 index 000000000000..91d692ef8d75 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_01.json @@ -0,0 +1,102 @@ +{ + "spans": [ + { + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABkfZg=", + "operationName": "get", + "references": [ + { + "refType": "CHILD_OF", + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABoxOM=" + } + ], + "startTime": "2017-01-26T16:46:31.639875-05:00", + "duration": "22938000ns", + "tags": [ + { + "key": "http.url", + "vType": "STRING", + "vStr": "http://127.0.0.1:15598/client_transactions" + }, + { + "key": "span.kind", + "vType": "STRING", + "vStr": "server" + }, + { + "key": "peer.port", + "vType": "INT64", + "vInt64": 53931 + }, + { + "key": "someBool", + "vType": "BOOL", + "vBool": true + }, + { + "key": "someDouble", + "vType": "FLOAT64", + "vFloat64": 129.8 + }, + { + "key": "peer.service", + "vType": "STRING", + "vStr": "rtapi" + }, + { + "key": "peer.ipv4", + "vType": "INT64", + "vInt64": 3224716605 + } + ], + "process": { + "serviceName": "api", + "tags": [ + { + "key": "hostname", + "vType": "STRING", + "vStr": "api246-sjc1" + }, + { + "key": "ip", + "vType": "STRING", + "vStr": "10.53.69.61" + }, + { + "key": "jaeger.version", + "vType": "STRING", + "vStr": "Python-3.1.0" + } + ] + }, + "logs": [ + { + "timestamp": "2017-01-26T16:46:31.639875-05:00", + "fields": [ + { + "key": "key1", + "vType": "STRING", + "vStr": "value1" + }, + { + "key": "key2", + "vType": "STRING", + "vStr": "value2" + } + ] + }, + { + "timestamp": "2017-01-26T16:46:31.639875-05:00", + "fields": [ + { + "key": "event", + "vType": "STRING", + "vStr": "nothing" + } + ] + } + ] + } + ] +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_02.json b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_02.json new file mode 100644 index 000000000000..af2c062b8e8c --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_02.json @@ -0,0 +1,45 @@ +{ + "spans": [ + { + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABkfZg=", + "operationName": "get", + "references": [ + { + "refType": "CHILD_OF", + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABoxOM=" + } + ], + "startTime": "2017-01-26T16:46:31.639875-05:00", + "duration": "22938000ns", + "tags": [ + { + "key": "peer.service", + "vType": "BINARY", + "vBinary": "AAAAAAAAMDk=" + } + ], + "process": { + "serviceName": "api" + } + }, + { + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABkfZk=", + "operationName": "get", + "references": [ + { + "refType": "FOLLOWS_FROM", + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABoxOM=" + } + ], + "startTime": "2017-01-26T16:46:31.639875-05:00", + "duration": "22938000ns", + "process": { + "serviceName": "api" + } + } + ] +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_03.json b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_03.json new file mode 100644 index 000000000000..b1c8e5d3b8cd --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/domain_03.json @@ -0,0 +1,198 @@ +{ + "spans": [ + { + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABkfZg=", + "operationName": "get", + "startTime": "2017-01-26T16:46:31.639875-05:00", + "duration": "22938000ns", + "tags": [ + { + "key": "http.url", + "vType": "STRING", + "vStr": "http://127.0.0.1:15598/client_transactions" + }, + { + "key": "span.kind", + "vType": "STRING", + "vStr": "server" + }, + { + "key": "peer.port", + "vType": "INT64", + "vInt64": 53931 + }, + { + "key": "someBool", + "vType": "BOOL", + "vBool": true + }, + { + "key": "someDouble", + "vType": "FLOAT64", + "vFloat64": 129.8 + }, + { + "key": "peer.service", + "vType": "STRING", + "vStr": "rtapi" + }, + { + "key": "peer.ipv4", + "vType": "INT64", + "vInt64": 3224716605 + } + ], + "process": { + "serviceName": "api", + "tags": [ + { + "key": "hostname", + "vType": "STRING", + "vStr": "api246-sjc1" + }, + { + "key": "ip", + "vType": "STRING", + "vStr": "10.53.69.61" + }, + { + "key": "jaeger.version", + "vType": "STRING", + "vStr": "Python-3.1.0" + } + ] + }, + "logs": [ + { + "timestamp": "2017-01-26T16:46:31.639875-05:00", + "fields": [ + { + "key": "key1", + "vType": "STRING", + "vStr": "value1" + }, + { + "key": "key2", + "vType": "STRING", + "vStr": "value2" + } + ] + }, + { + "timestamp": "2017-01-26T16:46:31.639875-05:00", + "fields": [ + { + "key": "event", + "vType": "STRING", + "vStr": "nothing" + } + ] + } + ] + }, + { + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABSlHs=", + "operationName": "get", + "startTime": "2017-01-26T16:46:31.639875-05:00", + "duration": "22938000ns", + "references": [ + { + "refType": "CHILD_OF", + "traceId": "AAAAAAAAAABSlpqJVVcaPw==", + "spanId": "AAAAAABSlHs=" + } + ], + "tags": [ + { + "key": "http.url", + "vType": "STRING", + "vStr": "http://127.0.0.1:15598/client_transactions" + }, + { + "key": "span.kind", + "vType": "STRING", + "vStr": "server" + }, + { + "key": "peer.port", + "vType": "INT64", + "vInt64": 53931 + }, + { + "key": "someBool", + "vType": "BOOL", + "vBool": true + }, + { + "key": "someDouble", + "vType": "FLOAT64", + "vFloat64": 4638770948061370000 + }, + { + "key": "peer.service", + "vType": "STRING", + "vStr": "rtapi" + }, + { + "key": "peer.ipv4", + "vType": "INT64", + "vInt64": 3224716605 + }, + { + "key": "some.binary.data", + "vType": "BINARY", + "vBinary": "c29tZS1iaW5hcnktZGF0YQ==" + } + ], + "process": { + "serviceName": "api", + "tags": [ + { + "key": "hostname", + "vType": "STRING", + "vStr": "api246-sjc1" + }, + { + "key": "ip", + "vType": "STRING", + "vStr": "10.53.69.61" + }, + { + "key": "jaeger.version", + "vType": "STRING", + "vStr": "Python-3.1.0" + } + ] + }, + "logs": [ + { + "timestamp": "2017-01-26T16:46:31.639875-05:00", + "fields": [ + { + "key": "key1", + "vType": "STRING", + "vStr": "value1" + }, + { + "key": "key2", + "vType": "STRING", + "vStr": "value2" + } + ] + }, + { + "timestamp": "2017-01-26T16:46:31.639875-05:00", + "fields": [ + { + "key": "event", + "vType": "STRING", + "vStr": "nothing" + } + ] + } + ] + } + ] +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_01.json b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_01.json new file mode 100644 index 000000000000..1d40432dbb77 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_01.json @@ -0,0 +1,96 @@ +{ + "process": { + "serviceName": "api", + "tags": [ + { + "key": "hostname", + "vType": "STRING", + "vStr": "api246-sjc1" + }, + { + "key": "ip", + "vType": "STRING", + "vStr": "10.53.69.61" + }, + { + "key": "jaeger.version", + "vType": "STRING", + "vStr": "Python-3.1.0" + } + ] + }, + "spans": [ + { + "traceIdLow": 5951113872249657919, + "spanId": 6585752, + "parentSpanId": 6866147, + "operationName": "get", + "startTime": 1485467191639875, + "duration": 22938, + "tags": [ + { + "key": "http.url", + "vType": "STRING", + "vStr": "http://127.0.0.1:15598/client_transactions" + }, + { + "key": "span.kind", + "vType": "STRING", + "vStr": "server" + }, + { + "key": "peer.port", + "vType": "LONG", + "vLong": 53931 + }, + { + "key": "someBool", + "vType": "BOOL", + "vBool": true + }, + { + "key": "someDouble", + "vType": "DOUBLE", + "vDouble": 129.8 + }, + { + "key": "peer.service", + "vType": "STRING", + "vStr": "rtapi" + }, + { + "key": "peer.ipv4", + "vType": "LONG", + "vLong": 3224716605 + } + ], + "logs": [ + { + "timestamp": 1485467191639875, + "fields": [ + { + "key": "key1", + "vType": "STRING", + "vStr": "value1" + }, + { + "key": "key2", + "vType": "STRING", + "vStr": "value2" + } + ] + }, + { + "timestamp": 1485467191639875, + "fields": [ + { + "key": "event", + "vType": "STRING", + "vStr": "nothing" + } + ] + } + ] + } + ] +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_02.json b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_02.json new file mode 100644 index 000000000000..9607e291a720 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/fixtures/thrift_batch_02.json @@ -0,0 +1,38 @@ +{ + "process": { + "serviceName": "api", + "tags": [] + }, + "spans": [ + { + "traceIdLow": 5951113872249657919, + "spanId": 6585752, + "parentSpanId": 6866147, + "operationName": "get", + "startTime": 1485467191639875, + "duration": 22938, + "tags": [ + { + "key": "peer.service", + "vType": "BINARY", + "vBinary": "AAAAAAAAMDk=" + } + ] + }, + { + "traceIdLow": 5951113872249657919, + "spanId": 6585753, + "parentSpanId": 6866147, + "operationName": "get", + "references": [ + { + "refType": "FOLLOWS_FROM", + "traceIdLow": 5951113872249657919, + "spanId": 6866147 + } + ], + "startTime": 1485467191639875, + "duration": 22938 + } + ] +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/from_domain.go b/pkg/translator/jaeger/jaegerthriftcoverter/from_domain.go new file mode 100644 index 000000000000..e90993e9cf5e --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/from_domain.go @@ -0,0 +1,144 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2019 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/jaegerthriftcoverter" + +import ( + "fmt" + + "github.com/jaegertracing/jaeger-idl/model/v1" + "github.com/jaegertracing/jaeger-idl/thrift-gen/jaeger" +) + +// FromDomain takes an array of model.Span and returns +// an array of jaeger.Span. If errors are found during +// conversion of tags, then error tags are appended. +func FromDomain(spans []*model.Span) []*jaeger.Span { + jSpans := make([]*jaeger.Span, len(spans)) + dToJ := domainToJaegerTransformer{} + for idx, span := range spans { + jSpans[idx] = dToJ.transformSpan(span) + } + return jSpans +} + +// FromDomainSpan takes a single model.Span and +// converts it into a jaeger.Span. If errors are found +// during conversion of tags, then error tags are appended. +func FromDomainSpan(span *model.Span) *jaeger.Span { + dToJ := &domainToJaegerTransformer{} + return dToJ.transformSpan(span) +} + +type domainToJaegerTransformer struct{} + +func (domainToJaegerTransformer) keyValueToTag(kv *model.KeyValue) *jaeger.Tag { + if kv.VType == model.StringType { + stringValue := kv.VStr + return &jaeger.Tag{ + Key: kv.Key, + VType: jaeger.TagType_STRING, + VStr: &stringValue, + } + } + + if kv.VType == model.Int64Type { + intValue := kv.Int64() + return &jaeger.Tag{ + Key: kv.Key, + VType: jaeger.TagType_LONG, + VLong: &intValue, + } + } + + if kv.VType == model.BinaryType { + binaryValue := kv.Binary() + return &jaeger.Tag{ + Key: kv.Key, + VType: jaeger.TagType_BINARY, + VBinary: binaryValue, + } + } + + if kv.VType == model.BoolType { + boolValue := kv.Bool() + return &jaeger.Tag{ + Key: kv.Key, + VType: jaeger.TagType_BOOL, + VBool: &boolValue, + } + } + + if kv.VType == model.Float64Type { + floatValue := kv.Float64() + return &jaeger.Tag{ + Key: kv.Key, + VType: jaeger.TagType_DOUBLE, + VDouble: &floatValue, + } + } + + errString := fmt.Sprintf("No suitable tag type found for: %#v", kv.VType) + errTag := &jaeger.Tag{ + Key: "Error", + VType: jaeger.TagType_STRING, + VStr: &errString, + } + + return errTag +} + +func (d domainToJaegerTransformer) convertKeyValuesToTags(kvs model.KeyValues) []*jaeger.Tag { + jaegerTags := make([]*jaeger.Tag, len(kvs)) + for idx, kv := range kvs { + jaegerTags[idx] = d.keyValueToTag(&kv) + } + return jaegerTags +} + +func (d domainToJaegerTransformer) convertLogs(logs []model.Log) []*jaeger.Log { + jaegerLogs := make([]*jaeger.Log, len(logs)) + for idx, log := range logs { + jaegerLogs[idx] = &jaeger.Log{ + Timestamp: int64(model.TimeAsEpochMicroseconds(log.Timestamp)), + Fields: d.convertKeyValuesToTags(log.Fields), + } + } + return jaegerLogs +} + +func (domainToJaegerTransformer) convertSpanRefs(refs []model.SpanRef) []*jaeger.SpanRef { + jaegerSpanRefs := make([]*jaeger.SpanRef, len(refs)) + for idx, ref := range refs { + jaegerSpanRefs[idx] = &jaeger.SpanRef{ + RefType: jaeger.SpanRefType(ref.RefType), + TraceIdLow: int64(ref.TraceID.Low), + TraceIdHigh: int64(ref.TraceID.High), + SpanId: int64(ref.SpanID), + } + } + return jaegerSpanRefs +} + +func (d domainToJaegerTransformer) transformSpan(span *model.Span) *jaeger.Span { + tags := d.convertKeyValuesToTags(span.Tags) + logs := d.convertLogs(span.Logs) + refs := d.convertSpanRefs(span.References) + + jaegerSpan := &jaeger.Span{ + TraceIdLow: int64(span.TraceID.Low), + TraceIdHigh: int64(span.TraceID.High), + SpanId: int64(span.SpanID), + ParentSpanId: int64(span.ParentSpanID()), + OperationName: span.OperationName, + References: refs, + Flags: int32(span.Flags), + StartTime: int64(model.TimeAsEpochMicroseconds(span.StartTime)), + Duration: int64(model.DurationAsMicroseconds(span.Duration)), + Tags: tags, + Logs: logs, + } + return jaegerSpan +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/from_domain_test.go b/pkg/translator/jaeger/jaegerthriftcoverter/from_domain_test.go new file mode 100644 index 000000000000..58e66b533c61 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/from_domain_test.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2019 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger + +import ( + "testing" + + "github.com/jaegertracing/jaeger-idl/model/v1" + "github.com/stretchr/testify/assert" +) + +func TestFromDomainOneSpan(t *testing.T) { + spanFile := "fixtures/domain_01.json" + modelSpans := loadSpans(t, spanFile) + + batchFile := "fixtures/thrift_batch_01.json" + jaegerBatch := loadBatch(t, batchFile) + + modelSpan := modelSpans[0] + jaegerSpan := FromDomainSpan(modelSpan) + newModelSpan := ToDomainSpan(jaegerSpan, jaegerBatch.Process) + + modelSpan.NormalizeTimestamps() + newModelSpan.NormalizeTimestamps() + assert.Equal(t, modelSpan, newModelSpan) +} + +func TestFromDomain(t *testing.T) { + file := "fixtures/domain_03.json" + modelSpans := loadSpans(t, file) + + batchFile := "fixtures/thrift_batch_01.json" + jaegerBatch := loadBatch(t, batchFile) + + jaegerSpans := FromDomain(modelSpans) + newModelSpans := ToDomain(jaegerSpans, jaegerBatch.Process) + for i := range newModelSpans { + modelSpan := modelSpans[i] + newModelSpan := newModelSpans[i] + modelSpan.NormalizeTimestamps() + newModelSpan.NormalizeTimestamps() + } + assert.Equal(t, modelSpans, newModelSpans) +} + +func TestKeyValueToTag(t *testing.T) { + dToJ := domainToJaegerTransformer{} + jaegerTag := dToJ.keyValueToTag(&model.KeyValue{ + Key: "some-error", + VType: model.ValueType(-1), + }) + + assert.Equal(t, "Error", jaegerTag.Key) + assert.Equal(t, "No suitable tag type found for: -1", *jaegerTag.VStr) +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain.go b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain.go new file mode 100644 index 000000000000..48f631b51d96 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain.go @@ -0,0 +1,91 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/jaegerthriftcoverter" + +import ( + "errors" + "math" + + "github.com/jaegertracing/jaeger-idl/proto-gen/api_v2" + "github.com/jaegertracing/jaeger-idl/thrift-gen/sampling" +) + +// ConvertSamplingResponseFromDomain converts proto sampling response to its thrift representation. +func ConvertSamplingResponseFromDomain(r *api_v2.SamplingStrategyResponse) (*sampling.SamplingStrategyResponse, error) { + typ, err := convertStrategyTypeFromDomain(r.GetStrategyType()) + if err != nil { + return nil, err + } + rl, err := convertRateLimitingFromDomain(r.GetRateLimitingSampling()) + if err != nil { + return nil, err + } + thriftResp := &sampling.SamplingStrategyResponse{ + StrategyType: typ, + ProbabilisticSampling: convertProbabilisticFromDomain(r.GetProbabilisticSampling()), + RateLimitingSampling: rl, + OperationSampling: convertPerOperationFromDomain(r.GetOperationSampling()), + } + return thriftResp, nil +} + +func convertProbabilisticFromDomain(s *api_v2.ProbabilisticSamplingStrategy) *sampling.ProbabilisticSamplingStrategy { + if s == nil { + return nil + } + return &sampling.ProbabilisticSamplingStrategy{SamplingRate: s.GetSamplingRate()} +} + +func convertRateLimitingFromDomain(s *api_v2.RateLimitingSamplingStrategy) (*sampling.RateLimitingSamplingStrategy, error) { + if s == nil { + return nil, nil + } + if s.MaxTracesPerSecond > math.MaxInt16 { + return nil, errors.New("maxTracesPerSecond is higher than int16") + } + return &sampling.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int16(s.GetMaxTracesPerSecond()), + }, nil +} + +func convertPerOperationFromDomain(s *api_v2.PerOperationSamplingStrategies) *sampling.PerOperationSamplingStrategies { + if s == nil { + return nil + } + r := &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: s.GetDefaultSamplingProbability(), + DefaultLowerBoundTracesPerSecond: s.GetDefaultLowerBoundTracesPerSecond(), + DefaultUpperBoundTracesPerSecond: &s.DefaultUpperBoundTracesPerSecond, + } + + perOp := s.GetPerOperationStrategies() + // Default to empty array so that json.Marshal returns [] instead of null (Issue #3891). + r.PerOperationStrategies = make([]*sampling.OperationSamplingStrategy, len(perOp)) + for i, k := range perOp { + r.PerOperationStrategies[i] = convertOperationFromDomain(k) + } + return r +} + +func convertOperationFromDomain(s *api_v2.OperationSamplingStrategy) *sampling.OperationSamplingStrategy { + if s == nil { + return nil + } + return &sampling.OperationSamplingStrategy{ + Operation: s.GetOperation(), + ProbabilisticSampling: convertProbabilisticFromDomain(s.GetProbabilisticSampling()), + } +} + +func convertStrategyTypeFromDomain(s api_v2.SamplingStrategyType) (sampling.SamplingStrategyType, error) { + switch s { + case api_v2.SamplingStrategyType_PROBABILISTIC: + return sampling.SamplingStrategyType_PROBABILISTIC, nil + case api_v2.SamplingStrategyType_RATE_LIMITING: + return sampling.SamplingStrategyType_RATE_LIMITING, nil + default: + return sampling.SamplingStrategyType_PROBABILISTIC, errors.New("could not convert sampling strategy type") + } +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain_test.go b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain_test.go new file mode 100644 index 000000000000..950738852a97 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_from_domain_test.go @@ -0,0 +1,145 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger + +import ( + "math" + "testing" + + "github.com/jaegertracing/jaeger-idl/proto-gen/api_v2" + "github.com/jaegertracing/jaeger-idl/thrift-gen/sampling" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConvertStrategyTypeFromDomain(t *testing.T) { + tests := []struct { + expected sampling.SamplingStrategyType + in api_v2.SamplingStrategyType + err string + }{ + {expected: sampling.SamplingStrategyType_PROBABILISTIC, in: api_v2.SamplingStrategyType_PROBABILISTIC}, + {expected: sampling.SamplingStrategyType_RATE_LIMITING, in: api_v2.SamplingStrategyType_RATE_LIMITING}, + {in: 44, err: "could not convert sampling strategy type"}, + } + for _, test := range tests { + st, err := convertStrategyTypeFromDomain(test.in) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.NoError(t, err) + assert.Equal(t, test.expected, st) + } + } +} + +func TestConvertProbabilisticFromDomain(t *testing.T) { + tests := []struct { + in *api_v2.ProbabilisticSamplingStrategy + expected *sampling.ProbabilisticSamplingStrategy + }{ + {in: &api_v2.ProbabilisticSamplingStrategy{SamplingRate: 21}, expected: &sampling.ProbabilisticSamplingStrategy{SamplingRate: 21}}, + {}, + } + for _, test := range tests { + st := convertProbabilisticFromDomain(test.in) + assert.Equal(t, test.expected, st) + } +} + +func TestConvertRateLimitingFromDomain(t *testing.T) { + tests := []struct { + in *api_v2.RateLimitingSamplingStrategy + expected *sampling.RateLimitingSamplingStrategy + err string + }{ + {in: &api_v2.RateLimitingSamplingStrategy{MaxTracesPerSecond: 21}, expected: &sampling.RateLimitingSamplingStrategy{MaxTracesPerSecond: 21}}, + {in: &api_v2.RateLimitingSamplingStrategy{MaxTracesPerSecond: math.MaxInt32}, err: "maxTracesPerSecond is higher than int16"}, + {}, + } + for _, test := range tests { + st, err := convertRateLimitingFromDomain(test.in) + if test.err != "" { + require.EqualError(t, err, test.err) + require.Nil(t, st) + } else { + require.NoError(t, err) + assert.Equal(t, test.expected, st) + } + } +} + +func TestConvertOperationStrategyFromDomain(t *testing.T) { + tests := []struct { + in *api_v2.OperationSamplingStrategy + expected *sampling.OperationSamplingStrategy + }{ + {in: &api_v2.OperationSamplingStrategy{Operation: "foo"}, expected: &sampling.OperationSamplingStrategy{Operation: "foo"}}, + { + in: &api_v2.OperationSamplingStrategy{Operation: "foo", ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{SamplingRate: 2}}, + expected: &sampling.OperationSamplingStrategy{Operation: "foo", ProbabilisticSampling: &sampling.ProbabilisticSamplingStrategy{SamplingRate: 2}}, + }, + {}, + } + for _, test := range tests { + o := convertOperationFromDomain(test.in) + assert.Equal(t, test.expected, o) + } +} + +func TestConvertPerOperationStrategyFromDomain(t *testing.T) { + a := 11.2 + tests := []struct { + in *api_v2.PerOperationSamplingStrategies + expected *sampling.PerOperationSamplingStrategies + }{ + { + in: &api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 15.2, DefaultUpperBoundTracesPerSecond: a, DefaultLowerBoundTracesPerSecond: 2, + PerOperationStrategies: []*api_v2.OperationSamplingStrategy{{Operation: "fao"}}, + }, + expected: &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 15.2, DefaultUpperBoundTracesPerSecond: &a, DefaultLowerBoundTracesPerSecond: 2, + PerOperationStrategies: []*sampling.OperationSamplingStrategy{{Operation: "fao"}}, + }, + }, + { + in: &api_v2.PerOperationSamplingStrategies{DefaultSamplingProbability: 15.2, DefaultUpperBoundTracesPerSecond: a, DefaultLowerBoundTracesPerSecond: 2}, + expected: &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 15.2, DefaultUpperBoundTracesPerSecond: &a, DefaultLowerBoundTracesPerSecond: 2, + PerOperationStrategies: []*sampling.OperationSamplingStrategy{}, + }, + }, + } + for _, test := range tests { + o := convertPerOperationFromDomain(test.in) + assert.Equal(t, test.expected, o) + } +} + +func TestConvertSamplingResponseFromDomain(t *testing.T) { + tests := []struct { + in *api_v2.SamplingStrategyResponse + expected *sampling.SamplingStrategyResponse + err string + }{ + {in: &api_v2.SamplingStrategyResponse{StrategyType: 55}, err: "could not convert sampling strategy type"}, + { + in: &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC, RateLimitingSampling: &api_v2.RateLimitingSamplingStrategy{MaxTracesPerSecond: math.MaxInt32}}, + err: "maxTracesPerSecond is higher than int16", + }, + {in: &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, expected: &sampling.SamplingStrategyResponse{StrategyType: sampling.SamplingStrategyType_PROBABILISTIC}}, + } + for _, test := range tests { + r, err := ConvertSamplingResponseFromDomain(test.in) + if test.err != "" { + require.EqualError(t, err, test.err) + require.Nil(t, r) + } else { + require.NoError(t, err) + assert.Equal(t, test.expected, r) + } + } +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain.go b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain.go new file mode 100644 index 000000000000..059db71358c7 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain.go @@ -0,0 +1,74 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/jaegerthriftcoverter" + +import ( + "errors" + + "github.com/jaegertracing/jaeger-idl/proto-gen/api_v2" + "github.com/jaegertracing/jaeger-idl/thrift-gen/sampling" +) + +// ConvertSamplingResponseToDomain converts thrift sampling response to its proto representation. +func ConvertSamplingResponseToDomain(r *sampling.SamplingStrategyResponse) (*api_v2.SamplingStrategyResponse, error) { + if r == nil { + return nil, nil + } + t, err := convertStrategyTypeToDomain(r.GetStrategyType()) + if err != nil { + return nil, err + } + response := &api_v2.SamplingStrategyResponse{ + StrategyType: t, + ProbabilisticSampling: convertProbabilisticToDomain(r.GetProbabilisticSampling()), + RateLimitingSampling: convertRateLimitingToDomain(r.GetRateLimitingSampling()), + OperationSampling: convertPerOperationToDomain(r.GetOperationSampling()), + } + return response, nil +} + +func convertRateLimitingToDomain(s *sampling.RateLimitingSamplingStrategy) *api_v2.RateLimitingSamplingStrategy { + if s == nil { + return nil + } + return &api_v2.RateLimitingSamplingStrategy{MaxTracesPerSecond: int32(s.GetMaxTracesPerSecond())} +} + +func convertProbabilisticToDomain(s *sampling.ProbabilisticSamplingStrategy) *api_v2.ProbabilisticSamplingStrategy { + if s == nil { + return nil + } + return &api_v2.ProbabilisticSamplingStrategy{SamplingRate: s.GetSamplingRate()} +} + +func convertPerOperationToDomain(s *sampling.PerOperationSamplingStrategies) *api_v2.PerOperationSamplingStrategies { + if s == nil { + return nil + } + poss := make([]*api_v2.OperationSamplingStrategy, len(s.PerOperationStrategies)) + for i, pos := range s.PerOperationStrategies { + poss[i] = &api_v2.OperationSamplingStrategy{ + Operation: pos.Operation, + ProbabilisticSampling: convertProbabilisticToDomain(pos.GetProbabilisticSampling()), + } + } + return &api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: s.GetDefaultSamplingProbability(), + DefaultUpperBoundTracesPerSecond: s.GetDefaultUpperBoundTracesPerSecond(), + DefaultLowerBoundTracesPerSecond: s.GetDefaultLowerBoundTracesPerSecond(), + PerOperationStrategies: poss, + } +} + +func convertStrategyTypeToDomain(t sampling.SamplingStrategyType) (api_v2.SamplingStrategyType, error) { + switch t { + case sampling.SamplingStrategyType_PROBABILISTIC: + return api_v2.SamplingStrategyType_PROBABILISTIC, nil + case sampling.SamplingStrategyType_RATE_LIMITING: + return api_v2.SamplingStrategyType_RATE_LIMITING, nil + default: + return api_v2.SamplingStrategyType_PROBABILISTIC, errors.New("could not convert sampling strategy type") + } +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain_test.go b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain_test.go new file mode 100644 index 000000000000..189a2f33f55d --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/sampling_to_domain_test.go @@ -0,0 +1,110 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger + +import ( + "errors" + "testing" + + "github.com/jaegertracing/jaeger-idl/proto-gen/api_v2" + "github.com/jaegertracing/jaeger-idl/thrift-gen/sampling" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConvertStrategyTypeToDomain(t *testing.T) { + tests := []struct { + in sampling.SamplingStrategyType + expected api_v2.SamplingStrategyType + err error + }{ + {in: sampling.SamplingStrategyType_PROBABILISTIC, expected: api_v2.SamplingStrategyType_PROBABILISTIC}, + {in: sampling.SamplingStrategyType_RATE_LIMITING, expected: api_v2.SamplingStrategyType_RATE_LIMITING}, + {in: 44, err: errors.New("could not convert sampling strategy type")}, + } + for _, test := range tests { + st, err := convertStrategyTypeToDomain(test.in) + if test.err != nil { + require.EqualError(t, test.err, err.Error()) + } else { + require.NoError(t, err) + assert.Equal(t, test.expected, st) + } + } +} + +func TestConvertProbabilisticToDomain(t *testing.T) { + tests := []struct { + expected *api_v2.ProbabilisticSamplingStrategy + in *sampling.ProbabilisticSamplingStrategy + }{ + {expected: &api_v2.ProbabilisticSamplingStrategy{SamplingRate: 21}, in: &sampling.ProbabilisticSamplingStrategy{SamplingRate: 21}}, + {}, + } + for _, test := range tests { + st := convertProbabilisticToDomain(test.in) + assert.Equal(t, test.expected, st) + } +} + +func TestConvertRateLimitingToDomain(t *testing.T) { + tests := []struct { + expected *api_v2.RateLimitingSamplingStrategy + in *sampling.RateLimitingSamplingStrategy + }{ + {expected: &api_v2.RateLimitingSamplingStrategy{MaxTracesPerSecond: 21}, in: &sampling.RateLimitingSamplingStrategy{MaxTracesPerSecond: 21}}, + {}, + } + for _, test := range tests { + st := convertRateLimitingToDomain(test.in) + assert.Equal(t, test.expected, st) + } +} + +func TestConvertPerOperationStrategyToDomain(t *testing.T) { + a := 11.2 + tests := []struct { + expected *api_v2.PerOperationSamplingStrategies + in *sampling.PerOperationSamplingStrategies + }{ + { + expected: &api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 15.2, DefaultUpperBoundTracesPerSecond: a, DefaultLowerBoundTracesPerSecond: 2, + PerOperationStrategies: []*api_v2.OperationSamplingStrategy{{Operation: "fao"}}, + }, + in: &sampling.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 15.2, DefaultUpperBoundTracesPerSecond: &a, DefaultLowerBoundTracesPerSecond: 2, + PerOperationStrategies: []*sampling.OperationSamplingStrategy{{Operation: "fao"}}, + }, + }, + {}, + } + for _, test := range tests { + o := convertPerOperationToDomain(test.in) + assert.Equal(t, test.expected, o) + } +} + +func TestConvertSamplingResponseToDomain(t *testing.T) { + tests := []struct { + expected *api_v2.SamplingStrategyResponse + in *sampling.SamplingStrategyResponse + err string + }{ + {in: &sampling.SamplingStrategyResponse{StrategyType: 55}, err: "could not convert sampling strategy type"}, + {expected: &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, in: &sampling.SamplingStrategyResponse{StrategyType: sampling.SamplingStrategyType_PROBABILISTIC}}, + {}, + } + for _, test := range tests { + r, err := ConvertSamplingResponseToDomain(test.in) + if test.err != "" { + require.EqualError(t, err, test.err) + require.Nil(t, r) + } else { + require.NoError(t, err) + assert.Equal(t, test.expected, r) + } + } +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/to_domain.go b/pkg/translator/jaeger/jaegerthriftcoverter/to_domain.go new file mode 100644 index 000000000000..e7ff04692650 --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/to_domain.go @@ -0,0 +1,149 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2019 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/jaegerthriftcoverter" + +import ( + "fmt" + + "github.com/jaegertracing/jaeger-idl/model/v1" + "github.com/jaegertracing/jaeger-idl/thrift-gen/jaeger" +) + +// ToDomain transforms a set of spans and a process in jaeger.thrift format into a slice of model.Span. +// A valid []*model.Span is always returned, even when there are errors. +// Errors are presented as tags on spans +func ToDomain(jSpans []*jaeger.Span, jProcess *jaeger.Process) []*model.Span { + return toDomain{}.ToDomain(jSpans, jProcess) +} + +// ToDomainSpan transforms a span in jaeger.thrift format into model.Span. +// A valid model.Span is always returned, even when there are errors. +// Errors are presented as tags on spans +func ToDomainSpan(jSpan *jaeger.Span, jProcess *jaeger.Process) *model.Span { + return toDomain{}.ToDomainSpan(jSpan, jProcess) +} + +// ToDomainProcess transforms a process in jaeger.thrift format to model.Span. +func ToDomainProcess(jProcess *jaeger.Process) *model.Process { + return toDomain{}.getProcess(jProcess) +} + +type toDomain struct{} + +func (td toDomain) ToDomain(jSpans []*jaeger.Span, jProcess *jaeger.Process) []*model.Span { + spans := make([]*model.Span, len(jSpans)) + mProcess := td.getProcess(jProcess) + for i, jSpan := range jSpans { + spans[i] = td.transformSpan(jSpan, mProcess) + } + return spans +} + +func (td toDomain) ToDomainSpan(jSpan *jaeger.Span, jProcess *jaeger.Process) *model.Span { + mProcess := td.getProcess(jProcess) + return td.transformSpan(jSpan, mProcess) +} + +func (td toDomain) transformSpan(jSpan *jaeger.Span, mProcess *model.Process) *model.Span { + traceID := model.NewTraceID(uint64(jSpan.TraceIdHigh), uint64(jSpan.TraceIdLow)) + // allocate extra space for future append operation + tags := td.getTags(jSpan.Tags, 1) + refs := td.getReferences(jSpan.References) + // We no longer store ParentSpanID in the domain model, but the data in Thrift model + // might still have these IDs without representing them in the References, so we + // convert it back into child-of reference. + if jSpan.ParentSpanId != 0 { + parentSpanID := model.NewSpanID(uint64(jSpan.ParentSpanId)) + refs = model.MaybeAddParentSpanID(traceID, parentSpanID, refs) + } + return &model.Span{ + TraceID: traceID, + SpanID: model.NewSpanID(uint64(jSpan.SpanId)), + OperationName: jSpan.OperationName, + References: refs, + Flags: model.Flags(jSpan.Flags), + StartTime: model.EpochMicrosecondsAsTime(uint64(jSpan.StartTime)), + Duration: model.MicrosecondsAsDuration(uint64(jSpan.Duration)), + Tags: tags, + Logs: td.getLogs(jSpan.Logs), + Process: mProcess, + } +} + +func (toDomain) getReferences(jRefs []*jaeger.SpanRef) []model.SpanRef { + if len(jRefs) == 0 { + return nil + } + + mRefs := make([]model.SpanRef, len(jRefs)) + for idx, jRef := range jRefs { + mRefs[idx] = model.SpanRef{ + RefType: model.SpanRefType(int(jRef.RefType)), + TraceID: model.NewTraceID(uint64(jRef.TraceIdHigh), uint64(jRef.TraceIdLow)), + SpanID: model.NewSpanID(uint64(jRef.SpanId)), + } + } + + return mRefs +} + +// getProcess takes a jaeger.thrift process and produces a model.Process. +// Any errors are presented as tags +func (td toDomain) getProcess(jProcess *jaeger.Process) *model.Process { + if jProcess == nil { + return nil + } + tags := td.getTags(jProcess.Tags, 0) + return &model.Process{ + Tags: tags, + ServiceName: jProcess.ServiceName, + } +} + +// convert the jaeger.Tag slice to domain KeyValue slice +// zipkin/to_domain.go does not give a default slice size since it has to filter annotations, jaeger conversion is more predictable +// thus to avoid future full array copy when using append, pre-allocate extra space as an optimization +func (td toDomain) getTags(tags []*jaeger.Tag, extraSpace int) model.KeyValues { + if len(tags) == 0 { + return nil + } + retMe := make(model.KeyValues, len(tags), len(tags)+extraSpace) + for i, tag := range tags { + retMe[i] = td.getTag(tag) + } + return retMe +} + +func (toDomain) getTag(tag *jaeger.Tag) model.KeyValue { + switch tag.VType { + case jaeger.TagType_BOOL: + return model.Bool(tag.Key, tag.GetVBool()) + case jaeger.TagType_BINARY: + return model.Binary(tag.Key, tag.GetVBinary()) + case jaeger.TagType_DOUBLE: + return model.Float64(tag.Key, tag.GetVDouble()) + case jaeger.TagType_LONG: + return model.Int64(tag.Key, tag.GetVLong()) + case jaeger.TagType_STRING: + return model.String(tag.Key, tag.GetVStr()) + default: + return model.String(tag.Key, fmt.Sprintf("Unknown VType: %+v", tag)) + } +} + +func (td toDomain) getLogs(logs []*jaeger.Log) []model.Log { + if len(logs) == 0 { + return nil + } + retMe := make([]model.Log, len(logs)) + for i, log := range logs { + retMe[i] = model.Log{ + Timestamp: model.EpochMicrosecondsAsTime(uint64(log.Timestamp)), + Fields: td.getTags(log.Fields, 0), + } + } + return retMe +} diff --git a/pkg/translator/jaeger/jaegerthriftcoverter/to_domain_test.go b/pkg/translator/jaeger/jaegerthriftcoverter/to_domain_test.go new file mode 100644 index 000000000000..e0967c6a84ad --- /dev/null +++ b/pkg/translator/jaeger/jaegerthriftcoverter/to_domain_test.go @@ -0,0 +1,106 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2019 The Jaeger Authors. +// Copyright (c) 2017 Uber Technologies, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package jaeger + +import ( + "encoding/json" + "fmt" + "os" + "testing" + "time" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gogo/protobuf/proto" + "github.com/jaegertracing/jaeger-idl/model/v1" + "github.com/jaegertracing/jaeger-idl/thrift-gen/jaeger" + "github.com/kr/pretty" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const NumberOfFixtures = 2 + +func TestToDomain(t *testing.T) { + for i := 1; i <= NumberOfFixtures; i++ { + in := fmt.Sprintf("fixtures/thrift_batch_%02d.json", i) + out := fmt.Sprintf("fixtures/domain_%02d.json", i) + mSpans := loadSpans(t, out) + for _, s := range mSpans { + s.NormalizeTimestamps() + } + + jBatch := loadBatch(t, in) + name := in + " -> " + out + " : " + jBatch.Process.ServiceName + t.Run(name, func(t *testing.T) { + actualSpans := ToDomain(jBatch.Spans, jBatch.Process) + for _, s := range actualSpans { + s.NormalizeTimestamps() + } + if !assert.EqualValues(t, mSpans, actualSpans) { + for _, err := range pretty.Diff(mSpans, actualSpans) { + t.Log(err) + } + out, err := json.Marshal(actualSpans) + require.NoError(t, err) + t.Logf("Actual trace %v: %s", i, string(out)) + } + }) + if i == 1 { + t.Run("ToDomainSpan", func(t *testing.T) { + jSpan := jBatch.Spans[0] + mSpan := ToDomainSpan(jSpan, jBatch.Process) + mSpan.NormalizeTimestamps() + assert.Equal(t, mSpans[0], mSpan) + }) + } + } +} + +func loadSpans(t *testing.T, file string) []*model.Span { + var trace model.Trace + loadJSONPB(t, file, &trace) + return trace.Spans +} + +func loadJSONPB(t *testing.T, fileName string, obj proto.Message) { + jsonFile, err := os.Open(fileName) + require.NoError(t, err, "Failed to open json fixture file %s", fileName) + require.NoError(t, jsonpb.Unmarshal(jsonFile, obj), fileName) +} + +func loadBatch(t *testing.T, file string) *jaeger.Batch { + var batch jaeger.Batch + loadJSON(t, file, &batch) + return &batch +} + +func loadJSON(t *testing.T, fileName string, obj any) { + jsonFile, err := os.Open(fileName) + require.NoError(t, err, "Failed to load json fixture file %s", fileName) + jsonParser := json.NewDecoder(jsonFile) + err = jsonParser.Decode(obj) + require.NoError(t, err, "Failed to parse json fixture file %s", fileName) +} + +func TestUnknownJaegerType(t *testing.T) { + mkv := toDomain{}.getTag(&jaeger.Tag{ + VType: 999, + Key: "sneh", + }) + expected := model.String("sneh", "Unknown VType: Tag({Key:sneh VType: VStr: VDouble: VBool: VLong: VBinary:[]})") + assert.Equal(t, expected, mkv) +} + +func TestToDomain_ToDomainProcess(t *testing.T) { + p := ToDomainProcess(&jaeger.Process{ServiceName: "foo", Tags: []*jaeger.Tag{{Key: "foo", VType: jaeger.TagType_BOOL}}}) + assert.Equal(t, &model.Process{ServiceName: "foo", Tags: []model.KeyValue{{Key: "foo", VType: model.BoolType}}}, p) +} + +func TestToDomain_ToDomainSpanProcessNull(t *testing.T) { + tm := time.Unix(158, 0) + s := ToDomainSpan(&jaeger.Span{OperationName: "foo", StartTime: int64(model.TimeAsEpochMicroseconds(tm))}, nil) + assert.Equal(t, &model.Span{OperationName: "foo", StartTime: tm.UTC()}, s) +} diff --git a/receiver/jaegerreceiver/jaeger_agent_test.go b/receiver/jaegerreceiver/jaeger_agent_test.go index d46ee9c8b40f..06f4a915b889 100644 --- a/receiver/jaegerreceiver/jaeger_agent_test.go +++ b/receiver/jaegerreceiver/jaeger_agent_test.go @@ -14,7 +14,6 @@ import ( "github.com/jaegertracing/jaeger-idl/thrift-gen/agent" jaegerthrift "github.com/jaegertracing/jaeger-idl/thrift-gen/jaeger" "github.com/jaegertracing/jaeger/cmd/agent/app/servers/thriftudp" - jaegerconvert "github.com/jaegertracing/jaeger/model/converter/thrift/jaeger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" @@ -26,6 +25,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" + jaegerconvert "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/jaegerthriftcoverter" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver/internal/metadata" )