Skip to content

Commit 931d738

Browse files
MikeShi42wrn14897
andauthored
fix: bugs with showing non otel spans (ex. clickhouse opentelemetry span logs) (#789)
<img width="1645" alt="image" src="https://github.com/user-attachments/assets/2f7eb93f-9648-4c98-8bfd-a2d0f65be9d5" /> fixing a few bugs that prevented us from properly rendering trace view for `system.opentelemetry_span_log` fix HDX-1676 Co-authored-by: Warren <[email protected]>
1 parent 1674ab8 commit 931d738

File tree

8 files changed

+72
-20
lines changed

8 files changed

+72
-20
lines changed

.changeset/sour-gifts-thank.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@hyperdx/common-utils": patch
3+
"@hyperdx/api": patch
4+
"@hyperdx/app": patch
5+
---
6+
7+
fix: bugs with showing non otel spans (ex. clickhouse opentelemetry span logs)

packages/api/src/models/source.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const Source = mongoose.model<ISource>(
6363
spanKindExpression: String,
6464
statusCodeExpression: String,
6565
statusMessageExpression: String,
66+
spanEventsValueExpression: String,
6667

6768
metricTables: {
6869
type: {

packages/app/src/components/DBRowDataPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ export function useRowData({
8282
},
8383
]
8484
: []),
85-
...(source.kind === SourceKind.Trace
85+
...(source.kind === SourceKind.Trace && source.spanEventsValueExpression
8686
? [
8787
{
88-
valueExpression: `Events.Attributes[indexOf(Events.Name, 'exception')]`,
88+
valueExpression: `${source.spanEventsValueExpression}.Attributes[indexOf(${source.spanEventsValueExpression}.Name, 'exception')]`,
8989
alias: '__hdx_events_exception_attributes',
9090
},
9191
]

packages/app/src/components/DBRowSidePanel.tsx

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,21 @@ export default function DBRowSidePanel({
9494
Infrastructure = 'infrastructure',
9595
}
9696

97+
const hasOverviewPanel = useMemo(() => {
98+
if (
99+
source.resourceAttributesExpression ||
100+
source.eventAttributesExpression
101+
) {
102+
return true;
103+
}
104+
return false;
105+
}, [source.eventAttributesExpression, source.resourceAttributesExpression]);
106+
107+
const defaultTab = hasOverviewPanel ? Tab.Overview : Tab.Parsed;
108+
97109
const [queryTab, setQueryTab] = useQueryState(
98110
'tab',
99-
parseAsStringEnum<Tab>(Object.values(Tab)).withDefault(Tab.Overview),
111+
parseAsStringEnum<Tab>(Object.values(Tab)).withDefault(defaultTab),
100112
);
101113

102114
const initialWidth = 80;
@@ -114,7 +126,7 @@ export default function DBRowSidePanel({
114126
// Keep track of sub-drawers so we can disable closing this root drawer
115127
const [subDrawerOpen, setSubDrawerOpen] = useState(false);
116128

117-
const [stateTab, setStateTab] = useState<Tab>(Tab.Overview);
129+
const [stateTab, setStateTab] = useState<Tab>(defaultTab);
118130
// Nested panels can't share the query param or else they'll conflict, so we'll use local state for nested panels
119131
// We'll need to handle this properly eventually...
120132
const tab = isNestedPanel ? stateTab : queryTab;
@@ -211,15 +223,20 @@ export default function DBRowSidePanel({
211223
});
212224

213225
const hasK8sContext = useMemo(() => {
214-
if (!source?.resourceAttributesExpression || !normalizedRow) {
226+
try {
227+
if (!source?.resourceAttributesExpression || !normalizedRow) {
228+
return false;
229+
}
230+
return (
231+
normalizedRow[source.resourceAttributesExpression]?.['k8s.pod.uid'] !=
232+
null ||
233+
normalizedRow[source.resourceAttributesExpression]?.['k8s.node.name'] !=
234+
null
235+
);
236+
} catch (e) {
237+
console.error(e);
215238
return false;
216239
}
217-
return (
218-
normalizedRow[source.resourceAttributesExpression]['k8s.pod.uid'] !=
219-
null ||
220-
normalizedRow[source.resourceAttributesExpression]['k8s.node.name'] !=
221-
null
222-
);
223240
}, [source, normalizedRow]);
224241

225242
return (
@@ -264,10 +281,14 @@ export default function DBRowSidePanel({
264281
<TabBar
265282
className="fs-8 mt-2"
266283
items={[
267-
{
268-
text: 'Overview',
269-
value: Tab.Overview,
270-
},
284+
...(hasOverviewPanel
285+
? [
286+
{
287+
text: 'Overview',
288+
value: Tab.Overview,
289+
},
290+
]
291+
: []),
271292
{
272293
text: 'Column Values',
273294
value: Tab.Parsed,

packages/app/src/components/SourceForm.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function FormRow({
9393
...(!helpText ? { opacity: 0, pointerEvents: 'none' } : {}),
9494
}}
9595
>
96-
<Tooltip label={helpText} color="dark" c="white">
96+
<Tooltip label={helpText} color="dark" c="white" multiline maw={600}>
9797
<i className="bi bi-question-circle cursor-pointer" />
9898
</Tooltip>
9999
</Text>
@@ -413,7 +413,10 @@ export function TraceTableModelForm({
413413
rules={{ required: 'Table is required' }}
414414
/>
415415
</FormRow>
416-
<FormRow label={'Timestamp Column'}>
416+
<FormRow
417+
label={'Timestamp Column'}
418+
helpText="DateTime column or expression defines the start of the span"
419+
>
417420
<SQLInlineEditorControlled
418421
tableConnections={{
419422
databaseName,
@@ -619,6 +622,21 @@ export function TraceTableModelForm({
619622
placeholder="SpanAttributes"
620623
/>
621624
</FormRow>
625+
<FormRow
626+
label={'Span Events Expression'}
627+
helpText="Expression to extract span events. Used to capture events associated with spans. Expected to be Nested ( Timestamp DateTime64(9), Name LowCardinality(String), Attributes Map(LowCardinality(String), String)"
628+
>
629+
<SQLInlineEditorControlled
630+
tableConnections={{
631+
databaseName,
632+
tableName,
633+
connectionId,
634+
}}
635+
control={control}
636+
name="spanEventsValueExpression"
637+
placeholder="Events"
638+
/>
639+
</FormRow>
622640
<FormRow
623641
label={'Implicit Column Expression'}
624642
helpText="Column used for full text search if no property is specified in a Lucene-based search. Typically the message body of a log."

packages/app/src/source.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ export async function inferTableSourceConfig({
246246
'StatusMessage',
247247
]);
248248

249+
// Check if SpanEvents column is available
250+
const hasSpanEvents = columns.some(col => col.name === 'Events');
251+
249252
const timestampColumns = filterColumnMetaByType(columns, [JSDataType.Date]);
250253
const primaryKeyTimestampColumn = timestampColumns?.find(c =>
251254
keys.find(
@@ -299,17 +302,18 @@ export async function inferTableSourceConfig({
299302
traceIdExpression: 'TraceId',
300303
statusCodeExpression: 'StatusCode',
301304
statusMessageExpression: 'StatusMessage',
305+
...(hasSpanEvents ? { spanEventsValueExpression: 'Events' } : {}),
302306
}
303307
: {}),
304308
};
305309
}
306310

307311
export function getDurationMsExpression(source: TSource) {
308-
return `${source.durationExpression}/1e${(source.durationPrecision ?? 9) - 3}`;
312+
return `(${source.durationExpression})/1e${(source.durationPrecision ?? 9) - 3}`;
309313
}
310314

311315
export function getDurationSecondsExpression(source: TSource) {
312-
return `${source.durationExpression}/1e${source.durationPrecision ?? 9}`;
316+
return `(${source.durationExpression})/1e${source.durationPrecision ?? 9}`;
313317
}
314318

315319
const ReqMetricTableColumns = {

packages/common-utils/src/renderChartConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ const fastifySQL = ({
251251

252252
return parser.sqlify(ast);
253253
} catch (e) {
254-
console.error('[renderWhereExpression]feat: Failed to parse SQL AST', e);
254+
console.debug('[renderWhereExpression]feat: Failed to parse SQL AST', e);
255255
return rawSQL;
256256
}
257257
};

packages/common-utils/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ export const SourceSchema = z.object({
510510
durationPrecision: z.number().min(0).max(9).optional(),
511511
parentSpanIdExpression: z.string().optional(),
512512
spanNameExpression: z.string().optional(),
513+
spanEventsValueExpression: z.string().optional(),
513514

514515
spanKindExpression: z.string().optional(),
515516
statusCodeExpression: z.string().optional(),

0 commit comments

Comments
 (0)