From e62787567cf7aeec18bfe3e7c77ffa1cd2e3a593 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 19 Apr 2022 08:53:43 +0900 Subject: [PATCH] [processor/transform] Add logs data model (#9271) * [processor/transform] Add logs data model * Fix and test instrumentation_scope * goporto --- .../transformprocessor/internal/logs/logs.go | 466 ++++++++++++ .../internal/logs/logs_test.go | 672 ++++++++++++++++++ 2 files changed, 1138 insertions(+) create mode 100644 processor/transformprocessor/internal/logs/logs.go create mode 100644 processor/transformprocessor/internal/logs/logs_test.go diff --git a/processor/transformprocessor/internal/logs/logs.go b/processor/transformprocessor/internal/logs/logs.go new file mode 100644 index 000000000000..b2293907466f --- /dev/null +++ b/processor/transformprocessor/internal/logs/logs.go @@ -0,0 +1,466 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logs // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/logs" + +import ( + "encoding/hex" + "fmt" + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common" +) + +type logTransformContext struct { + log plog.LogRecord + il pcommon.InstrumentationScope + resource pcommon.Resource +} + +func (ctx logTransformContext) GetItem() interface{} { + return ctx.log +} + +func (ctx logTransformContext) GetInstrumentationScope() pcommon.InstrumentationScope { + return ctx.il +} + +func (ctx logTransformContext) GetResource() pcommon.Resource { + return ctx.resource +} + +// pathGetSetter is a getSetter which has been resolved using a path expression provided by a user. +type pathGetSetter struct { + getter common.ExprFunc + setter func(ctx common.TransformContext, val interface{}) +} + +func (path pathGetSetter) Get(ctx common.TransformContext) interface{} { + return path.getter(ctx) +} + +func (path pathGetSetter) Set(ctx common.TransformContext, val interface{}) { + path.setter(ctx, val) +} + +func ParsePath(val *common.Path) (common.GetSetter, error) { + return newPathGetSetter(val.Fields) +} + +func newPathGetSetter(path []common.Field) (common.GetSetter, error) { + switch path[0].Name { + case "resource": + if len(path) == 1 { + return accessResource(), nil + } + switch path[1].Name { + case "attributes": + mapKey := path[1].MapKey + if mapKey == nil { + return accessResourceAttributes(), nil + } + return accessResourceAttributesKey(mapKey), nil + } + case "instrumentation_scope": + if len(path) == 1 { + return accessInstrumentationScope(), nil + } + switch path[1].Name { + case "name": + return accessInstrumentationScopeName(), nil + case "version": + return accessInstrumentationScopeVersion(), nil + } + case "time_unix_nano": + return accessTimeUnixNano(), nil + case "observed_time_unix_nano": + return accessObservedTimeUnixNano(), nil + case "severity_number": + return accessSeverityNumber(), nil + case "severity_text": + return accessSeverityText(), nil + case "body": + return accessBody(), nil + case "attributes": + mapKey := path[0].MapKey + if mapKey == nil { + return accessAttributes(), nil + } + return accessAttributesKey(mapKey), nil + case "dropped_attributes_count": + return accessDroppedAttributesCount(), nil + case "flags": + return accessFlags(), nil + case "trace_id": + return accessTraceID(), nil + case "span_id": + return accessSpanID(), nil + } + + return nil, fmt.Errorf("invalid path expression %v", path) +} + +func accessResource() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetResource() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if newRes, ok := val.(pcommon.Resource); ok { + ctx.GetResource().Attributes().Clear() + newRes.CopyTo(ctx.GetResource()) + } + }, + } +} + +func accessResourceAttributes() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetResource().Attributes() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if attrs, ok := val.(pcommon.Map); ok { + ctx.GetResource().Attributes().Clear() + attrs.CopyTo(ctx.GetResource().Attributes()) + } + }, + } +} + +func accessResourceAttributesKey(mapKey *string) pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return getAttr(ctx.GetResource().Attributes(), *mapKey) + }, + setter: func(ctx common.TransformContext, val interface{}) { + setAttr(ctx.GetResource().Attributes(), *mapKey, val) + }, + } +} + +func accessInstrumentationScope() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetInstrumentationScope() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if newIl, ok := val.(pcommon.InstrumentationScope); ok { + newIl.CopyTo(ctx.GetInstrumentationScope()) + } + }, + } +} + +func accessInstrumentationScopeName() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetInstrumentationScope().Name() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if str, ok := val.(string); ok { + ctx.GetInstrumentationScope().SetName(str) + } + }, + } +} + +func accessInstrumentationScopeVersion() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetInstrumentationScope().Version() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if str, ok := val.(string); ok { + ctx.GetInstrumentationScope().SetVersion(str) + } + }, + } +} + +func accessTimeUnixNano() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).Timestamp().AsTime().UnixNano() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if i, ok := val.(int64); ok { + ctx.GetItem().(plog.LogRecord).SetTimestamp(pcommon.NewTimestampFromTime(time.Unix(0, i))) + } + }, + } +} + +func accessObservedTimeUnixNano() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).ObservedTimestamp().AsTime().UnixNano() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if i, ok := val.(int64); ok { + ctx.GetItem().(plog.LogRecord).SetObservedTimestamp(pcommon.NewTimestampFromTime(time.Unix(0, i))) + } + }, + } +} + +func accessSeverityNumber() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).SeverityNumber() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if i, ok := val.(int64); ok { + ctx.GetItem().(plog.LogRecord).SetSeverityNumber(plog.SeverityNumber(i)) + } + }, + } +} + +func accessSeverityText() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).SeverityText() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if s, ok := val.(string); ok { + ctx.GetItem().(plog.LogRecord).SetSeverityText(s) + } + }, + } +} + +func accessBody() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return getValue(ctx.GetItem().(plog.LogRecord).Body()) + }, + setter: func(ctx common.TransformContext, val interface{}) { + setValue(ctx.GetItem().(plog.LogRecord).Body(), val) + }, + } +} + +func accessAttributes() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).Attributes() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if attrs, ok := val.(pcommon.Map); ok { + ctx.GetItem().(plog.LogRecord).Attributes().Clear() + attrs.CopyTo(ctx.GetItem().(plog.LogRecord).Attributes()) + } + }, + } +} + +func accessAttributesKey(mapKey *string) pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return getAttr(ctx.GetItem().(plog.LogRecord).Attributes(), *mapKey) + }, + setter: func(ctx common.TransformContext, val interface{}) { + setAttr(ctx.GetItem().(plog.LogRecord).Attributes(), *mapKey, val) + }, + } +} + +func accessDroppedAttributesCount() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).DroppedAttributesCount() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if i, ok := val.(int64); ok { + ctx.GetItem().(plog.LogRecord).SetDroppedAttributesCount(uint32(i)) + } + }, + } +} + +func accessFlags() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).Flags() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if i, ok := val.(int64); ok { + ctx.GetItem().(plog.LogRecord).SetFlags(uint32(i)) + } + }, + } +} + +func accessTraceID() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).TraceID() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if str, ok := val.(string); ok { + id, _ := hex.DecodeString(str) + var idArr [16]byte + copy(idArr[:16], id) + ctx.GetItem().(plog.LogRecord).SetTraceID(pcommon.NewTraceID(idArr)) + } + }, + } +} + +func accessSpanID() pathGetSetter { + return pathGetSetter{ + getter: func(ctx common.TransformContext) interface{} { + return ctx.GetItem().(plog.LogRecord).SpanID() + }, + setter: func(ctx common.TransformContext, val interface{}) { + if str, ok := val.(string); ok { + id, _ := hex.DecodeString(str) + var idArr [8]byte + copy(idArr[:8], id) + ctx.GetItem().(plog.LogRecord).SetSpanID(pcommon.NewSpanID(idArr)) + } + }, + } +} + +func getAttr(attrs pcommon.Map, mapKey string) interface{} { + val, ok := attrs.Get(mapKey) + if !ok { + return nil + } + return getValue(val) +} + +func getValue(val pcommon.Value) interface{} { + switch val.Type() { + case pcommon.ValueTypeString: + return val.StringVal() + case pcommon.ValueTypeBool: + return val.BoolVal() + case pcommon.ValueTypeInt: + return val.IntVal() + case pcommon.ValueTypeDouble: + return val.DoubleVal() + case pcommon.ValueTypeMap: + return val.MapVal() + case pcommon.ValueTypeSlice: + return val.SliceVal() + case pcommon.ValueTypeBytes: + return val.BytesVal() + } + return nil +} + +func setAttr(attrs pcommon.Map, mapKey string, val interface{}) { + switch v := val.(type) { + case string: + attrs.UpsertString(mapKey, v) + case bool: + attrs.UpsertBool(mapKey, v) + case int64: + attrs.UpsertInt(mapKey, v) + case float64: + attrs.UpsertDouble(mapKey, v) + case []byte: + attrs.UpsertBytes(mapKey, v) + case []string: + arr := pcommon.NewValueSlice() + for _, str := range v { + arr.SliceVal().AppendEmpty().SetStringVal(str) + } + attrs.Upsert(mapKey, arr) + case []bool: + arr := pcommon.NewValueSlice() + for _, b := range v { + arr.SliceVal().AppendEmpty().SetBoolVal(b) + } + attrs.Upsert(mapKey, arr) + case []int64: + arr := pcommon.NewValueSlice() + for _, i := range v { + arr.SliceVal().AppendEmpty().SetIntVal(i) + } + attrs.Upsert(mapKey, arr) + case []float64: + arr := pcommon.NewValueSlice() + for _, f := range v { + arr.SliceVal().AppendEmpty().SetDoubleVal(f) + } + attrs.Upsert(mapKey, arr) + case [][]byte: + arr := pcommon.NewValueSlice() + for _, b := range v { + arr.SliceVal().AppendEmpty().SetBytesVal(b) + } + attrs.Upsert(mapKey, arr) + default: + // TODO(anuraaga): Support set of map type. + } +} + +func setValue(value pcommon.Value, val interface{}) { + switch v := val.(type) { + case string: + value.SetStringVal(v) + case bool: + value.SetBoolVal(v) + case int64: + value.SetIntVal(v) + case float64: + value.SetDoubleVal(v) + case []byte: + value.SetBytesVal(v) + case []string: + value.SliceVal().RemoveIf(func(_ pcommon.Value) bool { + return true + }) + for _, str := range v { + value.SliceVal().AppendEmpty().SetStringVal(str) + } + case []bool: + value.SliceVal().RemoveIf(func(_ pcommon.Value) bool { + return true + }) + for _, b := range v { + value.SliceVal().AppendEmpty().SetBoolVal(b) + } + case []int64: + value.SliceVal().RemoveIf(func(_ pcommon.Value) bool { + return true + }) + for _, i := range v { + value.SliceVal().AppendEmpty().SetIntVal(i) + } + case []float64: + value.SliceVal().RemoveIf(func(_ pcommon.Value) bool { + return true + }) + for _, f := range v { + value.SliceVal().AppendEmpty().SetDoubleVal(f) + } + case [][]byte: + value.SliceVal().RemoveIf(func(_ pcommon.Value) bool { + return true + }) + for _, b := range v { + value.SliceVal().AppendEmpty().SetBytesVal(b) + } + default: + // TODO(anuraaga): Support set of map type. + } +} diff --git a/processor/transformprocessor/internal/logs/logs_test.go b/processor/transformprocessor/internal/logs/logs_test.go new file mode 100644 index 000000000000..ad7e2c75f52c --- /dev/null +++ b/processor/transformprocessor/internal/logs/logs_test.go @@ -0,0 +1,672 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logs + +import ( + "encoding/hex" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor/internal/common" +) + +var ( + traceID = [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + traceID2 = [16]byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + spanID = [8]byte{1, 2, 3, 4, 5, 6, 7, 8} + spanID2 = [8]byte{8, 7, 6, 5, 4, 3, 2, 1} +) + +func Test_newPathGetSetter(t *testing.T) { + refLog, _, _ := createTelemetry() + + newAttrs := pcommon.NewMap() + newAttrs.UpsertString("hello", "world") + + newArrStr := pcommon.NewValueSlice() + newArrStr.SliceVal().AppendEmpty().SetStringVal("new") + + newArrBool := pcommon.NewValueSlice() + newArrBool.SliceVal().AppendEmpty().SetBoolVal(false) + + newArrInt := pcommon.NewValueSlice() + newArrInt.SliceVal().AppendEmpty().SetIntVal(20) + + newArrFloat := pcommon.NewValueSlice() + newArrFloat.SliceVal().AppendEmpty().SetDoubleVal(2.0) + + newArrBytes := pcommon.NewValueSlice() + newArrBytes.SliceVal().AppendEmpty().SetBytesVal([]byte{9, 6, 4}) + + tests := []struct { + name string + path []common.Field + orig interface{} + new interface{} + modified func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) + }{ + { + name: "time_unix_nano", + path: []common.Field{ + { + Name: "time_unix_nano", + }, + }, + orig: int64(100_000_000), + new: int64(200_000_000), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) + }, + }, + { + name: "observed_time_unix_nano", + path: []common.Field{ + { + Name: "observed_time_unix_nano", + }, + }, + orig: int64(500_000_000), + new: int64(200_000_000), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetObservedTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(200))) + }, + }, + { + name: "severity_number", + path: []common.Field{ + { + Name: "severity_number", + }, + }, + orig: plog.SeverityNumberFATAL, + new: int64(3), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetSeverityNumber(plog.SeverityNumberTRACE3) + }, + }, + { + name: "severity_text", + path: []common.Field{ + { + Name: "severity_text", + }, + }, + orig: "blue screen of death", + new: "black screen of death", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetSeverityText("black screen of death") + }, + }, + { + name: "body", + path: []common.Field{ + { + Name: "body", + }, + }, + orig: "body", + new: "head", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Body().SetStringVal("head") + }, + }, + { + name: "flags", + path: []common.Field{ + { + Name: "flags", + }, + }, + orig: uint32(3), + new: int64(4), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetFlags(uint32(4)) + }, + }, + { + name: "trace_id", + path: []common.Field{ + { + Name: "trace_id", + }, + }, + orig: pcommon.NewTraceID(traceID), + new: hex.EncodeToString(traceID2[:]), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetTraceID(pcommon.NewTraceID(traceID2)) + }, + }, + { + name: "span_id", + path: []common.Field{ + { + Name: "span_id", + }, + }, + orig: pcommon.NewSpanID(spanID), + new: hex.EncodeToString(spanID2[:]), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetSpanID(pcommon.NewSpanID(spanID2)) + }, + }, + { + name: "attributes", + path: []common.Field{ + { + Name: "attributes", + }, + }, + orig: refLog.Attributes(), + new: newAttrs, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().Clear() + newAttrs.CopyTo(log.Attributes()) + }, + }, + { + name: "attributes string", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("str"), + }, + }, + orig: "val", + new: "newVal", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().UpsertString("str", "newVal") + }, + }, + { + name: "attributes bool", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("bool"), + }, + }, + orig: true, + new: false, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().UpsertBool("bool", false) + }, + }, + { + name: "attributes int", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("int"), + }, + }, + orig: int64(10), + new: int64(20), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().UpsertInt("int", 20) + }, + }, + { + name: "attributes float", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("double"), + }, + }, + orig: float64(1.2), + new: float64(2.4), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().UpsertDouble("double", 2.4) + }, + }, + { + name: "attributes bytes", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("bytes"), + }, + }, + orig: []byte{1, 3, 2}, + new: []byte{2, 3, 4}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().UpsertBytes("bytes", []byte{2, 3, 4}) + }, + }, + { + name: "attributes array string", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("arr_str"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_str") + return val.SliceVal() + }(), + new: []string{"new"}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().Upsert("arr_str", newArrStr) + }, + }, + { + name: "attributes array bool", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("arr_bool"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_bool") + return val.SliceVal() + }(), + new: []bool{false}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().Upsert("arr_bool", newArrBool) + }, + }, + { + name: "attributes array int", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("arr_int"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_int") + return val.SliceVal() + }(), + new: []int64{20}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().Upsert("arr_int", newArrInt) + }, + }, + { + name: "attributes array float", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("arr_float"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_float") + return val.SliceVal() + }(), + new: []float64{2.0}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().Upsert("arr_float", newArrFloat) + }, + }, + { + name: "attributes array bytes", + path: []common.Field{ + { + Name: "attributes", + MapKey: strp("arr_bytes"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_bytes") + return val.SliceVal() + }(), + new: [][]byte{{9, 6, 4}}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.Attributes().Upsert("arr_bytes", newArrBytes) + }, + }, + { + name: "dropped_attributes_count", + path: []common.Field{ + { + Name: "dropped_attributes_count", + }, + }, + orig: uint32(10), + new: int64(20), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + log.SetDroppedAttributesCount(20) + }, + }, + { + name: "instrumentation_scope name", + path: []common.Field{ + { + Name: "instrumentation_scope", + }, + { + Name: "name", + }, + }, + orig: "library", + new: "park", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + il.SetName("park") + }, + }, + { + name: "instrumentation_scope version", + path: []common.Field{ + { + Name: "instrumentation_scope", + }, + { + Name: "version", + }, + }, + orig: "version", + new: "next", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + il.SetVersion("next") + }, + }, + { + name: "resource attributes", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + }, + }, + orig: refLog.Attributes(), + new: newAttrs, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().Clear() + newAttrs.CopyTo(resource.Attributes()) + }, + }, + { + name: "resource attributes string", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("str"), + }, + }, + orig: "val", + new: "newVal", + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().UpsertString("str", "newVal") + }, + }, + { + name: "resource attributes bool", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("bool"), + }, + }, + orig: true, + new: false, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().UpsertBool("bool", false) + }, + }, + { + name: "resource attributes int", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("int"), + }, + }, + orig: int64(10), + new: int64(20), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().UpsertInt("int", 20) + }, + }, + { + name: "resource attributes float", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("double"), + }, + }, + orig: float64(1.2), + new: float64(2.4), + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().UpsertDouble("double", 2.4) + }, + }, + { + name: "resource attributes bytes", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("bytes"), + }, + }, + orig: []byte{1, 3, 2}, + new: []byte{2, 3, 4}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().UpsertBytes("bytes", []byte{2, 3, 4}) + }, + }, + { + name: "resource attributes array string", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("arr_str"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_str") + return val.SliceVal() + }(), + new: []string{"new"}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().Upsert("arr_str", newArrStr) + }, + }, + { + name: "resource attributes array bool", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("arr_bool"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_bool") + return val.SliceVal() + }(), + new: []bool{false}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().Upsert("arr_bool", newArrBool) + }, + }, + { + name: "resource attributes array int", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("arr_int"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_int") + return val.SliceVal() + }(), + new: []int64{20}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().Upsert("arr_int", newArrInt) + }, + }, + { + name: "resource attributes array float", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("arr_float"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_float") + return val.SliceVal() + }(), + new: []float64{2.0}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().Upsert("arr_float", newArrFloat) + }, + }, + { + name: "resource attributes array bytes", + path: []common.Field{ + { + Name: "resource", + }, + { + Name: "attributes", + MapKey: strp("arr_bytes"), + }, + }, + orig: func() pcommon.Slice { + val, _ := refLog.Attributes().Get("arr_bytes") + return val.SliceVal() + }(), + new: [][]byte{{9, 6, 4}}, + modified: func(log plog.LogRecord, il pcommon.InstrumentationScope, resource pcommon.Resource) { + resource.Attributes().Upsert("arr_bytes", newArrBytes) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + accessor, err := newPathGetSetter(tt.path) + assert.NoError(t, err) + + log, il, resource := createTelemetry() + + got := accessor.Get(logTransformContext{ + log: log, + il: il, + resource: resource, + }) + assert.Equal(t, tt.orig, got) + + accessor.Set(logTransformContext{ + log: log, + il: il, + resource: resource, + }, tt.new) + + exSpan, exIl, exRes := createTelemetry() + tt.modified(exSpan, exIl, exRes) + + assert.Equal(t, exSpan, log) + assert.Equal(t, exIl, il) + assert.Equal(t, exRes, resource) + }) + } +} + +func createTelemetry() (plog.LogRecord, pcommon.InstrumentationScope, pcommon.Resource) { + log := plog.NewLogRecord() + log.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(100))) + log.SetObservedTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(500))) + log.SetSeverityNumber(plog.SeverityNumberFATAL) + log.SetSeverityText("blue screen of death") + log.Body().SetStringVal("body") + log.Attributes().UpsertString("str", "val") + log.Attributes().UpsertBool("bool", true) + log.Attributes().UpsertInt("int", 10) + log.Attributes().UpsertDouble("double", 1.2) + log.Attributes().UpsertBytes("bytes", []byte{1, 3, 2}) + + arrStr := pcommon.NewValueSlice() + arrStr.SliceVal().AppendEmpty().SetStringVal("one") + arrStr.SliceVal().AppendEmpty().SetStringVal("two") + log.Attributes().Upsert("arr_str", arrStr) + + arrBool := pcommon.NewValueSlice() + arrBool.SliceVal().AppendEmpty().SetBoolVal(true) + arrBool.SliceVal().AppendEmpty().SetBoolVal(false) + log.Attributes().Upsert("arr_bool", arrBool) + + arrInt := pcommon.NewValueSlice() + arrInt.SliceVal().AppendEmpty().SetIntVal(2) + arrInt.SliceVal().AppendEmpty().SetIntVal(3) + log.Attributes().Upsert("arr_int", arrInt) + + arrFloat := pcommon.NewValueSlice() + arrFloat.SliceVal().AppendEmpty().SetDoubleVal(1.0) + arrFloat.SliceVal().AppendEmpty().SetDoubleVal(2.0) + log.Attributes().Upsert("arr_float", arrFloat) + + arrBytes := pcommon.NewValueSlice() + arrBytes.SliceVal().AppendEmpty().SetBytesVal([]byte{1, 2, 3}) + arrBytes.SliceVal().AppendEmpty().SetBytesVal([]byte{2, 3, 4}) + log.Attributes().Upsert("arr_bytes", arrBytes) + + log.SetDroppedAttributesCount(10) + + log.SetFlags(uint32(3)) + + log.SetTraceID(pcommon.NewTraceID(traceID)) + log.SetSpanID(pcommon.NewSpanID(spanID)) + + il := pcommon.NewInstrumentationScope() + il.SetName("library") + il.SetVersion("version") + + resource := pcommon.NewResource() + log.Attributes().CopyTo(resource.Attributes()) + + return log, il, resource +} + +func strp(s string) *string { + return &s +}