Skip to content

Commit c4766f2

Browse files
Fix: Support attributes filtering in /api/v3/traces endpoint
Signed-off-by: Divyansh Khatri <[email protected]>
1 parent 39ef71e commit c4766f2

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

cmd/query/app/apiv3/http_gateway.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const (
4444
paramDurationMin = "query.duration_min"
4545
paramDurationMax = "query.duration_max"
4646
paramQueryRawTraces = "query.raw_traces"
47+
paramAttributes = "query.attributes"
4748

4849
routeGetTrace = "/api/v3/traces/{" + paramTraceID + "}"
4950
routeFindTraces = "/api/v3/traces"
@@ -209,11 +210,31 @@ func (h *HTTPGateway) findTraces(w http.ResponseWriter, r *http.Request) {
209210
}
210211

211212
func (h *HTTPGateway) parseFindTracesQuery(q url.Values, w http.ResponseWriter) (*querysvc.TraceQueryParams, bool) {
213+
// Parse attributes first
214+
attributes := pcommon.NewMap() // Initialize empty map
215+
attrStr := q.Get(paramAttributes)
216+
if attrStr != "" {
217+
var attrMap map[string]string
218+
// The attributes are expected to be a JSON string map, e.g., {"key":"value"}
219+
// This handles URL-encoded JSON like %7B%22driver%22%3A%22T751767C%22%7D
220+
if err := json.Unmarshal([]byte(attrStr), &attrMap); err != nil {
221+
// Pass the underlying JSON parsing error
222+
err := fmt.Errorf("cannot parse attributes JSON: %w", err)
223+
if h.tryParamError(w, err, paramAttributes) {
224+
return nil, true
225+
}
226+
}
227+
// Populate the pcommon.Map from the parsed map
228+
for k, v := range attrMap {
229+
attributes.PutStr(k, v)
230+
}
231+
}
232+
212233
queryParams := &querysvc.TraceQueryParams{
213234
TraceQueryParams: tracestore.TraceQueryParams{
214235
ServiceName: q.Get(paramServiceName),
215236
OperationName: q.Get(paramOperationName),
216-
Attributes: pcommon.NewMap(), // most curiously not supported by grpc-gateway
237+
Attributes: attributes,
217238
},
218239
}
219240

cmd/query/app/apiv3/http_gateway_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,55 @@ func TestHTTPGatewayFindTracesEmptyResponse(t *testing.T) {
183183
assert.Contains(t, w.Body.String(), "No traces found")
184184
}
185185

186+
func TestHTTPGatewayFindTracesWithAttributes(t *testing.T) {
187+
time1 := time.Now().UTC().Truncate(time.Nanosecond)
188+
time2 := time1.Add(-time.Second).UTC().Truncate(time.Nanosecond)
189+
190+
q := url.Values{}
191+
q.Set(paramServiceName, "foo")
192+
q.Set(paramTimeMin, time1.Format(time.RFC3339Nano))
193+
q.Set(paramTimeMax, time2.Format(time.RFC3339Nano))
194+
q.Set(paramNumTraces, "10")
195+
q.Set(paramAttributes, `{"http.status_code":"200", "error":"true"}`)
196+
197+
expectedAttrs := pcommon.NewMap()
198+
expectedAttrs.PutStr("http.status_code", "200")
199+
expectedAttrs.PutStr("error", "true")
200+
201+
qp := tracestore.TraceQueryParams{
202+
ServiceName: "foo",
203+
OperationName: "",
204+
Attributes: expectedAttrs,
205+
StartTimeMin: time1,
206+
StartTimeMax: time2,
207+
DurationMin: 0,
208+
DurationMax: 0,
209+
SearchDepth: 10,
210+
}
211+
212+
r, err := http.NewRequest(http.MethodGet, "/api/v3/traces?"+q.Encode(), http.NoBody)
213+
require.NoError(t, err)
214+
w := httptest.NewRecorder()
215+
216+
gw := setupHTTPGatewayNoServer(t, "")
217+
218+
testTrace := ptrace.NewTraces()
219+
rs := testTrace.ResourceSpans().AppendEmpty()
220+
ss := rs.ScopeSpans().AppendEmpty()
221+
ss.Spans().AppendEmpty().SetName("test-span")
222+
223+
gw.reader.
224+
On("FindTraces", matchContext, qp).
225+
Return(iter.Seq2[[]ptrace.Traces, error](func(yield func([]ptrace.Traces, error) bool) {
226+
yield([]ptrace.Traces{testTrace}, nil)
227+
})).Once()
228+
229+
gw.router.ServeHTTP(w, r)
230+
assert.Equal(t, http.StatusOK, w.Code)
231+
gw.reader.AssertExpectations(t)
232+
}
233+
234+
186235
func TestHTTPGatewayGetTraceMalformedInputErrors(t *testing.T) {
187236
testCases := []struct {
188237
name string
@@ -328,6 +377,15 @@ func TestHTTPGatewayFindTracesErrors(t *testing.T) {
328377
},
329378
expErr: paramQueryRawTraces,
330379
},
380+
{
381+
name: "bad attributes json",
382+
params: map[string]string{
383+
paramTimeMin: goodTime,
384+
paramTimeMax: goodTime,
385+
paramAttributes: `{"key":"value"`,
386+
},
387+
expErr: paramAttributes,
388+
},
331389
}
332390
for _, tc := range testCases {
333391
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)