mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-02-03 13:11:53 +02:00
534ce5ab09
1. Create scope map with resource key to map the correct log record. 2. Add test case with different resource and scope combination Fixes #5782 ### Benchmarks ``` goos: darwin goarch: arm64 pkg: go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc/internal/transform │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ ResourceLogs-8 3.266µ ± 3% 1.100µ ± 5% -66.33% (p=0.000 n=10) │ old.txt │ new.txt │ │ B/op │ B/op vs base │ ResourceLogs-8 8.297Ki ± 0% 2.430Ki ± 0% -70.72% (p=0.000 n=10) │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ ResourceLogs-8 178.00 ± 0% 52.00 ± 0% -70.79% (p=0.000 n=10) ``` --------- Co-authored-by: Sam Xie <sam@samxie.me>
390 lines
10 KiB
Cheetah
390 lines
10 KiB
Cheetah
// Code created by gotmpl. DO NOT MODIFY.
|
|
// source: internal/shared/otlp/otlplog/transform/log.go.tmpl
|
|
|
|
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Package transform provides transformation functionality from the
|
|
// sdk/log data-types into OTLP data-types.
|
|
package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform"
|
|
|
|
import (
|
|
"time"
|
|
|
|
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
|
lpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
|
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
api "go.opentelemetry.io/otel/log"
|
|
"go.opentelemetry.io/otel/sdk/instrumentation"
|
|
"go.opentelemetry.io/otel/sdk/log"
|
|
)
|
|
|
|
// ResourceLogs returns an slice of OTLP ResourceLogs generated from records.
|
|
func ResourceLogs(records []log.Record) []*lpb.ResourceLogs {
|
|
if len(records) == 0 {
|
|
return nil
|
|
}
|
|
|
|
resMap := make(map[attribute.Distinct]*lpb.ResourceLogs)
|
|
|
|
type key struct {
|
|
r attribute.Distinct
|
|
is instrumentation.Scope
|
|
}
|
|
scopeMap := make(map[key]*lpb.ScopeLogs)
|
|
|
|
var resources int
|
|
for _, r := range records {
|
|
res := r.Resource()
|
|
rKey := res.Equivalent()
|
|
scope := r.InstrumentationScope()
|
|
k := key{
|
|
r: rKey,
|
|
is: scope,
|
|
}
|
|
sl, iOk := scopeMap[k]
|
|
if !iOk {
|
|
sl = new(lpb.ScopeLogs)
|
|
var emptyScope instrumentation.Scope
|
|
if scope != emptyScope {
|
|
sl.Scope = &cpb.InstrumentationScope{
|
|
Name: scope.Name,
|
|
Version: scope.Version,
|
|
}
|
|
sl.SchemaUrl = scope.SchemaURL
|
|
}
|
|
scopeMap[k] = sl
|
|
}
|
|
|
|
sl.LogRecords = append(sl.LogRecords, LogRecord(r))
|
|
rl, rOk := resMap[rKey]
|
|
if !rOk {
|
|
resources++
|
|
rl = new(lpb.ResourceLogs)
|
|
if res.Len() > 0 {
|
|
rl.Resource = &rpb.Resource{
|
|
Attributes: AttrIter(res.Iter()),
|
|
}
|
|
}
|
|
rl.SchemaUrl = res.SchemaURL()
|
|
resMap[rKey] = rl
|
|
}
|
|
if !iOk {
|
|
rl.ScopeLogs = append(rl.ScopeLogs, sl)
|
|
}
|
|
}
|
|
|
|
// Transform the categorized map into a slice
|
|
resLogs := make([]*lpb.ResourceLogs, 0, resources)
|
|
for _, rl := range resMap {
|
|
resLogs = append(resLogs, rl)
|
|
}
|
|
|
|
return resLogs
|
|
}
|
|
|
|
// LogRecord returns an OTLP LogRecord generated from record.
|
|
func LogRecord(record log.Record) *lpb.LogRecord {
|
|
r := &lpb.LogRecord{
|
|
TimeUnixNano: timeUnixNano(record.Timestamp()),
|
|
ObservedTimeUnixNano: timeUnixNano(record.ObservedTimestamp()),
|
|
SeverityNumber: SeverityNumber(record.Severity()),
|
|
SeverityText: record.SeverityText(),
|
|
Body: LogAttrValue(record.Body()),
|
|
Attributes: make([]*cpb.KeyValue, 0, record.AttributesLen()),
|
|
Flags: uint32(record.TraceFlags()),
|
|
// TODO: DroppedAttributesCount: /* ... */,
|
|
}
|
|
record.WalkAttributes(func(kv api.KeyValue) bool {
|
|
r.Attributes = append(r.Attributes, LogAttr(kv))
|
|
return true
|
|
})
|
|
if tID := record.TraceID(); tID.IsValid() {
|
|
r.TraceId = tID[:]
|
|
}
|
|
if sID := record.SpanID(); sID.IsValid() {
|
|
r.SpanId = sID[:]
|
|
}
|
|
return r
|
|
}
|
|
|
|
// timeUnixNano returns t as a Unix time, the number of nanoseconds elapsed
|
|
// since January 1, 1970 UTC as uint64. The result is undefined if the Unix
|
|
// time in nanoseconds cannot be represented by an int64 (a date before the
|
|
// year 1678 or after 2262). timeUnixNano on the zero Time returns 0. The
|
|
// result does not depend on the location associated with t.
|
|
func timeUnixNano(t time.Time) uint64 {
|
|
nano := t.UnixNano()
|
|
if nano < 0 {
|
|
return 0
|
|
}
|
|
return uint64(nano) // nolint:gosec // Overflow checked.
|
|
}
|
|
|
|
// AttrIter transforms an [attribute.Iterator] into OTLP key-values.
|
|
func AttrIter(iter attribute.Iterator) []*cpb.KeyValue {
|
|
l := iter.Len()
|
|
if l == 0 {
|
|
return nil
|
|
}
|
|
|
|
out := make([]*cpb.KeyValue, 0, l)
|
|
for iter.Next() {
|
|
out = append(out, Attr(iter.Attribute()))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Attrs transforms a slice of [attribute.KeyValue] into OTLP key-values.
|
|
func Attrs(attrs []attribute.KeyValue) []*cpb.KeyValue {
|
|
if len(attrs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
out := make([]*cpb.KeyValue, 0, len(attrs))
|
|
for _, kv := range attrs {
|
|
out = append(out, Attr(kv))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Attr transforms an [attribute.KeyValue] into an OTLP key-value.
|
|
func Attr(kv attribute.KeyValue) *cpb.KeyValue {
|
|
return &cpb.KeyValue{Key: string(kv.Key), Value: AttrValue(kv.Value)}
|
|
}
|
|
|
|
// AttrValue transforms an [attribute.Value] into an OTLP AnyValue.
|
|
func AttrValue(v attribute.Value) *cpb.AnyValue {
|
|
av := new(cpb.AnyValue)
|
|
switch v.Type() {
|
|
case attribute.BOOL:
|
|
av.Value = &cpb.AnyValue_BoolValue{
|
|
BoolValue: v.AsBool(),
|
|
}
|
|
case attribute.BOOLSLICE:
|
|
av.Value = &cpb.AnyValue_ArrayValue{
|
|
ArrayValue: &cpb.ArrayValue{
|
|
Values: boolSliceValues(v.AsBoolSlice()),
|
|
},
|
|
}
|
|
case attribute.INT64:
|
|
av.Value = &cpb.AnyValue_IntValue{
|
|
IntValue: v.AsInt64(),
|
|
}
|
|
case attribute.INT64SLICE:
|
|
av.Value = &cpb.AnyValue_ArrayValue{
|
|
ArrayValue: &cpb.ArrayValue{
|
|
Values: int64SliceValues(v.AsInt64Slice()),
|
|
},
|
|
}
|
|
case attribute.FLOAT64:
|
|
av.Value = &cpb.AnyValue_DoubleValue{
|
|
DoubleValue: v.AsFloat64(),
|
|
}
|
|
case attribute.FLOAT64SLICE:
|
|
av.Value = &cpb.AnyValue_ArrayValue{
|
|
ArrayValue: &cpb.ArrayValue{
|
|
Values: float64SliceValues(v.AsFloat64Slice()),
|
|
},
|
|
}
|
|
case attribute.STRING:
|
|
av.Value = &cpb.AnyValue_StringValue{
|
|
StringValue: v.AsString(),
|
|
}
|
|
case attribute.STRINGSLICE:
|
|
av.Value = &cpb.AnyValue_ArrayValue{
|
|
ArrayValue: &cpb.ArrayValue{
|
|
Values: stringSliceValues(v.AsStringSlice()),
|
|
},
|
|
}
|
|
default:
|
|
av.Value = &cpb.AnyValue_StringValue{
|
|
StringValue: "INVALID",
|
|
}
|
|
}
|
|
return av
|
|
}
|
|
|
|
func boolSliceValues(vals []bool) []*cpb.AnyValue {
|
|
converted := make([]*cpb.AnyValue, len(vals))
|
|
for i, v := range vals {
|
|
converted[i] = &cpb.AnyValue{
|
|
Value: &cpb.AnyValue_BoolValue{
|
|
BoolValue: v,
|
|
},
|
|
}
|
|
}
|
|
return converted
|
|
}
|
|
|
|
func int64SliceValues(vals []int64) []*cpb.AnyValue {
|
|
converted := make([]*cpb.AnyValue, len(vals))
|
|
for i, v := range vals {
|
|
converted[i] = &cpb.AnyValue{
|
|
Value: &cpb.AnyValue_IntValue{
|
|
IntValue: v,
|
|
},
|
|
}
|
|
}
|
|
return converted
|
|
}
|
|
|
|
func float64SliceValues(vals []float64) []*cpb.AnyValue {
|
|
converted := make([]*cpb.AnyValue, len(vals))
|
|
for i, v := range vals {
|
|
converted[i] = &cpb.AnyValue{
|
|
Value: &cpb.AnyValue_DoubleValue{
|
|
DoubleValue: v,
|
|
},
|
|
}
|
|
}
|
|
return converted
|
|
}
|
|
|
|
func stringSliceValues(vals []string) []*cpb.AnyValue {
|
|
converted := make([]*cpb.AnyValue, len(vals))
|
|
for i, v := range vals {
|
|
converted[i] = &cpb.AnyValue{
|
|
Value: &cpb.AnyValue_StringValue{
|
|
StringValue: v,
|
|
},
|
|
}
|
|
}
|
|
return converted
|
|
}
|
|
|
|
// Attrs transforms a slice of [api.KeyValue] into OTLP key-values.
|
|
func LogAttrs(attrs []api.KeyValue) []*cpb.KeyValue {
|
|
if len(attrs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
out := make([]*cpb.KeyValue, 0, len(attrs))
|
|
for _, kv := range attrs {
|
|
out = append(out, LogAttr(kv))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// LogAttr transforms an [api.KeyValue] into an OTLP key-value.
|
|
func LogAttr(attr api.KeyValue) *cpb.KeyValue {
|
|
return &cpb.KeyValue{
|
|
Key: attr.Key,
|
|
Value: LogAttrValue(attr.Value),
|
|
}
|
|
}
|
|
|
|
// LogAttrValues transforms a slice of [api.Value] into an OTLP []AnyValue.
|
|
func LogAttrValues(vals []api.Value) []*cpb.AnyValue {
|
|
if len(vals) == 0 {
|
|
return nil
|
|
}
|
|
|
|
out := make([]*cpb.AnyValue, 0, len(vals))
|
|
for _, v := range vals {
|
|
out = append(out, LogAttrValue(v))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// LogAttrValue transforms an [api.Value] into an OTLP AnyValue.
|
|
func LogAttrValue(v api.Value) *cpb.AnyValue {
|
|
av := new(cpb.AnyValue)
|
|
switch v.Kind() {
|
|
case api.KindBool:
|
|
av.Value = &cpb.AnyValue_BoolValue{
|
|
BoolValue: v.AsBool(),
|
|
}
|
|
case api.KindInt64:
|
|
av.Value = &cpb.AnyValue_IntValue{
|
|
IntValue: v.AsInt64(),
|
|
}
|
|
case api.KindFloat64:
|
|
av.Value = &cpb.AnyValue_DoubleValue{
|
|
DoubleValue: v.AsFloat64(),
|
|
}
|
|
case api.KindString:
|
|
av.Value = &cpb.AnyValue_StringValue{
|
|
StringValue: v.AsString(),
|
|
}
|
|
case api.KindBytes:
|
|
av.Value = &cpb.AnyValue_BytesValue{
|
|
BytesValue: v.AsBytes(),
|
|
}
|
|
case api.KindSlice:
|
|
av.Value = &cpb.AnyValue_ArrayValue{
|
|
ArrayValue: &cpb.ArrayValue{
|
|
Values: LogAttrValues(v.AsSlice()),
|
|
},
|
|
}
|
|
case api.KindMap:
|
|
av.Value = &cpb.AnyValue_KvlistValue{
|
|
KvlistValue: &cpb.KeyValueList{
|
|
Values: LogAttrs(v.AsMap()),
|
|
},
|
|
}
|
|
default:
|
|
av.Value = &cpb.AnyValue_StringValue{
|
|
StringValue: "INVALID",
|
|
}
|
|
}
|
|
return av
|
|
}
|
|
|
|
// SeverityNumber transforms a [log.Severity] into an OTLP SeverityNumber.
|
|
func SeverityNumber(s api.Severity) lpb.SeverityNumber {
|
|
switch s {
|
|
case api.SeverityTrace:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE
|
|
case api.SeverityTrace2:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE2
|
|
case api.SeverityTrace3:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE3
|
|
case api.SeverityTrace4:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_TRACE4
|
|
case api.SeverityDebug:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG
|
|
case api.SeverityDebug2:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG2
|
|
case api.SeverityDebug3:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG3
|
|
case api.SeverityDebug4:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_DEBUG4
|
|
case api.SeverityInfo:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO
|
|
case api.SeverityInfo2:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO2
|
|
case api.SeverityInfo3:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO3
|
|
case api.SeverityInfo4:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_INFO4
|
|
case api.SeverityWarn:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN
|
|
case api.SeverityWarn2:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN2
|
|
case api.SeverityWarn3:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN3
|
|
case api.SeverityWarn4:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_WARN4
|
|
case api.SeverityError:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
|
|
case api.SeverityError2:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR2
|
|
case api.SeverityError3:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR3
|
|
case api.SeverityError4:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_ERROR4
|
|
case api.SeverityFatal:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL
|
|
case api.SeverityFatal2:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL2
|
|
case api.SeverityFatal3:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL3
|
|
case api.SeverityFatal4:
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_FATAL4
|
|
}
|
|
return lpb.SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED
|
|
}
|