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 }