Skip to content

Commit 9456d04

Browse files
committed
fix: errors only
1 parent 12b9d11 commit 9456d04

7 files changed

Lines changed: 78 additions & 37 deletions

File tree

apps/api/src/services/QueryEngineService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ export const makeQueryEngineExecute = (tinybird: QueryEngineTinybird) =>
337337
service_name: request.query.filters?.serviceName,
338338
span_name: request.query.filters?.spanName,
339339
root_only: request.query.filters?.rootSpansOnly ? "1" : undefined,
340+
errors_only: request.query.filters?.errorsOnly ? "1" : undefined,
340341
environments: request.query.filters?.environments?.join(","),
341342
commit_shas: request.query.filters?.commitShas?.join(","),
342343
group_by_service: request.query.groupBy?.includes("service") ? "1" : undefined,
@@ -461,6 +462,7 @@ export const makeQueryEngineExecute = (tinybird: QueryEngineTinybird) =>
461462
span_name: request.query.filters?.spanName,
462463
limit: request.query.limit,
463464
root_only: request.query.filters?.rootSpansOnly ? "1" : undefined,
465+
errors_only: request.query.filters?.errorsOnly ? "1" : undefined,
464466
environments: request.query.filters?.environments?.join(","),
465467
commit_shas: request.query.filters?.commitShas?.join(","),
466468
group_by_service: request.query.groupBy === "service" ? "1" : undefined,

apps/web/src/components/query-builder/where-clause-editor.tsx

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,40 @@ export function WhereClauseEditor({
4747
const [isDismissed, setIsDismissed] = React.useState(false)
4848
const [activeIndex, setActiveIndex] = React.useState(0)
4949

50-
React.useEffect(() => {
51-
setCursor((current) => Math.min(current, value.length))
52-
}, [value])
50+
const lastAttrKeyRef = React.useRef<string | null>(null)
51+
const lastResourceKeyRef = React.useRef<string | null>(null)
52+
53+
const notifyActiveKeys = React.useCallback(
54+
(expression: string, cursorPos: number) => {
55+
const ac = getWhereClauseAutocomplete({
56+
expression,
57+
cursor: cursorPos,
58+
dataSource,
59+
values,
60+
scope: autocompleteScope,
61+
maxSuggestions,
62+
})
63+
64+
const nextAttrKey =
65+
ac.context === "value" && ac.key?.startsWith("attr.")
66+
? ac.key.slice(5)
67+
: null
68+
if (nextAttrKey !== lastAttrKeyRef.current) {
69+
lastAttrKeyRef.current = nextAttrKey
70+
onActiveAttributeKey?.(nextAttrKey)
71+
}
72+
73+
const nextResourceKey =
74+
ac.context === "value" && ac.key?.startsWith("resource.")
75+
? ac.key.slice(9)
76+
: null
77+
if (nextResourceKey !== lastResourceKeyRef.current) {
78+
lastResourceKeyRef.current = nextResourceKey
79+
onActiveResourceAttributeKey?.(nextResourceKey)
80+
}
81+
},
82+
[autocompleteScope, dataSource, maxSuggestions, onActiveAttributeKey, onActiveResourceAttributeKey, values],
83+
)
5384

5485
const autocomplete = React.useMemo(
5586
() =>
@@ -64,37 +95,19 @@ export function WhereClauseEditor({
6495
[autocompleteScope, cursor, dataSource, maxSuggestions, value, values],
6596
)
6697

67-
// Notify parent when user is editing a value for an attr.* key
68-
React.useEffect(() => {
69-
if (!onActiveAttributeKey) return
70-
if (autocomplete.context === "value" && autocomplete.key?.startsWith("attr.")) {
71-
onActiveAttributeKey(autocomplete.key.slice(5))
72-
} else {
73-
onActiveAttributeKey(null)
74-
}
75-
}, [autocomplete.context, autocomplete.key, onActiveAttributeKey])
76-
77-
// Notify parent when user is editing a value for a resource.* key
78-
React.useEffect(() => {
79-
if (!onActiveResourceAttributeKey) return
80-
if (autocomplete.context === "value" && autocomplete.key?.startsWith("resource.")) {
81-
onActiveResourceAttributeKey(autocomplete.key.slice(9))
82-
} else {
83-
onActiveResourceAttributeKey(null)
84-
}
85-
}, [autocomplete.context, autocomplete.key, onActiveResourceAttributeKey])
86-
8798
const suggestions = autocomplete.suggestions
8899
const isOpen = isFocused && !isDismissed && suggestions.length > 0
89100

90-
React.useEffect(() => {
91-
setActiveIndex(0)
92-
}, [autocomplete.context, autocomplete.query, suggestions.length])
93-
94-
const syncCursor = React.useCallback((target: HTMLTextAreaElement) => {
95-
setCursor(target.selectionStart ?? target.value.length)
96-
setIsDismissed(false)
97-
}, [])
101+
const syncCursor = React.useCallback(
102+
(target: HTMLTextAreaElement) => {
103+
const pos = target.selectionStart ?? target.value.length
104+
setCursor(pos)
105+
setIsDismissed(false)
106+
setActiveIndex(0)
107+
notifyActiveKeys(target.value, pos)
108+
},
109+
[notifyActiveKeys],
110+
)
98111

99112
const applySuggestion = React.useCallback(
100113
(index: number) => {
@@ -114,6 +127,7 @@ export function WhereClauseEditor({
114127
onChange(applied.expression)
115128
setCursor(applied.cursor)
116129
setIsDismissed(false)
130+
notifyActiveKeys(applied.expression, applied.cursor)
117131

118132
const schedule = (callback: () => void) => {
119133
if (typeof window !== "undefined" && window.requestAnimationFrame) {
@@ -134,7 +148,7 @@ export function WhereClauseEditor({
134148
textarea.setSelectionRange(applied.cursor, applied.cursor)
135149
})
136150
},
137-
[autocomplete.context, autocomplete.replaceEnd, autocomplete.replaceStart, onChange, suggestions, value],
151+
[autocomplete.context, autocomplete.replaceEnd, autocomplete.replaceStart, notifyActiveKeys, onChange, suggestions, value],
138152
)
139153

140154
return (
@@ -155,8 +169,12 @@ export function WhereClauseEditor({
155169
setIsFocused(false)
156170
}}
157171
onChange={(event) => {
158-
syncCursor(event.currentTarget)
172+
const pos = event.currentTarget.selectionStart ?? event.currentTarget.value.length
173+
setCursor(pos)
174+
setIsDismissed(false)
175+
setActiveIndex(0)
159176
onChange(event.target.value)
177+
notifyActiveKeys(event.target.value, pos)
160178
}}
161179
onClick={(event) => syncCursor(event.currentTarget)}
162180
onSelect={(event) => syncCursor(event.currentTarget)}

apps/web/src/lib/query-builder/model.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ export function buildTimeseriesQuerySpec(
305305
serviceName?: string
306306
spanName?: string
307307
rootSpansOnly?: boolean
308+
errorsOnly?: boolean
308309
environments?: string[]
309310
commitShas?: string[]
310311
attributeKey?: string
@@ -354,6 +355,16 @@ export function buildTimeseriesQuerySpec(
354355
continue
355356
}
356357

358+
if (clause.key === "has_error" || clause.key === "errors_only") {
359+
const boolValue = toBoolean(clause.value)
360+
if (boolValue == null) {
361+
warnings.push(`Invalid has_error value ignored: ${clause.value}`)
362+
} else {
363+
filters.errorsOnly = boolValue
364+
}
365+
continue
366+
}
367+
357368
if (clause.key.startsWith("attr.")) {
358369
const attributeKey = clause.key.slice(5)
359370
if (!filters.attributeKey) {

apps/web/src/lib/query-builder/where-clause-autocomplete.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ const KEY_DEFINITIONS: Record<QueryBuilderDataSource, KeyDefinition[]> = {
102102
insertText: "root_only",
103103
description: "true or false",
104104
},
105+
{
106+
label: "has_error",
107+
insertText: "has_error",
108+
description: "Filter to error spans only (true or false)",
109+
},
105110
{
106111
label: "attr.<key>",
107112
insertText: "attr.",
@@ -674,7 +679,7 @@ function buildValueSuggestions(
674679
]
675680
}
676681

677-
if (scope === "trace_search" && normalizedKey === "has_error") {
682+
if (normalizedKey === "has_error") {
678683
return [
679684
{
680685
id: "value:has_error:true",

0 commit comments

Comments
 (0)