Skip to content

Commit

Permalink
[pkg/ottl] add string_converter (#31790)
Browse files Browse the repository at this point in the history
**Description:** 
- implemented func_string (string_converter)
- added new unit tests in `pkg/ottl/ottlfuncs/func_string_test.go`
- added function to `pkg/ottl/ottlfuncs/functions.go`
- updatet README.md at `pkg/ottl/ottlfuncs/README.md`
- added changelog

**Link to tracking Issue:** 
- #27867 

**Testing:** 

- added similar tests as in `pkg/ottl/ottlfuncs/func_double_test.go`


**Documentation:** <Describe the documentation added.>

---------

Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com>
  • Loading branch information
led0nk and TylerHelmuth authored Mar 28, 2024
1 parent ac74006 commit 002ae45
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .chloggen/string-converter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# 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: "Adding a string converter into pkg/ottl"

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

# (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:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
30 changes: 30 additions & 0 deletions pkg/ottl/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,36 @@ func Test_e2e_converters(t *testing.T) {
s.AppendEmpty().SetStr("C")
},
},
{
statement: `set(attributes["test"], String("test"))`,
want: func(tCtx ottllog.TransformContext) {
tCtx.GetLogRecord().Attributes().PutStr("test", "test")
},
},
{
statement: `set(attributes["test"], String(attributes["http.method"]))`,
want: func(tCtx ottllog.TransformContext) {
tCtx.GetLogRecord().Attributes().PutStr("test", "get")
},
},
{
statement: `set(attributes["test"], String(span_id))`,
want: func(tCtx ottllog.TransformContext) {
tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3,4,5,6,7,8]")
},
},
{
statement: `set(attributes["test"], String([1,2,3]))`,
want: func(tCtx ottllog.TransformContext) {
tCtx.GetLogRecord().Attributes().PutStr("test", "[1,2,3]")
},
},
{
statement: `set(attributes["test"], String(true))`,
want: func(tCtx ottllog.TransformContext) {
tCtx.GetLogRecord().Attributes().PutStr("test", "true")
},
},
{
statement: `set(attributes["test"], Substring("pass", 0, 2))`,
want: func(tCtx ottllog.TransformContext) {
Expand Down
28 changes: 28 additions & 0 deletions pkg/ottl/ottlfuncs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ Available Converters:
- [SHA256](#sha256)
- [SpanID](#spanid)
- [Split](#split)
- [String](#string)
- [Substring](#substring)
- [Time](#time)
- [TraceID](#traceid)
Expand Down Expand Up @@ -1069,6 +1070,33 @@ Examples:

- `Split("A|B|C", "|")`

### String

`String(value)`

The `String` Converter converts the `value` to string type.

The returned type is `string`.

- string. The function returns the `value` without changes.
- []byte. The function returns the `value` as a string encoded in hexadecimal.
- map. The function returns the `value` as a key-value-pair of type string.
- slice. The function returns the `value` as a list formatted string.
- pcommon.Value. The function returns the `value` as a string type.

If `value` is of another type it gets marshalled to string type.
If `value` is empty, or parsing failed, nil is always returned.

The `value` is either a path expression to a telemetry field to retrieve, or a literal.

Examples:

- `String("test")`
- `String(attributes["http.method"])`
- `String(span_id)`
- `String([1,2,3])`
- `String(false)`

### Substring

`Substring(target, start, length)`
Expand Down
42 changes: 42 additions & 0 deletions pkg/ottl/ottlfuncs/func_string.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

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

import (
"context"
"fmt"

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

type StringArguments[K any] struct {
Target ottl.StringLikeGetter[K]
}

func NewStringFactory[K any]() ottl.Factory[K] {
return ottl.NewFactory("String", &StringArguments[K]{}, createStringFunction[K])
}

func createStringFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (ottl.ExprFunc[K], error) {
args, ok := oArgs.(*StringArguments[K])

if !ok {
return nil, fmt.Errorf("StringFactory args must be of type *StringArguments[K]")
}

return stringFunc(args.Target), nil
}

func stringFunc[K any](target ottl.StringLikeGetter[K]) ottl.ExprFunc[K] {
return func(ctx context.Context, tCtx K) (any, error) {
value, err := target.Get(ctx, tCtx)
if err != nil {
return nil, err
}
if value == nil {
return nil, nil
}
return *value, nil
}
}
99 changes: 99 additions & 0 deletions pkg/ottl/ottlfuncs/func_string_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package ottlfuncs

import (
"context"
"testing"

"github.com/stretchr/testify/assert"

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

func Test_String(t *testing.T) {
tests := []struct {
name string
value any
expected any
err bool
}{
{
name: "string",
value: "test",
expected: string("test"),
},
{
name: "empty string",
value: "",
expected: string(""),
},
{
name: "a number string",
value: "333",
expected: string("333"),
},
{
name: "int64",
value: int64(333),
expected: string("333"),
},
{
name: "float64",
value: float64(2.7),
expected: string("2.7"),
},
{
name: "float64 without decimal",
value: float64(55),
expected: string("55"),
},
{
name: "true",
value: true,
expected: string("true"),
},
{
name: "false",
value: false,
expected: string("false"),
},
{
name: "nil",
value: nil,
expected: nil,
},
{
name: "byte",
value: []byte{123},
expected: string("7b"),
},
{
name: "map",
value: map[int]bool{1: true, 2: false},
expected: string("{\"1\":true,\"2\":false}"),
},
{
name: "slice",
value: []int{1, 2, 3},
expected: string("[1,2,3]"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
exprFunc := stringFunc(&ottl.StandardStringLikeGetter[any]{
Getter: func(context.Context, any) (any, error) {
return tt.value, nil
},
})
result, err := exprFunc(nil, nil)
if tt.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.expected, result)
})
}
}
1 change: 1 addition & 0 deletions pkg/ottl/ottlfuncs/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func converters[K any]() []ottl.Factory[K] {
NewSHA256Factory[K](),
NewSpanIDFactory[K](),
NewSplitFactory[K](),
NewStringFactory[K](),
NewSubstringFactory[K](),
NewTimeFactory[K](),
NewTruncateTimeFactory[K](),
Expand Down

0 comments on commit 002ae45

Please sign in to comment.