Skip to content

Commit f8be2cb

Browse files
authored
fix: context logs (#31)
1 parent 8e89e59 commit f8be2cb

File tree

2 files changed

+130
-113
lines changed

2 files changed

+130
-113
lines changed

src/data/CHDatasource.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,8 @@ export class Datasource
761761
}
762762
const queryType = target.refId === 'Trace ID' ? 'Trace' : target.queryType || builderOptions.queryType
763763
if (queryType === QueryType.Logs) {
764-
const logFrame = transformGreptimeDBLogs(greptimeData, target.refId) as DataFrame
764+
const contextColumns = this.getLogContextColumnNames()
765+
const logFrame = transformGreptimeDBLogs(greptimeData, target, contextColumns) as DataFrame
765766
return logFrame? [logFrame] : []
766767
} else if (queryType === 'Trace') {
767768
const frames = transformGreptimeDBTraceDetails(greptimeData, builderOptions as QueryBuilderOptions)
@@ -1066,7 +1067,7 @@ export class Datasource
10661067
filterType: 'custom',
10671068
hint: ColumnHint.Time,
10681069
key: '',
1069-
value: `fromUnixTimestamp64Nano(${row.timeEpochNs})`,
1070+
value: new Date(Number(row.timeEpochNs) / 1000000).toISOString(),
10701071
type: 'datetime',
10711072
condition: 'AND'
10721073
});

src/greptimedb/index.ts

Lines changed: 127 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { DataFrame,
1+
import {
2+
DataFrame,
23
Field,
34
FieldType, // Imported for mapGreptimeTypeToGrafana
45
FieldConfig,
@@ -9,6 +10,7 @@ import { DataFrame,
910
import { GreptimeDataTypes } from './types';
1011
import { getColumnsByHint } from 'data/sqlGenerator';
1112
import { ColumnHint, QueryBuilderOptions } from 'types/queryBuilder';
13+
import { CHQuery } from 'types/sql';
1214

1315

1416
/**
@@ -44,7 +46,7 @@ function mapGreptimeTypeToGrafana(greptimeType: string | undefined | null): Fiel
4446
}
4547
// Interval types -> map to string for now
4648
if (lowerType.includes('interval')) {
47-
return FieldType.string;
49+
return FieldType.string;
4850
}
4951

5052
// Log unhandled types and default to 'other'
@@ -232,7 +234,7 @@ export const greptimeTypeToGrafana: Record<GreptimeDataTypes, FieldType> = {
232234

233235
type GreptimeTimeType = GreptimeDataTypes.TimestampSecond | GreptimeDataTypes.TimestampMillisecond | GreptimeDataTypes.TimestampMicrosecond | GreptimeDataTypes.TimestampNanosecond
234236
export function toMs(time: number, columnType: GreptimeTimeType) {
235-
switch(columnType) {
237+
switch (columnType) {
236238
case GreptimeDataTypes.TimestampSecond:
237239
return time * 1000
238240
case GreptimeDataTypes.TimestampMillisecond:
@@ -248,7 +250,7 @@ export function toMs(time: number, columnType: GreptimeTimeType) {
248250
}
249251

250252

251-
export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, refId?: string) {
253+
export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, query: CHQuery, contextColumns: string[]) {
252254
if (!sqlResponse.output || sqlResponse.output.length === 0) {
253255
console.error('GreptimeDB query failed or returned no data:', sqlResponse.error);
254256
return null; // Or handle the error as needed
@@ -268,9 +270,9 @@ export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, refId?: s
268270
let severityColumnIndex = -1;
269271
let idColumnIndex = -1;
270272
const labelColumnIndices: Record<string, number> = {};
273+
const contextColumnIndices: Record<string, number> = {};
271274

272275
columnSchemas.forEach((schema, index) => {
273-
console.log(schema)
274276
const lowerCaseName = schema.name.toLowerCase();
275277
if (lowerCaseName === 'ts' || lowerCaseName === 'timestamp') {
276278
timestampColumnIndex = index;
@@ -280,6 +282,8 @@ export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, refId?: s
280282
severityColumnIndex = index;
281283
} else if (lowerCaseName === 'id') {
282284
idColumnIndex = index;
285+
} else if (contextColumns.includes(schema.name)) {
286+
contextColumnIndices[schema.name] = index;
283287
} else {
284288
// Consider other columns as potential labels
285289
labelColumnIndices[schema.name] = index;
@@ -296,10 +300,10 @@ export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, refId?: s
296300
const severities: string[] = [];
297301
const ids: string[] = [];
298302
const labelsArray: Array<Record<string, any>> = [];
299-
303+
const contextColumnValues: Record<string, string[]> = {};
300304
rows.forEach((row) => {
301305
const timestampValue = toMs(row[timestampColumnIndex], columnSchemas[timestampColumnIndex].data_type as GreptimeTimeType);
302-
306+
303307
timestamps.push(
304308
typeof timestampValue === 'string' || typeof timestampValue === 'number'
305309
? new Date(timestampValue).getTime()
@@ -316,6 +320,14 @@ export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, refId?: s
316320
}
317321
}
318322
labelsArray.push(labels);
323+
324+
for (const contextName in contextColumnIndices) {
325+
if (!contextColumnValues[contextName]) {
326+
contextColumnValues[contextName] = [];
327+
}
328+
contextColumnValues[contextName].push(row[contextColumnIndices[contextName]]);
329+
}
330+
319331
});
320332

321333
const fields = [
@@ -331,10 +343,14 @@ export function transformGreptimeDBLogs(sqlResponse: GreptimeResponse, refId?: s
331343
fields.push({ name: 'id', type: FieldType.string, values: ids });
332344
}
333345

346+
for (const contextName in contextColumnValues) {
347+
fields.push({ name: contextName, type: FieldType.string, values: contextColumnValues[contextName] });
348+
}
349+
334350
fields.push({ name: 'labels', type: FieldType.other, values: labelsArray });
335351

336352
const result = createDataFrame({
337-
refId: refId,
353+
refId: query.refId,
338354
fields: fields,
339355
meta: {
340356
preferredVisualisationType: 'logs',
@@ -376,7 +392,7 @@ export function transformGreptimeDBTraceDetails(response: GreptimeResponse, buil
376392
const rows = records.rows;
377393

378394
const spans: GrafanaTraceSpan[] = rows.map(row => {
379-
const data: Record<string, any> = {span_attributes: [], service_attributes: []};
395+
const data: Record<string, any> = { span_attributes: [], service_attributes: [] };
380396
const tagColumnNames = getColumnsByHint(builderOptions, ColumnHint.TraceTags)?.map((v) => v.name) || []
381397
const serticeTagColumnNames = getColumnsByHint(builderOptions, ColumnHint.TraceServiceTags)?.map((v) => v.name) || []
382398
columns.forEach((schema, index) => {
@@ -393,7 +409,7 @@ export function transformGreptimeDBTraceDetails(response: GreptimeResponse, buil
393409
} else {
394410
data[schema.name] = row[index];
395411
}
396-
412+
397413
});
398414

399415

@@ -419,12 +435,12 @@ export function transformGreptimeDBTraceDetails(response: GreptimeResponse, buil
419435
{ name: 'operationName', type: FieldType.string, values: spans.map(s => s.operationName) },
420436
{ name: 'serviceName', type: FieldType.string, values: spans.map(s => s.serviceName) },
421437
{ name: 'startTime', type: FieldType.time, values: spans.map(s => s.startTime) },
422-
// { name: 'duration', type: FieldType.number, values: [
423-
// 50,
424-
// 100
425-
// ],
426-
// "config": { "unit": "ms" }, },
427-
{ name: 'duration', type: FieldType.number, values: spans.map(s => s.duration), "config": { "unit": "ms" }, },
438+
// { name: 'duration', type: FieldType.number, values: [
439+
// 50,
440+
// 100
441+
// ],
442+
// "config": { "unit": "ms" }, },
443+
{ name: 'duration', type: FieldType.number, values: spans.map(s => s.duration), "config": { "unit": "ms" }, },
428444
{ name: 'tags', type: FieldType.other, values: spans.map(s => s.tags) },
429445
{ name: 'serviceTags', type: FieldType.other, values: spans.map(s => s.serviceTags) },
430446
// { name: 'logs', type: FieldType.other, values: spans.map(s => s.logs) },
@@ -527,107 +543,107 @@ export function transformGreptimeResponseToGrafana(
527543

528544
// Case 1: No grouping columns or only one grouping column.
529545
if (groupingColumnIndices.length === 0) {
530-
const columnValueArrays: any[][] = Array.from({ length: numCols }, () => new Array(numRows));
531-
532-
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
533-
const row = rows[rowIndex];
534-
for (let colIndex = 0; colIndex < numCols; colIndex++) {
535-
const colSchema = columnSchemas[colIndex];
536-
const grafanaDataType = mapGreptimeTypeToGrafana(colSchema.data_type);
537-
if (grafanaDataType === FieldType.time) {
538-
columnValueArrays[colIndex][rowIndex] = toMs(row[colIndex], colSchema.data_type as GreptimeTimeType);
539-
} else {
540-
columnValueArrays[colIndex][rowIndex] = row[colIndex];
541-
}
542-
}
546+
const columnValueArrays: any[][] = Array.from({ length: numCols }, () => new Array(numRows));
547+
548+
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
549+
const row = rows[rowIndex];
550+
for (let colIndex = 0; colIndex < numCols; colIndex++) {
551+
const colSchema = columnSchemas[colIndex];
552+
const grafanaDataType = mapGreptimeTypeToGrafana(colSchema.data_type);
553+
if (grafanaDataType === FieldType.time) {
554+
columnValueArrays[colIndex][rowIndex] = toMs(row[colIndex], colSchema.data_type as GreptimeTimeType);
555+
} else {
556+
columnValueArrays[colIndex][rowIndex] = row[colIndex];
557+
}
543558
}
544-
const fields: Field[] = columnSchemas.map((colSchema, i) => {
545-
const fieldName = colSchema.name || `column_${i + 1}`;
546-
const fieldType = mapGreptimeTypeToGrafana(colSchema.data_type);
547-
const values = columnValueArrays[i];
548-
const config: FieldConfig = {
549-
displayName: fieldName,
550-
};
551-
return {
552-
name: fieldName,
553-
type: fieldType,
554-
config: config,
555-
values: values,
556-
};
557-
});
558-
const frame: DataFrame = {
559-
name: `Result ${index + 1}`,
560-
refId: refId,
561-
fields: fields,
562-
length: numRows,
559+
}
560+
const fields: Field[] = columnSchemas.map((colSchema, i) => {
561+
const fieldName = colSchema.name || `column_${i + 1}`;
562+
const fieldType = mapGreptimeTypeToGrafana(colSchema.data_type);
563+
const values = columnValueArrays[i];
564+
const config: FieldConfig = {
565+
displayName: fieldName,
563566
};
564-
dataFrames.push(frame);
567+
return {
568+
name: fieldName,
569+
type: fieldType,
570+
config: config,
571+
values: values,
572+
};
573+
});
574+
const frame: DataFrame = {
575+
name: `Result ${index + 1}`,
576+
refId: refId,
577+
fields: fields,
578+
length: numRows,
579+
};
580+
dataFrames.push(frame);
565581
} else {
566-
// Case 2: Multiple grouping columns - create multiple series
567-
// Iterate through rows from the response
568-
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
569-
const row = rows[rowIndex];
570-
571-
// Validate row structure
572-
if (!Array.isArray(row) || row.length !== numCols) {
573-
console.error(`Row ${rowIndex} in result set ${index} has incorrect length (${row?.length ?? 'undefined'}), expected ${numCols}. Skipping row.`);
574-
continue; // Move to the next row
575-
}
576-
577-
// Construct a unique key for the series based on the grouping column values.
578-
let groupKey = '';
579-
for (const groupIndex of groupingColumnIndices) {
580-
groupKey += `${columnSchemas[groupIndex].name}:${row[groupIndex]};`;
581-
}
582-
583-
// Initialize the series entry if it doesn't exist
584-
if (!seriesMap[groupKey]) {
585-
seriesMap[groupKey] = Array.from({ length: numCols }, () => []);
586-
}
587-
588-
// Populate the series data
589-
for (let colIndex = 0; colIndex < numCols; colIndex++) {
590-
const colSchema = columnSchemas[colIndex];
591-
const grafanaDataType = mapGreptimeTypeToGrafana(colSchema.data_type);
592-
593-
if (grafanaDataType === FieldType.time) {
594-
seriesMap[groupKey][colIndex].push(toMs(row[colIndex], colSchema.data_type as GreptimeTimeType));
595-
} else {
596-
seriesMap[groupKey][colIndex].push(row[colIndex]);
597-
}
598-
}
582+
// Case 2: Multiple grouping columns - create multiple series
583+
// Iterate through rows from the response
584+
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
585+
const row = rows[rowIndex];
586+
587+
// Validate row structure
588+
if (!Array.isArray(row) || row.length !== numCols) {
589+
console.error(`Row ${rowIndex} in result set ${index} has incorrect length (${row?.length ?? 'undefined'}), expected ${numCols}. Skipping row.`);
590+
continue; // Move to the next row
599591
}
600-
// --- Create Grafana DataFrames from Series ---
601-
for (const groupKey in seriesMap) {
602-
if (!Object.prototype.hasOwnProperty.call(seriesMap, groupKey)) {
603-
continue;
592+
593+
// Construct a unique key for the series based on the grouping column values.
594+
let groupKey = '';
595+
for (const groupIndex of groupingColumnIndices) {
596+
groupKey += `${columnSchemas[groupIndex].name}:${row[groupIndex]};`;
597+
}
598+
599+
// Initialize the series entry if it doesn't exist
600+
if (!seriesMap[groupKey]) {
601+
seriesMap[groupKey] = Array.from({ length: numCols }, () => []);
602+
}
603+
604+
// Populate the series data
605+
for (let colIndex = 0; colIndex < numCols; colIndex++) {
606+
const colSchema = columnSchemas[colIndex];
607+
const grafanaDataType = mapGreptimeTypeToGrafana(colSchema.data_type);
608+
609+
if (grafanaDataType === FieldType.time) {
610+
seriesMap[groupKey][colIndex].push(toMs(row[colIndex], colSchema.data_type as GreptimeTimeType));
611+
} else {
612+
seriesMap[groupKey][colIndex].push(row[colIndex]);
604613
}
605-
const columnValueArrays = seriesMap[groupKey];
606-
const fields: Field[] = columnSchemas.map((colSchema, i) => {
607-
const fieldName = colSchema.name || `column_${i + 1}`;
608-
const fieldType = mapGreptimeTypeToGrafana(colSchema.data_type);
609-
const values = columnValueArrays[i];
610-
611-
const config: FieldConfig = {
612-
displayName: fieldName + '_' + groupKey,
613-
};
614-
615-
return {
616-
name: fieldName,
617-
type: fieldType,
618-
config: config,
619-
values: values,
620-
};
621-
});
622-
623-
const frame: DataFrame = {
624-
name: groupKey, // Use the group key as the frame name
625-
refId: refId,
626-
fields: fields,
627-
length: columnValueArrays[0]?.length || 0,
628-
};
629-
dataFrames.push(frame);
630614
}
615+
}
616+
// --- Create Grafana DataFrames from Series ---
617+
for (const groupKey in seriesMap) {
618+
if (!Object.prototype.hasOwnProperty.call(seriesMap, groupKey)) {
619+
continue;
620+
}
621+
const columnValueArrays = seriesMap[groupKey];
622+
const fields: Field[] = columnSchemas.map((colSchema, i) => {
623+
const fieldName = colSchema.name || `column_${i + 1}`;
624+
const fieldType = mapGreptimeTypeToGrafana(colSchema.data_type);
625+
const values = columnValueArrays[i];
626+
627+
const config: FieldConfig = {
628+
displayName: fieldName + '_' + groupKey,
629+
};
630+
631+
return {
632+
name: fieldName,
633+
type: fieldType,
634+
config: config,
635+
values: values,
636+
};
637+
});
638+
639+
const frame: DataFrame = {
640+
name: groupKey, // Use the group key as the frame name
641+
refId: refId,
642+
fields: fields,
643+
length: columnValueArrays[0]?.length || 0,
644+
};
645+
dataFrames.push(frame);
646+
}
631647
}
632648
});
633649

0 commit comments

Comments
 (0)