in store/kustoSpan.go [54:160]
func transformKustoSpanToModelSpan(kustoSpan *kustoSpan, logger hclog.Logger) (*model.Span, error) {
// eMin":"datetime(2024-03-13T15:56:28.628Z)"}: EXTRA_VALUE_AT_END=<nil> @module=jaeger-kusto timestamp=2024-03-15T15:56:28.634Z
//2024-03-15T15:56:29.206Z [ERROR] jaeger-kusto: Error parsing span to domain. Error not a valid SpanRefType string . The TraceId is d1b06c73d963045e657158dbd0ccf6d9 and the SpanId is cfb683d327e4dd90 : @module=jaeger-kusto timestamp=2024-03-15T15:56:29.205Z
//
spanReferences, err := transformReferencesToLinks(kustoSpan, logger)
if err != nil {
logger.Error(fmt.Sprintf("Error in Unmarshal Refs %s. TraceId: %s SpanId: %s ", kustoSpan.Tags.String(), kustoSpan.TraceID, kustoSpan.SpanID), err)
return nil, err
}
var tags map[string]interface{}
err = json.Unmarshal(kustoSpan.Tags.Value, &tags)
if err != nil {
logger.Error(fmt.Sprintf("Error in Unmarshal tags %s. TraceId: %s SpanId: %s ", kustoSpan.Tags.String(), kustoSpan.TraceID, kustoSpan.SpanID), err)
return nil, err
}
// Fix issues where there are JSON Array types in tags. On nested tag types convert arrays to string. Else this causes issues in span parsing in Jaeger span transformations
for key, element := range tags {
elementString := fmt.Sprint(element)
isArray := len(elementString) > 0 && elementString[0] == '['
if isArray {
tags[key] = elementString
}
}
// https://opentelemetry.io/docs/specs/otel/trace/sdk_exporters/jaeger/#status
switch kustoSpan.SpanStatus {
case "STATUS_CODE_ERROR":
tags["otel.status_code"] = "ERROR"
tags["error"] = true
case "STATUS_CODE_OK":
tags["otel.status_code"] = "OK"
default:
break
}
// https://opentelemetry.io/docs/specs/otel/trace/sdk_exporters/jaeger/#spankind
switch kustoSpan.SpanKind {
case "SPAN_KIND_SERVER":
tags["span.kind"] = "server"
case "SPAN_KIND_CLIENT":
tags["span.kind"] = "client"
case "SPAN_KIND_CONSUMER":
tags["span.kind"] = "consumer"
case "SPAN_KIND_PRODUCER":
tags["span.kind"] = "producer"
default:
break
}
logs, err := transformEventsToLogs(kustoSpan, logger)
if err != nil {
logger.Error(fmt.Sprintf("Error in transform (transformEventsToLogs) %s. TraceId: %s SpanId: %s ", kustoSpan.Tags.String(), kustoSpan.TraceID, kustoSpan.SpanID), err)
return nil, err
}
process := dbmodel.Process{
ServiceName: kustoSpan.ProcessServiceName,
Tags: nil,
Tag: nil,
}
escapeProcessTags(kustoSpan.ProcessTags.Value)
// Replace the special chars(including start and end []) for correct JSON parsing
replacer := strings.NewReplacer(":[", ":\"[", "],", "]\",", "\\", "")
processTag := []byte(replacer.Replace(string(kustoSpan.ProcessTags.Value)))
err = json.Unmarshal(processTag, &process.Tag)
// See if this parsing yielded an error ?
if err != nil {
logger.Error(fmt.Sprintf("ERROR in Unmarshal processTags %s. TraceId: %s SpanId: %s ", string(kustoSpan.ProcessTags.Value), kustoSpan.TraceID, kustoSpan.SpanID), err)
return nil, err
}
jsonSpan := &dbmodel.Span{
TraceID: dbmodel.TraceID(kustoSpan.TraceID),
SpanID: dbmodel.SpanID(kustoSpan.SpanID),
Flags: uint32(kustoSpan.Flags),
OperationName: kustoSpan.SpanName,
References: spanReferences,
StartTime: uint64(kustoSpan.StartTime.UnixMicro()),
StartTimeMillis: uint64(kustoSpan.StartTime.UnixMilli()),
Duration: uint64(kustoSpan.Duration),
Tags: nil,
Tag: tags,
Logs: logs,
Process: process,
}
spanConverter := dbmodel.NewToDomain(TagDotReplacementCharacter)
convertedSpan, err := spanConverter.SpanToDomain(jsonSpan)
if err != nil {
logger.Error(fmt.Sprintf("Error parsing span to domain. Error %s. The TraceId is %s and the SpanId is %s ", err, kustoSpan.TraceID, kustoSpan.SpanID))
return nil, err
}
span := &model.Span{
TraceID: convertedSpan.TraceID,
SpanID: convertedSpan.SpanID,
OperationName: kustoSpan.SpanName,
References: convertedSpan.References,
Flags: convertedSpan.Flags,
StartTime: kustoSpan.StartTime,
Duration: time.Duration(kustoSpan.Duration) * time.Microsecond,
Tags: convertedSpan.Tags,
Logs: convertedSpan.Logs,
Process: convertedSpan.Process,
}
return span, err
}