mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-01-16 02:47:20 +02:00
Generate internal/transform
in otlploggrpc
(#5553)
Part of #5056 It abstracts the `transform` package from `otlploghttp` and makes it a shared template. Then, it generates the abstracted `transform` package into `otlploggrpc`. For full usage of this transform package, check https://github.com/open-telemetry/opentelemetry-go/pull/5522
This commit is contained in:
parent
f3a2d96022
commit
82fe9aa1e3
@ -6,7 +6,10 @@ require (
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.opentelemetry.io/otel v1.27.0
|
||||
go.opentelemetry.io/otel/log v0.3.0
|
||||
go.opentelemetry.io/otel/sdk v1.27.0
|
||||
go.opentelemetry.io/otel/sdk/log v0.3.0
|
||||
go.opentelemetry.io/otel/trace v1.27.0
|
||||
go.opentelemetry.io/proto/otlp v1.3.1
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d
|
||||
google.golang.org/grpc v1.64.0
|
||||
@ -22,10 +25,7 @@ require (
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.3.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
|
@ -5,3 +5,8 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlp
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry.go.tmpl "--data={}" --out=retry/retry.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry_test.go.tmpl "--data={}" --out=retry/retry_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/attr_test.go.tmpl "--data={}" --out=transform/attr_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/log.go.tmpl "--data={}" --out=transform/log.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl "--data={}" --out=transform/log_attr_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/log_test.go.tmpl "--data={}" --out=transform/log_test.go
|
||||
|
@ -0,0 +1,186 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/attr_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
attrBool = attribute.Bool("bool", true)
|
||||
attrBoolSlice = attribute.BoolSlice("bool slice", []bool{true, false})
|
||||
attrInt = attribute.Int("int", 1)
|
||||
attrIntSlice = attribute.IntSlice("int slice", []int{-1, 1})
|
||||
attrInt64 = attribute.Int64("int64", 1)
|
||||
attrInt64Slice = attribute.Int64Slice("int64 slice", []int64{-1, 1})
|
||||
attrFloat64 = attribute.Float64("float64", 1)
|
||||
attrFloat64Slice = attribute.Float64Slice("float64 slice", []float64{-1, 1})
|
||||
attrString = attribute.String("string", "o")
|
||||
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
|
||||
attrInvalid = attribute.KeyValue{
|
||||
Key: attribute.Key("invalid"),
|
||||
Value: attribute.Value{},
|
||||
}
|
||||
|
||||
valBoolTrue = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: true}}
|
||||
valBoolFalse = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: false}}
|
||||
valBoolSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valBoolTrue, valBoolFalse},
|
||||
},
|
||||
}}
|
||||
valIntOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: 1}}
|
||||
valIntNOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: -1}}
|
||||
valIntSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valIntNOne, valIntOne},
|
||||
},
|
||||
}}
|
||||
valDblOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: 1}}
|
||||
valDblNOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: -1}}
|
||||
valDblSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
|
||||
},
|
||||
}}
|
||||
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
|
||||
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
|
||||
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valStrO, valStrN},
|
||||
},
|
||||
}}
|
||||
|
||||
kvBool = &cpb.KeyValue{Key: "bool", Value: valBoolTrue}
|
||||
kvBoolSlice = &cpb.KeyValue{Key: "bool slice", Value: valBoolSlice}
|
||||
kvInt = &cpb.KeyValue{Key: "int", Value: valIntOne}
|
||||
kvIntSlice = &cpb.KeyValue{Key: "int slice", Value: valIntSlice}
|
||||
kvInt64 = &cpb.KeyValue{Key: "int64", Value: valIntOne}
|
||||
kvInt64Slice = &cpb.KeyValue{Key: "int64 slice", Value: valIntSlice}
|
||||
kvFloat64 = &cpb.KeyValue{Key: "float64", Value: valDblOne}
|
||||
kvFloat64Slice = &cpb.KeyValue{Key: "float64 slice", Value: valDblSlice}
|
||||
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
|
||||
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
|
||||
kvInvalid = &cpb.KeyValue{
|
||||
Key: "invalid",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "INVALID"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestAttrTransforms(t *testing.T) {
|
||||
type attrTest struct {
|
||||
name string
|
||||
in []attribute.KeyValue
|
||||
want []*cpb.KeyValue
|
||||
}
|
||||
|
||||
for _, test := range []attrTest{
|
||||
{"nil", nil, nil},
|
||||
{"empty", []attribute.KeyValue{}, nil},
|
||||
{
|
||||
"invalid",
|
||||
[]attribute.KeyValue{attrInvalid},
|
||||
[]*cpb.KeyValue{kvInvalid},
|
||||
},
|
||||
{
|
||||
"bool",
|
||||
[]attribute.KeyValue{attrBool},
|
||||
[]*cpb.KeyValue{kvBool},
|
||||
},
|
||||
{
|
||||
"bool slice",
|
||||
[]attribute.KeyValue{attrBoolSlice},
|
||||
[]*cpb.KeyValue{kvBoolSlice},
|
||||
},
|
||||
{
|
||||
"int",
|
||||
[]attribute.KeyValue{attrInt},
|
||||
[]*cpb.KeyValue{kvInt},
|
||||
},
|
||||
{
|
||||
"int slice",
|
||||
[]attribute.KeyValue{attrIntSlice},
|
||||
[]*cpb.KeyValue{kvIntSlice},
|
||||
},
|
||||
{
|
||||
"int64",
|
||||
[]attribute.KeyValue{attrInt64},
|
||||
[]*cpb.KeyValue{kvInt64},
|
||||
},
|
||||
{
|
||||
"int64 slice",
|
||||
[]attribute.KeyValue{attrInt64Slice},
|
||||
[]*cpb.KeyValue{kvInt64Slice},
|
||||
},
|
||||
{
|
||||
"float64",
|
||||
[]attribute.KeyValue{attrFloat64},
|
||||
[]*cpb.KeyValue{kvFloat64},
|
||||
},
|
||||
{
|
||||
"float64 slice",
|
||||
[]attribute.KeyValue{attrFloat64Slice},
|
||||
[]*cpb.KeyValue{kvFloat64Slice},
|
||||
},
|
||||
{
|
||||
"string",
|
||||
[]attribute.KeyValue{attrString},
|
||||
[]*cpb.KeyValue{kvString},
|
||||
},
|
||||
{
|
||||
"string slice",
|
||||
[]attribute.KeyValue{attrStringSlice},
|
||||
[]*cpb.KeyValue{kvStringSlice},
|
||||
},
|
||||
{
|
||||
"all",
|
||||
[]attribute.KeyValue{
|
||||
attrBool,
|
||||
attrBoolSlice,
|
||||
attrInt,
|
||||
attrIntSlice,
|
||||
attrInt64,
|
||||
attrInt64Slice,
|
||||
attrFloat64,
|
||||
attrFloat64Slice,
|
||||
attrString,
|
||||
attrStringSlice,
|
||||
attrInvalid,
|
||||
},
|
||||
[]*cpb.KeyValue{
|
||||
kvBool,
|
||||
kvBoolSlice,
|
||||
kvInt,
|
||||
kvIntSlice,
|
||||
kvInt64,
|
||||
kvInt64Slice,
|
||||
kvFloat64,
|
||||
kvFloat64Slice,
|
||||
kvString,
|
||||
kvStringSlice,
|
||||
kvInvalid,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Run("Attrs", func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.want, Attrs(test.in))
|
||||
})
|
||||
t.Run("AttrIter", func(t *testing.T) {
|
||||
s := attribute.NewSet(test.in...)
|
||||
assert.ElementsMatch(t, test.want, AttrIter(s.Iter()))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
411
exporters/otlp/otlplog/otlploggrpc/internal/transform/log.go
Normal file
411
exporters/otlp/otlplog/otlploggrpc/internal/transform/log.go
Normal file
@ -0,0 +1,411 @@
|
||||
// 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/otlploggrpc/internal/transform"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"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 := resourceLogsMapPool.Get().(map[attribute.Distinct]*lpb.ResourceLogs)
|
||||
defer func() {
|
||||
clear(resMap)
|
||||
resourceLogsMapPool.Put(resMap)
|
||||
}()
|
||||
resourceLogsMap(&resMap, records)
|
||||
|
||||
out := make([]*lpb.ResourceLogs, 0, len(resMap))
|
||||
for _, rl := range resMap {
|
||||
out = append(out, rl)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var resourceLogsMapPool = sync.Pool{
|
||||
New: func() any {
|
||||
return make(map[attribute.Distinct]*lpb.ResourceLogs)
|
||||
},
|
||||
}
|
||||
|
||||
func resourceLogsMap(dst *map[attribute.Distinct]*lpb.ResourceLogs, records []log.Record) {
|
||||
for _, r := range records {
|
||||
res := r.Resource()
|
||||
rl, ok := (*dst)[res.Equivalent()]
|
||||
if !ok {
|
||||
rl = new(lpb.ResourceLogs)
|
||||
if res.Len() > 0 {
|
||||
rl.Resource = &rpb.Resource{
|
||||
Attributes: AttrIter(res.Iter()),
|
||||
}
|
||||
}
|
||||
rl.SchemaUrl = res.SchemaURL()
|
||||
(*dst)[res.Equivalent()] = rl
|
||||
}
|
||||
rl.ScopeLogs = ScopeLogs(records)
|
||||
}
|
||||
}
|
||||
|
||||
// ScopeLogs returns a slice of OTLP ScopeLogs generated from recoreds.
|
||||
func ScopeLogs(records []log.Record) []*lpb.ScopeLogs {
|
||||
scopeMap := scopeLogsMapPool.Get().(map[instrumentation.Scope]*lpb.ScopeLogs)
|
||||
defer func() {
|
||||
clear(scopeMap)
|
||||
scopeLogsMapPool.Put(scopeMap)
|
||||
}()
|
||||
scopeLogsMap(&scopeMap, records)
|
||||
|
||||
out := make([]*lpb.ScopeLogs, 0, len(scopeMap))
|
||||
for _, sl := range scopeMap {
|
||||
out = append(out, sl)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var scopeLogsMapPool = sync.Pool{
|
||||
New: func() any {
|
||||
return make(map[instrumentation.Scope]*lpb.ScopeLogs)
|
||||
},
|
||||
}
|
||||
|
||||
func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.Record) {
|
||||
for _, r := range records {
|
||||
scope := r.InstrumentationScope()
|
||||
sl, ok := (*dst)[scope]
|
||||
if !ok {
|
||||
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
|
||||
}
|
||||
(*dst)[scope] = sl
|
||||
}
|
||||
sl.LogRecords = append(sl.LogRecords, LogRecord(r))
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if t.IsZero() {
|
||||
return 0
|
||||
}
|
||||
return uint64(t.UnixNano())
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/log"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
logAttrBool = log.Bool("bool", true)
|
||||
logAttrInt = log.Int("int", 1)
|
||||
logAttrInt64 = log.Int64("int64", 1)
|
||||
logAttrFloat64 = log.Float64("float64", 1)
|
||||
logAttrString = log.String("string", "o")
|
||||
logAttrBytes = log.Bytes("bytes", []byte("test"))
|
||||
logAttrSlice = log.Slice("slice", log.BoolValue(true))
|
||||
logAttrMap = log.Map("map", logAttrString)
|
||||
logAttrEmpty = log.Empty("")
|
||||
|
||||
kvBytes = &cpb.KeyValue{
|
||||
Key: "bytes",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_BytesValue{
|
||||
BytesValue: []byte("test"),
|
||||
},
|
||||
},
|
||||
}
|
||||
kvSlice = &cpb.KeyValue{
|
||||
Key: "slice",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valBoolTrue},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kvMap = &cpb.KeyValue{
|
||||
Key: "map",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: []*cpb.KeyValue{kvString},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kvEmpty = &cpb.KeyValue{
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "INVALID"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestLogAttrs(t *testing.T) {
|
||||
type logAttrTest struct {
|
||||
name string
|
||||
in []log.KeyValue
|
||||
want []*cpb.KeyValue
|
||||
}
|
||||
|
||||
for _, test := range []logAttrTest{
|
||||
{"nil", nil, nil},
|
||||
{"len(0)", []log.KeyValue{}, nil},
|
||||
{
|
||||
"empty",
|
||||
[]log.KeyValue{logAttrEmpty},
|
||||
[]*cpb.KeyValue{kvEmpty},
|
||||
},
|
||||
{
|
||||
"bool",
|
||||
[]log.KeyValue{logAttrBool},
|
||||
[]*cpb.KeyValue{kvBool},
|
||||
},
|
||||
{
|
||||
"int",
|
||||
[]log.KeyValue{logAttrInt},
|
||||
[]*cpb.KeyValue{kvInt},
|
||||
},
|
||||
{
|
||||
"int64",
|
||||
[]log.KeyValue{logAttrInt64},
|
||||
[]*cpb.KeyValue{kvInt64},
|
||||
},
|
||||
{
|
||||
"float64",
|
||||
[]log.KeyValue{logAttrFloat64},
|
||||
[]*cpb.KeyValue{kvFloat64},
|
||||
},
|
||||
{
|
||||
"string",
|
||||
[]log.KeyValue{logAttrString},
|
||||
[]*cpb.KeyValue{kvString},
|
||||
},
|
||||
{
|
||||
"bytes",
|
||||
[]log.KeyValue{logAttrBytes},
|
||||
[]*cpb.KeyValue{kvBytes},
|
||||
},
|
||||
{
|
||||
"slice",
|
||||
[]log.KeyValue{logAttrSlice},
|
||||
[]*cpb.KeyValue{kvSlice},
|
||||
},
|
||||
{
|
||||
"map",
|
||||
[]log.KeyValue{logAttrMap},
|
||||
[]*cpb.KeyValue{kvMap},
|
||||
},
|
||||
{
|
||||
"all",
|
||||
[]log.KeyValue{
|
||||
logAttrBool,
|
||||
logAttrInt,
|
||||
logAttrInt64,
|
||||
logAttrFloat64,
|
||||
logAttrString,
|
||||
logAttrBytes,
|
||||
logAttrSlice,
|
||||
logAttrMap,
|
||||
logAttrEmpty,
|
||||
},
|
||||
[]*cpb.KeyValue{
|
||||
kvBool,
|
||||
kvInt,
|
||||
kvInt64,
|
||||
kvFloat64,
|
||||
kvString,
|
||||
kvBytes,
|
||||
kvSlice,
|
||||
kvMap,
|
||||
kvEmpty,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.want, LogAttrs(test.in))
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
lpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
|
||||
api "go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/log/logtest"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
// Sat Jan 01 2000 00:00:00 GMT+0000.
|
||||
ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0))
|
||||
obs = ts.Add(30 * time.Second)
|
||||
|
||||
alice = api.String("user", "alice")
|
||||
bob = api.String("user", "bob")
|
||||
|
||||
pbAlice = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "alice"},
|
||||
}}
|
||||
pbBob = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "bob"},
|
||||
}}
|
||||
|
||||
sevA = api.SeverityInfo
|
||||
sevB = api.SeverityError
|
||||
|
||||
pbSevA = lpb.SeverityNumber_SEVERITY_NUMBER_INFO
|
||||
pbSevB = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
|
||||
|
||||
bodyA = api.StringValue("a")
|
||||
bodyB = api.StringValue("b")
|
||||
|
||||
pbBodyA = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: "a",
|
||||
},
|
||||
}
|
||||
pbBodyB = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: "b",
|
||||
},
|
||||
}
|
||||
|
||||
spanIDA = []byte{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
spanIDB = []byte{0, 0, 0, 0, 0, 0, 0, 2}
|
||||
traceIDA = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
traceIDB = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
|
||||
flagsA = byte(1)
|
||||
flagsB = byte(0)
|
||||
|
||||
scope = instrumentation.Scope{
|
||||
Name: "test/code/path",
|
||||
Version: "v0.1.0",
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
}
|
||||
pbScope = &cpb.InstrumentationScope{
|
||||
Name: "test/code/path",
|
||||
Version: "v0.1.0",
|
||||
}
|
||||
|
||||
res = resource.NewWithAttributes(
|
||||
semconv.SchemaURL,
|
||||
semconv.ServiceName("test server"),
|
||||
semconv.ServiceVersion("v0.1.0"),
|
||||
)
|
||||
pbRes = &rpb.Resource{
|
||||
Attributes: []*cpb.KeyValue{
|
||||
{
|
||||
Key: "service.name",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "test server"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "service.version",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
records = func() []log.Record {
|
||||
var out []log.Record
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevA,
|
||||
SeverityText: "A",
|
||||
Body: bodyA,
|
||||
Attributes: []api.KeyValue{alice},
|
||||
TraceID: trace.TraceID(traceIDA),
|
||||
SpanID: trace.SpanID(spanIDA),
|
||||
TraceFlags: trace.TraceFlags(flagsA),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevA,
|
||||
SeverityText: "A",
|
||||
Body: bodyA,
|
||||
Attributes: []api.KeyValue{bob},
|
||||
TraceID: trace.TraceID(traceIDA),
|
||||
SpanID: trace.SpanID(spanIDA),
|
||||
TraceFlags: trace.TraceFlags(flagsA),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevB,
|
||||
SeverityText: "B",
|
||||
Body: bodyB,
|
||||
Attributes: []api.KeyValue{alice},
|
||||
TraceID: trace.TraceID(traceIDB),
|
||||
SpanID: trace.SpanID(spanIDB),
|
||||
TraceFlags: trace.TraceFlags(flagsB),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevB,
|
||||
SeverityText: "B",
|
||||
Body: bodyB,
|
||||
Attributes: []api.KeyValue{bob},
|
||||
TraceID: trace.TraceID(traceIDB),
|
||||
SpanID: trace.SpanID(spanIDB),
|
||||
TraceFlags: trace.TraceFlags(flagsB),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
return out
|
||||
}()
|
||||
|
||||
pbLogRecords = []*lpb.LogRecord{
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevA,
|
||||
SeverityText: "A",
|
||||
Body: pbBodyA,
|
||||
Attributes: []*cpb.KeyValue{pbAlice},
|
||||
Flags: uint32(flagsA),
|
||||
TraceId: traceIDA,
|
||||
SpanId: spanIDA,
|
||||
},
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevA,
|
||||
SeverityText: "A",
|
||||
Body: pbBodyA,
|
||||
Attributes: []*cpb.KeyValue{pbBob},
|
||||
Flags: uint32(flagsA),
|
||||
TraceId: traceIDA,
|
||||
SpanId: spanIDA,
|
||||
},
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevB,
|
||||
SeverityText: "B",
|
||||
Body: pbBodyB,
|
||||
Attributes: []*cpb.KeyValue{pbAlice},
|
||||
Flags: uint32(flagsB),
|
||||
TraceId: traceIDB,
|
||||
SpanId: spanIDB,
|
||||
},
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevB,
|
||||
SeverityText: "B",
|
||||
Body: pbBodyB,
|
||||
Attributes: []*cpb.KeyValue{pbBob},
|
||||
Flags: uint32(flagsB),
|
||||
TraceId: traceIDB,
|
||||
SpanId: spanIDB,
|
||||
},
|
||||
}
|
||||
|
||||
pbScopeLogs = &lpb.ScopeLogs{
|
||||
Scope: pbScope,
|
||||
SchemaUrl: semconv.SchemaURL,
|
||||
LogRecords: pbLogRecords,
|
||||
}
|
||||
|
||||
pbResourceLogs = &lpb.ResourceLogs{
|
||||
Resource: pbRes,
|
||||
SchemaUrl: semconv.SchemaURL,
|
||||
ScopeLogs: []*lpb.ScopeLogs{pbScopeLogs},
|
||||
}
|
||||
)
|
||||
|
||||
func TestResourceLogs(t *testing.T) {
|
||||
want := []*lpb.ResourceLogs{pbResourceLogs}
|
||||
assert.Equal(t, want, ResourceLogs(records))
|
||||
}
|
||||
|
||||
func TestSeverityNumber(t *testing.T) {
|
||||
for i := 0; i <= int(api.SeverityFatal4); i++ {
|
||||
want := lpb.SeverityNumber(i)
|
||||
want += lpb.SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED
|
||||
assert.Equal(t, want, SeverityNumber(api.Severity(i)))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkResourceLogs(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
var out []*lpb.ResourceLogs
|
||||
for pb.Next() {
|
||||
out = ResourceLogs(records)
|
||||
}
|
||||
_ = out
|
||||
})
|
||||
}
|
@ -5,3 +5,8 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlp
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry.go.tmpl "--data={}" --out=retry/retry.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/retry/retry_test.go.tmpl "--data={}" --out=retry/retry_test.go
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/attr_test.go.tmpl "--data={}" --out=transform/attr_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/log.go.tmpl "--data={}" --out=transform/log.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl "--data={}" --out=transform/log_attr_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlplog/transform/log_test.go.tmpl "--data={}" --out=transform/log_test.go
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/attr_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
186
internal/shared/otlp/otlplog/transform/attr_test.go.tmpl
Normal file
186
internal/shared/otlp/otlplog/transform/attr_test.go.tmpl
Normal file
@ -0,0 +1,186 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/attr_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
attrBool = attribute.Bool("bool", true)
|
||||
attrBoolSlice = attribute.BoolSlice("bool slice", []bool{true, false})
|
||||
attrInt = attribute.Int("int", 1)
|
||||
attrIntSlice = attribute.IntSlice("int slice", []int{-1, 1})
|
||||
attrInt64 = attribute.Int64("int64", 1)
|
||||
attrInt64Slice = attribute.Int64Slice("int64 slice", []int64{-1, 1})
|
||||
attrFloat64 = attribute.Float64("float64", 1)
|
||||
attrFloat64Slice = attribute.Float64Slice("float64 slice", []float64{-1, 1})
|
||||
attrString = attribute.String("string", "o")
|
||||
attrStringSlice = attribute.StringSlice("string slice", []string{"o", "n"})
|
||||
attrInvalid = attribute.KeyValue{
|
||||
Key: attribute.Key("invalid"),
|
||||
Value: attribute.Value{},
|
||||
}
|
||||
|
||||
valBoolTrue = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: true}}
|
||||
valBoolFalse = &cpb.AnyValue{Value: &cpb.AnyValue_BoolValue{BoolValue: false}}
|
||||
valBoolSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valBoolTrue, valBoolFalse},
|
||||
},
|
||||
}}
|
||||
valIntOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: 1}}
|
||||
valIntNOne = &cpb.AnyValue{Value: &cpb.AnyValue_IntValue{IntValue: -1}}
|
||||
valIntSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valIntNOne, valIntOne},
|
||||
},
|
||||
}}
|
||||
valDblOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: 1}}
|
||||
valDblNOne = &cpb.AnyValue{Value: &cpb.AnyValue_DoubleValue{DoubleValue: -1}}
|
||||
valDblSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valDblNOne, valDblOne},
|
||||
},
|
||||
}}
|
||||
valStrO = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "o"}}
|
||||
valStrN = &cpb.AnyValue{Value: &cpb.AnyValue_StringValue{StringValue: "n"}}
|
||||
valStrSlice = &cpb.AnyValue{Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valStrO, valStrN},
|
||||
},
|
||||
}}
|
||||
|
||||
kvBool = &cpb.KeyValue{Key: "bool", Value: valBoolTrue}
|
||||
kvBoolSlice = &cpb.KeyValue{Key: "bool slice", Value: valBoolSlice}
|
||||
kvInt = &cpb.KeyValue{Key: "int", Value: valIntOne}
|
||||
kvIntSlice = &cpb.KeyValue{Key: "int slice", Value: valIntSlice}
|
||||
kvInt64 = &cpb.KeyValue{Key: "int64", Value: valIntOne}
|
||||
kvInt64Slice = &cpb.KeyValue{Key: "int64 slice", Value: valIntSlice}
|
||||
kvFloat64 = &cpb.KeyValue{Key: "float64", Value: valDblOne}
|
||||
kvFloat64Slice = &cpb.KeyValue{Key: "float64 slice", Value: valDblSlice}
|
||||
kvString = &cpb.KeyValue{Key: "string", Value: valStrO}
|
||||
kvStringSlice = &cpb.KeyValue{Key: "string slice", Value: valStrSlice}
|
||||
kvInvalid = &cpb.KeyValue{
|
||||
Key: "invalid",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "INVALID"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestAttrTransforms(t *testing.T) {
|
||||
type attrTest struct {
|
||||
name string
|
||||
in []attribute.KeyValue
|
||||
want []*cpb.KeyValue
|
||||
}
|
||||
|
||||
for _, test := range []attrTest{
|
||||
{"nil", nil, nil},
|
||||
{"empty", []attribute.KeyValue{}, nil},
|
||||
{
|
||||
"invalid",
|
||||
[]attribute.KeyValue{attrInvalid},
|
||||
[]*cpb.KeyValue{kvInvalid},
|
||||
},
|
||||
{
|
||||
"bool",
|
||||
[]attribute.KeyValue{attrBool},
|
||||
[]*cpb.KeyValue{kvBool},
|
||||
},
|
||||
{
|
||||
"bool slice",
|
||||
[]attribute.KeyValue{attrBoolSlice},
|
||||
[]*cpb.KeyValue{kvBoolSlice},
|
||||
},
|
||||
{
|
||||
"int",
|
||||
[]attribute.KeyValue{attrInt},
|
||||
[]*cpb.KeyValue{kvInt},
|
||||
},
|
||||
{
|
||||
"int slice",
|
||||
[]attribute.KeyValue{attrIntSlice},
|
||||
[]*cpb.KeyValue{kvIntSlice},
|
||||
},
|
||||
{
|
||||
"int64",
|
||||
[]attribute.KeyValue{attrInt64},
|
||||
[]*cpb.KeyValue{kvInt64},
|
||||
},
|
||||
{
|
||||
"int64 slice",
|
||||
[]attribute.KeyValue{attrInt64Slice},
|
||||
[]*cpb.KeyValue{kvInt64Slice},
|
||||
},
|
||||
{
|
||||
"float64",
|
||||
[]attribute.KeyValue{attrFloat64},
|
||||
[]*cpb.KeyValue{kvFloat64},
|
||||
},
|
||||
{
|
||||
"float64 slice",
|
||||
[]attribute.KeyValue{attrFloat64Slice},
|
||||
[]*cpb.KeyValue{kvFloat64Slice},
|
||||
},
|
||||
{
|
||||
"string",
|
||||
[]attribute.KeyValue{attrString},
|
||||
[]*cpb.KeyValue{kvString},
|
||||
},
|
||||
{
|
||||
"string slice",
|
||||
[]attribute.KeyValue{attrStringSlice},
|
||||
[]*cpb.KeyValue{kvStringSlice},
|
||||
},
|
||||
{
|
||||
"all",
|
||||
[]attribute.KeyValue{
|
||||
attrBool,
|
||||
attrBoolSlice,
|
||||
attrInt,
|
||||
attrIntSlice,
|
||||
attrInt64,
|
||||
attrInt64Slice,
|
||||
attrFloat64,
|
||||
attrFloat64Slice,
|
||||
attrString,
|
||||
attrStringSlice,
|
||||
attrInvalid,
|
||||
},
|
||||
[]*cpb.KeyValue{
|
||||
kvBool,
|
||||
kvBoolSlice,
|
||||
kvInt,
|
||||
kvIntSlice,
|
||||
kvInt64,
|
||||
kvInt64Slice,
|
||||
kvFloat64,
|
||||
kvFloat64Slice,
|
||||
kvString,
|
||||
kvStringSlice,
|
||||
kvInvalid,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Run("Attrs", func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.want, Attrs(test.in))
|
||||
})
|
||||
t.Run("AttrIter", func(t *testing.T) {
|
||||
s := attribute.NewSet(test.in...)
|
||||
assert.ElementsMatch(t, test.want, AttrIter(s.Iter()))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
411
internal/shared/otlp/otlplog/transform/log.go.tmpl
Normal file
411
internal/shared/otlp/otlplog/transform/log.go.tmpl
Normal file
@ -0,0 +1,411 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
"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 := resourceLogsMapPool.Get().(map[attribute.Distinct]*lpb.ResourceLogs)
|
||||
defer func() {
|
||||
clear(resMap)
|
||||
resourceLogsMapPool.Put(resMap)
|
||||
}()
|
||||
resourceLogsMap(&resMap, records)
|
||||
|
||||
out := make([]*lpb.ResourceLogs, 0, len(resMap))
|
||||
for _, rl := range resMap {
|
||||
out = append(out, rl)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var resourceLogsMapPool = sync.Pool{
|
||||
New: func() any {
|
||||
return make(map[attribute.Distinct]*lpb.ResourceLogs)
|
||||
},
|
||||
}
|
||||
|
||||
func resourceLogsMap(dst *map[attribute.Distinct]*lpb.ResourceLogs, records []log.Record) {
|
||||
for _, r := range records {
|
||||
res := r.Resource()
|
||||
rl, ok := (*dst)[res.Equivalent()]
|
||||
if !ok {
|
||||
rl = new(lpb.ResourceLogs)
|
||||
if res.Len() > 0 {
|
||||
rl.Resource = &rpb.Resource{
|
||||
Attributes: AttrIter(res.Iter()),
|
||||
}
|
||||
}
|
||||
rl.SchemaUrl = res.SchemaURL()
|
||||
(*dst)[res.Equivalent()] = rl
|
||||
}
|
||||
rl.ScopeLogs = ScopeLogs(records)
|
||||
}
|
||||
}
|
||||
|
||||
// ScopeLogs returns a slice of OTLP ScopeLogs generated from recoreds.
|
||||
func ScopeLogs(records []log.Record) []*lpb.ScopeLogs {
|
||||
scopeMap := scopeLogsMapPool.Get().(map[instrumentation.Scope]*lpb.ScopeLogs)
|
||||
defer func() {
|
||||
clear(scopeMap)
|
||||
scopeLogsMapPool.Put(scopeMap)
|
||||
}()
|
||||
scopeLogsMap(&scopeMap, records)
|
||||
|
||||
out := make([]*lpb.ScopeLogs, 0, len(scopeMap))
|
||||
for _, sl := range scopeMap {
|
||||
out = append(out, sl)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var scopeLogsMapPool = sync.Pool{
|
||||
New: func() any {
|
||||
return make(map[instrumentation.Scope]*lpb.ScopeLogs)
|
||||
},
|
||||
}
|
||||
|
||||
func scopeLogsMap(dst *map[instrumentation.Scope]*lpb.ScopeLogs, records []log.Record) {
|
||||
for _, r := range records {
|
||||
scope := r.InstrumentationScope()
|
||||
sl, ok := (*dst)[scope]
|
||||
if !ok {
|
||||
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
|
||||
}
|
||||
(*dst)[scope] = sl
|
||||
}
|
||||
sl.LogRecords = append(sl.LogRecords, LogRecord(r))
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if t.IsZero() {
|
||||
return 0
|
||||
}
|
||||
return uint64(t.UnixNano())
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
149
internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl
Normal file
149
internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl
Normal file
@ -0,0 +1,149 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log_attr_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"go.opentelemetry.io/otel/log"
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
logAttrBool = log.Bool("bool", true)
|
||||
logAttrInt = log.Int("int", 1)
|
||||
logAttrInt64 = log.Int64("int64", 1)
|
||||
logAttrFloat64 = log.Float64("float64", 1)
|
||||
logAttrString = log.String("string", "o")
|
||||
logAttrBytes = log.Bytes("bytes", []byte("test"))
|
||||
logAttrSlice = log.Slice("slice", log.BoolValue(true))
|
||||
logAttrMap = log.Map("map", logAttrString)
|
||||
logAttrEmpty = log.Empty("")
|
||||
|
||||
kvBytes = &cpb.KeyValue{
|
||||
Key: "bytes",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_BytesValue{
|
||||
BytesValue: []byte("test"),
|
||||
},
|
||||
},
|
||||
}
|
||||
kvSlice = &cpb.KeyValue{
|
||||
Key: "slice",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &cpb.ArrayValue{
|
||||
Values: []*cpb.AnyValue{valBoolTrue},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kvMap = &cpb.KeyValue{
|
||||
Key: "map",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_KvlistValue{
|
||||
KvlistValue: &cpb.KeyValueList{
|
||||
Values: []*cpb.KeyValue{kvString},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kvEmpty = &cpb.KeyValue{
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "INVALID"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestLogAttrs(t *testing.T) {
|
||||
type logAttrTest struct {
|
||||
name string
|
||||
in []log.KeyValue
|
||||
want []*cpb.KeyValue
|
||||
}
|
||||
|
||||
for _, test := range []logAttrTest{
|
||||
{"nil", nil, nil},
|
||||
{"len(0)", []log.KeyValue{}, nil},
|
||||
{
|
||||
"empty",
|
||||
[]log.KeyValue{logAttrEmpty},
|
||||
[]*cpb.KeyValue{kvEmpty},
|
||||
},
|
||||
{
|
||||
"bool",
|
||||
[]log.KeyValue{logAttrBool},
|
||||
[]*cpb.KeyValue{kvBool},
|
||||
},
|
||||
{
|
||||
"int",
|
||||
[]log.KeyValue{logAttrInt},
|
||||
[]*cpb.KeyValue{kvInt},
|
||||
},
|
||||
{
|
||||
"int64",
|
||||
[]log.KeyValue{logAttrInt64},
|
||||
[]*cpb.KeyValue{kvInt64},
|
||||
},
|
||||
{
|
||||
"float64",
|
||||
[]log.KeyValue{logAttrFloat64},
|
||||
[]*cpb.KeyValue{kvFloat64},
|
||||
},
|
||||
{
|
||||
"string",
|
||||
[]log.KeyValue{logAttrString},
|
||||
[]*cpb.KeyValue{kvString},
|
||||
},
|
||||
{
|
||||
"bytes",
|
||||
[]log.KeyValue{logAttrBytes},
|
||||
[]*cpb.KeyValue{kvBytes},
|
||||
},
|
||||
{
|
||||
"slice",
|
||||
[]log.KeyValue{logAttrSlice},
|
||||
[]*cpb.KeyValue{kvSlice},
|
||||
},
|
||||
{
|
||||
"map",
|
||||
[]log.KeyValue{logAttrMap},
|
||||
[]*cpb.KeyValue{kvMap},
|
||||
},
|
||||
{
|
||||
"all",
|
||||
[]log.KeyValue{
|
||||
logAttrBool,
|
||||
logAttrInt,
|
||||
logAttrInt64,
|
||||
logAttrFloat64,
|
||||
logAttrString,
|
||||
logAttrBytes,
|
||||
logAttrSlice,
|
||||
logAttrMap,
|
||||
logAttrEmpty,
|
||||
},
|
||||
[]*cpb.KeyValue{
|
||||
kvBool,
|
||||
kvInt,
|
||||
kvInt64,
|
||||
kvFloat64,
|
||||
kvString,
|
||||
kvBytes,
|
||||
kvSlice,
|
||||
kvMap,
|
||||
kvEmpty,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.ElementsMatch(t, test.want, LogAttrs(test.in))
|
||||
})
|
||||
}
|
||||
}
|
246
internal/shared/otlp/otlplog/transform/log_test.go.tmpl
Normal file
246
internal/shared/otlp/otlplog/transform/log_test.go.tmpl
Normal file
@ -0,0 +1,246 @@
|
||||
// Code created by gotmpl. DO NOT MODIFY.
|
||||
// source: internal/shared/otlp/otlplog/transform/log_test.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package transform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
cpb "go.opentelemetry.io/proto/otlp/common/v1"
|
||||
lpb "go.opentelemetry.io/proto/otlp/logs/v1"
|
||||
rpb "go.opentelemetry.io/proto/otlp/resource/v1"
|
||||
|
||||
api "go.opentelemetry.io/otel/log"
|
||||
"go.opentelemetry.io/otel/sdk/instrumentation"
|
||||
"go.opentelemetry.io/otel/sdk/log"
|
||||
"go.opentelemetry.io/otel/sdk/log/logtest"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
// Sat Jan 01 2000 00:00:00 GMT+0000.
|
||||
ts = time.Date(2000, time.January, 0o1, 0, 0, 0, 0, time.FixedZone("GMT", 0))
|
||||
obs = ts.Add(30 * time.Second)
|
||||
|
||||
alice = api.String("user", "alice")
|
||||
bob = api.String("user", "bob")
|
||||
|
||||
pbAlice = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "alice"},
|
||||
}}
|
||||
pbBob = &cpb.KeyValue{Key: "user", Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "bob"},
|
||||
}}
|
||||
|
||||
sevA = api.SeverityInfo
|
||||
sevB = api.SeverityError
|
||||
|
||||
pbSevA = lpb.SeverityNumber_SEVERITY_NUMBER_INFO
|
||||
pbSevB = lpb.SeverityNumber_SEVERITY_NUMBER_ERROR
|
||||
|
||||
bodyA = api.StringValue("a")
|
||||
bodyB = api.StringValue("b")
|
||||
|
||||
pbBodyA = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: "a",
|
||||
},
|
||||
}
|
||||
pbBodyB = &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{
|
||||
StringValue: "b",
|
||||
},
|
||||
}
|
||||
|
||||
spanIDA = []byte{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
spanIDB = []byte{0, 0, 0, 0, 0, 0, 0, 2}
|
||||
traceIDA = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
traceIDB = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}
|
||||
flagsA = byte(1)
|
||||
flagsB = byte(0)
|
||||
|
||||
scope = instrumentation.Scope{
|
||||
Name: "test/code/path",
|
||||
Version: "v0.1.0",
|
||||
SchemaURL: semconv.SchemaURL,
|
||||
}
|
||||
pbScope = &cpb.InstrumentationScope{
|
||||
Name: "test/code/path",
|
||||
Version: "v0.1.0",
|
||||
}
|
||||
|
||||
res = resource.NewWithAttributes(
|
||||
semconv.SchemaURL,
|
||||
semconv.ServiceName("test server"),
|
||||
semconv.ServiceVersion("v0.1.0"),
|
||||
)
|
||||
pbRes = &rpb.Resource{
|
||||
Attributes: []*cpb.KeyValue{
|
||||
{
|
||||
Key: "service.name",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "test server"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: "service.version",
|
||||
Value: &cpb.AnyValue{
|
||||
Value: &cpb.AnyValue_StringValue{StringValue: "v0.1.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
records = func() []log.Record {
|
||||
var out []log.Record
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevA,
|
||||
SeverityText: "A",
|
||||
Body: bodyA,
|
||||
Attributes: []api.KeyValue{alice},
|
||||
TraceID: trace.TraceID(traceIDA),
|
||||
SpanID: trace.SpanID(spanIDA),
|
||||
TraceFlags: trace.TraceFlags(flagsA),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevA,
|
||||
SeverityText: "A",
|
||||
Body: bodyA,
|
||||
Attributes: []api.KeyValue{bob},
|
||||
TraceID: trace.TraceID(traceIDA),
|
||||
SpanID: trace.SpanID(spanIDA),
|
||||
TraceFlags: trace.TraceFlags(flagsA),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevB,
|
||||
SeverityText: "B",
|
||||
Body: bodyB,
|
||||
Attributes: []api.KeyValue{alice},
|
||||
TraceID: trace.TraceID(traceIDB),
|
||||
SpanID: trace.SpanID(spanIDB),
|
||||
TraceFlags: trace.TraceFlags(flagsB),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
out = append(out, logtest.RecordFactory{
|
||||
Timestamp: ts,
|
||||
ObservedTimestamp: obs,
|
||||
Severity: sevB,
|
||||
SeverityText: "B",
|
||||
Body: bodyB,
|
||||
Attributes: []api.KeyValue{bob},
|
||||
TraceID: trace.TraceID(traceIDB),
|
||||
SpanID: trace.SpanID(spanIDB),
|
||||
TraceFlags: trace.TraceFlags(flagsB),
|
||||
InstrumentationScope: &scope,
|
||||
Resource: res,
|
||||
}.NewRecord())
|
||||
|
||||
return out
|
||||
}()
|
||||
|
||||
pbLogRecords = []*lpb.LogRecord{
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevA,
|
||||
SeverityText: "A",
|
||||
Body: pbBodyA,
|
||||
Attributes: []*cpb.KeyValue{pbAlice},
|
||||
Flags: uint32(flagsA),
|
||||
TraceId: traceIDA,
|
||||
SpanId: spanIDA,
|
||||
},
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevA,
|
||||
SeverityText: "A",
|
||||
Body: pbBodyA,
|
||||
Attributes: []*cpb.KeyValue{pbBob},
|
||||
Flags: uint32(flagsA),
|
||||
TraceId: traceIDA,
|
||||
SpanId: spanIDA,
|
||||
},
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevB,
|
||||
SeverityText: "B",
|
||||
Body: pbBodyB,
|
||||
Attributes: []*cpb.KeyValue{pbAlice},
|
||||
Flags: uint32(flagsB),
|
||||
TraceId: traceIDB,
|
||||
SpanId: spanIDB,
|
||||
},
|
||||
{
|
||||
TimeUnixNano: uint64(ts.UnixNano()),
|
||||
ObservedTimeUnixNano: uint64(obs.UnixNano()),
|
||||
SeverityNumber: pbSevB,
|
||||
SeverityText: "B",
|
||||
Body: pbBodyB,
|
||||
Attributes: []*cpb.KeyValue{pbBob},
|
||||
Flags: uint32(flagsB),
|
||||
TraceId: traceIDB,
|
||||
SpanId: spanIDB,
|
||||
},
|
||||
}
|
||||
|
||||
pbScopeLogs = &lpb.ScopeLogs{
|
||||
Scope: pbScope,
|
||||
SchemaUrl: semconv.SchemaURL,
|
||||
LogRecords: pbLogRecords,
|
||||
}
|
||||
|
||||
pbResourceLogs = &lpb.ResourceLogs{
|
||||
Resource: pbRes,
|
||||
SchemaUrl: semconv.SchemaURL,
|
||||
ScopeLogs: []*lpb.ScopeLogs{pbScopeLogs},
|
||||
}
|
||||
)
|
||||
|
||||
func TestResourceLogs(t *testing.T) {
|
||||
want := []*lpb.ResourceLogs{pbResourceLogs}
|
||||
assert.Equal(t, want, ResourceLogs(records))
|
||||
}
|
||||
|
||||
func TestSeverityNumber(t *testing.T) {
|
||||
for i := 0; i <= int(api.SeverityFatal4); i++ {
|
||||
want := lpb.SeverityNumber(i)
|
||||
want += lpb.SeverityNumber_SEVERITY_NUMBER_UNSPECIFIED
|
||||
assert.Equal(t, want, SeverityNumber(api.Severity(i)))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkResourceLogs(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
var out []*lpb.ResourceLogs
|
||||
for pb.Next() {
|
||||
out = ResourceLogs(records)
|
||||
}
|
||||
_ = out
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user