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  }