Skip to content

Commit

Permalink
fix(trace): show status of span in trace panel (#950)
Browse files Browse the repository at this point in the history
Co-authored-by: Andreas Christou <[email protected]>
  • Loading branch information
Defman and aangelisc authored Sep 12, 2024
1 parent ad0aed4 commit 8ad9766
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 39 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,16 @@ If using the [Open Telemetry Collector and ClickHouse exporter](https://github.c

```sql
SELECT
TraceId AS traceID,
SpanId AS spanID,
SpanName AS operationName,
ParentSpanId AS parentSpanID,
ServiceName AS serviceName,
Duration / 1000000 AS duration,
Timestamp AS startTime,
arrayMap(key -> map('key', key, 'value', SpanAttributes[key]), mapKeys(SpanAttributes)) AS tags,
arrayMap(key -> map('key', key, 'value', ResourceAttributes[key]), mapKeys(ResourceAttributes)) AS serviceTags
TraceId AS traceID,
SpanId AS spanID,
SpanName AS operationName,
ParentSpanId AS parentSpanID,
ServiceName AS serviceName,
Duration / 1000000 AS duration,
Timestamp AS startTime,
arrayMap(key -> map('key', key, 'value', SpanAttributes[key]), mapKeys(SpanAttributes)) AS tags,
arrayMap(key -> map('key', key, 'value', ResourceAttributes[key]), mapKeys(ResourceAttributes)) AS serviceTags,
if(StatusCode IN ('Error', 'STATUS_CODE_ERROR'), 2, 0) AS statusCode
FROM otel.otel_traces
WHERE TraceId = '61d489320c01243966700e172ab37081'
ORDER BY startTime ASC
Expand Down
8 changes: 6 additions & 2 deletions src/dashboards/opentelemetry-clickhouse.json
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@
},
"pluginVersion": "4.0.6",
"queryType": "timeseries",
"rawSql": "SELECT\r\n $__timeInterval(Timestamp) as time,\r\n count(*) as ` `,\r\n ServiceName\r\nFROM otel_traces\r\nWHERE\r\n $__conditionalAll(TraceId IN (${trace_id:singlequote}), $trace_id)\r\n AND $__timeFilter(Timestamp)\r\n AND ServiceName IN (${serviceName:singlequote})\r\n AND StatusCode = 'STATUS_CODE_ERROR'\r\n AND ServiceName != 'loadgenerator' GROUP BY ServiceName, time\r\nORDER BY time ASC\r\nLIMIT 100000",
"rawSql": "SELECT\r\n $__timeInterval(Timestamp) as time,\r\n count(*) as ` `,\r\n ServiceName\r\nFROM otel_traces\r\nWHERE\r\n $__conditionalAll(TraceId IN (${trace_id:singlequote}), $trace_id)\r\n AND $__timeFilter(Timestamp)\r\n AND ServiceName IN (${serviceName:singlequote})\r\n AND StatusCode IN ('Error', 'STATUS_CODE_ERROR')\r\n AND ServiceName != 'loadgenerator' GROUP BY ServiceName, time\r\nORDER BY time ASC\r\nLIMIT 100000",
"refId": "A"
}
],
Expand Down Expand Up @@ -728,6 +728,10 @@
{
"hint": "trace_service_tags",
"name": "ResourceAttributes"
},
{
"hint": "trace_status_code",
"name": "StatusCode"
}
],
"database": "default",
Expand Down Expand Up @@ -977,4 +981,4 @@
"uid": "8klBUGfVk",
"version": 2,
"weekStart": ""
}
}
8 changes: 6 additions & 2 deletions src/data/sqlGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ describe('SQL Generator', () => {
{ name: 'Duration', type: 'Int64', hint: ColumnHint.TraceDurationTime },
{ name: 'SpanAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceTags },
{ name: 'ResourceAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceServiceTags },
{ name: 'StatusCode', type: 'LowCardinality(String)', hint: ColumnHint.TraceStatusCode },
],
filters: [],
meta: {
Expand All @@ -215,7 +216,8 @@ describe('SQL Generator', () => {
'multiply("Duration", 0.000001) as duration,',
`arrayMap(key -> map('key', key, 'value',"SpanAttributes"[key]),`,
`mapKeys("SpanAttributes")) as tags,`,
`arrayMap(key -> map('key', key, 'value',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags`,
`arrayMap(key -> map('key', key, 'value',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags,`,
`if("StatusCode" IN ('Error', 'STATUS_CODE_ERROR'), 2, 0) as statusCode`,
`FROM "default"."otel_traces" WHERE traceID = 'abcdefg'`,
'LIMIT 1000'
];
Expand All @@ -239,6 +241,7 @@ describe('SQL Generator', () => {
{ name: 'Duration', type: 'Int64', hint: ColumnHint.TraceDurationTime },
{ name: 'SpanAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceTags },
{ name: 'ResourceAttributes', type: 'Map(LowCardinality(String), String)', hint: ColumnHint.TraceServiceTags },
{ name: 'StatusCode', type: 'LowCardinality(String)', hint: ColumnHint.TraceStatusCode },
],
filters: [],
meta: {
Expand All @@ -260,7 +263,8 @@ describe('SQL Generator', () => {
'multiply("Duration", 0.000001) as duration,',
`arrayMap(key -> map('key', key, 'value',"SpanAttributes"[key]),`,
`mapKeys("SpanAttributes")) as tags,`,
`arrayMap(key -> map('key', key, 'value',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags`,
`arrayMap(key -> map('key', key, 'value',"ResourceAttributes"[key]), mapKeys("ResourceAttributes")) as serviceTags,`,
`if("StatusCode" IN ('Error', 'STATUS_CODE_ERROR'), 2, 0) as statusCode`,
`FROM "default"."otel_traces" WHERE traceID = trace_id AND "Timestamp" >= trace_start AND "Timestamp" <= trace_end`,
'LIMIT 1000'
];
Expand Down
57 changes: 31 additions & 26 deletions src/data/sqlGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const generateSql = (options: QueryBuilderOptions): string => {
*/
const generateTraceSearchQuery = (options: QueryBuilderOptions): string => {
const { database, table } = options;

const queryParts: string[] = [];

// TODO: these columns could be a map or some other convenience function
Expand All @@ -37,28 +37,28 @@ const generateTraceSearchQuery = (options: QueryBuilderOptions): string => {
if (traceId !== undefined) {
selectParts.push(`${escapeIdentifier(traceId.name)} as traceID`);
}

const traceServiceName = getColumnByHint(options, ColumnHint.TraceServiceName);
if (traceServiceName !== undefined) {
selectParts.push(`${escapeIdentifier(traceServiceName.name)} as serviceName`);
}

const traceOperationName = getColumnByHint(options, ColumnHint.TraceOperationName);
if (traceOperationName !== undefined) {
selectParts.push(`${escapeIdentifier(traceOperationName.name)} as operationName`);
}

const traceStartTime = getColumnByHint(options, ColumnHint.Time);
if (traceStartTime !== undefined) {
selectParts.push(`${escapeIdentifier(traceStartTime.name)} as startTime`);
}

const traceDurationTime = getColumnByHint(options, ColumnHint.TraceDurationTime);
if (traceDurationTime !== undefined) {
const timeUnit = options.meta?.traceDurationUnit;
selectParts.push(getTraceDurationSelectSql(escapeIdentifier(traceDurationTime.name), timeUnit));
}

const selectPartsSql = selectParts.join(', ');

queryParts.push('SELECT');
Expand Down Expand Up @@ -93,7 +93,7 @@ const generateTraceSearchQuery = (options: QueryBuilderOptions): string => {
*/
const generateTraceIdQuery = (options: QueryBuilderOptions): string => {
const { database, table } = options;

const queryParts: string[] = [];

// TODO: these columns could be a map or some other convenience function
Expand All @@ -102,48 +102,53 @@ const generateTraceIdQuery = (options: QueryBuilderOptions): string => {
if (traceId !== undefined) {
selectParts.push(`${escapeIdentifier(traceId.name)} as traceID`);
}

const traceSpanId = getColumnByHint(options, ColumnHint.TraceSpanId);
if (traceSpanId !== undefined) {
selectParts.push(`${escapeIdentifier(traceSpanId.name)} as spanID`);
}

const traceParentSpanId = getColumnByHint(options, ColumnHint.TraceParentSpanId);
if (traceParentSpanId !== undefined) {
selectParts.push(`${escapeIdentifier(traceParentSpanId.name)} as parentSpanID`);
}

const traceServiceName = getColumnByHint(options, ColumnHint.TraceServiceName);
if (traceServiceName !== undefined) {
selectParts.push(`${escapeIdentifier(traceServiceName.name)} as serviceName`);
}

const traceOperationName = getColumnByHint(options, ColumnHint.TraceOperationName);
if (traceOperationName !== undefined) {
selectParts.push(`${escapeIdentifier(traceOperationName.name)} as operationName`);
}

const traceStartTime = getColumnByHint(options, ColumnHint.Time);
if (traceStartTime !== undefined) {
selectParts.push(`${convertTimeFieldToMilliseconds(escapeIdentifier(traceStartTime.name))} as startTime`);
}

const traceDurationTime = getColumnByHint(options, ColumnHint.TraceDurationTime);
if (traceDurationTime !== undefined) {
const timeUnit = options.meta?.traceDurationUnit;
selectParts.push(getTraceDurationSelectSql(escapeIdentifier(traceDurationTime.name), timeUnit));
}

// TODO: for tags and serviceTags, consider the column type. They might not require mapping, they could already be JSON.
const traceTags = getColumnByHint(options, ColumnHint.TraceTags);
if (traceTags !== undefined) {
selectParts.push(`arrayMap(key -> map('key', key, 'value',${escapeIdentifier(traceTags.name)}[key]), mapKeys(${escapeIdentifier(traceTags.name)})) as tags`);
}

const traceServiceTags = getColumnByHint(options, ColumnHint.TraceServiceTags);
if (traceServiceTags !== undefined) {
selectParts.push(`arrayMap(key -> map('key', key, 'value',${escapeIdentifier(traceServiceTags.name)}[key]), mapKeys(${escapeIdentifier(traceServiceTags.name)})) as serviceTags`);
}

const traceStatusCode = getColumnByHint(options, ColumnHint.TraceStatusCode);
if (traceStatusCode !== undefined) {
selectParts.push(`if(${escapeIdentifier(traceStatusCode.name)} IN ('Error', 'STATUS_CODE_ERROR'), 2, 0) as statusCode`);
}
const selectPartsSql = selectParts.join(', ');

// Optimize trace ID filtering for OTel enabled trace lookups
Expand Down Expand Up @@ -207,14 +212,14 @@ const generateTraceIdQuery = (options: QueryBuilderOptions): string => {
* Generates logs query with columns that fit Grafana's Logs panel
* Column aliases follow this structure:
* https://grafana.com/developers/plugin-tools/tutorials/build-a-logs-data-source-plugin#logs-data-frame-format
*
*
* note: column order seems to matter as well as alias name
*/
const generateLogsQuery = (_options: QueryBuilderOptions): string => {
// Copy columns so column aliases can be safely mutated
const options = { ..._options, columns: _options.columns?.map(c => ({ ...c })) };
const { database, table } = options;

const queryParts: string[] = [];

// TODO: these columns could be a map or some other convenience function
Expand Down Expand Up @@ -263,7 +268,7 @@ const generateLogsQuery = (_options: QueryBuilderOptions): string => {
queryParts.push('FROM');
queryParts.push(getTableIdentifier(database, table));


const filterParts = getFilters(options);
const hasLogMessageFilter = logMessage && options.meta?.logMessageLike;

Expand Down Expand Up @@ -304,7 +309,7 @@ const generateSimpleTimeSeriesQuery = (_options: QueryBuilderOptions): string =>
// Copy columns so column aliases can be safely mutated
const options = { ..._options, columns: _options.columns?.map(c => ({ ...c })) };
const { database, table } = options;

const queryParts: string[] = [];

const selectParts: string[] = [];
Expand Down Expand Up @@ -341,7 +346,7 @@ const generateSimpleTimeSeriesQuery = (_options: QueryBuilderOptions): string =>

// (v3) aggregate selections go AFTER group by
aggregateSelectParts.forEach(a => selectParts.push(a));

const selectPartsSql = selectParts.join(', ');

queryParts.push('SELECT');
Expand All @@ -354,7 +359,7 @@ const generateSimpleTimeSeriesQuery = (_options: QueryBuilderOptions): string =>
queryParts.push('WHERE');
queryParts.push(filterParts);
}

const hasAggregates = (options.aggregates?.length || 0 > 0);
const hasGroupBy = (options.groupBy?.length || 0 > 0);
if (hasAggregates || hasGroupBy) {
Expand Down Expand Up @@ -389,7 +394,7 @@ const generateAggregateTimeSeriesQuery = (_options: QueryBuilderOptions): string
// Copy columns so column aliases can be safely mutated
const options = { ..._options, columns: _options.columns?.map(c => ({ ...c })) };
const { database, table } = options;

const queryParts: string[] = [];
const selectParts: string[] = [];

Expand All @@ -407,7 +412,7 @@ const generateAggregateTimeSeriesQuery = (_options: QueryBuilderOptions): string
const name = `${agg.aggregateType}(${agg.column})`;
selectParts.push(`${name}${alias}`);
});

const selectPartsSql = selectParts.join(', ');

queryParts.push('SELECT');
Expand Down Expand Up @@ -477,7 +482,7 @@ const generateTableQuery = (options: QueryBuilderOptions): string => {
// selectParts.push(g)
});
}

const selectPartsSql = selectParts.join(', ');

queryParts.push('SELECT');
Expand Down Expand Up @@ -528,7 +533,7 @@ export const getColumnsByHints = (options: QueryBuilderOptions, hints: readonly

const getColumnIdentifier = (col: SelectedColumn): string => {
let colName = col.name;

// allow for functions like count()
if (colName.includes('(') || colName.includes(')') || colName.includes('"') || colName.includes('"') || colName.includes(' as ')) {
colName = col.name
Expand Down Expand Up @@ -690,7 +695,7 @@ const getFilters = (options: QueryBuilderOptions): string => {
if (operator) {
filterParts.push(operator);
}

if (isNullFilter(filter.operator)) {
// empty
} else if (filter.operator === FilterOperator.IsEmpty) {
Expand Down
1 change: 1 addition & 0 deletions src/labels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ export default {
[ColumnHint.TraceDurationTime]: 'Duration Time',
[ColumnHint.TraceTags]: 'Tags',
[ColumnHint.TraceServiceTags]: 'Service Tags',
[ColumnHint.TraceStatusCode]: 'Status Code',
}
}
}
1 change: 1 addition & 0 deletions src/otel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const otel129: OtelVersion = {
[ColumnHint.TraceDurationTime, 'Duration'],
[ColumnHint.TraceTags, 'SpanAttributes'],
[ColumnHint.TraceServiceTags, 'ResourceAttributes'],
[ColumnHint.TraceStatusCode, 'StatusCode'],
]),
traceDurationUnit: TimeUnit.Nanoseconds,
};
Expand Down
1 change: 1 addition & 0 deletions src/types/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export enum ColumnHint {
TraceDurationTime = 'trace_duration_time',
TraceTags = 'trace_tags',
TraceServiceTags = 'trace_service_tags',
TraceStatusCode = 'trace_status_code',
}

/**
Expand Down

0 comments on commit 8ad9766

Please sign in to comment.