Skip to content

[processor/transform] Create extract percentile transform function#45518

Merged
edmocosta merged 22 commits into
open-telemetry:mainfrom
alexcams:create-extract-percentile-transform-function
Apr 17, 2026
Merged

[processor/transform] Create extract percentile transform function#45518
edmocosta merged 22 commits into
open-telemetry:mainfrom
alexcams:create-extract-percentile-transform-function

Conversation

@alexcams
Copy link
Copy Markdown
Contributor

@alexcams alexcams commented Jan 20, 2026

Description

The extract_percentile_metric function creates a new Gauge metric from a Histogram or ExponentialHistogram by calculating the specified percentile value from the bucket counts. A metric will only be created if there is at least one data point.

percentile is a float64 value between 0 and 100 representing the desired percentile to extract (e.g., 50 for median, 95 for p95, 99 for p99).

suffix is an optional string that defines the suffix for the metric name. By default, it is set to _p{percentile} (e.g., _p50, _p95, _p99).

Link to tracking issue

Closes #44316

Testing

Included unit tests and tested manually with this configuration:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  debug:
    verbosity: detailed
    sampling_initial: 10000
    sampling_thereafter: 10000

processors:
  transform:
    error_mode: ignore
    metric_statements:
      - context: metric
        statements:
          - extract_percentile_metric(50.0)
          - extract_percentile_metric(95.0)
          - extract_percentile_metric(99.0, "_p99_custom")

service:
  telemetry:
    metrics:
      level: none
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [transform]
      exporters:
        - debug

tested with telemetrygen tool with params to generate regular histogram metrics:

./bin/telemetrygen_darwin_arm64 metrics \                    ok | 14:34:29 
  --otlp-insecure \
  --otlp-endpoint localhost:4317 \
  --metrics 1 \
  --otlp-attributes 'service.name="histogram-service"' \
  --metric-type Histogram \
  --otlp-metric-name "http.server.duration"

Obtained output:

Metric #1
Descriptor:
     -> Name: http.server.duration_p50
     -> Description:  (p50)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 137.500000
Metric #2
Descriptor:
     -> Name: http.server.duration_p95
     -> Description:  (p95)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 750.000000
Metric #3
Descriptor:
     -> Name: http.server.duration_p99_custom
     -> Description:  (p99)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 750.000000 # p99 is placed in last bucket which is [bucket_lower_bound, + Inf ]. Cannot interpolate with Inf values so it is necessary to use lowerbound (750)

Tested with Exponential histogram as well:

./bin/telemetrygen_darwin_arm64 metrics \                    ok | 14:42:11 
  --otlp-insecure \
  --otlp-endpoint localhost:4317 \
  --metrics 1 \
  --otlp-attributes 'service.name="histogram-service"' \
  --metric-type ExponentialHistogram \
  --otlp-metric-name "http.server.duration"

and got the output:

Metric #1
Descriptor:
     -> Name: http.server.duration_p50
     -> Description:  (p50)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 545.301038
Metric #2
Descriptor:
     -> Name: http.server.duration_p95
     -> Description:  (p95)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 961.465253
Metric #3
Descriptor:
     -> Name: http.server.duration_p99_custom
     -> Description:  (p99)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 1024.000000

Documentation

Added proper documentation to transform processor README.md.

@alexcams
Copy link
Copy Markdown
Contributor Author

/rerun

@alexcams alexcams changed the title Create extract percentile transform function [processor/transform] Create extract percentile transform function Jan 20, 2026
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
Copy link
Copy Markdown
Contributor

@atoulme atoulme left a comment

Choose a reason for hiding this comment

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

some nits, LGTM - needs a codeowner review.

@alexcams
Copy link
Copy Markdown
Contributor Author

alexcams commented Feb 4, 2026

/rerun

Copy link
Copy Markdown
Contributor

@edmocosta edmocosta left a comment

Choose a reason for hiding this comment

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

Thank you for working on this @alexcams, I've to admit it's not so easy for me to review this PR as the logic is a bit complex! I've left a few questions/suggestions related to that, and I'm glad to discuss it further :)

Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
@alexcams
Copy link
Copy Markdown
Contributor Author

/rerun

@github-actions
Copy link
Copy Markdown
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added the Stale label Feb 26, 2026
@edmocosta edmocosta removed the Stale label Feb 26, 2026
Comment thread processor/transformprocessor/internal/metrics/metric_datapoint.go Outdated
Comment thread processor/transformprocessor/internal/metrics/metric_datapoint.go Outdated
Comment thread processor/transformprocessor/internal/metrics/metric_datapoint.go Outdated
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
@alexcams alexcams requested a review from edmocosta March 11, 2026 16:16
Comment thread processor/transformprocessor/README.md Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 1, 2026

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added the Stale label Apr 1, 2026
@edmocosta edmocosta removed the Stale label Apr 1, 2026
@alexcams alexcams force-pushed the create-extract-percentile-transform-function branch from 1b646e3 to cf8766e Compare April 7, 2026 13:29
@alexcams alexcams requested a review from edmocosta April 8, 2026 09:01
Comment thread processor/transformprocessor/internal/metrics/func_extract_percentile_metric.go Outdated
@alexcams alexcams force-pushed the create-extract-percentile-transform-function branch from 4262fdd to f33a54c Compare April 13, 2026 07:59
@alexcams alexcams requested a review from edmocosta April 13, 2026 14:01
Comment thread processor/transformprocessor/README.md
@alexcams alexcams requested a review from edmocosta April 13, 2026 15:17
Copy link
Copy Markdown
Contributor

@edmocosta edmocosta left a comment

Choose a reason for hiding this comment

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

LGTM! Great work, @alexcams! 👏

@edmocosta
Copy link
Copy Markdown
Contributor

edmocosta commented Apr 13, 2026

@evan-bradley @TylerHelmuth @bogdandrutu I'm planning to merge it by Friday, please let me know if you want to review it, or if you need more time for that. Thank you!

@edmocosta edmocosta merged commit 42b10c2 into open-telemetry:main Apr 17, 2026
191 checks passed
@otelbot
Copy link
Copy Markdown
Contributor

otelbot Bot commented Apr 17, 2026

Thank you for your contribution @alexcams! 🎉 We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this survey. If you are getting started contributing, you can also join the CNCF Slack channel #opentelemetry-new-contributors to ask for guidance and get help.

AndrewCharlesHay pushed a commit to AndrewCharlesHay/opentelemetry-collector-contrib that referenced this pull request Apr 23, 2026
…pen-telemetry#45518)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
The `extract_percentile_metric` function creates a new Gauge metric from
a Histogram or ExponentialHistogram by calculating the specified
percentile value from the bucket counts. A metric will only be created
if there is at least one data point.

`percentile` is a float64 value between 0 and 100 representing the
desired percentile to extract (e.g., 50 for median, 95 for p95, 99 for
p99).

`suffix` is an optional string that defines the suffix for the metric
name. By default, it is set to `_p{percentile}` (e.g., `_p50`, `_p95`,
`_p99`).

<!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. -->
#### Link to tracking issue
Closes open-telemetry#44316

<!--Describe what testing was performed and which tests were added.-->
#### Testing
Included unit tests and tested manually with this configuration:

``` yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  debug:
    verbosity: detailed
    sampling_initial: 10000
    sampling_thereafter: 10000

processors:
  transform:
    error_mode: ignore
    metric_statements:
      - context: metric
        statements:
          - extract_percentile_metric(50.0)
          - extract_percentile_metric(95.0)
          - extract_percentile_metric(99.0, "_p99_custom")

service:
  telemetry:
    metrics:
      level: none
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [transform]
      exporters:
        - debug

```
tested with `telemetrygen` tool with params to generate regular
histogram metrics:
```
./bin/telemetrygen_darwin_arm64 metrics \                    ok | 14:34:29 
  --otlp-insecure \
  --otlp-endpoint localhost:4317 \
  --metrics 1 \
  --otlp-attributes 'service.name="histogram-service"' \
  --metric-type Histogram \
  --otlp-metric-name "http.server.duration"
```
Obtained output: 
```
Metric open-telemetry#1
Descriptor:
     -> Name: http.server.duration_p50
     -> Description:  (p50)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 137.500000
Metric open-telemetry#2
Descriptor:
     -> Name: http.server.duration_p95
     -> Description:  (p95)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 750.000000
Metric open-telemetry#3
Descriptor:
     -> Name: http.server.duration_p99_custom
     -> Description:  (p99)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 750.000000 # p99 is placed in last bucket which is [bucket_lower_bound, + Inf ]. Cannot interpolate with Inf values so it is necessary to use lowerbound (750)
```
Tested with Exponential histogram as well:
```
./bin/telemetrygen_darwin_arm64 metrics \                    ok | 14:42:11 
  --otlp-insecure \
  --otlp-endpoint localhost:4317 \
  --metrics 1 \
  --otlp-attributes 'service.name="histogram-service"' \
  --metric-type ExponentialHistogram \
  --otlp-metric-name "http.server.duration"
```
and got the output:
```
Metric open-telemetry#1
Descriptor:
     -> Name: http.server.duration_p50
     -> Description:  (p50)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 545.301038
Metric open-telemetry#2
Descriptor:
     -> Name: http.server.duration_p95
     -> Description:  (p95)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 961.465253
Metric open-telemetry#3
Descriptor:
     -> Name: http.server.duration_p99_custom
     -> Description:  (p99)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 1024.000000
```
<!--Describe the documentation added.-->
#### Documentation
Added proper documentation to transform processor `README.md`.

<!--Please delete paragraphs that you did not use before submitting.-->

---------

Co-authored-by: Edmo Vamerlatti Costa <11836452+edmocosta@users.noreply.github.com>
gracewehner pushed a commit to gracewehner/opentelemetry-collector-contrib that referenced this pull request Apr 29, 2026
…pen-telemetry#45518)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
The `extract_percentile_metric` function creates a new Gauge metric from
a Histogram or ExponentialHistogram by calculating the specified
percentile value from the bucket counts. A metric will only be created
if there is at least one data point.

`percentile` is a float64 value between 0 and 100 representing the
desired percentile to extract (e.g., 50 for median, 95 for p95, 99 for
p99).

`suffix` is an optional string that defines the suffix for the metric
name. By default, it is set to `_p{percentile}` (e.g., `_p50`, `_p95`,
`_p99`).

<!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. -->
#### Link to tracking issue
Closes open-telemetry#44316

<!--Describe what testing was performed and which tests were added.-->
#### Testing
Included unit tests and tested manually with this configuration:

``` yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  debug:
    verbosity: detailed
    sampling_initial: 10000
    sampling_thereafter: 10000

processors:
  transform:
    error_mode: ignore
    metric_statements:
      - context: metric
        statements:
          - extract_percentile_metric(50.0)
          - extract_percentile_metric(95.0)
          - extract_percentile_metric(99.0, "_p99_custom")

service:
  telemetry:
    metrics:
      level: none
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [transform]
      exporters:
        - debug

```
tested with `telemetrygen` tool with params to generate regular
histogram metrics:
```
./bin/telemetrygen_darwin_arm64 metrics \                    ok | 14:34:29 
  --otlp-insecure \
  --otlp-endpoint localhost:4317 \
  --metrics 1 \
  --otlp-attributes 'service.name="histogram-service"' \
  --metric-type Histogram \
  --otlp-metric-name "http.server.duration"
```
Obtained output: 
```
Metric open-telemetry#1
Descriptor:
     -> Name: http.server.duration_p50
     -> Description:  (p50)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 137.500000
Metric open-telemetry#2
Descriptor:
     -> Name: http.server.duration_p95
     -> Description:  (p95)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 750.000000
Metric open-telemetry#3
Descriptor:
     -> Name: http.server.duration_p99_custom
     -> Description:  (p99)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Timestamp: 2026-01-20 13:34:29.409727 +0000 UTC
Value: 750.000000 # p99 is placed in last bucket which is [bucket_lower_bound, + Inf ]. Cannot interpolate with Inf values so it is necessary to use lowerbound (750)
```
Tested with Exponential histogram as well:
```
./bin/telemetrygen_darwin_arm64 metrics \                    ok | 14:42:11 
  --otlp-insecure \
  --otlp-endpoint localhost:4317 \
  --metrics 1 \
  --otlp-attributes 'service.name="histogram-service"' \
  --metric-type ExponentialHistogram \
  --otlp-metric-name "http.server.duration"
```
and got the output:
```
Metric open-telemetry#1
Descriptor:
     -> Name: http.server.duration_p50
     -> Description:  (p50)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 545.301038
Metric open-telemetry#2
Descriptor:
     -> Name: http.server.duration_p95
     -> Description:  (p95)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 961.465253
Metric open-telemetry#3
Descriptor:
     -> Name: http.server.duration_p99_custom
     -> Description:  (p99)
     -> Unit: 
     -> DataType: Gauge
NumberDataPoints #0
StartTimestamp: 2026-01-20 13:42:11.599314 +0000 UTC
Timestamp: 2026-01-20 13:42:11.599315 +0000 UTC
Value: 1024.000000
```
<!--Describe the documentation added.-->
#### Documentation
Added proper documentation to transform processor `README.md`.

<!--Please delete paragraphs that you did not use before submitting.-->

---------

Co-authored-by: Edmo Vamerlatti Costa <11836452+edmocosta@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[processor/transform] support extracting a percentile gauge from a histogram metric

7 participants