Skip to content

Commit

Permalink
[pkg/ottl] Add log body indexing (open-telemetry#22102)
Browse files Browse the repository at this point in the history
* Add log body indexing

* Validate that an index was actually passed when trying to index an empty pdata value

* Fix lint

* Address PR feedback

---------

Co-authored-by: Evan Bradley <evan-bradley@users.noreply.github.com>
  • Loading branch information
evan-bradley and evan-bradley committed May 24, 2023
1 parent 35739c4 commit 85a618f
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 80 deletions.
20 changes: 20 additions & 0 deletions .chloggen/ottl-log-body-indexing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use this changelog template to create an entry for release notes.
# If your change doesn't affect end users, such as a test fix or a tooling change,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: pkg/ottl

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Allow indexing map and slice log bodies

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [17396, 22068]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
74 changes: 2 additions & 72 deletions pkg/ottl/contexts/internal/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/internal/ottlcommon"
)

func GetMapValue(m pcommon.Map, keys []ottl.Key) (interface{}, error) {
Expand All @@ -24,29 +23,7 @@ func GetMapValue(m pcommon.Map, keys []ottl.Key) (interface{}, error) {
if !ok {
return nil, nil
}
for i := 1; i < len(keys); i++ {
switch val.Type() {
case pcommon.ValueTypeMap:
if keys[i].String == nil {
return nil, fmt.Errorf("map must be indexed by a string")
}
val, ok = val.Map().Get(*keys[i].String)
if !ok {
return nil, nil
}
case pcommon.ValueTypeSlice:
if keys[i].Int == nil {
return nil, fmt.Errorf("slice must be indexed by an int")
}
if int(*keys[i].Int) >= val.Slice().Len() || int(*keys[i].Int) < 0 {
return nil, fmt.Errorf("index %v out of bounds", *keys[i].Int)
}
val = val.Slice().At(int(*keys[i].Int))
default:
return nil, fmt.Errorf("type %v does not support string indexing", val.Type())
}
}
return ottlcommon.GetValue(val), nil
return getIndexableValue(val, keys[1:])
}

func SetMapValue(m pcommon.Map, keys []ottl.Key, val interface{}) error {
Expand All @@ -57,57 +34,10 @@ func SetMapValue(m pcommon.Map, keys []ottl.Key, val interface{}) error {
return fmt.Errorf("non-string indexing is not supported")
}

var newValue pcommon.Value
switch val.(type) {
case []string, []bool, []int64, []float64, [][]byte, []any:
newValue = pcommon.NewValueSlice()
default:
newValue = pcommon.NewValueEmpty()
}
err := SetValue(newValue, val)
if err != nil {
return err
}

currentValue, ok := m.Get(*keys[0].String)
if !ok {
currentValue = m.PutEmpty(*keys[0].String)
}

for i := 1; i < len(keys); i++ {
switch currentValue.Type() {
case pcommon.ValueTypeMap:
if keys[i].String == nil {
return fmt.Errorf("map must be indexed by a string")
}
potentialValue, ok := currentValue.Map().Get(*keys[i].String)
if !ok {
currentValue = currentValue.Map().PutEmpty(*keys[i].String)
} else {
currentValue = potentialValue
}
case pcommon.ValueTypeSlice:
if keys[i].Int == nil {
return fmt.Errorf("slice must be indexed by an int")
}
if int(*keys[i].Int) >= currentValue.Slice().Len() || int(*keys[i].Int) < 0 {
return fmt.Errorf("index %v out of bounds", *keys[i].Int)
}
currentValue = currentValue.Slice().At(int(*keys[i].Int))
case pcommon.ValueTypeEmpty:
if keys[i].String != nil {
currentValue = currentValue.SetEmptyMap().PutEmpty(*keys[i].String)
} else if keys[i].Int != nil {
currentValue.SetEmptySlice()
for k := 0; k < int(*keys[i].Int); k++ {
currentValue.Slice().AppendEmpty()
}
currentValue = currentValue.Slice().AppendEmpty()
}
default:
return fmt.Errorf("type %v does not support string indexing", currentValue.Type())
}
}
newValue.CopyTo(currentValue)
return nil
return setIndexableValue(currentValue, val, keys[1:])
}
44 changes: 44 additions & 0 deletions pkg/ottl/contexts/internal/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal"

import (
"fmt"

"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
)

func GetSliceValue(s pcommon.Slice, keys []ottl.Key) (interface{}, error) {
if len(keys) == 0 {
return nil, fmt.Errorf("cannot get slice value without key")
}
if keys[0].Int == nil {
return nil, fmt.Errorf("non-integer indexing is not supported")
}
idx := int(*keys[0].Int)

if idx < 0 || idx >= s.Len() {
return nil, fmt.Errorf("index %d out of bounds", idx)
}

return getIndexableValue(s.At(int(*keys[0].Int)), keys[1:])
}

func SetSliceValue(s pcommon.Slice, keys []ottl.Key, val interface{}) error {
if len(keys) == 0 {
return fmt.Errorf("cannot set slice value without key")
}
if keys[0].Int == nil {
return fmt.Errorf("non-integer indexing is not supported")
}
idx := int(*keys[0].Int)

if idx < 0 || idx >= s.Len() {
return fmt.Errorf("index %d out of bounds", idx)
}

return setIndexableValue(s.At(int(*keys[0].Int)), val, keys[1:])
}
141 changes: 141 additions & 0 deletions pkg/ottl/contexts/internal/slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package internal

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/pcommon"

"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest"
)

func Test_GetSliceValue_Invalid(t *testing.T) {
tests := []struct {
name string
keys []ottl.Key
err error
}{
{
name: "no keys",
keys: []ottl.Key{},
err: fmt.Errorf("cannot get slice value without key"),
},
{
name: "first key not an integer",
keys: []ottl.Key{
{
String: ottltest.Strp("key"),
},
},
err: fmt.Errorf("non-integer indexing is not supported"),
},
{
name: "index too large",
keys: []ottl.Key{
{
Int: ottltest.Intp(1),
},
},
err: fmt.Errorf("index 1 out of bounds"),
},
{
name: "index too small",
keys: []ottl.Key{
{
Int: ottltest.Intp(-1),
},
},
err: fmt.Errorf("index -1 out of bounds"),
},
{
name: "invalid type",
keys: []ottl.Key{
{
Int: ottltest.Intp(0),
},
{
String: ottltest.Strp("string"),
},
},
err: fmt.Errorf("type Str does not support string indexing"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := pcommon.NewSlice()
s.AppendEmpty().SetStr("val")

_, err := GetSliceValue(s, tt.keys)
assert.Equal(t, tt.err, err)
})
}
}

func Test_SetSliceValue_Invalid(t *testing.T) {
tests := []struct {
name string
keys []ottl.Key
err error
}{
{
name: "no keys",
keys: []ottl.Key{},
err: fmt.Errorf("cannot set slice value without key"),
},
{
name: "first key not an integer",
keys: []ottl.Key{
{
String: ottltest.Strp("key"),
},
},
err: fmt.Errorf("non-integer indexing is not supported"),
},
{
name: "index too large",
keys: []ottl.Key{
{
Int: ottltest.Intp(1),
},
},
err: fmt.Errorf("index 1 out of bounds"),
},
{
name: "index too small",
keys: []ottl.Key{
{
Int: ottltest.Intp(-1),
},
},
err: fmt.Errorf("index -1 out of bounds"),
},
{
name: "invalid type",
keys: []ottl.Key{
{
Int: ottltest.Intp(0),
},
{
String: ottltest.Strp("string"),
},
},
err: fmt.Errorf("type Str does not support string indexing"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := pcommon.NewSlice()
s.AppendEmpty().SetStr("val")

err := SetSliceValue(s, tt.keys, "value")
assert.Equal(t, tt.err, err)
})
}
}

0 comments on commit 85a618f

Please sign in to comment.