github.com/kiali/kiali@v1.84.0/tracing/otel/model/converter/converter.go (about) 1 package converter 2 3 import ( 4 "strconv" 5 6 "github.com/kiali/kiali/log" 7 jaegerModels "github.com/kiali/kiali/tracing/jaeger/model/json" 8 otel "github.com/kiali/kiali/tracing/otel/model" 9 otelModels "github.com/kiali/kiali/tracing/otel/model/json" 10 "github.com/kiali/kiali/tracing/tempo/tempopb" 11 v1 "github.com/kiali/kiali/tracing/tempo/tempopb/common/v1" 12 v11 "github.com/kiali/kiali/tracing/tempo/tempopb/resource/v1" 13 ) 14 15 // convertID 16 func ConvertId(id string) jaegerModels.TraceID { 17 return jaegerModels.TraceID(id) 18 } 19 20 // convertSpanId 21 func convertSpanId(id string) jaegerModels.SpanID { 22 return jaegerModels.SpanID(id) 23 } 24 25 // ConvertSpans 26 // https://opentelemetry.io/docs/specs/otel/trace/sdk_exporters/jaeger 27 func ConvertSpans(spans []otelModels.Span, serviceName string, traceID string) []jaegerModels.Span { 28 var toRet []jaegerModels.Span 29 for _, span := range spans { 30 31 startTime, err := strconv.ParseUint(span.StartTimeUnixNano, 10, 64) 32 if err != nil { 33 log.Errorf("Error converting start time. Skipping trace") 34 continue 35 } 36 37 duration, err := getDuration(span.EndTimeUnixNano, span.StartTimeUnixNano) 38 if err != nil { 39 log.Errorf("Error converting duration. Skipping trace") 40 continue 41 } 42 jaegerTraceId := ConvertId(traceID) // The traceID from the SpanID doesn't look to match (ex. Q3xfr1lMsbi2OX9CxUbYug==) 43 jaegerSpanId := convertSpanId(span.SpanID) 44 parentSpanId := convertSpanId(span.ParentSpanId) 45 46 jaegerSpan := jaegerModels.Span{ 47 TraceID: jaegerTraceId, 48 SpanID: jaegerSpanId, 49 Duration: duration, 50 StartTime: startTime / 1000, 51 // No more mapped data 52 Flags: 0, 53 OperationName: span.Name, 54 References: convertReferences(jaegerTraceId, parentSpanId), 55 Tags: convertAttributes(span.Attributes, span.Status), 56 Logs: []jaegerModels.Log{}, 57 ProcessID: "", 58 Process: &jaegerModels.Process{Tags: []jaegerModels.KeyValue{}, ServiceName: serviceName}, 59 Warnings: []string{}, 60 } 61 62 // This is how Jaeger reports it 63 // Used to determine the envoy direction 64 atb_val := "" 65 if span.Kind == "SPAN_KIND_CLIENT" { 66 atb_val = "client" 67 } else if span.Kind == "SPAN_KIND_SERVER" { 68 atb_val = "server" 69 } 70 if atb_val != "" { 71 atb := jaegerModels.KeyValue{Key: "span.kind", Value: atb_val, Type: "string"} 72 jaegerSpan.Tags = append(jaegerSpan.Tags, atb) 73 } 74 75 toRet = append(toRet, jaegerSpan) 76 } 77 return toRet 78 } 79 80 // ConvertTraceMetadata used by the GRPC Client 81 func ConvertTraceMetadata(trace tempopb.TraceSearchMetadata, serviceName string) (*jaegerModels.Trace, error) { 82 jaegerTrace := jaegerModels.Trace{ 83 TraceID: ConvertId(trace.TraceID), 84 Processes: map[jaegerModels.ProcessID]jaegerModels.Process{}, 85 Warnings: []string{}, 86 } 87 for _, span := range trace.SpanSet.Spans { 88 spanSet := convertOtelSpan(span, serviceName, trace.TraceID, trace.RootTraceName) 89 jaegerTrace.Spans = append(jaegerTrace.Spans, spanSet) 90 } 91 jaegerTrace.Matched = len(jaegerTrace.Spans) 92 return &jaegerTrace, nil 93 } 94 95 // convertOtelSpan used for GRPC format Spans 96 func convertOtelSpan(span *tempopb.Span, serviceName, traceID, rootTrace string) jaegerModels.Span { 97 98 modelSpan := jaegerModels.Span{ 99 SpanID: jaegerModels.SpanID(span.SpanID), 100 TraceID: jaegerModels.TraceID(traceID), 101 Duration: span.DurationNanos / 1000, 102 StartTime: span.StartTimeUnixNano / 1000, 103 // No more mapped data 104 Flags: 0, 105 References: []jaegerModels.Reference{}, // convertReferences(traceID, rootTrace), 106 Tags: convertModelAttributes(span.Attributes), 107 Logs: []jaegerModels.Log{}, 108 OperationName: rootTrace, 109 ProcessID: "", 110 Process: &jaegerModels.Process{Tags: []jaegerModels.KeyValue{}, ServiceName: serviceName}, 111 Warnings: []string{}, 112 } 113 114 return modelSpan 115 } 116 117 func ConvertSpanSet(span otel.Span, serviceName string, traceId string, rootName string) []jaegerModels.Span { 118 var toRet []jaegerModels.Span 119 120 startTime, err := strconv.ParseUint(span.StartTimeUnixNano, 10, 64) 121 if err != nil { 122 log.Errorf("Error converting start time.") 123 } 124 duration, err := strconv.ParseUint(span.DurationNanos, 10, 64) 125 if err != nil { 126 log.Errorf("Error converting duration.") 127 } 128 129 jaegerTraceId := ConvertId(traceId) 130 jaegerSpanId := convertSpanId(span.SpanID) 131 132 jaegerSpan := jaegerModels.Span{ 133 TraceID: jaegerTraceId, 134 SpanID: jaegerSpanId, 135 Duration: duration / 1000, // Provided in ns, Jaeger uses ms 136 StartTime: startTime / 1000, 137 // No more mapped data 138 Flags: 0, 139 //OperationName: span.Name, 140 References: []jaegerModels.Reference{}, 141 Tags: convertAttributes(span.Attributes, span.Status), 142 Logs: []jaegerModels.Log{}, 143 OperationName: rootName, 144 ProcessID: "", 145 Process: &jaegerModels.Process{Tags: []jaegerModels.KeyValue{}, ServiceName: serviceName}, 146 Warnings: []string{}, 147 } 148 149 toRet = append(toRet, jaegerSpan) 150 151 return toRet 152 } 153 154 func getDuration(end string, start string) (uint64, error) { 155 endInt, err := strconv.ParseUint(end, 10, 64) 156 if err != nil { 157 log.Errorf("Error converting end date: %s", err.Error()) 158 return 0, err 159 } 160 startInt, err := strconv.ParseUint(start, 10, 64) 161 if err != nil { 162 log.Errorf("Error converting start date: %s", err.Error()) 163 return 0, err 164 } 165 // nano to micro 166 return (endInt - startInt) / 1000, nil 167 } 168 169 func convertReferences(traceId jaegerModels.TraceID, parentSpanId jaegerModels.SpanID) []jaegerModels.Reference { 170 var references []jaegerModels.Reference 171 172 if parentSpanId == "" { 173 return references 174 } 175 176 var ref = jaegerModels.Reference{ 177 RefType: jaegerModels.ReferenceType("CHILD_OF"), 178 TraceID: traceId, 179 SpanID: parentSpanId, 180 } 181 182 references = append(references, ref) 183 return references 184 } 185 186 func convertAttributes(attributes []otelModels.Attribute, status otelModels.Status) []jaegerModels.KeyValue { 187 var tags []jaegerModels.KeyValue 188 for _, atb := range attributes { 189 if atb.Key == "status" && atb.Value.StringValue == "error" { 190 tag := jaegerModels.KeyValue{Key: "error", Value: true, Type: "bool"} 191 tags = append(tags, tag) 192 } else { 193 tag := jaegerModels.KeyValue{Key: atb.Key, Value: atb.Value.StringValue, Type: "string"} 194 tags = append(tags, tag) 195 } 196 } 197 // When Span Status is set to ERROR, an error span tag MUST be added with the Boolean value of true 198 if status.Code == "STATUS_CODE_ERROR" { 199 tag := jaegerModels.KeyValue{Key: "error", Value: true, Type: "bool"} 200 tags = append(tags, tag) 201 } 202 return tags 203 } 204 205 func convertModelAttributes(attributes []*v1.KeyValue) []jaegerModels.KeyValue { 206 var tags []jaegerModels.KeyValue 207 for _, atb := range attributes { 208 if atb.Key == "status" { 209 if atb.Value.GetStringValue() == "error" { 210 tag := jaegerModels.KeyValue{Key: "error", Value: true, Type: "bool"} 211 tags = append(tags, tag) 212 } 213 } else { 214 tag := jaegerModels.KeyValue{Key: atb.Key, Value: atb.Value.GetStringValue(), Type: "string"} 215 tags = append(tags, tag) 216 } 217 } 218 return tags 219 } 220 221 func ConvertResource(resourceSpans *v11.Resource) jaegerModels.Span { 222 span := jaegerModels.Span{} 223 return span 224 }