diff --git a/factories/processors.go b/factories/processors.go index 4fb43a0f5..cde11368c 100644 --- a/factories/processors.go +++ b/factories/processors.go @@ -27,6 +27,7 @@ import ( "github.com/observiq/bindplane-agent/processor/samplingprocessor" "github.com/observiq/bindplane-agent/processor/spancountprocessor" "github.com/observiq/bindplane-agent/processor/throughputmeasurementprocessor" + "github.com/observiq/bindplane-agent/processor/unrollprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributesprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatorateprocessor" @@ -87,4 +88,5 @@ var defaultProcessors = []processor.Factory{ throughputmeasurementprocessor.NewFactory(), tailsamplingprocessor.NewFactory(), transformprocessor.NewFactory(), + unrollprocessor.NewFactory(), } diff --git a/go.mod b/go.mod index 7905e3dc2..ccc92e103 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/observiq/bindplane-agent/processor/samplingprocessor v1.66.0 github.com/observiq/bindplane-agent/processor/spancountprocessor v1.66.0 github.com/observiq/bindplane-agent/processor/throughputmeasurementprocessor v1.66.0 + github.com/observiq/bindplane-agent/processor/unrollprocessor v1.66.0 github.com/observiq/bindplane-agent/receiver/awss3rehydrationreceiver v1.66.0 github.com/observiq/bindplane-agent/receiver/azureblobrehydrationreceiver v1.66.0 github.com/observiq/bindplane-agent/receiver/httpreceiver v1.66.0 @@ -853,6 +854,8 @@ replace github.com/observiq/bindplane-agent/processor/datapointcountprocessor => replace github.com/observiq/bindplane-agent/processor/lookupprocessor => ./processor/lookupprocessor +replace github.com/observiq/bindplane-agent/processor/unrollprocessor => ./processor/unrollprocessor + replace github.com/observiq/bindplane-agent/expr => ./expr replace github.com/observiq/bindplane-agent/counter => ./counter diff --git a/processor/unrollprocessor/README.md b/processor/unrollprocessor/README.md new file mode 100644 index 000000000..e2d2455f4 --- /dev/null +++ b/processor/unrollprocessor/README.md @@ -0,0 +1,124 @@ +# Unroll Processor + +This is an experimental processor that will take a log record with slice bodies and expand each element of the slice into its own log record within the slice. + +## Important Note + +This is an experimental processor and is expected that this functionality would eventually be moved to an [OTTL function](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl). + +## Supported pipelines + +- Logs + + +## How it works + +1. The user configures the `unroll` processor in their desired logs pipeline +2. Logs that go into this pipeline with a pcommon.Slice body will have each element of that body be expanded into its own log record + + +## Configuration +| Field | Type | Default | Description | +| --------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------- | +| field | string | body | note: body is currently the only available value for unrolling; making this configuration currently static | +| recursive | bool | false | whether to recursively unroll body slices of slices | + + +### Example configuration + +```yaml +unroll: + recursive: false +``` + + + +## How To + +### Split a log record into multiple via a delimiter: "," + +The following configuration utilizes the [transformprocessor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor) to first split the original string body and then the unroll processor can create multiple events + +```yaml +receivers: + filelog: + include: [ ./test.txt ] + start_at: beginning +processors: + transform: + log_statements: + - context: log + statements: + - set(body, Split(body, ",")) + unroll: +exporters: + file: + path: ./test/output.json +service: + pipelines: + logs: + receivers: [filelog] + processors: [transform, unroll] + exporters: [file] +``` + +
+ Sample Data + +```txt +1,2,3 +``` + +```json +{ + "resourceLogs": [ + { + "resource": {}, + "scopeLogs": [ + { + "scope": {}, + "logRecords": [ + { + "observedTimeUnixNano": "1733240156591852000", + "body": { "stringValue": "1" }, + "attributes": [ + { + "key": "log.file.name", + "value": { "stringValue": "test.txt" } + }, + ], + "traceId": "", + "spanId": "" + }, + { + "observedTimeUnixNano": "1733240156591852000", + "body": { "stringValue": "2" }, + "attributes": [ + { + "key": "log.file.name", + "value": { "stringValue": "test.txt" } + }, + ], + "traceId": "", + "spanId": "" + }, + { + "observedTimeUnixNano": "1733240156591852000", + "body": { "stringValue": "3" }, + "attributes": [ + { + "key": "log.file.name", + "value": { "stringValue": "test.txt" } + }, + ], + "traceId": "", + "spanId": "" + } + ] + } + ] + } + ] +} +``` +
diff --git a/processor/unrollprocessor/config.go b/processor/unrollprocessor/config.go new file mode 100644 index 000000000..c0c5199b6 --- /dev/null +++ b/processor/unrollprocessor/config.go @@ -0,0 +1,43 @@ +// Copyright observIQ, Inc. +// +// 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 unrollprocessor contains the logic to unroll logs from a slice in the body field. +package unrollprocessor + +import ( + "errors" +) + +// Config is the configuration for the unroll processor. +type Config struct { + Field UnrollField `mapstructure:"field"` + Recursive bool `mapstructure:"recursive"` +} + +// UnrollField is the field to unroll. +type UnrollField string + +const ( + // UnrollFieldBody is the only supported field for unrolling logs. + UnrollFieldBody UnrollField = "body" +) + +// Validate checks the configuration for any issues. +func (c *Config) Validate() error { + if c.Field != UnrollFieldBody { + return errors.New("only unrolling logs from a body slice is currently supported") + } + + return nil +} diff --git a/processor/unrollprocessor/config_test.go b/processor/unrollprocessor/config_test.go new file mode 100644 index 000000000..96d783681 --- /dev/null +++ b/processor/unrollprocessor/config_test.go @@ -0,0 +1,53 @@ +// Copyright observIQ, Inc. +// +// 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 unrollprocessor + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestValidate(t *testing.T) { + + testCases := []struct { + desc string + cfg *Config + expectedErr string + }{ + { + desc: "valid config", + cfg: createDefaultConfig().(*Config), + }, + { + desc: "config without body field", + cfg: &Config{ + Field: "attributes", + }, + expectedErr: "only unrolling logs from a body slice is currently supported", + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + err := tc.cfg.Validate() + if tc.expectedErr != "" { + require.ErrorContains(t, err, tc.expectedErr) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/processor/unrollprocessor/factory.go b/processor/unrollprocessor/factory.go new file mode 100644 index 000000000..36d7aa616 --- /dev/null +++ b/processor/unrollprocessor/factory.go @@ -0,0 +1,70 @@ +// Copyright observIQ, Inc. +// +// 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 unrollprocessor // import "github.com/observiq/bindplane-agent/processor/unrollprocessor" + +import ( + "context" + "fmt" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/processor" + "go.opentelemetry.io/collector/processor/processorhelper" +) + +var processorCapabilities = consumer.Capabilities{MutatesData: true} + +// componentType is the value of the "type" key in configuration. +var componentType = component.MustNewType("unroll") + +const ( + stability = component.StabilityLevelAlpha +) + +// NewFactory returns a new factory for the Transform processor. +func NewFactory() processor.Factory { + return processor.NewFactory( + componentType, + createDefaultConfig, + processor.WithLogs(createLogsProcessor, stability), + ) +} + +func createDefaultConfig() component.Config { + return &Config{ + Field: UnrollFieldBody, + } +} + +func createLogsProcessor( + ctx context.Context, + set processor.Settings, + cfg component.Config, + nextConsumer consumer.Logs, +) (processor.Logs, error) { + oCfg := cfg.(*Config) + + proc, err := newUnrollProcessor(oCfg) + if err != nil { + return nil, fmt.Errorf("invalid config for \"unroll\" processor %w", err) + } + return processorhelper.NewLogs( + ctx, + set, + cfg, + nextConsumer, + proc.ProcessLogs, + processorhelper.WithCapabilities(processorCapabilities)) +} diff --git a/processor/unrollprocessor/factory_test.go b/processor/unrollprocessor/factory_test.go new file mode 100644 index 000000000..f0f11ba14 --- /dev/null +++ b/processor/unrollprocessor/factory_test.go @@ -0,0 +1,47 @@ +// Copyright observIQ, Inc. +// +// 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 unrollprocessor + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/processor/processortest" +) + +func TestNewFactory(t *testing.T) { + factory := NewFactory() + require.Equal(t, componentType, factory.Type()) + + expectedCfg := &Config{ + Field: UnrollFieldBody, + } + + cfg, ok := factory.CreateDefaultConfig().(*Config) + require.True(t, ok) + require.Equal(t, expectedCfg, cfg) +} + +func TestBadFactory(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*Config) + cfg.Field = "invalid" + + _, err := factory.CreateLogs(context.Background(), processortest.NewNopSettings(), cfg, &consumertest.LogsSink{}) + require.Error(t, err) + require.ErrorContains(t, err, "invalid config for \"unroll\" processor") +} diff --git a/processor/unrollprocessor/go.mod b/processor/unrollprocessor/go.mod new file mode 100644 index 000000000..5c5060d6e --- /dev/null +++ b/processor/unrollprocessor/go.mod @@ -0,0 +1,52 @@ +module github.com/observiq/bindplane-agent/processor/unrollprocessor + +go 1.22.7 + +require ( + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.114.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.114.0 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/component v0.114.0 + go.opentelemetry.io/collector/consumer v0.114.0 + go.opentelemetry.io/collector/consumer/consumertest v0.114.0 + go.opentelemetry.io/collector/pdata v1.20.0 + go.opentelemetry.io/collector/processor v0.114.0 + go.opentelemetry.io/collector/processor/processortest v0.114.0 +) + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.114.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.114.0 // indirect + go.opentelemetry.io/collector/component/componenttest v0.114.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.114.0 // indirect + go.opentelemetry.io/collector/consumer/consumerprofiles v0.114.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.114.0 // indirect + go.opentelemetry.io/collector/pdata/testdata v0.114.0 // indirect + go.opentelemetry.io/collector/pipeline v0.114.0 // indirect + go.opentelemetry.io/collector/processor/processorprofiles v0.114.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.30.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.19.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd // indirect + google.golang.org/grpc v1.67.1 // indirect + google.golang.org/protobuf v1.35.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/processor/unrollprocessor/go.sum b/processor/unrollprocessor/go.sum new file mode 100644 index 000000000..e8c8de141 --- /dev/null +++ b/processor/unrollprocessor/go.sum @@ -0,0 +1,132 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.114.0 h1:SXi6JSSs2cWROnC1U2v3XysG3t58ilGUwoLqxpGuwFU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.114.0/go.mod h1:LSd6sus2Jvpg3M3vM4HgmVh3/dmMtcJmTqELrFOQFRg= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.114.0 h1:m8uPYU2rTj0sKiYgzCvIPajD3meiYsu+nX0hplUnlEU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.114.0/go.mod h1:P0BaP92pXPkTyTmObfLYUoRBfMYU+i0hdS3oM1DpGJo= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.114.0 h1:Qg80zPfNMlub7LO07VMDElOu3M2oxqdZgvvB+X72a4U= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.114.0/go.mod h1:5qsGcjFV3WFI6J2onAlkR7Xd/8VtwJcECaDRZfW4Tb4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector/component v0.114.0 h1:SVGbm5LvHGSTEDv7p92oPuBgK5tuiWR82I9+LL4TtBE= +go.opentelemetry.io/collector/component v0.114.0/go.mod h1:MLxtjZ6UVHjDxSdhGLuJfHBHvfl1iT/Y7IaQPD24Eww= +go.opentelemetry.io/collector/component/componentstatus v0.114.0 h1:y9my/xink8KB5lK8zFAjgB2+pEh0QYy5TM972fxZY9w= +go.opentelemetry.io/collector/component/componentstatus v0.114.0/go.mod h1:RIoeCYZpPaae7QLE/1RacqzhHuXBmzRAk9H/EwYtIIs= +go.opentelemetry.io/collector/component/componenttest v0.114.0 h1:GM4FTTlfeXoVm6sZYBHImwlRN8ayh2oAfUhvaFj7Zo8= +go.opentelemetry.io/collector/component/componenttest v0.114.0/go.mod h1:ZZEJMtbJtoVC/3/9R1HzERq+cYQRxuMFQrPCpfZ4Xos= +go.opentelemetry.io/collector/config/configtelemetry v0.114.0 h1:kjLeyrumge6wsX6ZIkicdNOlBXaEyW2PI2ZdVXz/rzY= +go.opentelemetry.io/collector/config/configtelemetry v0.114.0/go.mod h1:R0MBUxjSMVMIhljuDHWIygzzJWQyZHXXWIgQNxcFwhc= +go.opentelemetry.io/collector/consumer v0.114.0 h1:1zVaHvfIZowGwZRitRBRo3i+RP2StlU+GClYiofSw0Q= +go.opentelemetry.io/collector/consumer v0.114.0/go.mod h1:d+Mrzt9hsH1ub3zmwSlnQVPLeTYir4Mgo7CrWfnncN4= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.114.0 h1:5pXYy3E6UK5Huu3aQbsYL8B6E6MyWx4fvXXDn+oXZaA= +go.opentelemetry.io/collector/consumer/consumerprofiles v0.114.0/go.mod h1:PMq3f54KcJQO4v1tue0QxQScu7REFVADlXxXSAYMiN0= +go.opentelemetry.io/collector/consumer/consumertest v0.114.0 h1:isaTwJK5DOy8Bs7GuLq23ejfgj8gLIo5dOUvkRnLF4g= +go.opentelemetry.io/collector/consumer/consumertest v0.114.0/go.mod h1:GNeLPkfRPdh06n/Rv1UKa/cAtCKjN0a7ADyHjIj4HFE= +go.opentelemetry.io/collector/pdata v1.20.0 h1:ePcwt4bdtISP0loHaE+C9xYoU2ZkIvWv89Fob16o9SM= +go.opentelemetry.io/collector/pdata v1.20.0/go.mod h1:Ox1YVLe87cZDB/TL30i4SUz1cA5s6AM6SpFMfY61ICs= +go.opentelemetry.io/collector/pdata/pprofile v0.114.0 h1:pUNfTzsI/JUTiE+DScDM4lsrPoxnVNLI2fbTxR/oapo= +go.opentelemetry.io/collector/pdata/pprofile v0.114.0/go.mod h1:4aNcj6WM1n1uXyFSXlhVs4ibrERgNYsTbzcYI2zGhxA= +go.opentelemetry.io/collector/pdata/testdata v0.114.0 h1:+AzszWSL1i4K6meQ8rU0JDDW55SYCXa6FVqfDixhhTo= +go.opentelemetry.io/collector/pdata/testdata v0.114.0/go.mod h1:bv8XFdCTZxG2MQB5l9dKxSxf5zBrcodwO6JOy1+AxXM= +go.opentelemetry.io/collector/pipeline v0.114.0 h1:v3YOhc5z0tD6QbO5n/pnftpIeroihM2ks9Z2yKPCcwY= +go.opentelemetry.io/collector/pipeline v0.114.0/go.mod h1:4vOvjVsoYTHVGTbfFwqfnQOSV2K3RKUHofh3jNRc2Mg= +go.opentelemetry.io/collector/processor v0.114.0 h1:6bqQgLL7BtKrNv4YkEOGjZfkcfZv/ciJSQx1epGG9Zk= +go.opentelemetry.io/collector/processor v0.114.0/go.mod h1:DV/wa+nAmSHIDeD9NblPwkY9PbgtDQAZJ+PE5biZwPc= +go.opentelemetry.io/collector/processor/processorprofiles v0.114.0 h1:+P/1nLouEXTnN8DVQl+qWwO4BTkQyNPG9t/FrpUqrSI= +go.opentelemetry.io/collector/processor/processorprofiles v0.114.0/go.mod h1:3fuHeNIpINwx3bqFMprmDJyr6y5tWoWbJH599kltO5Y= +go.opentelemetry.io/collector/processor/processortest v0.114.0 h1:3FTaVXAp0LoVmUJn1ewBFckAby7AHa6/Kcdj0xuW14c= +go.opentelemetry.io/collector/processor/processortest v0.114.0/go.mod h1:OgsdOs1Fv5ZGTTJPF5nNIUJh2YkuV1acWd73yWgnti4= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd h1:6TEm2ZxXoQmFWFlt1vNxvVOa1Q0dXFQD1m/rYjXmS0E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240822170219-fc7c04adadcd/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/processor/unrollprocessor/processor.go b/processor/unrollprocessor/processor.go new file mode 100644 index 000000000..f065aae34 --- /dev/null +++ b/processor/unrollprocessor/processor.go @@ -0,0 +1,100 @@ +// Copyright observIQ, Inc. +// +// 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 unrollprocessor + +import ( + "context" + "fmt" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" +) + +type unrollProcessor struct { + cfg *Config +} + +// newUnrollProcessor returns a new unrollProcessor. +func newUnrollProcessor(config *Config) (*unrollProcessor, error) { + if err := config.Validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + return &unrollProcessor{ + cfg: config, + }, nil +} + +// ProcessLogs implements the processor interface +func (p *unrollProcessor) ProcessLogs(_ context.Context, ld plog.Logs) (plog.Logs, error) { + var errs error + for i := 0; i < ld.ResourceLogs().Len(); i++ { + rls := ld.ResourceLogs().At(i) + for j := 0; j < rls.ScopeLogs().Len(); j++ { + sls := rls.ScopeLogs().At(j) + origLen := sls.LogRecords().Len() + var last func() int + if p.cfg.Recursive { + last = sls.LogRecords().Len + } else { + last = func() int { return origLen } + } + for k := 0; k < last(); k++ { + lr := sls.LogRecords().At(k) + if lr.Body().Type() != pcommon.ValueTypeSlice { + continue + } + for l := 0; l < lr.Body().Slice().Len(); l++ { + newRecord := sls.LogRecords().AppendEmpty() + lr.CopyTo(newRecord) + p.setBody(newRecord, lr.Body().Slice().At(l)) + } + } + sls.LogRecords().RemoveIf(func(lr plog.LogRecord) bool { + if p.cfg.Recursive { + return lr.Body().Type() == pcommon.ValueTypeSlice + } + if origLen == 0 { + return false + } + origLen-- + return lr.Body().Type() == pcommon.ValueTypeSlice + }) + } + } + return ld, errs +} + +// setBody will set the body of the log record to the provided value +func (p *unrollProcessor) setBody(newLogRecord plog.LogRecord, expansion pcommon.Value) { + switch expansion.Type() { + case pcommon.ValueTypeStr: + newLogRecord.Body().SetStr(expansion.Str()) + case pcommon.ValueTypeInt: + newLogRecord.Body().SetInt(expansion.Int()) + case pcommon.ValueTypeDouble: + newLogRecord.Body().SetDouble(expansion.Double()) + case pcommon.ValueTypeBool: + newLogRecord.Body().SetBool(expansion.Bool()) + case pcommon.ValueTypeMap: + expansion.Map().CopyTo(newLogRecord.Body().SetEmptyMap()) + case pcommon.ValueTypeSlice: + expansion.Slice().CopyTo(newLogRecord.Body().SetEmptySlice()) + case pcommon.ValueTypeBytes: + expansion.Bytes().CopyTo(newLogRecord.Body().SetEmptyBytes()) + case pcommon.ValueTypeEmpty: + expansion.CopyTo(newLogRecord.Body()) + } +} diff --git a/processor/unrollprocessor/processor_test.go b/processor/unrollprocessor/processor_test.go new file mode 100644 index 000000000..78e0810ff --- /dev/null +++ b/processor/unrollprocessor/processor_test.go @@ -0,0 +1,112 @@ +// Copyright observIQ, Inc. +// +// 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 unrollprocessor + +import ( + "context" + "path/filepath" + "testing" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/plogtest" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/processor/processortest" +) + +func BenchmarkUnroll(b *testing.B) { + unrollProcessor := &unrollProcessor{ + cfg: createDefaultConfig().(*Config), + } + testLogs := createTestResourceLogs() + + for n := 0; n < b.N; n++ { + unrollProcessor.ProcessLogs(context.Background(), testLogs) + } +} + +func TestInvalidConfig(t *testing.T) { + _, err := newUnrollProcessor(&Config{ + Field: "invalid", + Recursive: true, + }) + require.Error(t, err) +} + +func createTestResourceLogs() plog.Logs { + rl := plog.NewLogs() + for i := 0; i < 10; i++ { + resourceLog := rl.ResourceLogs().AppendEmpty() + for j := 0; j < 10; j++ { + scopeLogs := resourceLog.ScopeLogs().AppendEmpty() + scopeLogs.LogRecords().AppendEmpty().Body().SetEmptySlice().FromRaw([]any{1, 2, 3, 4, 5, 6, 7}) + } + } + return rl +} + +func TestProcessor(t *testing.T) { + for _, test := range []struct { + name string + recursive bool + }{ + { + name: "nop", + }, + { + name: "simple", + }, + { + name: "mixed_slice_types", + }, + { + name: "some_not_slices", + }, + { + name: "recursive_false", + }, + { + name: "recursive_true", + recursive: true, + }, + { + name: "empty", + }, + } { + t.Run(test.name, func(t *testing.T) { + input, err := golden.ReadLogs(filepath.Join("testdata", test.name, "input.yaml")) + require.NoError(t, err) + expected, err := golden.ReadLogs(filepath.Join("testdata", test.name, "expected.yaml")) + require.NoError(t, err) + + f := NewFactory() + cfg := f.CreateDefaultConfig().(*Config) + cfg.Recursive = test.recursive + set := processortest.NewNopSettings() + sink := &consumertest.LogsSink{} + p, err := f.CreateLogs(context.Background(), set, cfg, sink) + require.NoError(t, err) + + err = p.ConsumeLogs(context.Background(), input) + require.NoError(t, err) + + actual := sink.AllLogs() + require.Equal(t, 1, len(actual)) + + require.NoError(t, plogtest.CompareLogs(expected, actual[0])) + }) + } +} diff --git a/processor/unrollprocessor/testdata/empty/expected.yaml b/processor/unrollprocessor/testdata/empty/expected.yaml new file mode 100644 index 000000000..5bb334b5e --- /dev/null +++ b/processor/unrollprocessor/testdata/empty/expected.yaml @@ -0,0 +1,28 @@ +resourceLogs: + - resource: {} + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: {} + spanId: "" + traceId: "" + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value2 + spanId: "" + traceId: "" + - attributes: + - key: recordName + value: + stringValue: recordA + body: + bytesValue: aGVsbG8gd29ybGQ= + spanId: "" + traceId: "" + scope: {} diff --git a/processor/unrollprocessor/testdata/empty/input.yaml b/processor/unrollprocessor/testdata/empty/input.yaml new file mode 100644 index 000000000..b11bc44d5 --- /dev/null +++ b/processor/unrollprocessor/testdata/empty/input.yaml @@ -0,0 +1,14 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + arrayValue: + values: + - emptyValue: + - stringValue: value2 + - bytesValue: aGVsbG8gd29ybGQ= diff --git a/processor/unrollprocessor/testdata/mixed_slice_types/expected.yaml b/processor/unrollprocessor/testdata/mixed_slice_types/expected.yaml new file mode 100644 index 000000000..a111b370f --- /dev/null +++ b/processor/unrollprocessor/testdata/mixed_slice_types/expected.yaml @@ -0,0 +1,73 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: strings + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: strings + body: + stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: ints + body: + intValue: 1 + - attributes: + - key: recordName + value: + stringValue: ints + body: + intValue: 2 + - attributes: + - key: recordName + value: + stringValue: doubles + body: + doubleValue: 1.1 + - attributes: + - key: recordName + value: + stringValue: doubles + body: + doubleValue: 2.2 + - attributes: + - key: recordName + value: + stringValue: bools + body: + boolValue: true + - attributes: + - key: recordName + value: + stringValue: bools + body: + boolValue: false + - attributes: + - key: recordName + value: + stringValue: maps + body: + kvlistValue: + values: + - key: foo + value: + stringValue: bar + - attributes: + - key: recordName + value: + stringValue: maps + body: + kvlistValue: + values: + - key: hello + value: + stringValue: world + diff --git a/processor/unrollprocessor/testdata/mixed_slice_types/input.yaml b/processor/unrollprocessor/testdata/mixed_slice_types/input.yaml new file mode 100644 index 000000000..26c1823aa --- /dev/null +++ b/processor/unrollprocessor/testdata/mixed_slice_types/input.yaml @@ -0,0 +1,57 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: strings + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: ints + body: + arrayValue: + values: + - intValue: 1 + - intValue: 2 + - attributes: + - key: recordName + value: + stringValue: doubles + body: + arrayValue: + values: + - doubleValue: 1.1 + - doubleValue: 2.2 + - attributes: + - key: recordName + value: + stringValue: bools + body: + arrayValue: + values: + - boolValue: true + - boolValue: false + - attributes: + - key: recordName + value: + stringValue: maps + body: + arrayValue: + values: + - kvlistValue: + values: + - key: foo + value: + stringValue: bar + - kvlistValue: + values: + - key: hello + value: + stringValue: world diff --git a/processor/unrollprocessor/testdata/nop/expected.yaml b/processor/unrollprocessor/testdata/nop/expected.yaml new file mode 100644 index 000000000..7e2da488a --- /dev/null +++ b/processor/unrollprocessor/testdata/nop/expected.yaml @@ -0,0 +1,16 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 diff --git a/processor/unrollprocessor/testdata/nop/input.yaml b/processor/unrollprocessor/testdata/nop/input.yaml new file mode 100644 index 000000000..7e2da488a --- /dev/null +++ b/processor/unrollprocessor/testdata/nop/input.yaml @@ -0,0 +1,16 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 diff --git a/processor/unrollprocessor/testdata/recursive_false/expected.yaml b/processor/unrollprocessor/testdata/recursive_false/expected.yaml new file mode 100644 index 000000000..42243198f --- /dev/null +++ b/processor/unrollprocessor/testdata/recursive_false/expected.yaml @@ -0,0 +1,22 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: slices + body: + arrayValue: + values: + - stringValue: one + - stringValue: two + - attributes: + - key: recordName + value: + stringValue: slices + body: + arrayValue: + values: + - stringValue: three + - stringValue: four diff --git a/processor/unrollprocessor/testdata/recursive_false/input.yaml b/processor/unrollprocessor/testdata/recursive_false/input.yaml new file mode 100644 index 000000000..f7b018b5f --- /dev/null +++ b/processor/unrollprocessor/testdata/recursive_false/input.yaml @@ -0,0 +1,19 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: slices + body: + arrayValue: + values: + - arrayValue: + values: + - stringValue: one + - stringValue: two + - arrayValue: + values: + - stringValue: three + - stringValue: four diff --git a/processor/unrollprocessor/testdata/recursive_true/expected.yaml b/processor/unrollprocessor/testdata/recursive_true/expected.yaml new file mode 100644 index 000000000..f8f9a6e5d --- /dev/null +++ b/processor/unrollprocessor/testdata/recursive_true/expected.yaml @@ -0,0 +1,29 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: slices + body: + stringValue: one + - attributes: + - key: recordName + value: + stringValue: slices + body: + stringValue: two + - attributes: + - key: recordName + value: + stringValue: slices + body: + stringValue: three + - attributes: + - key: recordName + value: + stringValue: slices + body: + stringValue: four + diff --git a/processor/unrollprocessor/testdata/recursive_true/input.yaml b/processor/unrollprocessor/testdata/recursive_true/input.yaml new file mode 100644 index 000000000..f7b018b5f --- /dev/null +++ b/processor/unrollprocessor/testdata/recursive_true/input.yaml @@ -0,0 +1,19 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: slices + body: + arrayValue: + values: + - arrayValue: + values: + - stringValue: one + - stringValue: two + - arrayValue: + values: + - stringValue: three + - stringValue: four diff --git a/processor/unrollprocessor/testdata/simple/expected.yaml b/processor/unrollprocessor/testdata/simple/expected.yaml new file mode 100644 index 000000000..418a08d3e --- /dev/null +++ b/processor/unrollprocessor/testdata/simple/expected.yaml @@ -0,0 +1,121 @@ +resourceLogs: + - resource: + attributes: + - key: resourceName + value: + stringValue: resourceA + scopeLogs: + - scope: + name: scopeA + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value2 + - scope: + name: scopeB + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value2 + - resource: + attributes: + - key: resourceName + value: + stringValue: resourceA + scopeLogs: + - scope: + name: scopeB + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value2 + - scope: + name: scopeB + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value2 diff --git a/processor/unrollprocessor/testdata/simple/input.yaml b/processor/unrollprocessor/testdata/simple/input.yaml new file mode 100644 index 000000000..c5ca99eaa --- /dev/null +++ b/processor/unrollprocessor/testdata/simple/input.yaml @@ -0,0 +1,97 @@ +resourceLogs: + - resource: + attributes: + - key: resourceName + value: + stringValue: resourceA + scopeLogs: + - scope: + name: scopeA + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - scope: + name: scopeB + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - resource: + attributes: + - key: resourceName + value: + stringValue: resourceA + scopeLogs: + - scope: + name: scopeB + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - scope: + name: scopeB + logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 diff --git a/processor/unrollprocessor/testdata/some_not_slices/expected.yaml b/processor/unrollprocessor/testdata/some_not_slices/expected.yaml new file mode 100644 index 000000000..583da7f2f --- /dev/null +++ b/processor/unrollprocessor/testdata/some_not_slices/expected.yaml @@ -0,0 +1,34 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordA + body: + stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordC + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordC + body: + stringValue: value2 diff --git a/processor/unrollprocessor/testdata/some_not_slices/input.yaml b/processor/unrollprocessor/testdata/some_not_slices/input.yaml new file mode 100644 index 000000000..c7380d858 --- /dev/null +++ b/processor/unrollprocessor/testdata/some_not_slices/input.yaml @@ -0,0 +1,28 @@ +resourceLogs: + - resource: + scopeLogs: + - logRecords: + - attributes: + - key: recordName + value: + stringValue: recordA + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2 + - attributes: + - key: recordName + value: + stringValue: recordB + body: + stringValue: value1 + - attributes: + - key: recordName + value: + stringValue: recordC + body: + arrayValue: + values: + - stringValue: value1 + - stringValue: value2