transformprocessor

package module
v0.98.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2024 License: Apache-2.0 Imports: 14 Imported by: 16

README

Transform Processor

Status
Stability alpha: traces, metrics, logs
Distributions contrib
Warnings Unsound Transformations, Identity Conflict, Orphaned Telemetry, Other
Issues Open issues Closed issues
Code Owners @TylerHelmuth, @kentquirk, @bogdandrutu, @evan-bradley

The transform processor modifies telemetry based on configuration using the OpenTelemetry Transformation Language.

For each signal type, the processor takes a list of statements associated to a Context type and executes the statements against the incoming telemetry in the order specified in the config. Each statement can access and transform telemetry using functions and allow the use of a condition to help decide whether the function should be executed.

Config

The transform processor allows configuring multiple context statements for traces, metrics, and logs. The value of context specifies which OTTL Context to use when interpreting the associated statements. The statement strings, which must be OTTL compatible, will be passed to the OTTL and interpreted using the associated context. Each context will be processed in the order specified and each statement for a context will be executed in the order specified.

The transform processor also allows configuring an optional field, error_mode, which will determine how the processor reacts to errors that occur while processing a statement.

error_mode description
ignore The processor ignores errors returned by statements, logs the error, and continues on to the next statement. This is the recommended mode.
silent The processor ignores errors returned by statements, does not log the error, and continues on to the next statement.
propagate The processor returns the error up the pipeline. This will result in the payload being dropped from the collector.

If not specified, propagate will be used.

transform:
  error_mode: ignore
  <trace|metric|log>_statements:
    - context: string
      statements:
        - string
        - string
        - string
    - context: string
      statements:
        - string
        - string
        - string

Proper use of contexts will provide increased performance and capabilities. See Contexts for more details.

Valid values for context are:

Signal Context Values
trace_statements resource, scope, span, and spanevent
metric_statements resource, scope, metric, and datapoint
log_statements resource, scope, and log
Example

The example takes advantage of context efficiency by grouping transformations with the context which it intends to transform. See Contexts for more details.

Example configuration:

transform:
  error_mode: ignore
  trace_statements:
    - context: resource
      statements:
        - keep_keys(attributes, ["service.name", "service.namespace", "cloud.region", "process.command_line"])
        - replace_pattern(attributes["process.command_line"], "password\\=[^\\s]*(\\s?)", "password=***")
        - limit(attributes, 100, [])
        - truncate_all(attributes, 4096)
    - context: span
      statements:
        - set(status.code, 1) where attributes["http.path"] == "/health"
        - set(name, attributes["http.route"])
        - replace_match(attributes["http.target"], "/user/*/list/*", "/user/{userId}/list/{listId}")
        - limit(attributes, 100, [])
        - truncate_all(attributes, 4096)

  metric_statements:
    - context: resource
      statements:
      - keep_keys(attributes, ["host.name"])
      - truncate_all(attributes, 4096)
    - context: metric
      statements:
        - set(description, "Sum") where type == "Sum"
    - context: datapoint
      statements:
        - limit(attributes, 100, ["host.name"])
        - truncate_all(attributes, 4096)
        - convert_sum_to_gauge() where metric.name == "system.processes.count"
        - convert_gauge_to_sum("cumulative", false) where metric.name == "prometheus_metric"
        
  log_statements:
    - context: resource
      statements:
        - keep_keys(attributes, ["service.name", "service.namespace", "cloud.region"])
    - context: log
      statements:
        - set(severity_text, "FAIL") where body == "request failed"
        - replace_all_matches(attributes, "/user/*/list/*", "/user/{userId}/list/{listId}")
        - replace_all_patterns(attributes, "value", "/account/\\d{4}", "/account/{accountId}")
        - set(body, attributes["http.route"])

Grammar

You can learn more in-depth details on the capabilities and limitations of the OpenTelemetry Transformation Language used by the transform processor by reading about its grammar.

Contexts

The transform processor utilizes the OTTL's contexts to transform Resource, Scope, Span, SpanEvent, Metric, DataPoint, and Log telemetry. The contexts allow the OTTL to interact with the underlying telemetry data in its pdata form.

Each context allows transformation of its type of telemetry.
For example, statements associated to a resource context will be able to transform the resource's attributes and dropped_attributes_count.

Contexts NEVER supply access to individual items "lower" in the protobuf definition.

  • This means statements associated to a resource WILL NOT be able to access the underlying instrumentation scopes.
  • This means statements associated to a scope WILL NOT be able to access the underlying telemetry slices (spans, metrics, or logs).
  • Similarly, statements associated to a metric WILL NOT be able to access individual datapoints, but can access the entire datapoints slice.
  • Similarly, statements associated to a span WILL NOT be able to access individual SpanEvents, but can access the entire SpanEvents slice.

For practical purposes, this means that a context cannot make decisions on its telemetry based on telemetry "lower" in the structure. For example, the following context statement is not possible because it attempts to use individual datapoint attributes in the condition of a statements that is associated to a metric

metric_statements:
- context: metric
  statements:
  - set(description, "test passed") where datapoints.attributes["test"] == "pass"

Context ALWAYS supply access to the items "higher" in the protobuf definition that are associated to the telemetry being transformed.

  • This means that statements associated to a datapoint have access to a datapoint's metric, instrumentation scope, and resource.
  • This means that statements associated to a spanevent have access to a spanevent's span, instrumentation scope, and resource.
  • This means that statements associated to a span/metric/log have access to the telemetry's instrumentation scope, and resource.
  • This means that statements associated to a scope have access to the scope's resource.

For example, the following context statement is possible because datapoint statements can access the datapoint's metric.

metric_statements:
- context: datapoint
  statements:
    - set(metric.description, "test passed") where attributes["test"] == "pass"

Whenever possible, associate your statements to the context that the statement intend to transform. Although you can modify resource attributes associated to a span using the span context, it is more efficient to use the resource context. This is because contexts are nested: the efficiency comes because higher-level contexts can avoid iterating through any of the contexts at a lower level.

Supported functions:

Since the transform processor utilizes the OTTL's contexts for Traces, Metrics, and Logs, it is able to utilize functions that expect pdata in addition to any common functions. These common functions can be used for any signal.

In addition to OTTL functions, the processor defines its own functions to help with transformations specific to this processor:

Metrics only functions

convert_sum_to_gauge

convert_sum_to_gauge()

Converts incoming metrics of type "Sum" to type "Gauge", retaining the metric's datapoints. Noop for metrics that are not of type "Sum".

NOTE: This function may cause a metric to break semantics for Gauge metrics. Use at your own risk.

Examples:

  • convert_sum_to_gauge()
convert_gauge_to_sum

convert_gauge_to_sum(aggregation_temporality, is_monotonic)

Converts incoming metrics of type "Gauge" to type "Sum", retaining the metric's datapoints and setting its aggregation temporality and monotonicity accordingly. Noop for metrics that are not of type "Gauge".

aggregation_temporality is a string ("cumulative" or "delta") that specifies the resultant metric's aggregation temporality. is_monotonic is a boolean that specifies the resultant metric's monotonicity.

NOTE: This function may cause a metric to break semantics for Sum metrics. Use at your own risk.

Examples:

  • convert_gauge_to_sum("cumulative", false)

  • convert_gauge_to_sum("delta", true)

extract_count_metric

[!NOTE]
This function supports Histograms, ExponentialHistograms and Summaries.

extract_count_metric(is_monotonic)

The extract_count_metric function creates a new Sum metric from a Histogram, ExponentialHistogram or Summary's count value. A metric will only be created if there is at least one data point.

is_monotonic is a boolean representing the monotonicity of the new metric.

The name for the new metric will be <original metric name>_count. The fields that are copied are: timestamp, starttimestamp, attibutes, description, and aggregation_temporality. As metrics of type Summary don't have an aggregation_temporality field, this field will be set to AGGREGATION_TEMPORALITY_CUMULATIVE for those metrics.

The new metric that is created will be passed to all subsequent statements in the metrics statements list.

[!WARNING]
This function may cause a metric to break semantics for Sum metrics. Use only if you're confident you know what the resulting monotonicity should be.

Examples:

  • extract_count_metric(true)

  • extract_count_metric(false)

extract_sum_metric

[!NOTE]
This function supports Histograms, ExponentialHistograms and Summaries.

extract_sum_metric(is_monotonic)

The extract_sum_metric function creates a new Sum metric from a Histogram, ExponentialHistogram or Summary's sum value. If the sum value of a Histogram or ExponentialHistogram data point is missing, no data point is added to the output metric. A metric will only be created if there is at least one data point.

is_monotonic is a boolean representing the monotonicity of the new metric.

The name for the new metric will be <original metric name>_sum. The fields that are copied are: timestamp, starttimestamp, attibutes, description, and aggregation_temporality. As metrics of type Summary don't have an aggregation_temporality field, this field will be set to AGGREGATION_TEMPORALITY_CUMULATIVE for those metrics.

The new metric that is created will be passed to all subsequent statements in the metrics statements list.

[!WARNING]
This function may cause a metric to break semantics for Sum metrics. Use only if you're confident you know what the resulting monotonicity should be.

Examples:

  • extract_sum_metric(true)

  • extract_sum_metric(false)

convert_summary_count_val_to_sum

convert_summary_count_val_to_sum(aggregation_temporality, is_monotonic)

The convert_summary_count_val_to_sum function creates a new Sum metric from a Summary's count value.

aggregation_temporality is a string ("cumulative" or "delta") representing the desired aggregation temporality of the new metric. is_monotonic is a boolean representing the monotonicity of the new metric.

The name for the new metric will be <summary metric name>_count. The fields that are copied are: timestamp, starttimestamp, attibutes, and description. The new metric that is created will be passed to all functions in the metrics statements list. Function conditions will apply.

NOTE: This function may cause a metric to break semantics for Sum metrics. Use at your own risk.

Examples:

  • convert_summary_count_val_to_sum("delta", true)

  • convert_summary_count_val_to_sum("cumulative", false)

convert_summary_sum_val_to_sum

convert_summary_sum_val_to_sum(aggregation_temporality, is_monotonic)

The convert_summary_sum_val_to_sum function creates a new Sum metric from a Summary's sum value.

aggregation_temporality is a string ("cumulative" or "delta") representing the desired aggregation temporality of the new metric. is_monotonic is a boolean representing the monotonicity of the new metric.

The name for the new metric will be <summary metric name>_sum. The fields that are copied are: timestamp, starttimestamp, attibutes, and description. The new metric that is created will be passed to all functions in the metrics statements list. Function conditions will apply.

NOTE: This function may cause a metric to break semantics for Sum metrics. Use at your own risk.

Examples:

  • convert_summary_sum_val_to_sum("delta", true)

  • convert_summary_sum_val_to_sum("cumulative", false)

copy_metric

copy_metric(Optional[name], Optional[description], Optional[unit])

The copy_metric function copies the current metric, adding it to the end of the metric slice.

name is an optional string. description is an optional string. unit is an optional string.

The new metric will be exactly the same as the current metric. You can use the optional parameters to set the new metric's name, description, and unit.

NOTE: The new metric is appended to the end of the metric slice and therefore will be included in all the metric statements. It is a best practice to ALWAYS include a Where clause when copying a metric that WILL NOT match the new metric.

Examples:

  • copy_metric(name="http.request.status_code", unit="s") where name == "http.status_code

  • copy_metric(desc="new desc") where description == "old desc"

Examples

Perform transformation if field does not exist

Set attribute test to "pass" if the attribute test does not exist:

transform:
  error_mode: ignore
  trace_statements:
    - context: span
      statements:
        # accessing a map with a key that does not exist will return nil. 
        - set(attributes["test"], "pass") where attributes["test"] == nil
Rename attribute

There are 2 ways to rename an attribute key:

You can either set a new attribute and delete the old:

transform:
  error_mode: ignore
  trace_statements:
    - context: resource
      statements:
        - set(attributes["namespace"], attributes["k8s.namespace.name"])
        - delete_key(attributes, "k8s.namespace.name") 

Or you can update the key using regex:

transform:
  error_mode: ignore
  trace_statements:
    - context: resource
      statements:
        - replace_all_patterns(attributes, "key", "k8s\\.namespace\\.name", "namespace")
Move field to attribute

Set attribute body to the value of the log body:

transform:
  error_mode: ignore
  log_statements:
    - context: log
      statements: 
        - set(attributes["body"], body)
Combine two attributes

Set attribute test to the value of attributes "foo" and "bar" combined.

transform:
  error_mode: ignore
  trace_statements:
    - context: resource
      statements:
        # Use Concat function to combine any number of string, separated by a delimiter.
        - set(attributes["test"], Concat([attributes["foo"], attributes["bar"]], " "))
Parsing JSON logs

Given the following json body

{
  "name": "log",
  "attr1": "foo",
  "attr2": "bar",
  "nested": {
    "attr3": "example"
  }
}

add specific fields as attributes on the log:

transform:
  error_mode: ignore
  log_statements:
    - context: log
      statements:
        # Parse body as JSON and merge the resulting map with the cache map, ignoring non-json bodies.
        # cache is a field exposed by OTTL that is a temporary storage place for complex operations.
        - merge_maps(cache, ParseJSON(body), "upsert") where IsMatch(body, "^\\{") 
          
        # Set attributes using the values merged into cache.
        # If the attribute doesn't exist in cache then nothing happens.
        - set(attributes["attr1"], cache["attr1"])
        - set(attributes["attr2"], cache["attr2"])
        
        # To access nested maps you can chain index ([]) operations.
        # If nested or attr3 do no exist in cache then nothing happens.
        - set(attributes["nested.attr3"], cache["nested"]["attr3"])
Get Severity of an Unstructured Log Body

Given the following unstructured log body

[2023-09-22 07:38:22,570] INFO [Something]: some interesting log

You can find the severity using IsMatch:

transform:
  error_mode: ignore
  log_statements:
    - context: log
      statements:
        - set(severity_number, SEVERITY_NUMBER_INFO) where IsString(body) and IsMatch(body, "\\sINFO\\s")
        - set(severity_number, SEVERITY_NUMBER_WARN) where IsString(body) and IsMatch(body, "\\sWARN\\s")
        - set(severity_number, SEVERITY_NUMBER_ERROR) where IsString(body) and IsMatch(body, "\\sERROR\\s")

Contributing

See CONTRIBUTING.md.

Warnings

The transform processor's implementation of the OpenTelemetry Transformation Language (OTTL) allows users to modify all aspects of their telemetry. Some specific risks are listed below, but this is not an exhaustive list. In general, understand your data before using the transform processor.

  • Unsound Transformations: Several Metric-only functions allow you to transform one metric data type to another or create new metrics from an existing metrics. Transformations between metric data types are not defined in the metrics data model. These functions have the expectation that you understand the incoming data and know that it can be meaningfully converted to a new metric data type or can meaningfully be used to create new metrics.
    • Although the OTTL allows the set function to be used with metric.data_type, its implementation in the transform processor is NOOP. To modify a data type you must use a function specific to that purpose.
  • Identity Conflict: Transformation of metrics have the potential to affect the identity of a metric leading to an Identity Crisis. Be especially cautious when transforming metric name and when reducing/changing existing attributes. Adding new attributes is safe.
  • Orphaned Telemetry: The processor allows you to modify span_id, trace_id, and parent_span_id for traces and span_id, and trace_id logs. Modifying these fields could lead to orphaned spans or logs.

Documentation

Overview

Package transformprocessor contains the logic to execute telemetry transform based on the OpenTelemetry Transformation Language.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewFactory

func NewFactory() processor.Factory

Types

type Config

type Config struct {
	// ErrorMode determines how the processor reacts to errors that occur while processing a statement.
	// Valid values are `ignore` and `propagate`.
	// `ignore` means the processor ignores errors returned by statements and continues on to the next statement. This is the recommended mode.
	// `propagate` means the processor returns the error up the pipeline.  This will result in the payload being dropped from the collector.
	// The default value is `propagate`.
	ErrorMode ottl.ErrorMode `mapstructure:"error_mode"`

	TraceStatements  []common.ContextStatements `mapstructure:"trace_statements"`
	MetricStatements []common.ContextStatements `mapstructure:"metric_statements"`
	LogStatements    []common.ContextStatements `mapstructure:"log_statements"`
}

Config defines the configuration for the processor.

func (*Config) Validate added in v0.45.0

func (c *Config) Validate() error

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL