You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-11-25 22:41:46 +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:
@@ -6,7 +6,10 @@ require (
|
|||||||
github.com/cenkalti/backoff/v4 v4.3.0
|
github.com/cenkalti/backoff/v4 v4.3.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
go.opentelemetry.io/otel v1.27.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/sdk/log v0.3.0
|
||||||
|
go.opentelemetry.io/otel/trace v1.27.0
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1
|
go.opentelemetry.io/proto/otlp v1.3.1
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d
|
||||||
google.golang.org/grpc v1.64.0
|
google.golang.org/grpc v1.64.0
|
||||||
@@ -22,10 +25,7 @@ require (
|
|||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.12.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/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/net v0.26.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
golang.org/x/text v0.16.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.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/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.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/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
|
// Copyright The OpenTelemetry Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// 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
|
// Copyright The OpenTelemetry Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// 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
|
// Copyright The OpenTelemetry Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// 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
|
// Copyright The OpenTelemetry Authors
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// 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
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user