Micrometer

compared to
opentelemetry-java

opentelemetry has no dedicated Timer type.

public class MyService {
  MeterRegistry registry;

  public void call() {
    // base units automatically set based on base unit of time of each registry
    try (Timer.ResourceSample t = Timer.resource(registry, "calls")
        .description("calls to something")
        .publishPercentileHistogram()
        .publishPercentiles(0.95)
        .serviceLevelObjectives(Duration.ofSeconds(1))
        .tags("service", "hi")) {
      try {
        // do something
        t.tag("outcome", "success");
      } catch (Exception e) {
        t.tags("outcome", "error", "exception", e.getClass().getName());
      }
    }
  }
}
public class MyService {
  Meter meter = OpenTelemetry.getMeter("registry");
  Map<String, AtomicLong> callSum = Map.of(
      "success", new AtomicLong(0),
      "failure", new AtomicLong(0)
  );

  public MyService() {
    registerCallSum("success");
    registerCallSum("failure");
  }

  private void registerCallSum(String outcome) {
    meter.doubleSumObserverBuilder("calls.sum")
        .setDescription("calls to something")
        .setConstantLabels(Map.of("service", "hi"))
        .build()
        .setCallback(result -> result.observe(
            (double) callSum.get(outcome).get() / 1e9,
            "outcome", outcome));
  }

  public void call() {
    DoubleCounter.Builder callCounter = meter
        .doubleCounterBuilder("calls.count")
        .setDescription("calls to something")
        .setConstantLabels(Map.of("service", "hi"))
        .setUnit("requests");

    long start = System.nanoTime();
    try {
      // do something
      callCounter.build().add(1, "outcome", "success");
      callSum.get("success").addAndGet(System.nanoTime() - start);
    } catch (Exception e) {
      callCounter.build().add(1, "outcome", "failure",
          "exception", e.getClass().getName());
      callSum.get("failure").addAndGet(System.nanoTime() - start);
    }
  }
}

Still lacks max, percentiles, SLOs, histograms, etc.

No clock
abstraction

No time
scaling

opentelemetry has no dedicated Timer type.

  • No tracking of a decaying maximum individual timing
  • No percentile histograms
  • No pre-computed percentiles
  • No service level objective boundaries
  • No base unit of time scaling
  • Manual base unit setting
  • No AutoCloseable sampling
  • No record(..) convenience methods

opentelemetry has no dedicated DistributionSummary type.

  • No tracking of a decaying maximum individual timing
  • No percentile histograms
  • No pre-computed percentiles
  • No service level objective boundaries
  • No sample scaling
  • Manual base unit setting

opentelemetry has no naming convention normalization.

public class NamingConvention {
  // automatically converted to camel when the 
  // registry is configured for that convention
  Counter callCounter = Metrics.counter("service.calls", 
      "service.name", "hi");

  public void call() {
    callCounter.increment();
  }
}
public class NamingConvention {
  Meter camelRegistry = OpenTelemetry.getMeter("camel");
  Meter dotRegistry = OpenTelemetry.getMeter("dot");

  DoubleCounter camelCallCounter = camelRegistry
      .doubleCounterBuilder("callsCount")
      .setDescription("calls to something")
      .setConstantLabels(Map.of("serviceName", "hi"))
      .setUnit("requests")
      .build();

  DoubleCounter dotCallCounter = dotRegistry
      .doubleCounterBuilder("calls.count")
      .setDescription("calls to something")
      .setConstantLabels(Map.of("service.name", "hi"))
      .setUnit("requests")
      .build();

  public void call() {
    camelCallCounter.add(1);
    dotCallCounter.add(1);
  }
}

opentelemetry also has no equivalent for...

  • Time gauges
  • Function timers
  • Long task timers
  • Multi-gauges
  • Weak reference vs. strong reference gauges