Skip to content

Commit 2f592b8

Browse files
authored
Use official OTLP types in api_v3 and avoid triple-marshaling (jaegertracing#5098)
## Which problem is this PR solving? - Part of jaegertracing#5079 - Avoid triple marshaling due to our use of internally generated OTLP proto types, which prevents us from directly using the output of jaeger->otlp conversion from collector contrib. ## Description of the changes - 🛑 breaking: the JSON format is changed to match [OTLP-JSON][otlp-json], specifically - the trace & span IDs are returned as hex-encoded strings, not base64 as required by Proto-JSON - enums are returned as integers, not strings - Use Proposal 1 from open-telemetry/opentelemetry-collector#9233 (comment) - API-V3 proto is already declared to use official OTLP types; remove the overrides to our internally generated OTLP proto types - Replace `SpansResponseChunk` with official `otlp.TracesData`, but override it internally to use a custom type - Depends on jaegertracing/jaeger-idl#103 ## How was this change tested? - Unit tests [otlp-json]: https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md#json-protobuf-encoding --------- Signed-off-by: Yuri Shkuro <[email protected]>
1 parent 09f7a30 commit 2f592b8

19 files changed

+328
-249
lines changed

Makefile.Protobuf.mk

+12-9
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,17 @@ proto-otel: proto-prepare-otel
146146
$(foreach file,$(OTEL_PROTO_FILES), \
147147
$(call proto_compile, proto-gen/otel, $(file), -I$(PATCHED_OTEL_PROTO_DIR), paths=source_relative))
148148

149-
# Similar to 'proto-prepare-otel', this target modifies OTEL Protos by changing their Go package.
150-
# This way the API v3 service uses official OTEL types like opentelemetry.proto.trace.v1.ResourceSpans
151-
# which at runtime are mapped to our internal classes generated in proto-gen/otel by 'proto-otel' target.
149+
# The API v3 service uses official OTEL type opentelemetry.proto.trace.v1.TracesData,
150+
# which at runtime is mapped to a custom type in cmd/query/app/internal/api_v3/traces.go
151+
# Unfortunately, gogoproto.customtype annotation cannot be applied to a method's return type,
152+
# only to fields in a struct, so we use regex search/replace to swap it.
153+
# Note that the .pb.go types must be generated into the same internal package $(API_V3_PATH)
154+
# where a manually defined traces.go file is located.
155+
API_V3_PATH=cmd/query/app/internal/api_v3
152156
.PHONY: proto-api-v3
153157
proto-api-v3:
154-
$(call print_caption, Enriching OpenTelemetry Protos into $(PATCHED_OTEL_PROTO_DIR))
155-
rm -rf $(PATCHED_OTEL_PROTO_DIR)/*
156-
cp -R idl/opentelemetry-proto/* $(PATCHED_OTEL_PROTO_DIR)
157-
find $(PATCHED_OTEL_PROTO_DIR) -name "*.proto" | xargs -L 1 $(SED) -i 's+go.opentelemetry.io/proto/otlp+github.com/jaegertracing/jaeger/proto-gen/otel+g'
158-
159-
$(call proto_compile, proto-gen/api_v3, idl/proto/api_v3/query_service.proto, -I$(PATCHED_OTEL_PROTO_DIR))
158+
$(call proto_compile, $(API_V3_PATH), idl/proto/api_v3/query_service.proto, -Iidl/opentelemetry-proto)
159+
@echo "🏗️ replace TracesData with internal custom type"
160+
$(SED) -i 's/v1.TracesData/TracesData/g' $(API_V3_PATH)/query_service.pb.go
161+
@echo "🏗️ remove OTEL import because we're not using any other OTLP types"
162+
$(SED) -i 's+^.*v1 "go.opentelemetry.io/proto/otlp/trace/v1".*$$++' $(API_V3_PATH)/query_service.pb.go

cmd/all-in-one/all_in_one_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131

3232
ui "github.com/jaegertracing/jaeger/model/json"
3333
"github.com/jaegertracing/jaeger/proto-gen/api_v2"
34-
"github.com/jaegertracing/jaeger/proto-gen/api_v3"
3534
)
3635

3736
// These tests are only run when the environment variable TEST_MODE=integration is set.
@@ -180,9 +179,11 @@ func getServicesAPIV3(t *testing.T) {
180179
require.NoError(t, err)
181180
body, _ := io.ReadAll(resp.Body)
182181

183-
var servicesResponse api_v3.GetServicesResponse
182+
var servicesResponse struct {
183+
Services []string
184+
}
184185
err = json.Unmarshal(body, &servicesResponse)
185186
require.NoError(t, err)
186-
require.Len(t, servicesResponse.GetServices(), 1)
187-
assert.Contains(t, servicesResponse.GetServices()[0], "jaeger")
187+
require.Len(t, servicesResponse.Services, 1)
188+
assert.Contains(t, servicesResponse.Services[0], "jaeger")
188189
}

cmd/query/app/apiv3/gateway_test.go

+11-27
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ import (
3030
"github.com/stretchr/testify/assert"
3131
"github.com/stretchr/testify/require"
3232

33+
"github.com/jaegertracing/jaeger/cmd/query/app/internal/api_v3"
3334
"github.com/jaegertracing/jaeger/model"
3435
_ "github.com/jaegertracing/jaeger/pkg/gogocodec" // force gogo codec registration
3536
"github.com/jaegertracing/jaeger/pkg/tenancy"
36-
"github.com/jaegertracing/jaeger/proto-gen/api_v3"
3737
"github.com/jaegertracing/jaeger/storage/spanstore"
3838
spanstoremocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks"
3939
)
@@ -155,18 +155,7 @@ func (gw *testGateway) runGatewayGetOperations(t *testing.T) {
155155
func (gw *testGateway) runGatewayGetTrace(t *testing.T) {
156156
trace, traceID := makeTestTrace()
157157
gw.reader.On("GetTrace", matchContext, traceID).Return(trace, nil).Once()
158-
159-
body, statusCode := gw.execRequest(t, "/api/v3/traces/"+traceID.String())
160-
require.Equal(t, http.StatusOK, statusCode, "response=%s", string(body))
161-
body = gw.verifySnapshot(t, body)
162-
163-
var response api_v3.GRPCGatewayWrapper
164-
parseResponse(t, body, &response)
165-
166-
assert.Len(t, response.Result.ResourceSpans, 1)
167-
assert.EqualValues(t,
168-
bytesOfTraceID(t, traceID.High, traceID.Low),
169-
response.Result.ResourceSpans[0].ScopeSpans[0].Spans[0].TraceID)
158+
gw.getTracesAndVerify(t, "/api/v3/traces/"+traceID.String(), traceID)
170159
}
171160

172161
func (gw *testGateway) runGatewayFindTraces(t *testing.T) {
@@ -175,23 +164,18 @@ func (gw *testGateway) runGatewayFindTraces(t *testing.T) {
175164
gw.reader.
176165
On("FindTraces", matchContext, qp).
177166
Return([]*model.Trace{trace}, nil).Once()
178-
body, statusCode := gw.execRequest(t, "/api/v3/traces?"+q.Encode())
167+
gw.getTracesAndVerify(t, "/api/v3/traces?"+q.Encode(), traceID)
168+
}
169+
170+
func (gw *testGateway) getTracesAndVerify(t *testing.T, url string, expectedTraceID model.TraceID) {
171+
body, statusCode := gw.execRequest(t, url)
179172
require.Equal(t, http.StatusOK, statusCode, "response=%s", string(body))
180173
body = gw.verifySnapshot(t, body)
181174

182175
var response api_v3.GRPCGatewayWrapper
183176
parseResponse(t, body, &response)
184-
185-
assert.Len(t, response.Result.ResourceSpans, 1)
186-
assert.EqualValues(t,
187-
bytesOfTraceID(t, traceID.High, traceID.Low),
188-
response.Result.ResourceSpans[0].ScopeSpans[0].Spans[0].TraceID)
189-
}
190-
191-
func bytesOfTraceID(t *testing.T, high, low uint64) []byte {
192-
traceID := model.NewTraceID(high, low)
193-
buf := make([]byte, 16)
194-
_, err := traceID.MarshalTo(buf)
195-
require.NoError(t, err)
196-
return buf
177+
td := response.Result.ToTraces()
178+
assert.EqualValues(t, 1, td.SpanCount())
179+
traceID := td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).TraceID()
180+
assert.Equal(t, expectedTraceID.String(), traceID.String())
197181
}

cmd/query/app/apiv3/grpc_handler.go

+8-9
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import (
2222
"google.golang.org/grpc/codes"
2323
"google.golang.org/grpc/status"
2424

25+
"github.com/jaegertracing/jaeger/cmd/query/app/internal/api_v3"
2526
"github.com/jaegertracing/jaeger/cmd/query/app/querysvc"
2627
"github.com/jaegertracing/jaeger/model"
27-
"github.com/jaegertracing/jaeger/proto-gen/api_v3"
2828
"github.com/jaegertracing/jaeger/storage/spanstore"
2929
)
3030

@@ -33,6 +33,7 @@ type Handler struct {
3333
QueryService *querysvc.QueryService
3434
}
3535

36+
// remove me
3637
var _ api_v3.QueryServiceServer = (*Handler)(nil)
3738

3839
// GetTrace implements api_v3.QueryServiceServer's GetTrace
@@ -46,13 +47,12 @@ func (h *Handler) GetTrace(request *api_v3.GetTraceRequest, stream api_v3.QueryS
4647
if err != nil {
4748
return fmt.Errorf("cannot retrieve trace: %w", err)
4849
}
49-
resourceSpans, err := modelToOTLP(trace.GetSpans())
50+
td, err := modelToOTLP(trace.GetSpans())
5051
if err != nil {
5152
return err
5253
}
53-
return stream.Send(&api_v3.SpansResponseChunk{
54-
ResourceSpans: resourceSpans,
55-
})
54+
tracesData := api_v3.TracesData(td)
55+
return stream.Send(&tracesData)
5656
}
5757

5858
// FindTraces implements api_v3.QueryServiceServer's FindTraces
@@ -106,13 +106,12 @@ func (h *Handler) FindTraces(request *api_v3.FindTracesRequest, stream api_v3.Qu
106106
return err
107107
}
108108
for _, t := range traces {
109-
resourceSpans, err := modelToOTLP(t.GetSpans())
109+
td, err := modelToOTLP(t.GetSpans())
110110
if err != nil {
111111
return err
112112
}
113-
stream.Send(&api_v3.SpansResponseChunk{
114-
ResourceSpans: resourceSpans,
115-
})
113+
tracesData := api_v3.TracesData(td)
114+
stream.Send(&tracesData)
116115
}
117116
return nil
118117
}

cmd/query/app/apiv3/grpc_handler_test.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ import (
2727
"google.golang.org/grpc"
2828
"google.golang.org/grpc/credentials/insecure"
2929

30+
"github.com/jaegertracing/jaeger/cmd/query/app/internal/api_v3"
3031
"github.com/jaegertracing/jaeger/cmd/query/app/querysvc"
3132
"github.com/jaegertracing/jaeger/model"
3233
_ "github.com/jaegertracing/jaeger/pkg/gogocodec" // force gogo codec registration
33-
"github.com/jaegertracing/jaeger/proto-gen/api_v3"
3434
dependencyStoreMocks "github.com/jaegertracing/jaeger/storage/dependencystore/mocks"
3535
"github.com/jaegertracing/jaeger/storage/spanstore"
3636
spanstoremocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks"
@@ -106,10 +106,12 @@ func TestGetTrace(t *testing.T) {
106106
},
107107
)
108108
require.NoError(t, err)
109-
spansChunk, err := getTraceStream.Recv()
109+
recv, err := getTraceStream.Recv()
110110
require.NoError(t, err)
111-
require.Len(t, spansChunk.GetResourceSpans(), 1)
112-
assert.Equal(t, "foobar", spansChunk.GetResourceSpans()[0].GetScopeSpans()[0].GetSpans()[0].GetName())
111+
td := recv.ToTraces()
112+
require.EqualValues(t, 1, td.SpanCount())
113+
assert.Equal(t, "foobar",
114+
td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Name())
113115
}
114116

115117
func TestGetTraceStorageError(t *testing.T) {
@@ -121,10 +123,10 @@ func TestGetTraceStorageError(t *testing.T) {
121123
TraceId: "156",
122124
})
123125
require.NoError(t, err)
124-
spansChunk, err := getTraceStream.Recv()
126+
recv, err := getTraceStream.Recv()
125127
require.Error(t, err)
126128
assert.Contains(t, err.Error(), "storage_error")
127-
assert.Nil(t, spansChunk)
129+
assert.Nil(t, recv)
128130
}
129131

130132
func TestGetTraceTraceIDError(t *testing.T) {
@@ -138,10 +140,10 @@ func TestGetTraceTraceIDError(t *testing.T) {
138140
TraceId: "Z",
139141
})
140142
require.NoError(t, err)
141-
spansChunk, err := getTraceStream.Recv()
143+
recv, err := getTraceStream.Recv()
142144
require.Error(t, err)
143145
assert.Contains(t, err.Error(), "strconv.ParseUint:")
144-
assert.Nil(t, spansChunk)
146+
assert.Nil(t, recv)
145147
}
146148

147149
func TestFindTraces(t *testing.T) {
@@ -171,7 +173,8 @@ func TestFindTraces(t *testing.T) {
171173
require.NoError(t, err)
172174
recv, err := responseStream.Recv()
173175
require.NoError(t, err)
174-
assert.Len(t, recv.GetResourceSpans(), 1)
176+
td := recv.ToTraces()
177+
require.EqualValues(t, 1, td.SpanCount())
175178
}
176179

177180
func TestFindTracesQueryNil(t *testing.T) {

cmd/query/app/apiv3/http_gateway.go

+6-8
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ import (
1515
"github.com/gogo/protobuf/jsonpb"
1616
"github.com/gogo/protobuf/proto"
1717
"github.com/gorilla/mux"
18+
"go.opentelemetry.io/collector/pdata/ptrace"
1819
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
1920
"go.uber.org/zap"
2021

22+
"github.com/jaegertracing/jaeger/cmd/query/app/internal/api_v3"
2123
"github.com/jaegertracing/jaeger/cmd/query/app/querysvc"
2224
"github.com/jaegertracing/jaeger/model"
2325
"github.com/jaegertracing/jaeger/pkg/jtracer"
2426
"github.com/jaegertracing/jaeger/pkg/tenancy"
25-
"github.com/jaegertracing/jaeger/proto-gen/api_v3"
26-
tracev1 "github.com/jaegertracing/jaeger/proto-gen/otel/trace/v1"
2727
"github.com/jaegertracing/jaeger/storage/spanstore"
2828
)
2929

@@ -118,18 +118,16 @@ func (h *HTTPGateway) returnSpans(spans []*model.Span, w http.ResponseWriter) {
118118
func (h *HTTPGateway) returnSpansTestable(
119119
spans []*model.Span,
120120
w http.ResponseWriter,
121-
modelToOTLP func(spans []*model.Span) ([]*tracev1.ResourceSpans, error),
121+
modelToOTLP func(spans []*model.Span) (ptrace.Traces, error),
122122
) {
123-
resourceSpans, err := modelToOTLP(spans)
123+
td, err := modelToOTLP(spans)
124124
if h.tryHandleError(w, err, http.StatusInternalServerError) {
125125
return
126126
}
127+
tracesData := api_v3.TracesData(td)
127128
response := &api_v3.GRPCGatewayWrapper{
128-
Result: &api_v3.SpansResponseChunk{
129-
ResourceSpans: resourceSpans,
130-
},
129+
Result: &tracesData,
131130
}
132-
133131
h.marshalResponse(response, w)
134132
}
135133

cmd/query/app/apiv3/http_gateway_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import (
1515
"github.com/gorilla/mux"
1616
"github.com/stretchr/testify/assert"
1717
"github.com/stretchr/testify/require"
18+
"go.opentelemetry.io/collector/pdata/ptrace"
1819
"go.uber.org/zap"
1920

2021
"github.com/jaegertracing/jaeger/cmd/query/app/querysvc"
2122
"github.com/jaegertracing/jaeger/model"
2223
"github.com/jaegertracing/jaeger/pkg/jtracer"
2324
"github.com/jaegertracing/jaeger/pkg/tenancy"
2425
"github.com/jaegertracing/jaeger/pkg/testutils"
25-
tracev1 "github.com/jaegertracing/jaeger/proto-gen/otel/trace/v1"
2626
dependencyStoreMocks "github.com/jaegertracing/jaeger/storage/dependencystore/mocks"
2727
"github.com/jaegertracing/jaeger/storage/spanstore"
2828
spanstoremocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks"
@@ -117,8 +117,8 @@ func TestHTTPGatewayOTLPError(t *testing.T) {
117117
}
118118
const simErr = "simulated error"
119119
gw.returnSpansTestable(nil, w,
120-
func(spans []*model.Span) ([]*tracev1.ResourceSpans, error) {
121-
return nil, fmt.Errorf(simErr)
120+
func(spans []*model.Span) (ptrace.Traces, error) {
121+
return ptrace.Traces{}, fmt.Errorf(simErr)
122122
},
123123
)
124124
assert.Contains(t, w.Body.String(), simErr)

cmd/query/app/apiv3/otlp_translator.go

+3-23
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,13 @@
1515
package apiv3
1616

1717
import (
18-
"fmt"
19-
20-
"github.com/gogo/protobuf/proto"
2118
model2otel "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger"
22-
"go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp"
19+
"go.opentelemetry.io/collector/pdata/ptrace"
2320

2421
"github.com/jaegertracing/jaeger/model"
25-
"github.com/jaegertracing/jaeger/proto-gen/api_v3"
26-
tracev1 "github.com/jaegertracing/jaeger/proto-gen/otel/trace/v1"
2722
)
2823

29-
func modelToOTLP(spans []*model.Span) ([]*tracev1.ResourceSpans, error) {
24+
func modelToOTLP(spans []*model.Span) (ptrace.Traces, error) {
3025
batch := &model.Batch{Spans: spans}
31-
td, err := model2otel.ProtoToTraces([]*model.Batch{batch})
32-
if err != nil {
33-
return nil, fmt.Errorf("cannot convert trace to OpenTelemetry: %w", err)
34-
}
35-
req := ptraceotlp.NewExportRequestFromTraces(td)
36-
// OTEL Collector hides the internal proto implementation, so do a roundtrip conversion (inefficient)
37-
b, err := req.MarshalProto()
38-
if err != nil {
39-
return nil, fmt.Errorf("cannot marshal OTLP: %w", err)
40-
}
41-
// use api_v3.SpansResponseChunk which has the same shape as otlp.ExportTraceServiceRequest
42-
var chunk api_v3.SpansResponseChunk
43-
if err := proto.Unmarshal(b, &chunk); err != nil {
44-
return nil, fmt.Errorf("cannot marshal OTLP: %w", err)
45-
}
46-
return chunk.ResourceSpans, nil
26+
return model2otel.ProtoToTraces([]*model.Batch{batch})
4727
}

cmd/query/app/apiv3/snapshots/FindTraces.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
"spans": [
1010
{
1111
"endTimeUnixNano": "11651379494838206464",
12-
"kind": "SPAN_KIND_SERVER",
12+
"kind": 2,
1313
"name": "foobar",
1414
"parentSpanId": "",
15-
"spanId": "AAAAAAAAALQ=",
15+
"spanId": "00000000000000b4",
1616
"startTimeUnixNano": "11651379494838206464",
1717
"status": {
18-
"code": "STATUS_CODE_ERROR"
18+
"code": 2
1919
},
20-
"traceId": "AAAAAAAAAJYAAAAAAAAAoA=="
20+
"traceId": "000000000000009600000000000000a0"
2121
}
2222
]
2323
}

cmd/query/app/apiv3/snapshots/GetTrace.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
"spans": [
1010
{
1111
"endTimeUnixNano": "11651379494838206464",
12-
"kind": "SPAN_KIND_SERVER",
12+
"kind": 2,
1313
"name": "foobar",
1414
"parentSpanId": "",
15-
"spanId": "AAAAAAAAALQ=",
15+
"spanId": "00000000000000b4",
1616
"startTimeUnixNano": "11651379494838206464",
1717
"status": {
18-
"code": "STATUS_CODE_ERROR"
18+
"code": 2
1919
},
20-
"traceId": "AAAAAAAAAJYAAAAAAAAAoA=="
20+
"traceId": "000000000000009600000000000000a0"
2121
}
2222
]
2323
}

0 commit comments

Comments
 (0)