Skip to content

Commit 15cf51d

Browse files
authored
Prometheus compatibility (#1032)
1 parent 8e8807d commit 15cf51d

26 files changed

+642
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
plugins {
2+
id("java")
3+
}
4+
5+
val moduleName by extra { "io.opentelemetry.examples.docs.prometheus.migration" }
6+
7+
dependencies {
8+
implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.22.0"))
9+
10+
implementation("io.opentelemetry:opentelemetry-api")
11+
implementation("io.opentelemetry:opentelemetry-sdk")
12+
implementation("io.opentelemetry:opentelemetry-exporter-prometheus")
13+
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
14+
15+
implementation("io.prometheus:prometheus-metrics-core:1.3.1")
16+
implementation("io.prometheus:prometheus-metrics-exporter-httpserver:1.3.1")
17+
implementation("io.prometheus:prometheus-metrics-exporter-opentelemetry:1.3.1")
18+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.DoubleCounter;
7+
import io.opentelemetry.api.metrics.Meter;
8+
9+
public class OtelCounter {
10+
// Preallocate attribute keys and, when values are static, entire Attributes objects.
11+
private static final AttributeKey<String> ZONE = AttributeKey.stringKey("zone");
12+
private static final Attributes UPSTAIRS = Attributes.of(ZONE, "upstairs");
13+
private static final Attributes DOWNSTAIRS = Attributes.of(ZONE, "downstairs");
14+
15+
public static void counterUsage(OpenTelemetry openTelemetry) {
16+
Meter meter = openTelemetry.getMeter("smart.home");
17+
// HVAC on-time is fractional — use ofDoubles() to get a DoubleCounter.
18+
// No upfront label declaration: attributes are provided at record time.
19+
DoubleCounter hvacOnTime =
20+
meter
21+
.counterBuilder("hvac.on")
22+
.setDescription("Total time the HVAC system has been running")
23+
.setUnit("s")
24+
.ofDoubles()
25+
.build();
26+
27+
hvacOnTime.add(127.5, UPSTAIRS);
28+
hvacOnTime.add(3600.0, DOWNSTAIRS);
29+
}
30+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.Meter;
7+
8+
public class OtelCounterCallback {
9+
private static final AttributeKey<String> ZONE = AttributeKey.stringKey("zone");
10+
private static final Attributes UPSTAIRS = Attributes.of(ZONE, "upstairs");
11+
private static final Attributes DOWNSTAIRS = Attributes.of(ZONE, "downstairs");
12+
13+
public static void counterCallbackUsage(OpenTelemetry openTelemetry) {
14+
Meter meter = openTelemetry.getMeter("smart.home");
15+
// Each zone has its own smart energy meter tracking cumulative joule totals.
16+
// Use an asynchronous counter to report those values when a MetricReader
17+
// collects metrics, without maintaining separate counters in application code.
18+
meter
19+
.counterBuilder("energy.consumed")
20+
.setDescription("Total energy consumed")
21+
.setUnit("J")
22+
.ofDoubles()
23+
.buildWithCallback(
24+
measurement -> {
25+
measurement.record(SmartHomeDevices.totalEnergyJoules("upstairs"), UPSTAIRS);
26+
measurement.record(SmartHomeDevices.totalEnergyJoules("downstairs"), DOWNSTAIRS);
27+
});
28+
}
29+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.DoubleGauge;
7+
import io.opentelemetry.api.metrics.Meter;
8+
9+
public class OtelGauge {
10+
// Preallocate attribute keys and, when values are static, entire Attributes objects.
11+
private static final AttributeKey<String> ZONE = AttributeKey.stringKey("zone");
12+
private static final Attributes UPSTAIRS = Attributes.of(ZONE, "upstairs");
13+
private static final Attributes DOWNSTAIRS = Attributes.of(ZONE, "downstairs");
14+
15+
public static void gaugeUsage(OpenTelemetry openTelemetry) {
16+
Meter meter = openTelemetry.getMeter("smart.home");
17+
DoubleGauge thermostatSetpoint =
18+
meter
19+
.gaugeBuilder("thermostat.setpoint")
20+
.setDescription("Target temperature set on the thermostat")
21+
.setUnit("Cel")
22+
.build();
23+
24+
thermostatSetpoint.set(22.5, UPSTAIRS);
25+
thermostatSetpoint.set(20.0, DOWNSTAIRS);
26+
}
27+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.Meter;
7+
8+
public class OtelGaugeCallback {
9+
private static final AttributeKey<String> ROOM = AttributeKey.stringKey("room");
10+
private static final Attributes LIVING_ROOM = Attributes.of(ROOM, "living_room");
11+
private static final Attributes BEDROOM = Attributes.of(ROOM, "bedroom");
12+
13+
public static void gaugeCallbackUsage(OpenTelemetry openTelemetry) {
14+
Meter meter = openTelemetry.getMeter("smart.home");
15+
// Temperature sensors maintain their own readings in firmware.
16+
// Use an asynchronous gauge to report those values when a MetricReader
17+
// collects metrics, without maintaining separate gauges in application code.
18+
meter
19+
.gaugeBuilder("room.temperature")
20+
.setDescription("Current temperature in the room")
21+
.setUnit("Cel")
22+
.buildWithCallback(
23+
measurement -> {
24+
measurement.record(SmartHomeDevices.livingRoomTemperatureCelsius(), LIVING_ROOM);
25+
measurement.record(SmartHomeDevices.bedroomTemperatureCelsius(), BEDROOM);
26+
});
27+
}
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.DoubleHistogram;
7+
import io.opentelemetry.api.metrics.Meter;
8+
import java.util.List;
9+
10+
public class OtelHistogram {
11+
// Preallocate attribute keys and, when values are static, entire Attributes objects.
12+
private static final AttributeKey<String> DEVICE_TYPE = AttributeKey.stringKey("device_type");
13+
private static final Attributes THERMOSTAT = Attributes.of(DEVICE_TYPE, "thermostat");
14+
private static final Attributes LOCK = Attributes.of(DEVICE_TYPE, "lock");
15+
16+
public static void histogramUsage(OpenTelemetry openTelemetry) {
17+
Meter meter = openTelemetry.getMeter("smart.home");
18+
// setExplicitBucketBoundariesAdvice() sets default boundaries as a hint to the SDK.
19+
// Views configured at the SDK level take precedence over this advice.
20+
DoubleHistogram deviceCommandDuration =
21+
meter
22+
.histogramBuilder("device.command.duration")
23+
.setDescription("Time to receive acknowledgment from a smart home device")
24+
.setUnit("s")
25+
.setExplicitBucketBoundariesAdvice(List.of(0.1, 0.25, 0.5, 1.0, 2.5, 5.0))
26+
.build();
27+
28+
deviceCommandDuration.record(0.35, THERMOSTAT);
29+
deviceCommandDuration.record(0.85, LOCK);
30+
}
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.common.AttributeKey;
5+
import io.opentelemetry.api.common.Attributes;
6+
import io.opentelemetry.api.metrics.DoubleHistogram;
7+
import io.opentelemetry.api.metrics.Meter;
8+
import java.util.List;
9+
10+
public class OtelHistogramAsSummary {
11+
private static final AttributeKey<String> DEVICE_TYPE = AttributeKey.stringKey("device_type");
12+
private static final Attributes THERMOSTAT = Attributes.of(DEVICE_TYPE, "thermostat");
13+
private static final Attributes LOCK = Attributes.of(DEVICE_TYPE, "lock");
14+
15+
public static void summaryReplacement(OpenTelemetry openTelemetry) {
16+
Meter meter = openTelemetry.getMeter("smart.home");
17+
// No explicit bucket boundaries: captures count and sum, a good stand-in for most
18+
// Summary use cases. For quantile estimation, add boundaries that bracket your thresholds.
19+
DoubleHistogram deviceCommandDuration =
20+
meter
21+
.histogramBuilder("device.command.duration")
22+
.setDescription("Time to receive acknowledgment from a smart home device")
23+
.setUnit("s")
24+
.setExplicitBucketBoundariesAdvice(List.of())
25+
.build();
26+
27+
deviceCommandDuration.record(0.35, THERMOSTAT);
28+
deviceCommandDuration.record(0.85, LOCK);
29+
}
30+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package otel;
2+
3+
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
4+
import io.opentelemetry.sdk.metrics.Aggregation;
5+
import io.opentelemetry.sdk.metrics.InstrumentType;
6+
import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector;
7+
8+
public class OtelHistogramExponentialExporter {
9+
static OtlpHttpMetricExporter createExporter() {
10+
// Configure the exporter to use exponential histograms for all histogram instruments.
11+
// This is the preferred approach — it applies globally without modifying instrumentation code.
12+
return OtlpHttpMetricExporter.builder()
13+
.setEndpoint("http://localhost:4318")
14+
.setDefaultAggregationSelector(
15+
DefaultAggregationSelector.getDefault()
16+
.with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram()))
17+
.build();
18+
}
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package otel;
2+
3+
import io.opentelemetry.sdk.metrics.Aggregation;
4+
import io.opentelemetry.sdk.metrics.InstrumentSelector;
5+
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
6+
import io.opentelemetry.sdk.metrics.View;
7+
8+
public class OtelHistogramExponentialView {
9+
static SdkMeterProvider createMeterProvider() {
10+
// Use a view for per-instrument control — select a specific instrument by name
11+
// to use exponential histograms while keeping explicit buckets for others.
12+
return SdkMeterProvider.builder()
13+
.registerView(
14+
InstrumentSelector.builder().setName("device.command.duration").build(),
15+
View.builder().setAggregation(Aggregation.base2ExponentialBucketHistogram()).build())
16+
.build();
17+
}
18+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package otel;
2+
3+
import io.opentelemetry.api.OpenTelemetry;
4+
import io.opentelemetry.api.metrics.LongCounter;
5+
import io.opentelemetry.api.metrics.Meter;
6+
import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
7+
import io.opentelemetry.sdk.OpenTelemetrySdk;
8+
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
9+
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
10+
import java.time.Duration;
11+
12+
public class OtelOtlpInit {
13+
public static void main(String[] args) throws InterruptedException {
14+
// Configure the SDK: export metrics over OTLP/HTTP on a fixed interval.
15+
OpenTelemetrySdk sdk =
16+
OpenTelemetrySdk.builder()
17+
.setMeterProvider(
18+
SdkMeterProvider.builder()
19+
.registerMetricReader(
20+
PeriodicMetricReader.builder(
21+
OtlpHttpMetricExporter.builder()
22+
.setEndpoint("http://localhost:4318")
23+
.build())
24+
.setInterval(Duration.ofSeconds(60))
25+
.build())
26+
.build())
27+
.build();
28+
Runtime.getRuntime().addShutdownHook(new Thread(sdk::close));
29+
30+
// Instrumentation code uses the OpenTelemetry API type, not the SDK type directly.
31+
OpenTelemetry openTelemetry = sdk;
32+
33+
Meter meter = openTelemetry.getMeter("smart.home");
34+
LongCounter doorOpens =
35+
meter
36+
.counterBuilder("door.opens")
37+
.setDescription("Total number of times a door has been opened")
38+
.build();
39+
40+
doorOpens.add(1);
41+
42+
Thread.currentThread().join(); // sleep forever
43+
}
44+
}

0 commit comments

Comments
 (0)