Skip to content

6195: DelegatingMetricData.#7229

Merged
jack-berg merged 7 commits intoopen-telemetry:mainfrom
vasantteja:teja/creating-delegating-metric-data
Apr 11, 2025
Merged

6195: DelegatingMetricData.#7229
jack-berg merged 7 commits intoopen-telemetry:mainfrom
vasantteja:teja/creating-delegating-metric-data

Conversation

@vasantteja
Copy link
Contributor

This Change

Context

This PR tries to solve this ticket. I preserved the original PR that was initially raised to add this class but I added a couple other enhancements that were mentioned in the PR. @jack-berg proposed two approaches for exposing other PointData interfaces:

  1. Creating Delegating Classes
  2. Adding Static Create Methods to Interfaces

I went with the second approach because it keeps the API simpler, consistent and avoids code duplication..

Implementation Example

Finally I am thinking people might be using this class with Metric Exporter as below:

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.data.Data;
import io.opentelemetry.sdk.metrics.data.DelegatingMetricData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.data.MetricDataType;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.resources.Resource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Function;

/**
 * A MetricExporter that transforms metrics using a custom DelegatingMetricData implementation
 * before exporting them through a delegate exporter.
 */
public class TransformingMetricExporter implements MetricExporter {
    private final MetricExporter delegate;
    private final Function<MetricData, MetricData> transformer;

    /**
     * Creates a new TransformingMetricExporter.
     *
     * @param delegate The underlying exporter to send metrics to
     * @param namePrefix Prefix to add to all metric names
     * @param scaleFactor Factor to multiply all numeric values by (if applicable)
     */
    public TransformingMetricExporter(MetricExporter delegate, String namePrefix, double scaleFactor) {
        this.delegate = delegate;
        this.transformer = metric -> new CustomMetricData(metric, namePrefix, scaleFactor);
    }

    @Override
    public CompletableResultCode export(Collection<MetricData> metrics) {
        // Transform each metric before delegating to the wrapped exporter
        Collection<MetricData> transformedMetrics = new ArrayList<>(metrics.size());
        for (MetricData metric : metrics) {
            transformedMetrics.add(transformer.apply(metric));
        }
        return delegate.export(transformedMetrics);
    }

    @Override
    public CompletableResultCode flush() {
        return delegate.flush();
    }

    @Override
    public CompletableResultCode shutdown() {
        return delegate.shutdown();
    }

    /**
     * Custom implementation of DelegatingMetricData that modifies metric names and scales values.
     */
    private static class CustomMetricData extends DelegatingMetricData {
        private final String name;
        private final double scaleFactor;

        CustomMetricData(MetricData delegate, String namePrefix, double scaleFactor) {
            super(delegate);
            this.name = namePrefix + delegate.getName();
            this.scaleFactor = scaleFactor;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public Data<?> getData() {
            // Here you could implement value scaling logic based on the metric type
            // For example, you could create custom scaled versions of:
            // - LongSumData/DoubleSumData values
            // - HistogramData/ExponentialHistogramData values
            // - etc.
            
            return super.getData();
        }
        
        @Override
        public String toString() {
            return "CustomMetricData{name='" + name + "', scaled by " + scaleFactor + 
                   ", delegate=" + super.toString() + "}";
        }
    }
}
@vasantteja vasantteja requested a review from a team as a code owner March 30, 2025 23:25
@codecov
Copy link

codecov bot commented Mar 30, 2025

Codecov Report

Attention: Patch coverage is 89.61039% with 8 lines in your changes missing coverage. Please review.

Project coverage is 89.96%. Comparing base (10eda19) to head (055fe19).
Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
...ntelemetry/sdk/testing/metrics/TestMetricData.java 61.53% 5 Missing ⚠️
...lemetry/sdk/metrics/data/DelegatingMetricData.java 95.65% 0 Missing and 2 partials ⚠️
...io/opentelemetry/sdk/metrics/data/SummaryData.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7229      +/-   ##
============================================
- Coverage     89.98%   89.96%   -0.02%     
- Complexity     6685     6719      +34     
============================================
  Files           749      765      +16     
  Lines         20191    20268      +77     
  Branches       1977     1985       +8     
============================================
+ Hits          18168    18234      +66     
- Misses         1431     1439       +8     
- Partials        592      595       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
Copy link
Member

@jack-berg jack-berg left a comment

Choose a reason for hiding this comment

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

Just a handful of small comments. Looks pretty good!

*
* @return a ExponentialHistogramBuckets.
*/
@SuppressWarnings("TooManyParameters")
Copy link
Member

Choose a reason for hiding this comment

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

Looks like you lifted this from ImmutableExponentialHistogramBuckets.create, but I don't think its needed in either place

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried resolving it but the build was failing with parameter overload error. I had to keep this in place to resolve it. Any suggestion on how I can resolve it?

@jack-berg
Copy link
Member

This PR also resolves #4389.

Co-authored-by: jack-berg <34418638+jack-berg@users.noreply.github.com>
Copy link
Member

@jack-berg jack-berg left a comment

Choose a reason for hiding this comment

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

Just a handful of minor comments!

Copy link
Member

@jack-berg jack-berg left a comment

Choose a reason for hiding this comment

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

Thanks!

@jack-berg jack-berg merged commit 9cb6365 into open-telemetry:main Apr 11, 2025
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants