|
|
|
@ -1,4 +1,4 @@ |
|
|
|
|
import { SpanStatus, SpanStatusCode } from '@opentelemetry/api'; |
|
|
|
|
import { SpanStatus } from '@opentelemetry/api'; |
|
|
|
|
import { collectorTypes } from '@opentelemetry/exporter-collector'; |
|
|
|
|
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; |
|
|
|
|
|
|
|
|
@ -161,51 +161,25 @@ function resourceToProcess(resource: collectorTypes.opentelemetryProto.resource. |
|
|
|
|
return { serviceName, serviceTags }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getSpanTags( |
|
|
|
|
span: collectorTypes.opentelemetryProto.trace.v1.Span, |
|
|
|
|
instrumentationLibrary?: collectorTypes.opentelemetryProto.common.v1.InstrumentationLibrary |
|
|
|
|
): TraceKeyValuePair[] { |
|
|
|
|
function getSpanTags(span: collectorTypes.opentelemetryProto.trace.v1.Span): TraceKeyValuePair[] { |
|
|
|
|
const spanTags: TraceKeyValuePair[] = []; |
|
|
|
|
|
|
|
|
|
if (instrumentationLibrary) { |
|
|
|
|
if (instrumentationLibrary.name) { |
|
|
|
|
spanTags.push({ key: 'otel.library.name', value: instrumentationLibrary.name }); |
|
|
|
|
} |
|
|
|
|
if (instrumentationLibrary.version) { |
|
|
|
|
spanTags.push({ key: 'otel.library.version', value: instrumentationLibrary.version }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (span.attributes) { |
|
|
|
|
for (const attribute of span.attributes) { |
|
|
|
|
spanTags.push({ key: attribute.key, value: getAttributeValue(attribute.value) }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (span.status) { |
|
|
|
|
if (span.status.code && (span.status.code as any) !== SpanStatusCode.UNSET) { |
|
|
|
|
spanTags.push({ |
|
|
|
|
key: 'otel.status_code', |
|
|
|
|
value: SpanStatusCode[span.status.code], |
|
|
|
|
}); |
|
|
|
|
if (span.status.message) { |
|
|
|
|
spanTags.push({ key: 'otel.status_description', value: span.status.message }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (span.status.code === SpanStatusCode.ERROR) { |
|
|
|
|
spanTags.push({ key: 'error', value: true }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return spanTags; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (span.kind !== undefined) { |
|
|
|
|
function getSpanKind(span: collectorTypes.opentelemetryProto.trace.v1.Span) { |
|
|
|
|
let kind = undefined; |
|
|
|
|
if (span.kind) { |
|
|
|
|
const split = span.kind.toString().toLowerCase().split('_'); |
|
|
|
|
spanTags.push({ |
|
|
|
|
key: 'span.kind', |
|
|
|
|
value: split.length ? split[split.length - 1] : span.kind.toString(), |
|
|
|
|
}); |
|
|
|
|
kind = split.length ? split[split.length - 1] : span.kind.toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return spanTags; |
|
|
|
|
return kind; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getReferences(span: collectorTypes.opentelemetryProto.trace.v1.Span) { |
|
|
|
@ -254,6 +228,12 @@ export function transformFromOTLP( |
|
|
|
|
{ name: 'parentSpanID', type: FieldType.string }, |
|
|
|
|
{ name: 'operationName', type: FieldType.string }, |
|
|
|
|
{ name: 'serviceName', type: FieldType.string }, |
|
|
|
|
{ name: 'kind', type: FieldType.string }, |
|
|
|
|
{ name: 'statusCode', type: FieldType.number }, |
|
|
|
|
{ name: 'statusMessage', type: FieldType.string }, |
|
|
|
|
{ name: 'instrumentationLibraryName', type: FieldType.string }, |
|
|
|
|
{ name: 'instrumentationLibraryVersion', type: FieldType.string }, |
|
|
|
|
{ name: 'traceState', type: FieldType.string }, |
|
|
|
|
{ name: 'serviceTags', type: FieldType.other }, |
|
|
|
|
{ name: 'startTime', type: FieldType.number }, |
|
|
|
|
{ name: 'duration', type: FieldType.number }, |
|
|
|
@ -279,10 +259,16 @@ export function transformFromOTLP( |
|
|
|
|
parentSpanID: span.parentSpanId || '', |
|
|
|
|
operationName: span.name || '', |
|
|
|
|
serviceName, |
|
|
|
|
kind: getSpanKind(span), |
|
|
|
|
statusCode: span.status?.code, |
|
|
|
|
statusMessage: span.status?.message, |
|
|
|
|
instrumentationLibraryName: librarySpan.instrumentationLibrary?.name, |
|
|
|
|
instrumentationLibraryVersion: librarySpan.instrumentationLibrary?.version, |
|
|
|
|
traceState: span.traceState, |
|
|
|
|
serviceTags, |
|
|
|
|
startTime: span.startTimeUnixNano! / 1000000, |
|
|
|
|
duration: (span.endTimeUnixNano! - span.startTimeUnixNano!) / 1000000, |
|
|
|
|
tags: getSpanTags(span, librarySpan.instrumentationLibrary), |
|
|
|
|
tags: getSpanTags(span), |
|
|
|
|
logs: getLogs(span), |
|
|
|
|
references: getReferences(span), |
|
|
|
|
} as TraceSpanRow); |
|
|
|
@ -343,11 +329,10 @@ export function transformToOTLP(data: MutableDataFrame): { |
|
|
|
|
|
|
|
|
|
// Populate instrumentation library if it exists
|
|
|
|
|
if (!result.batches[batchIndex].instrumentationLibrarySpans[0].instrumentationLibrary) { |
|
|
|
|
let libraryName = span.tags.find((t: TraceKeyValuePair) => t.key === 'otel.library.name')?.value; |
|
|
|
|
if (libraryName) { |
|
|
|
|
if (span.instrumentationLibraryName) { |
|
|
|
|
result.batches[batchIndex].instrumentationLibrarySpans[0].instrumentationLibrary = { |
|
|
|
|
name: libraryName, |
|
|
|
|
version: span.tags.find((t: TraceKeyValuePair) => t.key === 'otel.library.version')?.value, |
|
|
|
|
name: span.instrumentationLibraryName, |
|
|
|
|
version: span.instrumentationLibraryVersion ? span.instrumentationLibraryVersion : '', |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -356,16 +341,16 @@ export function transformToOTLP(data: MutableDataFrame): { |
|
|
|
|
traceId: span.traceID.padStart(32, '0'), |
|
|
|
|
spanId: span.spanID, |
|
|
|
|
parentSpanId: span.parentSpanID || '', |
|
|
|
|
traceState: '', |
|
|
|
|
traceState: span.traceState || '', |
|
|
|
|
name: span.operationName, |
|
|
|
|
kind: getOTLPSpanKind(span.tags) as any, |
|
|
|
|
kind: getOTLPSpanKind(span.kind) as any, |
|
|
|
|
startTimeUnixNano: span.startTime * 1000000, |
|
|
|
|
endTimeUnixNano: (span.startTime + span.duration) * 1000000, |
|
|
|
|
attributes: tagsToAttributes(span.tags), |
|
|
|
|
attributes: span.tags ? tagsToAttributes(span.tags) : [], |
|
|
|
|
droppedAttributesCount: 0, |
|
|
|
|
droppedEventsCount: 0, |
|
|
|
|
droppedLinksCount: 0, |
|
|
|
|
status: getOTLPStatus(span.tags), |
|
|
|
|
status: getOTLPStatus(span), |
|
|
|
|
events: getOTLPEvents(span.logs), |
|
|
|
|
links: getOTLPReferences(span.references), |
|
|
|
|
}); |
|
|
|
@ -374,24 +359,27 @@ export function transformToOTLP(data: MutableDataFrame): { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getOTLPSpanKind(tags: TraceKeyValuePair[]): string | undefined { |
|
|
|
|
function getOTLPSpanKind(kind: string): string | undefined { |
|
|
|
|
let spanKind = undefined; |
|
|
|
|
const spanKindTagValue = tags.find((t) => t.key === 'span.kind')?.value; |
|
|
|
|
switch (spanKindTagValue) { |
|
|
|
|
case 'server': |
|
|
|
|
spanKind = 'SPAN_KIND_SERVER'; |
|
|
|
|
break; |
|
|
|
|
case 'client': |
|
|
|
|
spanKind = 'SPAN_KIND_CLIENT'; |
|
|
|
|
break; |
|
|
|
|
case 'producer': |
|
|
|
|
spanKind = 'SPAN_KIND_PRODUCER'; |
|
|
|
|
break; |
|
|
|
|
case 'consumer': |
|
|
|
|
spanKind = 'SPAN_KIND_CONSUMER'; |
|
|
|
|
break; |
|
|
|
|
if (kind) { |
|
|
|
|
switch (kind) { |
|
|
|
|
case 'server': |
|
|
|
|
spanKind = 'SPAN_KIND_SERVER'; |
|
|
|
|
break; |
|
|
|
|
case 'client': |
|
|
|
|
spanKind = 'SPAN_KIND_CLIENT'; |
|
|
|
|
break; |
|
|
|
|
case 'producer': |
|
|
|
|
spanKind = 'SPAN_KIND_PRODUCER'; |
|
|
|
|
break; |
|
|
|
|
case 'consumer': |
|
|
|
|
spanKind = 'SPAN_KIND_CONSUMER'; |
|
|
|
|
break; |
|
|
|
|
case 'internal': |
|
|
|
|
spanKind = 'SPAN_KIND_INTERNAL'; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return spanKind; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -399,21 +387,10 @@ function getOTLPSpanKind(tags: TraceKeyValuePair[]): string | undefined { |
|
|
|
|
* Converts key-value tags to OTLP attributes and removes tags added by Grafana |
|
|
|
|
*/ |
|
|
|
|
function tagsToAttributes(tags: TraceKeyValuePair[]): collectorTypes.opentelemetryProto.common.v1.KeyValue[] { |
|
|
|
|
return tags |
|
|
|
|
.filter( |
|
|
|
|
(t) => |
|
|
|
|
![ |
|
|
|
|
'span.kind', |
|
|
|
|
'otel.library.name', |
|
|
|
|
'otel.libary.version', |
|
|
|
|
'otel.status_description', |
|
|
|
|
'otel.status_code', |
|
|
|
|
].includes(t.key) |
|
|
|
|
) |
|
|
|
|
.reduce<collectorTypes.opentelemetryProto.common.v1.KeyValue[]>( |
|
|
|
|
(attributes, tag) => [...attributes, { key: tag.key, value: toAttributeValue(tag) }], |
|
|
|
|
[] |
|
|
|
|
); |
|
|
|
|
return tags.reduce<collectorTypes.opentelemetryProto.common.v1.KeyValue[]>( |
|
|
|
|
(attributes, tag) => [...attributes, { key: tag.key, value: toAttributeValue(tag) }], |
|
|
|
|
[] |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -443,16 +420,14 @@ function toAttributeValue(tag: TraceKeyValuePair): collectorTypes.opentelemetryP |
|
|
|
|
return { stringValue: tag.value }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getOTLPStatus(tags: TraceKeyValuePair[]): SpanStatus | undefined { |
|
|
|
|
function getOTLPStatus(span: TraceSpanRow): SpanStatus | undefined { |
|
|
|
|
let status = undefined; |
|
|
|
|
const statusCodeTag = tags.find((t) => t.key === 'otel.status_code'); |
|
|
|
|
if (statusCodeTag) { |
|
|
|
|
if (span.statusCode !== undefined) { |
|
|
|
|
status = { |
|
|
|
|
code: statusCodeTag.value, |
|
|
|
|
message: tags.find((t) => t.key === 'otel_status_description')?.value, |
|
|
|
|
code: span.statusCode, |
|
|
|
|
message: span.statusMessage ? span.statusMessage : '', |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return status; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|