github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/converter/otlp.go (about)

     1  // Copyright 2022 iLogtail Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package protocol
    16  
    17  import (
    18  	"encoding/hex"
    19  	"errors"
    20  	"fmt"
    21  	"strconv"
    22  	"time"
    23  
    24  	"go.opentelemetry.io/collector/pdata/pcommon"
    25  	"go.opentelemetry.io/collector/pdata/plog"
    26  	"go.opentelemetry.io/collector/pdata/pmetric"
    27  	"go.opentelemetry.io/collector/pdata/ptrace"
    28  
    29  	"github.com/alibaba/ilogtail/pkg/models"
    30  	"github.com/alibaba/ilogtail/pkg/protocol"
    31  	"github.com/alibaba/ilogtail/pkg/protocol/otlp"
    32  )
    33  
    34  var (
    35  	bodyKey  = "content"
    36  	levelKey = "level"
    37  )
    38  
    39  var (
    40  	errBytesLengthNotMatch = errors.New("bytes_length_not_match")
    41  )
    42  
    43  func (c *Converter) ConvertToOtlpResourseLogs(logGroup *protocol.LogGroup, targetFields []string) (plog.ResourceLogs, []map[string]string, error) {
    44  	rsLogs := plog.NewResourceLogs()
    45  	desiredValues := make([]map[string]string, len(logGroup.Logs))
    46  
    47  	if logGroup.GetSource() != "" {
    48  		rsLogs.Resource().Attributes().PutStr("source", logGroup.GetSource())
    49  	}
    50  
    51  	if logGroup.GetTopic() != "" {
    52  		rsLogs.Resource().Attributes().PutStr("topic", logGroup.GetTopic())
    53  	}
    54  
    55  	if logGroup.GetMachineUUID() != "" {
    56  		rsLogs.Resource().Attributes().PutStr("machine_uuid", logGroup.GetMachineUUID())
    57  	}
    58  
    59  	for _, t := range logGroup.LogTags {
    60  		rsLogs.Resource().Attributes().PutStr(t.Key, t.Value)
    61  	}
    62  
    63  	scopeLog := rsLogs.ScopeLogs().AppendEmpty()
    64  
    65  	for i, log := range logGroup.Logs {
    66  		logRecord := scopeLog.LogRecords().AppendEmpty()
    67  
    68  		contents, tags := convertLogToMap(log, logGroup.LogTags, logGroup.Source, logGroup.Topic, c.TagKeyRenameMap)
    69  		desiredValue, err := findTargetValues(targetFields, contents, tags, c.TagKeyRenameMap)
    70  		if err != nil {
    71  			return rsLogs, nil, err
    72  		}
    73  		desiredValues[i] = desiredValue
    74  
    75  		for k, v := range contents {
    76  			if k != bodyKey && k != levelKey {
    77  				logRecord.Attributes().PutStr(k, v)
    78  			}
    79  		}
    80  		for k, v := range tags {
    81  			logRecord.Attributes().PutStr(k, v)
    82  		}
    83  
    84  		logRecord.SetObservedTimestamp(pcommon.Timestamp(time.Now().UnixNano()))
    85  		if c.GlobalConfig.EnableTimestampNanosecond {
    86  			logRecord.SetTimestamp(pcommon.Timestamp(uint64(log.Time)*uint64(time.Second)) + pcommon.Timestamp(uint64(*log.TimeNs)*uint64(time.Nanosecond)))
    87  		} else {
    88  			logRecord.SetTimestamp(pcommon.Timestamp(uint64(log.Time) * uint64(time.Second)))
    89  		}
    90  
    91  		if body, has := contents[bodyKey]; has {
    92  			logRecord.Body().SetStr(body)
    93  		}
    94  		if level, has := contents[levelKey]; has {
    95  			logRecord.SetSeverityText(level)
    96  		} else if level, has = tags[level]; has {
    97  			logRecord.SetSeverityText(level)
    98  		}
    99  
   100  	}
   101  
   102  	return rsLogs, desiredValues, nil
   103  }
   104  
   105  func ConvertPipelineEventToOtlpEvent[
   106  	T1 plog.ResourceLogs,
   107  	T2 pmetric.ResourceMetrics,
   108  	T3 ptrace.ResourceSpans,
   109  ](c *Converter, ps *models.PipelineGroupEvents) (t1 T1, t2 T2, t3 T3, err error) {
   110  	switch c.Protocol {
   111  	case ProtocolOtlpV1:
   112  		rsLogs := plog.NewResourceLogs()
   113  		rsMetrics := pmetric.NewResourceMetrics()
   114  		rsTraces := ptrace.NewResourceSpans()
   115  		err = ConvertPipelineGroupEvenstsToOtlpEvents(ps, rsLogs, rsMetrics, rsTraces)
   116  		return T1(rsLogs), T2(rsMetrics), T3(rsTraces), err
   117  	default:
   118  		err = fmt.Errorf("unsupported protocol %v", c.Protocol)
   119  	}
   120  	return
   121  }
   122  
   123  // PipelineGroupEvents -> OTLP Logs/Metrics/Traces
   124  func (c *Converter) ConvertPipelineGroupEventsToOTLPEventsV1(ps *models.PipelineGroupEvents) (plog.ResourceLogs, pmetric.ResourceMetrics, ptrace.ResourceSpans, error) {
   125  	var err error
   126  	rsLogs := plog.NewResourceLogs()
   127  	rsMetrics := pmetric.NewResourceMetrics()
   128  	rsTraces := ptrace.NewResourceSpans()
   129  	err = ConvertPipelineGroupEvenstsToOtlpEvents(ps, rsLogs, rsMetrics, rsTraces)
   130  	return rsLogs, rsMetrics, rsTraces, err
   131  }
   132  
   133  func ConvertPipelineGroupEvenstsToOtlpEvents(ps *models.PipelineGroupEvents, rsLogs plog.ResourceLogs, rsMetrics pmetric.ResourceMetrics, rsTraces ptrace.ResourceSpans) error {
   134  	var err error
   135  	if ps == nil || len(ps.Events) == 0 {
   136  		return err
   137  	}
   138  	meta := ps.Group.Metadata
   139  	groupTags := ps.Group.Tags
   140  
   141  	var scopeLog plog.ScopeLogs
   142  	var scopeMetric pmetric.ScopeMetrics
   143  	var scopeTrace ptrace.ScopeSpans
   144  
   145  	hasLogs, hasMetrics, hasTraces := false, false, false
   146  	for _, v := range ps.Events {
   147  		switch v.GetType() {
   148  		case models.EventTypeLogging:
   149  			if !hasLogs {
   150  				setAttributes(rsLogs.Resource().Attributes(), meta)
   151  				scopeLog = plog.NewScopeLogs()
   152  				setScope(scopeLog, groupTags)
   153  				hasLogs = true
   154  			}
   155  			err = ConvertPipelineEventToOtlpLog(v, scopeLog)
   156  		case models.EventTypeMetric:
   157  			if !hasMetrics {
   158  				setAttributes(rsMetrics.Resource().Attributes(), meta)
   159  				scopeMetric = pmetric.NewScopeMetrics()
   160  				setScope(scopeMetric, groupTags)
   161  				hasMetrics = true
   162  			}
   163  			err = ConvertPipelineEventToOtlpMetric(v, scopeMetric)
   164  		case models.EventTypeSpan:
   165  			if !hasTraces {
   166  				setAttributes(rsTraces.Resource().Attributes(), meta)
   167  				scopeTrace = ptrace.NewScopeSpans()
   168  				setScope(scopeTrace, groupTags)
   169  				hasTraces = true
   170  			}
   171  			err = ConvertPipelineEventToOtlpSpan(v, scopeTrace)
   172  		}
   173  	}
   174  
   175  	if hasLogs {
   176  		newScopeLogs := rsLogs.ScopeLogs().AppendEmpty()
   177  		scopeLog.MoveTo(newScopeLogs)
   178  	}
   179  
   180  	if hasMetrics {
   181  		newScopeMetric := rsMetrics.ScopeMetrics().AppendEmpty()
   182  		scopeMetric.MoveTo(newScopeMetric)
   183  	}
   184  
   185  	if hasTraces {
   186  		newScopeTrace := rsTraces.ScopeSpans().AppendEmpty()
   187  		scopeTrace.MoveTo(newScopeTrace)
   188  	}
   189  
   190  	return err
   191  }
   192  
   193  func ConvertPipelineEventToOtlpLog(event models.PipelineEvent, scopeLog plog.ScopeLogs) (err error) {
   194  	if event.GetType() != models.EventTypeLogging {
   195  		return fmt.Errorf("pipeline_event:%s is not a log", event.GetName())
   196  	}
   197  
   198  	logEvent, ok := event.(*models.Log)
   199  
   200  	if !ok {
   201  		return fmt.Errorf("pipeline_event:%s is not a log", event.GetName())
   202  	}
   203  
   204  	log := scopeLog.LogRecords().AppendEmpty()
   205  	setAttributes(log.Attributes(), logEvent.Tags)
   206  
   207  	// set body as bytes
   208  	log.Body().SetEmptyBytes().Append(logEvent.GetBody()...)
   209  	log.SetTimestamp(pcommon.Timestamp(logEvent.Timestamp))
   210  	log.SetObservedTimestamp(pcommon.Timestamp(logEvent.ObservedTimestamp))
   211  	log.SetSeverityText(logEvent.Level)
   212  	log.SetSeverityNumber(otlp.SeverityTextToSeverityNumber(logEvent.Level))
   213  
   214  	if logEvent.Tags.Contains(otlp.TagKeyLogFlag) {
   215  		if flag, errConvert := strconv.Atoi(logEvent.Tags.Get(otlp.TagKeyLogFlag)); errConvert == nil {
   216  			log.SetFlags(plog.LogRecordFlags(flag))
   217  		}
   218  	}
   219  
   220  	if traceID, errConvert := convertTraceID(logEvent.TraceID); errConvert == nil {
   221  		log.SetTraceID(traceID)
   222  	}
   223  
   224  	if spanID, errConvert := convertSpanID(logEvent.SpanID); errConvert == nil {
   225  		log.SetSpanID(spanID)
   226  	}
   227  	return err
   228  }
   229  
   230  func ConvertPipelineEventToOtlpMetric(event models.PipelineEvent, scopeMetric pmetric.ScopeMetrics) (err error) {
   231  	if event.GetType() != models.EventTypeMetric {
   232  		return fmt.Errorf("pipeline_event:%s is not a metric", event.GetName())
   233  	}
   234  
   235  	metricEvent, ok := event.(*models.Metric)
   236  	if !ok {
   237  		return fmt.Errorf("pipeline_event:%s is not a metric", event.GetName())
   238  	}
   239  
   240  	m := scopeMetric.Metrics().AppendEmpty()
   241  	m.SetName(event.GetName())
   242  	m.SetDescription(metricEvent.Description)
   243  	m.SetUnit(metricEvent.Unit)
   244  
   245  	switch metricEvent.MetricType {
   246  	case models.MetricTypeUntyped:
   247  		// skip untyped metrics
   248  	case models.MetricTypeGauge:
   249  		gauge := m.SetEmptyGauge()
   250  		_, err = appgendNumberDatapoint(gauge, metricEvent)
   251  	case models.MetricTypeCounter:
   252  		sum := m.SetEmptySum()
   253  		sum, err = appgendNumberDatapoint(sum, metricEvent)
   254  		sum.SetAggregationTemporality(pmetric.AggregationTemporalityDelta)
   255  	case models.MetricTypeRateCounter:
   256  		sum := m.SetEmptySum()
   257  		sum, err = appgendNumberDatapoint(sum, metricEvent)
   258  		at := metricEvent.Tags.Get(otlp.TagKeyMetricAggregationTemporality)
   259  		if at == pmetric.AggregationTemporalityDelta.String() {
   260  			sum.SetAggregationTemporality(pmetric.AggregationTemporalityDelta)
   261  		} else if at == pmetric.AggregationTemporalityCumulative.String() {
   262  			sum.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
   263  		}
   264  		if metricEvent.Tags.Get(otlp.TagKeyMetricIsMonotonic) == "true" {
   265  			sum.SetIsMonotonic(true)
   266  		}
   267  	case models.MetricTypeMeter:
   268  		// otlp does not support metric.
   269  	case models.MetricTypeSummary:
   270  		summary := m.SetEmptySummary()
   271  		appgendSummaryDatapoint(summary, metricEvent)
   272  	case models.MetricTypeHistogram:
   273  		if metricEvent.Tags.Get(otlp.TagKeyMetricHistogramType) == pmetric.MetricTypeExponentialHistogram.String() {
   274  			exponentialHistogram := m.SetEmptyExponentialHistogram()
   275  			exponentialHistogram = appendExponentialHistogramDatapoint(exponentialHistogram, metricEvent)
   276  			at := metricEvent.Tags.Get(otlp.TagKeyMetricAggregationTemporality)
   277  			if at == pmetric.AggregationTemporalityDelta.String() {
   278  				exponentialHistogram.SetAggregationTemporality(pmetric.AggregationTemporalityDelta)
   279  			} else if at == pmetric.AggregationTemporalityCumulative.String() {
   280  				exponentialHistogram.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
   281  			}
   282  		} else {
   283  			histogram := m.SetEmptyHistogram()
   284  			appendHistogramDatapoint(histogram, metricEvent)
   285  		}
   286  	}
   287  
   288  	return err
   289  }
   290  
   291  func ConvertPipelineEventToOtlpSpan(event models.PipelineEvent, scopeTrace ptrace.ScopeSpans) error {
   292  	if event.GetType() != models.EventTypeSpan {
   293  		return fmt.Errorf("pipeline_event:%s is not a span: %v", event.GetName(), event.GetType())
   294  	}
   295  
   296  	spanEvent, ok := event.(*models.Span)
   297  	if !ok {
   298  		return fmt.Errorf("pipeline_event:%s is not a span: %v", event.GetName(), event.GetType())
   299  	}
   300  
   301  	span := scopeTrace.Spans().AppendEmpty()
   302  	span.SetName(event.GetName())
   303  	span.SetKind(ptrace.SpanKind(spanEvent.GetKind()))
   304  
   305  	if traceID, err := convertTraceID(spanEvent.TraceID); err == nil {
   306  		span.SetTraceID(traceID)
   307  	}
   308  
   309  	if spanID, err := convertSpanID(spanEvent.SpanID); err == nil {
   310  		span.SetSpanID(spanID)
   311  	}
   312  
   313  	if parentSpanID, err := convertSpanID(spanEvent.ParentSpanID); err == nil {
   314  		span.SetParentSpanID(parentSpanID)
   315  	}
   316  
   317  	span.SetStartTimestamp(pcommon.Timestamp(spanEvent.StartTime))
   318  	span.SetEndTimestamp(pcommon.Timestamp(spanEvent.EndTime))
   319  
   320  	setAttributes(span.Attributes(), spanEvent.Tags)
   321  
   322  	for _, v := range spanEvent.Events {
   323  		otSpanEvent := span.Events().AppendEmpty()
   324  		otSpanEvent.SetName(v.Name)
   325  		otSpanEvent.SetTimestamp(pcommon.Timestamp(v.Timestamp))
   326  		setAttributes(otSpanEvent.Attributes(), v.Tags)
   327  	}
   328  
   329  	for _, v := range spanEvent.Links {
   330  		otSpanLink := span.Links().AppendEmpty()
   331  		if traceID, err := convertTraceID(v.TraceID); err == nil {
   332  			otSpanLink.SetTraceID(traceID)
   333  		}
   334  
   335  		if spanID, err := convertSpanID(v.SpanID); err == nil {
   336  			otSpanLink.SetSpanID(spanID)
   337  		}
   338  		otSpanLink.TraceState().FromRaw(v.TraceState)
   339  		setAttributes(otSpanLink.Attributes(), v.Tags)
   340  	}
   341  
   342  	span.Status().SetCode(ptrace.StatusCode(spanEvent.Status))
   343  	if spanEvent.Tags.Contains(otlp.TagKeySpanStatusMessage) {
   344  		span.Status().SetMessage(spanEvent.Tags.Get(otlp.TagKeySpanStatusMessage))
   345  	}
   346  
   347  	if droppedAttributesCount, err := strconv.Atoi(spanEvent.Tags.Get(otlp.TagKeySpanDroppedAttrsCount)); err == nil {
   348  		span.SetDroppedAttributesCount(uint32(droppedAttributesCount))
   349  	}
   350  
   351  	if droppedEventsCount, err := strconv.Atoi(spanEvent.Tags.Get(otlp.TagKeySpanDroppedEventsCount)); err == nil {
   352  		span.SetDroppedAttributesCount(uint32(droppedEventsCount))
   353  	}
   354  
   355  	if droppedLinksCount, err := strconv.Atoi(spanEvent.Tags.Get(otlp.TagKeySpanDroppedLinksCount)); err == nil {
   356  		span.SetDroppedLinksCount(uint32(droppedLinksCount))
   357  	}
   358  
   359  	return nil
   360  }
   361  
   362  func setScope[T interface {
   363  	Scope() pcommon.InstrumentationScope
   364  }](t T, groupTags models.Tags) {
   365  	scope := t.Scope()
   366  	if groupTags.Contains(otlp.TagKeyScopeName) {
   367  		scope.SetName(groupTags.Get(otlp.TagKeyScopeName))
   368  	}
   369  
   370  	if groupTags.Contains(otlp.TagKeyScopeVersion) {
   371  		scope.SetVersion(groupTags.Get(otlp.TagKeyScopeVersion))
   372  	}
   373  	scopeDroppedAttributesCount, err := strconv.Atoi(groupTags.Get(otlp.TagKeyScopeDroppedAttributesCount))
   374  	if err == nil {
   375  		scope.SetDroppedAttributesCount(uint32(scopeDroppedAttributesCount))
   376  	}
   377  	setAttributes(scope.Attributes(), groupTags)
   378  }
   379  
   380  func appgendNumberDatapoint[T interface {
   381  	DataPoints() pmetric.NumberDataPointSlice
   382  }](t T, metricEvent *models.Metric) (T, error) {
   383  	datapoint := t.DataPoints().AppendEmpty()
   384  	datapoint = setDatapoint(datapoint, metricEvent)
   385  	datapoint.SetDoubleValue(metricEvent.GetValue().GetSingleValue())
   386  	return t, nil
   387  }
   388  
   389  func appgendSummaryDatapoint(summary pmetric.Summary, metricEvent *models.Metric) {
   390  	datapoint := summary.DataPoints().AppendEmpty()
   391  	datapoint = setDatapoint(datapoint, metricEvent)
   392  
   393  	multiValues := metricEvent.GetValue().GetMultiValues()
   394  	datapoint.SetSum(multiValues.Get(otlp.FieldSum))
   395  	datapoint.SetCount(uint64(multiValues.Get(otlp.FieldCount)))
   396  	for field, value := range multiValues.Iterator() {
   397  		if !otlp.IsInternalField(field) {
   398  			quantileValue := datapoint.QuantileValues().AppendEmpty()
   399  			quantile, err := strconv.ParseFloat(field, 64)
   400  			if err != nil {
   401  				continue
   402  			}
   403  			quantileValue.SetQuantile(quantile)
   404  			quantileValue.SetValue(value)
   405  		}
   406  	}
   407  }
   408  
   409  func appendHistogramDatapoint(histogram pmetric.Histogram, metricEvent *models.Metric) {
   410  	datapoint := histogram.DataPoints().AppendEmpty()
   411  	datapoint = setDatapoint(datapoint, metricEvent)
   412  
   413  	multiValues := metricEvent.GetValue().GetMultiValues()
   414  	datapoint.SetCount(uint64(multiValues.Get(otlp.FieldCount)))
   415  	if multiValues.Contains(otlp.FieldMin) {
   416  		datapoint.SetMin(multiValues.Get(otlp.FieldMin))
   417  	}
   418  
   419  	if multiValues.Contains(otlp.FieldMax) {
   420  		datapoint.SetMax(multiValues.Get(otlp.FieldMax))
   421  	}
   422  
   423  	if multiValues.Contains(otlp.FieldSum) {
   424  		datapoint.SetSum(multiValues.Get(otlp.FieldSum))
   425  	}
   426  
   427  	bucketBounds, bucketCounts := otlp.ComputeBuckets(multiValues, true)
   428  
   429  	if len(bucketCounts) >= 1 {
   430  		datapoint.ExplicitBounds().FromRaw(bucketBounds[1:])
   431  		for _, v := range bucketCounts {
   432  			datapoint.BucketCounts().Append(uint64(v))
   433  		}
   434  	}
   435  }
   436  
   437  func appendExponentialHistogramDatapoint(histogram pmetric.ExponentialHistogram, metricEvent *models.Metric) pmetric.ExponentialHistogram {
   438  	datapoint := histogram.DataPoints().AppendEmpty()
   439  	datapoint = setDatapoint(datapoint, metricEvent)
   440  
   441  	multiValues := metricEvent.GetValue().GetMultiValues()
   442  	datapoint.SetCount(uint64(multiValues.Get(otlp.FieldCount)))
   443  	if multiValues.Contains(otlp.FieldMin) {
   444  		datapoint.SetMin(multiValues.Get(otlp.FieldMin))
   445  	}
   446  
   447  	if multiValues.Contains(otlp.FieldMax) {
   448  		datapoint.SetMax(multiValues.Get(otlp.FieldMax))
   449  	}
   450  
   451  	if multiValues.Contains(otlp.FieldSum) {
   452  		datapoint.SetSum(multiValues.Get(otlp.FieldSum))
   453  	}
   454  
   455  	scale := int32(multiValues.Get(otlp.FieldScale))
   456  	datapoint.SetScale(scale)
   457  
   458  	postiveOffset := int32(multiValues.Get(otlp.FieldPositiveOffset))
   459  	datapoint.Positive().SetOffset(postiveOffset)
   460  	_, positveBucketCounts := otlp.ComputeBuckets(multiValues, true)
   461  	for _, v := range positveBucketCounts {
   462  		datapoint.Positive().BucketCounts().Append(uint64(v))
   463  	}
   464  
   465  	negativeOffset := int32(multiValues.Get(otlp.FieldNegativeOffset))
   466  	datapoint.Negative().SetOffset(negativeOffset)
   467  	_, negativeBucketCounts := otlp.ComputeBuckets(multiValues, false)
   468  	for _, v := range negativeBucketCounts {
   469  		datapoint.Negative().BucketCounts().Append(uint64(v))
   470  	}
   471  
   472  	return histogram
   473  }
   474  
   475  func setDatapoint[T interface {
   476  	SetTimestamp(v pcommon.Timestamp)
   477  	SetStartTimestamp(v pcommon.Timestamp)
   478  	Attributes() pcommon.Map
   479  }](t T, metricEvent *models.Metric) T {
   480  	t.SetTimestamp(pcommon.Timestamp(metricEvent.Timestamp))
   481  	t.SetStartTimestamp(pcommon.Timestamp(metricEvent.ObservedTimestamp))
   482  	setAttributes(t.Attributes(), metricEvent.Tags)
   483  	return t
   484  }
   485  
   486  func setAttributes[
   487  	T interface {
   488  		Iterator() map[string]string
   489  	},
   490  ](attributes pcommon.Map, tags T) {
   491  	for k, v := range tags.Iterator() {
   492  		if !otlp.IsInternalTag(k) {
   493  			attributes.PutStr(k, v)
   494  		}
   495  	}
   496  }
   497  
   498  func convertTraceID(hexString string) (pcommon.TraceID, error) {
   499  	var id pcommon.TraceID = pcommon.NewTraceIDEmpty()
   500  
   501  	if hexString == "" {
   502  		return id, nil
   503  	}
   504  
   505  	bytes, err := hex.DecodeString(hexString)
   506  	if err != nil {
   507  		return id, err
   508  	}
   509  
   510  	if len(bytes) != 16 {
   511  		return id, fmt.Errorf("%w: should be %d, but is %d after traceID: %s is decoded", errBytesLengthNotMatch, 16, len(bytes), hexString)
   512  	}
   513  
   514  	return pcommon.TraceID(*(*[16]byte)(bytes)), nil
   515  }
   516  
   517  func convertSpanID(hexString string) (pcommon.SpanID, error) {
   518  	id := pcommon.NewSpanIDEmpty()
   519  	if hexString == "" {
   520  		return id, nil
   521  	}
   522  
   523  	bytes, err := hex.DecodeString(hexString)
   524  	if err != nil {
   525  		return id, err
   526  	}
   527  
   528  	if len(bytes) != 8 {
   529  		return id, fmt.Errorf("%w: should be %d, but is %d after traceID: %s is decoded", errBytesLengthNotMatch, 8, len(bytes), hexString)
   530  	}
   531  	return pcommon.SpanID(*(*[8]byte)(bytes)), nil
   532  }