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

How to set histogram metric bucket size after meter registration #660

Open
JustasL opened this issue Dec 19, 2024 · 9 comments
Open

How to set histogram metric bucket size after meter registration #660

JustasL opened this issue Dec 19, 2024 · 9 comments

Comments

@JustasL
Copy link

JustasL commented Dec 19, 2024

Hello, is there a way to set histogram bucket after the metes has been registered?
From looking at source code and examples it does not seem like it's possible on iOS

My Android colleague is using this method, like this:

override fun trackHistogram(event: HistogramEvent) {
    with(event) {
        val otelAttributes = attributes.toOtelAttributes()
        val builder = GlobalOpenTelemetry.getMeter("")
            .histogramBuilder(eventName)

        if (buckets != null) {
            builder.setExplicitBucketBoundariesAdvice(buckets)
        }

        builder
            .build()
            .record(value, otelAttributes)

    }
}

Where as iOS histogram builder does not have such option

@bryce-b
Copy link
Member

bryce-b commented Jan 6, 2025

Hi @JustasL,
You should be able to set buckets if you explicitly construct the Histogram Aggregator:

 Aggregations.explicitBucketHistogram(buckets: [0.0, .... ])

and that can be set when building the View:

StableView.builder().withAggregation( Aggregations.explicitBucketHistogram(buckets: ...))

@JustasL
Copy link
Author

JustasL commented Jan 13, 2025

@bryce-b yes, but then I need to register multiple histogram instruments, right?

Can I register new instruments, after I've called registerStableMeterProvider?

Because on Android my colleagues can specify histogram bucket right before collecting the data, and on iOS it's not possible to

Here's how I ma currently doing it on iOS:

        OpenTelemetry.registerStableMeterProvider(
            meterProvider: StableMeterProviderBuilder()
                )
                .registerView(
                    selector: InstrumentSelector.builder()
                        .setInstrument(name: histogramInstrumentName)
                        .setInstrument(type: .histogram)
                        .build(),
                    view: StableView
                        .builder()
                        .withAggregation(aggregation: Aggregations.explicitBucketHistogram(buckets: []))
                        .build()
                ))
                .registerMetricReader(
                    reader: StablePeriodicMetricReaderBuilder(exporter: otlpHttpMetricExporter)
                        .setInterval(timeInterval: uploadInterval)
                        .build()
                )
                .build()
        )

        guard let stableMeterProvider = OpenTelemetry.instance.stableMeterProvider else {
            throw OpenTelemetryMetricsTrackerError.failedToCreateMeterProvider
        }

        meter = stableMeterProvider
            .meterBuilder(name: "Meter")
            .build()

And then:

    func trackHistogram(event: HistogramEvent) {
        var historgram = meter.histogramBuilder(name: histogramInstrumentName).build()

        historgram.record(
            value: event.value,
            attributes: event.attributes
        )
    }

@bryce-b
Copy link
Member

bryce-b commented Jan 17, 2025

You'll need to register multiple views and specify an InstrumentSelector.

.registerView(selector: IntrumentSelector.builder().setInstrument(name: HistogramInstrumentName).build(),
 view: StableView.builder().withAggregation(aggregation: Aggregations.explicitBucketHistogram(buckets: [...])))
...

This tells the MeterProvider which view is responsible for which histogram meter.

@JustasL
Copy link
Author

JustasL commented Jan 17, 2025

Meaning I can not modify histogram buckets at runtime, after StableMeterProvider was setup?
Is there any plans to align with Android, where such functionality is possible?

@bryce-b
Copy link
Member

bryce-b commented Jan 17, 2025

It would appear that the functionality provided in Android is not within the OpenTelemetry spec, so there is no intention to align this project with it. It is possible to create additional StableMeterProviders outside of the registered one, if that might help your problem, but updating the bucketing on the fly seems an unusual use-case

@JustasL
Copy link
Author

JustasL commented Jan 18, 2025

It is possible to create additional StableMeterProviders outside of the registered one

Could you share and example, or documentation? That would help greatly

@bryce-b
Copy link
Member

bryce-b commented Jan 21, 2025

It's done the same way as if you were registering it with the singleton, you just have to manage the StableMeterProvider yourself. https://github.com/open-telemetry/opentelemetry-swift/blob/main/Examples/Stable%20Metric%20Sample/main.swift#L61C60-L64C13

@JustasL
Copy link
Author

JustasL commented Jan 23, 2025

updating the bucketing on the fly seems an unusual use-case
It's not about updating the single bucket on runtime, but using different buckets, which sizes are not know at compile time

Let's say one flow needs bucket size A, other will need size B, how can I support these different buckets at runtime?

Sounds like I need manually keep track of created buckets sizes, and then if new bucket size is needed, I need to call registerStableMeterProvider with all of the parameters?
As registerView exist only on StableMeterProviderBuilder and not StableMeterProvider

This seems to be very basic histogram requirement, I am not fully sure why it's not implemented, and there is no plan to support specifying bucket size dynamically at runtime

@bryce-b
Copy link
Member

bryce-b commented Jan 23, 2025

Let's say one flow needs bucket size A, other will need size B, how can I support these different buckets at runtime?

When you say flow are you meaning a user flow, an instrument, or something else? Are you saying that you don't know what you want your buckets to be before capturing data, so you want to restructure the bucketing while data is being aggregated?

If you mean you have two instruments you can assign two separate views to each which will produce two separate Views with histogram aggregators.
e.g.:

 .registerView(
                    selector: InstrumentSelector.builder()
                        .setInstrument(name: "Flow A")
                        .setInstrument(type: .histogram)
                        .build(),
                    view: StableView
                        .builder()
                        .withAggregation(aggregation: Aggregations.explicitBucketHistogram(buckets: FlowA_Buckets))
                        .build()
 .registerView(
                    selector: InstrumentSelector.builder()
                        .setInstrument(name: "Flow B")
                        .setInstrument(type: .histogram)
                        .build(),
                    view: StableView
                        .builder()
                        .withAggregation(aggregation: Aggregations.explicitBucketHistogram(buckets: FlowB_buckets))
                        .build()

where there are two instruments:

        var historgram = meter.histogramBuilder(name: "Flow A").build()

        historgram.record(
            value: event.value,
            attributes: event.attributes
        var historgram = meter.histogramBuilder(name: "Flow B").build()

        historgram.record(
            value: event.value,
            attributes: event.attributes

If you want to produce two different bucketing for the same instrument. you can do it like this:

 .registerView(
                    selector: InstrumentSelector.builder()
                        .setInstrument(name: "Histogram Meter")
                        .setInstrument(type: .histogram)
                        .build(),
                    view: StableView
                        .builder()
                        .withName("FlowA Histogram")
                        .withAggregation(aggregation: Aggregations.explicitBucketHistogram(buckets: FlowA_Buckets))
                        .build()
 .registerView(
                    selector: InstrumentSelector.builder()
                        .setInstrument(name: "Histogram Meter")
                        .setInstrument(type: .histogram)
                        .build(),
                    view: StableView
                        .builder()
                        .withName("FlowB Histogram")
                        .withAggregation(aggregation: Aggregations.explicitBucketHistogram(buckets: FlowB_buckets))
                        .build()

Where the instrument is:

        var historgram = meter.histogramBuilder(name: "Histogram Meter").build()

        historgram.record(
            value: event.value,
            attributes: event.attributes

As registerView exist only on StableMeterProviderBuilder and not StableMeterProvider
The Java implementation is similar in that Views cannot be added after a SableMeterProvider is built, but that maybe a feature that needs to be addressed: https://opentelemetry.io/docs/specs/otel/metrics/sdk/#view "The SDK MUST provide the means to register Views with a MeterProvider."

I was looking at bit closer at the API you shared from Java, and it appears to actually be part of Instrument Advisory API, which doesn't appear to affect a view's aggregation, from what I can tell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants