diff --git a/cmd/jaeger/internal/integration/trace_reader.go b/cmd/jaeger/internal/integration/trace_reader.go index fca5bcaf7ae..8a50d20ba10 100644 --- a/cmd/jaeger/internal/integration/trace_reader.go +++ b/cmd/jaeger/internal/integration/trace_reader.go @@ -18,6 +18,7 @@ import ( "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" + "github.com/jaegertracing/jaeger/internal/jptrace" "github.com/jaegertracing/jaeger/internal/proto/api_v3" "github.com/jaegertracing/jaeger/internal/storage/v1/api/spanstore" "github.com/jaegertracing/jaeger/internal/storage/v2/api/tracestore" @@ -120,7 +121,7 @@ func (r *traceReader) FindTraces( Query: &api_v3.TraceQueryParameters{ ServiceName: query.ServiceName, OperationName: query.OperationName, - Attributes: query.Tags, + Attributes: jptrace.AttributesToMap(query.Attributes), StartTimeMin: query.StartTimeMin, StartTimeMax: query.StartTimeMax, DurationMin: query.DurationMin, diff --git a/cmd/query/app/apiv3/grpc_handler.go b/cmd/query/app/apiv3/grpc_handler.go index 271dd7b56bf..1716b6fed51 100644 --- a/cmd/query/app/apiv3/grpc_handler.go +++ b/cmd/query/app/apiv3/grpc_handler.go @@ -15,6 +15,7 @@ import ( "github.com/jaegertracing/jaeger-idl/model/v1" "github.com/jaegertracing/jaeger/cmd/query/app/querysvc/v2/querysvc" + "github.com/jaegertracing/jaeger/internal/jptrace" "github.com/jaegertracing/jaeger/internal/proto/api_v3" "github.com/jaegertracing/jaeger/internal/storage/v2/api/tracestore" "github.com/jaegertracing/jaeger/internal/storage/v2/v1adapter" @@ -73,7 +74,7 @@ func (h *Handler) internalFindTraces( TraceQueryParams: tracestore.TraceQueryParams{ ServiceName: query.GetServiceName(), OperationName: query.GetOperationName(), - Tags: query.GetAttributes(), + Attributes: jptrace.MapToAttributes(query.GetAttributes()), NumTraces: int(query.GetSearchDepth()), }, RawTraces: query.GetRawTraces(), diff --git a/cmd/query/app/apiv3/http_gateway.go b/cmd/query/app/apiv3/http_gateway.go index b4c73d5ba0d..34485e9e51f 100644 --- a/cmd/query/app/apiv3/http_gateway.go +++ b/cmd/query/app/apiv3/http_gateway.go @@ -15,6 +15,7 @@ import ( "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "github.com/gorilla/mux" + "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel/trace" @@ -211,7 +212,7 @@ func (h *HTTPGateway) parseFindTracesQuery(q url.Values, w http.ResponseWriter) TraceQueryParams: tracestore.TraceQueryParams{ ServiceName: q.Get(paramServiceName), OperationName: q.Get(paramOperationName), - Tags: nil, // most curiously not supported by grpc-gateway + Attributes: pcommon.NewMap(), // most curiously not supported by grpc-gateway }, } diff --git a/cmd/query/app/apiv3/http_gateway_test.go b/cmd/query/app/apiv3/http_gateway_test.go index 2c2022f248f..8fd2a0b6384 100644 --- a/cmd/query/app/apiv3/http_gateway_test.go +++ b/cmd/query/app/apiv3/http_gateway_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" "go.uber.org/zap" @@ -260,6 +261,7 @@ func mockFindQueries() (url.Values, tracestore.TraceQueryParams) { return q, tracestore.TraceQueryParams{ ServiceName: "foo", OperationName: "bar", + Attributes: pcommon.NewMap(), StartTimeMin: time1, StartTimeMax: time2, DurationMin: 1 * time.Second, diff --git a/internal/jptrace/attributes.go b/internal/jptrace/attributes.go index 0a570105f38..4365af0accb 100644 --- a/internal/jptrace/attributes.go +++ b/internal/jptrace/attributes.go @@ -2,6 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 package jptrace +import ( + "go.opentelemetry.io/collector/pdata/pcommon" +) + const ( // WarningsAttribute is the name of the span attribute where we can // store various warnings produced from transformations, @@ -13,3 +17,20 @@ const ( // e.g. proto, thrift, json. FormatAttribute = "@jaeger@format" ) + +func AttributesToMap(attributes pcommon.Map) map[string]string { + tags := make(map[string]string) + attributes.Range(func(k string, v pcommon.Value) bool { + tags[k] = v.AsString() + return true + }) + return tags +} + +func MapToAttributes(tags map[string]string) pcommon.Map { + attributes := pcommon.NewMap() + for k, v := range tags { + attributes.PutStr(k, v) + } + return attributes +} diff --git a/internal/jptrace/attributes_test.go b/internal/jptrace/attributes_test.go new file mode 100644 index 00000000000..8dd56fac96a --- /dev/null +++ b/internal/jptrace/attributes_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2025 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package jptrace + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" +) + +func TestAttributesToMap(t *testing.T) { + tests := []struct { + name string + attributes pcommon.Map + expected map[string]string + }{ + { + name: "empty attributes", + attributes: pcommon.NewMap(), + expected: map[string]string{}, + }, + { + name: "single attribute", + attributes: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("key1", "value1") + return m + }(), + expected: map[string]string{"key1": "value1"}, + }, + { + name: "multiple attributes", + attributes: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("key1", "value1") + m.PutStr("key2", "value2") + return m + }(), + expected: map[string]string{"key1": "value1", "key2": "value2"}, + }, + { + name: "non-string attributes", + attributes: func() pcommon.Map { + m := pcommon.NewMap() + m.PutInt("key1", 1) + m.PutDouble("key2", 3.14) + return m + }(), + expected: map[string]string{"key1": "1", "key2": "3.14"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := AttributesToMap(test.attributes) + assert.Equal(t, test.expected, result) + }) + } +} + +func TestMapToAttributes(t *testing.T) { + tests := []struct { + name string + tags map[string]string + expected pcommon.Map + }{ + { + name: "empty map", + tags: map[string]string{}, + expected: pcommon.NewMap(), + }, + { + name: "single tag", + tags: map[string]string{"key1": "value1"}, + expected: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("key1", "value1") + return m + }(), + }, + { + name: "multiple tags", + tags: map[string]string{"key1": "value1", "key2": "value2"}, + expected: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr("key1", "value1") + m.PutStr("key2", "value2") + return m + }(), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := MapToAttributes(test.tags) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/internal/storage/integration/fixtures/queries.json b/internal/storage/integration/fixtures/queries.json index 35707341379..182057cd7d5 100644 --- a/internal/storage/integration/fixtures/queries.json +++ b/internal/storage/integration/fixtures/queries.json @@ -6,9 +6,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -25,9 +25,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -44,9 +44,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -63,9 +63,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -180,9 +180,9 @@ "OperationName": "query12-operation", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -199,9 +199,9 @@ "OperationName": "query13-operation", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -218,9 +218,9 @@ "OperationName": "query14-operation", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -237,9 +237,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -256,9 +256,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -275,9 +275,9 @@ "OperationName": "query17-operation", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -294,9 +294,9 @@ "OperationName": "query18-operation", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -313,9 +313,9 @@ "OperationName": "query19-operation", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -332,9 +332,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", @@ -351,9 +351,9 @@ "OperationName": "", "Tags": { "sameplacetag1":"sameplacevalue", - "sameplacetag2":"123", - "sameplacetag3":"72.5", - "sameplacetag4":"true" + "sameplacetag2":123, + "sameplacetag3":72.5, + "sameplacetag4":true }, "StartTimeMin": "2017-01-26T15:46:31.639875Z", "StartTimeMax": "2017-01-26T17:46:31.639875Z", diff --git a/internal/storage/integration/integration.go b/internal/storage/integration/integration.go index b15c935fe39..630c4bb40f0 100644 --- a/internal/storage/integration/integration.go +++ b/internal/storage/integration/integration.go @@ -21,6 +21,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" "github.com/jaegertracing/jaeger-idl/model/v1" "github.com/jaegertracing/jaeger/internal/storage/v1/api/samplingstore" @@ -69,7 +70,7 @@ type StorageIntegration struct { type Query struct { ServiceName string OperationName string - Tags map[string]string + Tags map[string]any StartTimeMin time.Time StartTimeMax time.Time DurationMin time.Duration @@ -78,10 +79,24 @@ type Query struct { } func (q *Query) ToTraceQueryParams() *tracestore.TraceQueryParams { + attributes := pcommon.NewMap() + for k, v := range q.Tags { + switch v := v.(type) { + case string: + attributes.PutStr(k, v) + case int: + attributes.PutInt(k, int64(v)) + case float64: + attributes.PutDouble(k, v) + case bool: + attributes.PutBool(k, v) + } + } + return &tracestore.TraceQueryParams{ ServiceName: q.ServiceName, OperationName: q.OperationName, - Tags: q.Tags, + Attributes: attributes, StartTimeMin: q.StartTimeMin, StartTimeMax: q.StartTimeMax, DurationMin: q.DurationMin, diff --git a/internal/storage/v2/api/tracestore/reader.go b/internal/storage/v2/api/tracestore/reader.go index a5d51f79ede..eb29d25e639 100644 --- a/internal/storage/v2/api/tracestore/reader.go +++ b/internal/storage/v2/api/tracestore/reader.go @@ -11,6 +11,7 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" + "github.com/jaegertracing/jaeger/internal/jptrace" "github.com/jaegertracing/jaeger/internal/storage/v1/api/spanstore" ) @@ -80,7 +81,7 @@ type GetTraceParams struct { type TraceQueryParams struct { ServiceName string OperationName string - Tags map[string]string + Attributes pcommon.Map StartTimeMin time.Time StartTimeMax time.Time DurationMin time.Duration @@ -105,7 +106,7 @@ func (t *TraceQueryParams) ToSpanStoreQueryParameters() *spanstore.TraceQueryPar return &spanstore.TraceQueryParameters{ ServiceName: t.ServiceName, OperationName: t.OperationName, - Tags: t.Tags, + Tags: jptrace.AttributesToMap(t.Attributes), StartTimeMin: t.StartTimeMin, StartTimeMax: t.StartTimeMax, DurationMin: t.DurationMin, diff --git a/internal/storage/v2/api/tracestore/reader_test.go b/internal/storage/v2/api/tracestore/reader_test.go index ec59e4a93bb..d8c202f1454 100644 --- a/internal/storage/v2/api/tracestore/reader_test.go +++ b/internal/storage/v2/api/tracestore/reader_test.go @@ -8,16 +8,20 @@ import ( "time" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" "github.com/jaegertracing/jaeger/internal/storage/v1/api/spanstore" ) func TestToSpanStoreQueryParameters(t *testing.T) { now := time.Now() + attributes := pcommon.NewMap() + attributes.PutStr("tag-a", "val-a") + query := &TraceQueryParams{ ServiceName: "service", OperationName: "operation", - Tags: map[string]string{"tag-a": "val-a"}, + Attributes: attributes, StartTimeMin: now, StartTimeMax: now.Add(time.Minute), DurationMin: time.Minute, diff --git a/internal/storage/v2/v1adapter/spanreader.go b/internal/storage/v2/v1adapter/spanreader.go index cbc3344664f..b4431c29e9f 100644 --- a/internal/storage/v2/v1adapter/spanreader.go +++ b/internal/storage/v2/v1adapter/spanreader.go @@ -8,6 +8,7 @@ import ( "errors" "github.com/jaegertracing/jaeger-idl/model/v1" + "github.com/jaegertracing/jaeger/internal/jptrace" "github.com/jaegertracing/jaeger/internal/storage/v1/api/spanstore" "github.com/jaegertracing/jaeger/internal/storage/v2/api/tracestore" ) @@ -78,7 +79,7 @@ func (sr *SpanReader) FindTraces( getTracesIter := sr.traceReader.FindTraces(ctx, tracestore.TraceQueryParams{ ServiceName: query.ServiceName, OperationName: query.OperationName, - Tags: query.Tags, + Attributes: jptrace.MapToAttributes(query.Tags), StartTimeMin: query.StartTimeMin, StartTimeMax: query.StartTimeMax, DurationMin: query.DurationMin, @@ -95,7 +96,7 @@ func (sr *SpanReader) FindTraceIDs( traceIDsIter := sr.traceReader.FindTraceIDs(ctx, tracestore.TraceQueryParams{ ServiceName: query.ServiceName, OperationName: query.OperationName, - Tags: query.Tags, + Attributes: jptrace.MapToAttributes(query.Tags), StartTimeMin: query.StartTimeMin, StartTimeMax: query.StartTimeMax, DurationMin: query.DurationMin, diff --git a/internal/storage/v2/v1adapter/spanreader_test.go b/internal/storage/v2/v1adapter/spanreader_test.go index 3804ad8d235..8cef4116fe7 100644 --- a/internal/storage/v2/v1adapter/spanreader_test.go +++ b/internal/storage/v2/v1adapter/spanreader_test.go @@ -244,6 +244,7 @@ func TestSpanReader_FindTraces(t *testing.T) { }, expectedQuery: tracestore.TraceQueryParams{ ServiceName: "service1", + Attributes: pcommon.NewMap(), }, err: assert.AnError, expectedErr: assert.AnError, @@ -255,6 +256,7 @@ func TestSpanReader_FindTraces(t *testing.T) { }, expectedQuery: tracestore.TraceQueryParams{ ServiceName: "service1", + Attributes: pcommon.NewMap(), }, traces: []ptrace.Traces{}, expectedTraces: nil, @@ -266,6 +268,7 @@ func TestSpanReader_FindTraces(t *testing.T) { }, expectedQuery: tracestore.TraceQueryParams{ ServiceName: "service1", + Attributes: pcommon.NewMap(), }, traces: func() []ptrace.Traces { traces1 := ptrace.NewTraces() @@ -348,6 +351,7 @@ func TestSpanReader_FindTraceIDs(t *testing.T) { }, expectedQuery: tracestore.TraceQueryParams{ ServiceName: "service1", + Attributes: pcommon.NewMap(), }, err: assert.AnError, expectedErr: assert.AnError, @@ -359,6 +363,7 @@ func TestSpanReader_FindTraceIDs(t *testing.T) { }, expectedQuery: tracestore.TraceQueryParams{ ServiceName: "service1", + Attributes: pcommon.NewMap(), }, traceIDs: []tracestore.FoundTraceID{}, expectedTraceIDs: nil, @@ -370,6 +375,7 @@ func TestSpanReader_FindTraceIDs(t *testing.T) { }, expectedQuery: tracestore.TraceQueryParams{ ServiceName: "service1", + Attributes: pcommon.NewMap(), }, traceIDs: []tracestore.FoundTraceID{ { diff --git a/internal/storage/v2/v1adapter/tracereader_test.go b/internal/storage/v2/v1adapter/tracereader_test.go index f6a2b40d67e..cf0743e2ad4 100644 --- a/internal/storage/v2/v1adapter/tracereader_test.go +++ b/internal/storage/v2/v1adapter/tracereader_test.go @@ -295,12 +295,14 @@ func TestTraceReader_FindTracesDelegatesSuccessResponse(t *testing.T) { traceReader := &TraceReader{ spanReader: sr, } + attributes := pcommon.NewMap() + attributes.PutStr("tag-a", "val-a") traces, err := jiter.FlattenWithErrors(traceReader.FindTraces( context.Background(), tracestore.TraceQueryParams{ ServiceName: "service", OperationName: "operation", - Tags: map[string]string{"tag-a": "val-a"}, + Attributes: attributes, StartTimeMin: now, StartTimeMax: now.Add(time.Minute), DurationMin: time.Minute, @@ -360,7 +362,9 @@ func TestTraceReader_FindTracesEdgeCases(t *testing.T) { } traces, err := jiter.FlattenWithErrors(traceReader.FindTraces( context.Background(), - tracestore.TraceQueryParams{}, + tracestore.TraceQueryParams{ + Attributes: pcommon.NewMap(), + }, )) require.ErrorIs(t, err, test.err) require.Equal(t, test.expectedTraces, traces) @@ -380,7 +384,9 @@ func TestTraceReader_FindTracesEarlyStop(t *testing.T) { } called := 0 traceReader.FindTraces( - context.Background(), tracestore.TraceQueryParams{}, + context.Background(), tracestore.TraceQueryParams{ + Attributes: pcommon.NewMap(), + }, )(func(tr []ptrace.Traces, err error) bool { require.NoError(t, err) require.Len(t, tr, 1) @@ -390,7 +396,9 @@ func TestTraceReader_FindTracesEarlyStop(t *testing.T) { assert.Equal(t, 3, called) called = 0 traceReader.FindTraces( - context.Background(), tracestore.TraceQueryParams{}, + context.Background(), tracestore.TraceQueryParams{ + Attributes: pcommon.NewMap(), + }, )(func(tr []ptrace.Traces, err error) bool { require.NoError(t, err) require.Len(t, tr, 1) @@ -460,12 +468,14 @@ func TestTraceReader_FindTraceIDsDelegatesResponse(t *testing.T) { traceReader := &TraceReader{ spanReader: sr, } + attributes := pcommon.NewMap() + attributes.PutStr("tag-a", "val-a") traceIDs, err := jiter.FlattenWithErrors(traceReader.FindTraceIDs( context.Background(), tracestore.TraceQueryParams{ ServiceName: "service", OperationName: "operation", - Tags: map[string]string{"tag-a": "val-a"}, + Attributes: attributes, StartTimeMin: now, StartTimeMax: now.Add(time.Minute), DurationMin: time.Minute,