Skip to content

Latest commit

 

History

History
164 lines (122 loc) · 6.46 KB

File metadata and controls

164 lines (122 loc) · 6.46 KB

avaje-metrics-otel-producer

Exposes avaje-metrics to OpenTelemetry using the SDK-side MetricProducer bridge.

Unlike avaje-metrics-otel-reporter, this module does not run its own reporting schedule. Metric collection happens when the OpenTelemetry SDK reader/exporter collects.

  • counters, name.count, and name.total are exported as cumulative monotonic sums
  • name.max is exported as a gauge and resets on each collection

Maven dependency

<dependency>
  <groupId>io.avaje</groupId>
  <artifactId>avaje-metrics-otel-producer</artifactId>
  <version>9.9-RC5</version>
</dependency>

This module depends on the OpenTelemetry SDK metrics bridge types, so it is intended for SDK-based setups rather than API-only setups.

Basic usage

The MetricProducer is registered with an OpenTelemetry SDK SdkMeterProvider. The registerMetricReader(...) call is where you choose how metrics are actually collected/exported. Common reader choices are:

  • PeriodicMetricReader for OTLP push/export
  • Prometheus reader/server types for Prometheus scraping

If you only need a direct Prometheus text endpoint and do not otherwise need the OpenTelemetry SDK, use avaje-metrics-prometheus instead.

Any normal OpenTelemetry metrics created from the same SdkMeterProvider are collected alongside the avaje metrics exposed by OtelMetricProducer.

Example: OTLP push

MetricReader reader =
    PeriodicMetricReader.builder(
            OtlpGrpcMetricExporter.builder()
                .setEndpoint("http://otel-collector:4317")
                .build())
        .setInterval(Duration.ofSeconds(60))
        .build();

SdkMeterProvider meterProvider = SdkMeterProvider.builder()
    .registerMetricReader(reader)
    .registerMetricProducer(
        OtelMetricProducer.builder()
            .registry(Metrics.registry())
            .timedThresholdMicros(1_000)
            .build())
    .build();

OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
    .setMeterProvider(meterProvider)
    .build();

// Normal OpenTelemetry meters are collected too
Meter meter = openTelemetry.getMeter("my.app");
LongCounter counter = meter.counterBuilder("my.app.requests").build();
counter.add(1);

Example: Prometheus

PrometheusHttpServer prometheus = PrometheusHttpServer.builder()
    .setHost("0.0.0.0")
    .setPort(9464)
    .build();

SdkMeterProvider meterProvider = SdkMeterProvider.builder()
    .registerMetricReader(prometheus)
    .registerMetricProducer(
        OtelMetricProducer.builder()
            .registry(Metrics.registry())
            .build())
    .build();

Builder options

OtelMetricProducer producer = OtelMetricProducer.builder()

    // Use a non-default MetricRegistry (defaults to Metrics.registry())
    .registry(myRegistry)

    // Skip timer metrics whose cumulative total duration is below this threshold.
    // 1_000 = skip timers with less than 1ms cumulative execution.
    .timedThresholdMicros(1_000)

    // OpenTelemetry instrumentation scope name (default: "io.avaje.metrics")
    .scopeName("io.avaje.metrics")

    .build();

Metric mapping

avaje metrics are mapped to OTEL metric data as follows:

avaje type OTEL metric data Notes
Counter cumulative LongSum monotonic, uses the configured avaje unit ({event} by default)
Timer cumulative LongSum (name.count), cumulative LongSum (name.total), LongGauge (name.max) count/total are cumulative; max resets each collection; values in microseconds (us)
Meter cumulative LongSum (name.count), cumulative LongSum (name.total), LongGauge (name.max) count/total are cumulative; max resets each collection; name.count uses {event} and name.total / name.max use the configured avaje unit (empty by default)
GaugeLong LongGauge current value at collection time; uses the configured avaje unit (empty by default)
GaugeDouble DoubleGauge current value at collection time; uses the configured avaje unit (empty by default)

Timer success and error statistics are exported separately as name and name.error, matching the existing avaje timer reporting behavior.

By default, generic meters and gauges are exported with an empty OTEL unit rather than 1. This avoids downstream Prometheus-compatible backends inferring a ratio-style suffix from a dimensionless gauge. Explicit units remain unchanged for counters ({event}) and timers (us).

If you need explicit units for non-timer metrics, use the unit-aware avaje APIs such as registry.counterBuilder("app.rows").unit("row").build(), registry.meterBuilder("app.bytes.sent").unit("By").build(), or registry.gauge("jvm.memory.used").unit("MiBy").ofLongs(supplier).

When to use this module

Use avaje-metrics-otel-producer when you want collection-time intervals controlled by the OpenTelemetry SDK reader/exporter, for example:

  • you want Prometheus / Grafana-friendly cumulative sums for count / total
  • OTLP export or Prometheus / pull-style collection should drive when metrics are read
  • you want name.max to reflect the current collection window
  • You want to avoid a separate avaje reporting scheduler

Use avaje-metrics-otel-reporter when you want the lighter scheduled reporter and do not need a MetricProducer.

If you want a convenience module that builds an OTLP-backed OpenTelemetrySdk and registers OtelMetricProducer for you, use avaje-metrics-otel.

If you also want traced timers via Metrics.timerBuilder(...).buildTraced() or buildRootTraced(), add avaje-metrics-otel-trace.

Important limitation

This bridge should still be treated as a single-reader solution for a given avaje registry/export path. Although counters and totals are cumulative, name.max resets on collection, so one reader can consume the max window before another reads it.

Supplier-backed metrics only behave cumulatively when the supplier implements MetricSupplier.collectMetrics(CollectionMode).

avaje-metrics-ebean now supports both delta and cumulative collection modes. Its cumulative timed and query max values rely on the corresponding Ebean runtime fix that resets max on each collection; without that runtime change, cumulative count and total still work but max behaves like a lifetime high-water mark.

Do not use avaje-metrics-otel-reporter and avaje-metrics-otel-producer for the same registry/export path at the same time, or you will emit duplicate telemetry.