Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pkg/ottl] Add support for scaling values #33246

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b782416
[processor/transform] Add support for scaling values
bacherfl May 27, 2024
adcda48
revert change to e2e test
bacherfl May 28, 2024
4ebb38a
rename helper function
bacherfl May 28, 2024
3ba07ac
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl May 28, 2024
d610bf0
add documentation for Scale function
bacherfl May 28, 2024
9d741ac
Merge remote-tracking branch 'bacherfl/feat/16214/scale-metric-values…
bacherfl May 28, 2024
d098a77
adapt to possible types returned by metric context path
bacherfl May 28, 2024
f019ec0
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl May 28, 2024
15a2006
additional test for exponential histogram
bacherfl May 28, 2024
213fcce
Merge remote-tracking branch 'bacherfl/feat/16214/scale-metric-values…
bacherfl May 28, 2024
285a37d
use type switch for handling different types as Scale argument
bacherfl May 29, 2024
fc1dc7f
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl May 29, 2024
4084b53
incorporate feedback from PR review
bacherfl Jun 3, 2024
35064e1
Merge remote-tracking branch 'bacherfl/feat/16214/scale-metric-values…
bacherfl Jun 3, 2024
343ce4e
add license comment
bacherfl Jun 3, 2024
549bdc3
address PR review comments, add changelog entry
bacherfl Jun 4, 2024
fb04e21
fix linting
bacherfl Jun 5, 2024
75e24b1
Merge branch 'main' into feat/16214/scale-metric-values
evan-bradley Jun 5, 2024
49321a2
Update pkg/ottl/ottlfuncs/func_scale.go
bacherfl Jun 6, 2024
47d6834
fix e2e tests
bacherfl Jun 6, 2024
313cd7e
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl Jun 6, 2024
d76e6cc
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl Jun 13, 2024
44997a1
Merge branch 'main' into feat/16214/scale-metric-values
evan-bradley Jun 14, 2024
0f07a49
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl Jun 17, 2024
92d33ed
adapt scale function to be an editor function
bacherfl Jun 17, 2024
f660ca6
remove unused testing struct
bacherfl Jun 17, 2024
37f16bb
remove unused testing struct; adapt test
bacherfl Jun 17, 2024
62a539b
adapt test
bacherfl Jun 17, 2024
61e2a09
fix linting
bacherfl Jun 17, 2024
75a6d67
adapt the function to only work with metrics
bacherfl Jun 18, 2024
14bbdd8
remove obsolete test
bacherfl Jun 18, 2024
0090fcc
apply suggestions from PR review
bacherfl Jun 19, 2024
d398d64
move scale function to transformprocessor
bacherfl Jun 19, 2024
e495e03
fix failing checks
bacherfl Jun 20, 2024
c83e5ba
fix linting
bacherfl Jun 20, 2024
66c5bd5
appease linter
bacherfl Jun 20, 2024
bf746e7
make unit an optional value
bacherfl Jun 24, 2024
ffed4f1
Merge branch 'main' into feat/16214/scale-metric-values
bacherfl Jun 24, 2024
719daca
adapt unit tests to recent changes
bacherfl Jun 24, 2024
721c537
apply suggestion from code review
bacherfl Jul 2, 2024
a6c39a1
adapt to pr review
bacherfl Jul 2, 2024
876afc1
Apply suggestions from code review
bacherfl Jul 23, 2024
42d3140
incorporate review comments
bacherfl Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/ottl/e2e/e2e_test.go
evan-bradley marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,12 @@ func Test_e2e_converters(t *testing.T) {
m.PutStr("bar", "pass")
},
},
{
statement: `set(attributes["double_value"], Scale(attributes["double_value"],0.1))`,
want: func(tCtx ottllog.TransformContext) {
tCtx.GetLogRecord().Attributes().PutDouble("double_value", 1.05)
},
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -790,6 +796,7 @@ func constructLogTransformContext() ottllog.TransformContext {
logRecord.Attributes().PutStr("http.url", "http://localhost/health")
logRecord.Attributes().PutStr("flags", "A|B|C")
logRecord.Attributes().PutStr("total.string", "123456789")
logRecord.Attributes().PutDouble("double_value", 10.5)
m := logRecord.Attributes().PutEmptyMap("foo")
m.PutStr("bar", "pass")
m.PutStr("flags", "pass")
Expand Down
16 changes: 16 additions & 0 deletions pkg/ottl/ottlfuncs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ Available Converters:
- [UnixSeconds](#unixseconds)
- [UUID](#UUID)
- [Year](#year)
- [Scale](#scale)

### Base64Decode

Expand Down Expand Up @@ -1358,6 +1359,21 @@ Examples:

- `Year(Now())`

### Scale

`Scale(value, factor)`

The `Scale` function returns the original `value`, multiplied by the `factor`.
The supported data types are:

- `int`
- `double`
- `metric` - Supported metric types are `Gauge`, `Sum`, and `Histogram`
evan-bradley marked this conversation as resolved.
Show resolved Hide resolved

Examples:

- `Scale(10.0, 0.1)`

## Function syntax

Functions should be named and formatted according to the following standards.
Expand Down
132 changes: 132 additions & 0 deletions pkg/ottl/ottlfuncs/func_scale.go
TylerHelmuth marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package ottlfuncs

import (
"context"
"errors"
"fmt"
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
"go.opentelemetry.io/collector/pdata/pmetric"
)

type ScaleArguments[K any] struct {
Value ottl.Getter[K]
Multiplier float64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like @TylerHelmuth mentioned, could we add an optional parameter to adjust the unit? I think it would be nice for usability to do all scaling in one statement.

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we make this an editor, an optional unit could be good here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small question regarding this: This requires a modification of both the data_points and the unit property of a metric. Is there a way to access the complete metric via an OTTL statement? In the docs I only found a way to access either the data_points or the unit:

processors:
  transform:
    error_mode: ignore
    metric_statements:
      - context: metric
        statements:
          - scale_metric(data_points, 10.0) # this does not give access to the unit of the metric

I guess a workaround would be to set the unit in a separate function, but if somehow possible I would like to also add the possibility to set the unit here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have access to the metric in the TransformContext since this is a metrics-only function. So we could allow something like scale_metric(10.0, unit="s").

It can always be added later, so we could drop this idea until someone asks for it.


func NewScaleFactory[K any]() ottl.Factory[K] {
return ottl.NewFactory("Scale", &ScaleArguments[K]{}, createScaleFunction[K])
}

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

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

return Scale(args.Value, args.Multiplier)
}

func Scale[K any](value ottl.Getter[K], multiplier float64) (ottl.ExprFunc[K], error) {
evan-bradley marked this conversation as resolved.
Show resolved Hide resolved
return func(ctx context.Context, tCtx K) (any, error) {
get, err := value.Get(ctx, tCtx)
if err != nil {
return nil, err
}

switch get.(type) {
case float64:
return get.(float64) * multiplier, nil
case int64:
return float64(get.(int64)) * multiplier, nil
case pmetric.NumberDataPointSlice:
scaledMetric := pmetric.NewNumberDataPointSlice()
get.(pmetric.NumberDataPointSlice).CopyTo(scaledMetric)
scaleMetric(scaledMetric, multiplier)
return scaledMetric, nil
case pmetric.HistogramDataPointSlice:
scaledMetric := pmetric.NewHistogramDataPointSlice()
get.(pmetric.HistogramDataPointSlice).CopyTo(scaledMetric)
scaleHistogram(scaledMetric, multiplier)
return scaledMetric, nil
case pmetric.ExponentialHistogramDataPointSlice:
scaledMetric := pmetric.NewExponentialHistogramDataPointSlice()
get.(pmetric.ExponentialHistogramDataPointSlice).CopyTo(scaledMetric)
scaleExponentialHistogram(scaledMetric, multiplier)
return scaledMetric, nil
default:
bacherfl marked this conversation as resolved.
Show resolved Hide resolved
return nil, errors.New("unsupported data type")
bacherfl marked this conversation as resolved.
Show resolved Hide resolved
}
bacherfl marked this conversation as resolved.
Show resolved Hide resolved
}, nil
}

func scaleHistogram(datapoints pmetric.HistogramDataPointSlice, multiplier float64) {

for i := 0; i < datapoints.Len(); i++ {
dp := datapoints.At(i)

if dp.HasSum() {
dp.SetSum(dp.Sum() * multiplier)
}
if dp.HasMin() {
dp.SetMin(dp.Min() * multiplier)
}
if dp.HasMax() {
dp.SetMax(dp.Max() * multiplier)
}

for bounds, bi := dp.ExplicitBounds(), 0; bi < bounds.Len(); bi++ {
bounds.SetAt(bi, bounds.At(bi)*multiplier)
}

for exemplars, ei := dp.Exemplars(), 0; ei < exemplars.Len(); ei++ {
exemplar := exemplars.At(ei)
switch exemplar.ValueType() {
case pmetric.ExemplarValueTypeInt:
exemplar.SetIntValue(int64(float64(exemplar.IntValue()) * multiplier))
case pmetric.ExemplarValueTypeDouble:
exemplar.SetDoubleValue(exemplar.DoubleValue() * multiplier)
}
}
}
}

func scaleExponentialHistogram(datapoints pmetric.ExponentialHistogramDataPointSlice, multiplier float64) {
evan-bradley marked this conversation as resolved.
Show resolved Hide resolved
for i := 0; i < datapoints.Len(); i++ {
dp := datapoints.At(i)

if dp.HasSum() {
dp.SetSum(dp.Sum() * multiplier)
}
if dp.HasMin() {
dp.SetMin(dp.Min() * multiplier)
}
if dp.HasMax() {
dp.SetMax(dp.Max() * multiplier)
}

for exemplars, ei := dp.Exemplars(), 0; ei < exemplars.Len(); ei++ {
exemplar := exemplars.At(ei)
switch exemplar.ValueType() {
case pmetric.ExemplarValueTypeInt:
exemplar.SetIntValue(int64(float64(exemplar.IntValue()) * multiplier))
case pmetric.ExemplarValueTypeDouble:
exemplar.SetDoubleValue(exemplar.DoubleValue() * multiplier)
}
}
}
}

func scaleMetric(points pmetric.NumberDataPointSlice, multiplier float64) {
for i := 0; i < points.Len(); i++ {
dp := points.At(i)
switch dp.ValueType() {
case pmetric.NumberDataPointValueTypeInt:
dp.SetIntValue(int64(float64(dp.IntValue()) * multiplier))

case pmetric.NumberDataPointValueTypeDouble:
dp.SetDoubleValue(dp.DoubleValue() * multiplier)
default:
}
}
}
Loading
Loading