Skip to content

Commit

Permalink
Extend Metrics Transform to be able to add a label to an existing met…
Browse files Browse the repository at this point in the history
…ric (#441)

* Extend Metrics Transform to be able to add a label to an existing metric

* Update label values on timeseries

* Add missing config test
  • Loading branch information
ccaraman committed Jul 17, 2020
1 parent 6c9a91c commit 4deae62
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 24 deletions.
14 changes: 14 additions & 0 deletions processor/metricstransformprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The metrics transform processor can be used to rename metrics, labels, or label
- Aggregation_type: sum, average, max
- Aggregate across label values (e.g. want `memory{slab}`, but don’t care about `memory{slab_reclaimable}` & `memory{slab_unreclaimable}`)
- Aggregation_type: sum, average, max
- Add label to an existing metric

## Configuration
```yaml
Expand Down Expand Up @@ -97,3 +98,16 @@ operations:
new_value: slab
aggregation_type: sum
```

### Add a label to an existing metric
```yaml
transforms:
...
# The following will append label {Key: `mylabel`, Description: `myvalue`} to the metric `some_name`.
- metric_name: some_name
action: update
operation:
- action: add_label
new_label: mylabel
new_value: myvalue
```
8 changes: 7 additions & 1 deletion processor/metricstransformprocessor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const (

// NewLabelFieldName is the mapstructure field name for NewLabel field
NewLabelFieldName = "new_label"

// NewValueFieldName is the mapstructure field name for NewValue field
NewValueFieldName = "new_value"
)

// Config defines configuration for Resource processor.
Expand Down Expand Up @@ -80,7 +83,7 @@ type Operation struct {
// AggregatedValues is a list of label values to aggregate away.
AggregatedValues []string `mapstructure:"aggregated_values"`

// NewValue indicates what is the value called when the AggregatedValues are aggregated into one.
// NewValue is used to set a new label value either when the operation is `AggregatedValues` or `AddLabel`.
NewValue string `mapstructure:"new_value"`

// ValueActions is a list of renaming actions for label values.
Expand Down Expand Up @@ -115,6 +118,9 @@ const (
// ToggleScalarDataType changes the data type from int64 to double, or vice-versa
ToggleScalarDataType OperationAction = "toggle_scalar_data_type"

// AddLabel adds a new label to an existing metric.
AddLabel OperationAction = "add_label"

// UpdateLabel applies name changes to label and/or label values.
UpdateLabel OperationAction = "update_label"

Expand Down
22 changes: 22 additions & 0 deletions processor/metricstransformprocessor/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,28 @@ var (
},
},
},
{
filterName: "metricstransform/addlabel",
expCfg: &Config{
ProcessorSettings: configmodels.ProcessorSettings{
NameVal: "metricstransform/addlabel",
TypeVal: typeStr,
},
Transforms: []Transform{
{
MetricName: "some_name",
Action: Update,
Operations: []Operation{
{
Action: AddLabel,
NewLabel: "mylabel",
NewValue: "myvalue",
},
},
},
},
},
},
}
)

Expand Down
6 changes: 6 additions & 0 deletions processor/metricstransformprocessor/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ func validateConfiguration(config *Config) error {
if op.Action == UpdateLabel && op.Label == "" {
return fmt.Errorf("missing required field %q while %q is %v in the %vth operation", LabelFieldName, ActionFieldName, UpdateLabel, i)
}
if op.Action == AddLabel && op.NewLabel == "" {
return fmt.Errorf("missing required field %q while %q is %v in the %vth operation", NewLabelFieldName, ActionFieldName, AddLabel, i)
}
if op.Action == AddLabel && op.NewValue == "" {
return fmt.Errorf("missing required field %q while %q is %v in the %vth operation", NewValueFieldName, ActionFieldName, AddLabel, i)
}
}
}

Expand Down
38 changes: 38 additions & 0 deletions processor/metricstransformprocessor/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,41 @@ func TestCreateProcessors(t *testing.T) {
}
}
}

func TestFactory_validateConfiguration(t *testing.T) {
v1 := Config{
Transforms: []Transform{
{
MetricName: "mymetric",
Action: Update,
Operations: []Operation{
{
Action: AddLabel,
NewValue: "bar",
},
},
},
},
}
err := validateConfiguration(&v1)
assert.Equal(t, "missing required field \"new_label\" while \"action\" is add_label in the 0th operation", err.Error())

v2 := Config{
Transforms: []Transform{
{
MetricName: "mymetric",
Action: Update,
Operations: []Operation{
{
Action: AddLabel,
NewLabel: "foo",
},
},
},
},
}

err = validateConfiguration(&v2)
assert.Equal(t, "missing required field \"new_value\" while \"action\" is add_label in the 0th operation", err.Error())

}
16 changes: 16 additions & 0 deletions processor/metricstransformprocessor/metrics_transform_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,26 @@ func (mtp *metricsTransformProcessor) update(metric *metricspb.Metric, transform
mtp.updateLabelOp(metric, op)
} else if op.Action == ToggleScalarDataType {
mtp.ToggleScalarDataType(metric)
} else if op.Action == AddLabel {
mtp.addLabelOp(metric, op)
}
}
}

func (mtp *metricsTransformProcessor) addLabelOp(metric *metricspb.Metric, op Operation) {
var lb = metricspb.LabelKey{
Key: op.NewLabel,
}
metric.MetricDescriptor.LabelKeys = append(metric.MetricDescriptor.LabelKeys, &lb)
for _, ts := range metric.Timeseries {
lv := &metricspb.LabelValue{
Value: op.NewValue,
HasValue: true,
}
ts.LabelValues = append(ts.LabelValues, lv)
}
}

func (mtp *metricsTransformProcessor) updateLabelOp(metric *metricspb.Metric, op Operation) {
for _, label := range metric.MetricDescriptor.LabelKeys {
if label.Key != op.Label {
Expand Down

0 comments on commit 4deae62

Please sign in to comment.