github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/decoder/opentelemetry/otlp_trace_parser.go (about) 1 package opentelemetry 2 3 import ( 4 "encoding/base64" 5 "encoding/hex" 6 "encoding/json" 7 "strconv" 8 "time" 9 10 "go.opentelemetry.io/collector/pdata/pcommon" 11 "go.opentelemetry.io/collector/pdata/ptrace" 12 v1Common "go.opentelemetry.io/proto/otlp/common/v1" 13 v1Resource "go.opentelemetry.io/proto/otlp/resource/v1" 14 v1 "go.opentelemetry.io/proto/otlp/trace/v1" 15 16 "github.com/alibaba/ilogtail/pkg/protocol" 17 ) 18 19 const ( 20 traceIDField = "traceID" 21 spanIDField = "spanID" 22 parentSpanIDField = "parentSpanID" 23 nameField = "name" 24 kindField = "kind" 25 linksField = "links" 26 timeField = "time" 27 startTimeField = "start" 28 endTimeField = "end" 29 traceStateField = "traceState" 30 durationField = "duration" 31 attributeField = "attribute" 32 statusCodeField = "statusCode" 33 statusMessageField = "statusMessage" 34 logsField = "logs" 35 slsLogTimeUnixNano = "timeUnixNano" 36 slsLogSeverityNumber = "severityNumber" 37 slsLogSeverityText = "severityText" 38 slsLogName = "name" 39 slsLogContent = "content" 40 slsLogAttribute = "attribute" 41 slsLogFlags = "flags" 42 slsLogResource = "resource" 43 slsLogHost = "host" 44 slsLogService = "service" 45 // shortcut for "otlp.instrumentation.library.name" "otlp.instrumentation.library.version" 46 slsLogInstrumentationName = "otlp.name" 47 slsLogInstrumentationVersion = "otlp.version" 48 ) 49 50 // traceDataToLogService translates trace data into the LogService format. 51 func traceDataToLogServiceData(td ptrace.Traces) ([]*protocol.Log, int) { 52 var slsLogs []*protocol.Log 53 resourceSpansSlice := td.ResourceSpans() 54 for i := 0; i < resourceSpansSlice.Len(); i++ { 55 logs := resourceSpansToLogServiceData(resourceSpansSlice.At(i)) 56 slsLogs = append(slsLogs, logs...) 57 } 58 return slsLogs, 0 59 } 60 61 func resourceToLogContents(resource pcommon.Resource) []*protocol.Log_Content { 62 logContents := make([]*protocol.Log_Content, 3) 63 attrs := resource.Attributes() 64 if hostName, ok := attrs.Get("host.name"); ok { 65 logContents[0] = &protocol.Log_Content{ 66 Key: slsLogHost, 67 Value: hostName.AsString(), 68 } 69 attrs.Remove("host.name") 70 } else { 71 logContents[0] = &protocol.Log_Content{ 72 Key: slsLogHost, 73 Value: "", 74 } 75 } 76 77 if serviceName, ok := attrs.Get("service.name"); ok { 78 logContents[1] = &protocol.Log_Content{ 79 Key: slsLogService, 80 Value: serviceName.AsString(), 81 } 82 attrs.Remove("service.name") 83 } else { 84 logContents[1] = &protocol.Log_Content{ 85 Key: slsLogService, 86 Value: "", 87 } 88 } 89 90 attributeBuffer, _ := json.Marshal(attrs.AsRaw()) 91 logContents[2] = &protocol.Log_Content{ 92 Key: slsLogResource, 93 Value: string(attributeBuffer), 94 } 95 96 return logContents 97 } 98 99 func instrumentationLibraryToLogContents(instrumentationLibrary pcommon.InstrumentationScope) []*protocol.Log_Content { 100 logContents := make([]*protocol.Log_Content, 2) 101 logContents[0] = &protocol.Log_Content{ 102 Key: slsLogInstrumentationName, 103 Value: instrumentationLibrary.Name(), 104 } 105 logContents[1] = &protocol.Log_Content{ 106 Key: slsLogInstrumentationVersion, 107 Value: instrumentationLibrary.Version(), 108 } 109 return logContents 110 } 111 112 func resourceSpansToLogServiceData(resourceSpans ptrace.ResourceSpans) []*protocol.Log { 113 resourceContents := resourceToLogContents(resourceSpans.Resource()) 114 // v0.58.0 Beta: Remove the InstrumentationLibrary to Scope translation (part of transition to OTLP 0.19). (#5819) 115 // - This has a side effect that when sending JSON encoded telemetry using OTLP proto <= 0.15.0, telemetry will be dropped. 116 var slsLogs []*protocol.Log 117 scopeSpans := resourceSpans.ScopeSpans() 118 for i := 0; i < scopeSpans.Len(); i++ { 119 scopeSpan := resourceSpans.ScopeSpans().At(i) 120 instrumentationLibraryContents := instrumentationLibraryToLogContents(scopeSpan.Scope()) 121 spans := scopeSpan.Spans() 122 for j := 0; j < spans.Len(); j++ { 123 if slsLog := spanToLogServiceData(spans.At(j), resourceContents, instrumentationLibraryContents); slsLog != nil { 124 slsLogs = append(slsLogs, slsLog) 125 } 126 } 127 } 128 129 return slsLogs 130 } 131 132 func spanToLogServiceData(span ptrace.Span, resourceContents, instrumentationLibraryContents []*protocol.Log_Content) *protocol.Log { 133 endTimeNano := span.EndTimestamp() 134 if endTimeNano == 0 { 135 endTimeNano = pcommon.Timestamp(time.Now().UnixNano()) 136 } 137 slsLog := &protocol.Log{ 138 Time: uint32(endTimeNano / 1000 / 1000 / 1000), 139 } 140 // pre alloc, refine if logContent's len > 16 141 preAllocCount := 16 142 slsLog.Contents = make([]*protocol.Log_Content, 0, preAllocCount+len(resourceContents)+len(instrumentationLibraryContents)) 143 contentsBuffer := make([]protocol.Log_Content, 0, preAllocCount) 144 145 slsLog.Contents = append(slsLog.Contents, resourceContents...) 146 slsLog.Contents = append(slsLog.Contents, instrumentationLibraryContents...) 147 148 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 149 Key: traceIDField, 150 Value: span.TraceID().String(), 151 }) 152 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 153 Key: spanIDField, 154 Value: span.SpanID().String(), 155 }) 156 // if ParentSpanID is not valid, the return "", it is compatible for log service 157 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 158 Key: parentSpanIDField, 159 Value: span.ParentSpanID().String(), 160 }) 161 162 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 163 Key: kindField, 164 Value: spanKindToShortString(span.Kind()), 165 }) 166 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 167 Key: nameField, 168 Value: span.Name(), 169 }) 170 171 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 172 Key: linksField, 173 Value: spanLinksToString(span.Links()), 174 }) 175 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 176 Key: logsField, 177 Value: eventsToString(span.Events()), 178 }) 179 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 180 Key: traceStateField, 181 Value: span.TraceState().AsRaw(), 182 }) 183 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 184 Key: startTimeField, 185 Value: strconv.FormatUint(uint64(span.StartTimestamp()/1000), 10), 186 }) 187 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 188 Key: endTimeField, 189 Value: strconv.FormatUint(uint64(endTimeNano/1000), 10), 190 }) 191 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 192 Key: durationField, 193 Value: strconv.FormatUint(uint64((endTimeNano-span.StartTimestamp())/1000), 10), 194 }) 195 attributeMap := span.Attributes().AsRaw() 196 attributeJSONBytes, _ := json.Marshal(attributeMap) 197 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 198 Key: attributeField, 199 Value: string(attributeJSONBytes), 200 }) 201 202 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 203 Key: statusCodeField, 204 Value: statusCodeToShortString(span.Status().Code()), 205 }) 206 207 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 208 Key: statusMessageField, 209 Value: span.Status().Message(), 210 }) 211 212 for i := range contentsBuffer { 213 slsLog.Contents = append(slsLog.Contents, &contentsBuffer[i]) 214 } 215 return slsLog 216 } 217 218 func spanKindToShortString(kind ptrace.SpanKind) string { 219 switch kind { 220 case ptrace.SpanKindInternal: 221 return "internal" 222 case ptrace.SpanKindClient: 223 return "client" 224 case ptrace.SpanKindServer: 225 return "server" 226 case ptrace.SpanKindProducer: 227 return "producer" 228 case ptrace.SpanKindConsumer: 229 return "consumer" 230 default: 231 return "" 232 } 233 } 234 235 func statusCodeToShortString(code ptrace.StatusCode) string { 236 switch code { 237 case ptrace.StatusCodeError: 238 return "ERROR" 239 case ptrace.StatusCodeOk: 240 return "OK" 241 default: 242 return "UNSET" 243 } 244 } 245 246 func v1StatusCodeToShortString(code v1.Status_StatusCode) string { 247 switch code { 248 case 2: 249 return "ERROR" 250 case 1: 251 return "OK" 252 default: 253 return "UNSET" 254 } 255 } 256 257 func eventsToString(events ptrace.SpanEventSlice) string { 258 eventArray := make([]map[string]interface{}, 0, events.Len()) 259 for i := 0; i < events.Len(); i++ { 260 spanEvent := events.At(i) 261 event := map[string]interface{}{} 262 event[nameField] = spanEvent.Name() 263 event[timeField] = spanEvent.Timestamp() 264 event[attributeField] = spanEvent.Attributes().AsRaw() 265 eventArray = append(eventArray, event) 266 } 267 eventArrayBytes, _ := json.Marshal(&eventArray) 268 return string(eventArrayBytes) 269 270 } 271 272 func spanLinksToString(spanLinkSlice ptrace.SpanLinkSlice) string { 273 linkArray := make([]map[string]interface{}, 0, spanLinkSlice.Len()) 274 for i := 0; i < spanLinkSlice.Len(); i++ { 275 spanLink := spanLinkSlice.At(i) 276 link := map[string]interface{}{} 277 link[spanIDField] = spanLink.SpanID().String() 278 link[traceIDField] = spanLink.TraceID().String() 279 link[attributeField] = spanLink.Attributes().AsRaw() 280 linkArray = append(linkArray, link) 281 } 282 linkArrayBytes, _ := json.Marshal(&linkArray) 283 return string(linkArrayBytes) 284 } 285 286 // Special Add 287 func ConvertTrace(td ptrace.Traces) ([]*protocol.Log, int) { 288 return traceDataToLogServiceData(td) 289 } 290 291 func ConvertResourceSpans(rs *v1.ResourceSpans, traceIDNeedDecode, spanIDNeedDecode, parentSpanIDNeedDecode bool) ([]*protocol.Log, error) { 292 return convertResourceSpansToLogData(rs, traceIDNeedDecode, spanIDNeedDecode, parentSpanIDNeedDecode) 293 } 294 295 func convertResourceSpansToLogData(rs *v1.ResourceSpans, traceIDNeedDecode, spanIDNeedDecode, parentSpanIDNeedDecode bool) (slsLogs []*protocol.Log, e error) { 296 resourceContents := v1ResourceToLogContents(rs.GetResource()) 297 scopeSpans := rs.GetScopeSpans() 298 299 for _, span := range scopeSpans { 300 instrumentationLibraryContents := v1InstrumentationLibraryToLogContents(span.GetScope()) 301 for _, s := range span.GetSpans() { 302 if traceIDNeedDecode { 303 if s.TraceId, e = hex.DecodeString(base64.StdEncoding.EncodeToString(s.GetTraceId())); e != nil { 304 return nil, e 305 } 306 } 307 308 if spanIDNeedDecode { 309 if s.SpanId, e = hex.DecodeString(base64.StdEncoding.EncodeToString(s.GetSpanId())); e != nil { 310 return nil, e 311 } 312 } 313 314 if parentSpanIDNeedDecode && s.ParentSpanId != nil { 315 if s.ParentSpanId, e = hex.DecodeString(base64.StdEncoding.EncodeToString(s.GetParentSpanId())); e != nil { 316 return nil, e 317 } 318 } 319 320 if slsLog := v1SpanToLogServiceData(s, resourceContents, instrumentationLibraryContents); slsLog != nil { 321 slsLogs = append(slsLogs, slsLog) 322 } 323 } 324 } 325 326 return slsLogs, nil 327 } 328 329 func v1ResourceToLogContents(resource *v1Resource.Resource) []*protocol.Log_Content { 330 logContents := make([]*protocol.Log_Content, 3) 331 attrs := resource.GetAttributes() 332 333 var hostfined = false 334 var serviceName = false 335 fields := map[string]interface{}{} 336 337 for _, attr := range attrs { 338 switch attr.Key { 339 case "host.name": 340 logContents[0] = &protocol.Log_Content{ 341 Key: slsLogHost, 342 Value: attr.Value.GetStringValue(), 343 } 344 hostfined = true 345 case "service.name": 346 logContents[1] = &protocol.Log_Content{ 347 Key: slsLogService, 348 Value: attr.Value.GetStringValue(), 349 } 350 serviceName = true 351 default: 352 fields[attr.Key] = attr.Value.GetStringValue() 353 } 354 } 355 356 if !hostfined { 357 logContents[0] = &protocol.Log_Content{ 358 Key: slsLogHost, 359 Value: "", 360 } 361 } 362 363 if !serviceName { 364 logContents[1] = &protocol.Log_Content{ 365 Key: slsLogService, 366 Value: "", 367 } 368 } 369 370 attributeBuffer, _ := json.Marshal(fields) 371 logContents[2] = &protocol.Log_Content{ 372 Key: slsLogResource, 373 Value: string(attributeBuffer), 374 } 375 376 return logContents 377 } 378 379 func v1InstrumentationLibraryToLogContents(instrumentationLibrary *v1Common.InstrumentationScope) []*protocol.Log_Content { 380 logContents := make([]*protocol.Log_Content, 2) 381 logContents[0] = &protocol.Log_Content{ 382 Key: slsLogInstrumentationName, 383 Value: instrumentationLibrary.GetName(), 384 } 385 logContents[1] = &protocol.Log_Content{ 386 Key: slsLogInstrumentationVersion, 387 Value: instrumentationLibrary.GetVersion(), 388 } 389 return logContents 390 } 391 392 func v1SpanToLogServiceData(span *v1.Span, resourceContents, instrumentationLibraryContents []*protocol.Log_Content) *protocol.Log { 393 endTimeNano := span.GetEndTimeUnixNano() 394 if endTimeNano == 0 { 395 endTimeNano = uint64(time.Now().Unix() * 1000 * 1000 * 1000) 396 } 397 slsLog := &protocol.Log{ 398 Time: uint32(endTimeNano / 1000 / 1000 / 1000), 399 } 400 // pre alloc, refine if logContent's len > 16 401 preAllocCount := 16 402 slsLog.Contents = make([]*protocol.Log_Content, 0, preAllocCount+len(resourceContents)+len(instrumentationLibraryContents)) 403 contentsBuffer := make([]protocol.Log_Content, 0, preAllocCount) 404 405 slsLog.Contents = append(slsLog.Contents, resourceContents...) 406 slsLog.Contents = append(slsLog.Contents, instrumentationLibraryContents...) 407 408 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 409 Key: traceIDField, 410 Value: hex.EncodeToString(span.GetTraceId()), 411 }) 412 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 413 Key: spanIDField, 414 Value: hex.EncodeToString(span.GetSpanId()), 415 }) 416 // if ParentSpanID is not valid, the return "", it is compatible for log service 417 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 418 Key: parentSpanIDField, 419 Value: hex.EncodeToString(span.GetParentSpanId()), 420 }) 421 422 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 423 Key: kindField, 424 Value: v1SpanKindToShortString(span.Kind), 425 }) 426 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 427 Key: nameField, 428 Value: span.Name, 429 }) 430 431 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 432 Key: linksField, 433 Value: v1SpanLinksToString(span.GetLinks()), 434 }) 435 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 436 Key: logsField, 437 Value: v1EventsToString(span.GetEvents()), 438 }) 439 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 440 Key: traceStateField, 441 Value: span.TraceState, 442 }) 443 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 444 Key: startTimeField, 445 Value: strconv.FormatUint(span.StartTimeUnixNano/1000, 10), 446 }) 447 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 448 Key: endTimeField, 449 Value: strconv.FormatUint(endTimeNano/1000, 10), 450 }) 451 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 452 Key: durationField, 453 Value: strconv.FormatUint((endTimeNano-span.StartTimeUnixNano)/1000, 10), 454 }) 455 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 456 Key: attributeField, 457 Value: keyValueToString(span.GetAttributes()), 458 }) 459 460 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 461 Key: statusCodeField, 462 Value: v1StatusCodeToShortString(span.Status.Code), 463 }) 464 465 contentsBuffer = append(contentsBuffer, protocol.Log_Content{ 466 Key: statusMessageField, 467 Value: span.Status.String(), 468 }) 469 470 for i := range contentsBuffer { 471 slsLog.Contents = append(slsLog.Contents, &contentsBuffer[i]) 472 } 473 return slsLog 474 } 475 476 func v1SpanLinksToString(spanLinkSlice []*v1.Span_Link) string { 477 linkArray := make([]map[string]interface{}, 0, len(spanLinkSlice)) 478 for _, link := range spanLinkSlice { 479 linkMap := map[string]interface{}{} 480 linkMap[spanIDField] = string(link.SpanId) 481 linkMap[traceIDField] = string(link.TraceId) 482 linkMap[attributeField] = keyValueToString(link.Attributes) 483 linkArray = append(linkArray, linkMap) 484 } 485 linkArrayBytes, _ := json.Marshal(&linkArray) 486 return string(linkArrayBytes) 487 } 488 489 func v1EventsToString(events []*v1.Span_Event) string { 490 eventArray := make([]map[string]interface{}, 0, len(events)) 491 for _, event := range events { 492 eventMap := map[string]interface{}{} 493 eventMap[nameField] = event.Name 494 eventMap[timeField] = event.TimeUnixNano 495 eventMap[attributeField] = keyValueToString(event.Attributes) 496 eventArray = append(eventArray, eventMap) 497 } 498 499 eventArrayBytes, _ := json.Marshal(&eventArray) 500 return string(eventArrayBytes) 501 } 502 503 func keyValueToString(keyValues []*v1Common.KeyValue) string { 504 var results = make(map[string]string) 505 for _, keyValue := range keyValues { 506 results[keyValue.Key] = anyValueToString(keyValue.Value) 507 } 508 509 var d []byte 510 var e error 511 if d, e = json.Marshal(results); e != nil { 512 return "" 513 } 514 515 return string(d) 516 } 517 518 func v1SpanKindToShortString(kind v1.Span_SpanKind) string { 519 switch int32(kind.Number()) { 520 case int32(ptrace.SpanKindInternal): 521 return "internal" 522 case int32(ptrace.SpanKindClient): 523 return "client" 524 case int32(ptrace.SpanKindServer): 525 return "server" 526 case int32(ptrace.SpanKindProducer): 527 return "producer" 528 case int32(ptrace.SpanKindConsumer): 529 return "consumer" 530 default: 531 return "" 532 } 533 }