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  }