Browse Source

Bridge metrics batch api (#7762)

Resolves
https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/7454
Lauri Tulmin 2 years ago
parent
commit
8b552db495
24 changed files with 632 additions and 9 deletions
  1. 21 1
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/ApplicationOpenTelemetry110.java
  2. 5 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleCounterBuilder.java
  3. 5 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleGaugeBuilder.java
  4. 5 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleUpDownCounterBuilder.java
  5. 5 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongCounterBuilder.java
  6. 5 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongGaugeBuilder.java
  7. 5 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongUpDownCounterBuilder.java
  8. 2 2
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeter.java
  9. 6 2
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterBuilder.java
  10. 11 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterFactory.java
  11. 15 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterFactory14.java
  12. 7 2
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterProvider.java
  13. 8 1
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationObservableDoubleMeasurement.java
  14. 8 1
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationObservableLongMeasurement.java
  15. 11 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ObservableMeasurementWrapper.java
  16. 10 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/build.gradle.kts
  17. 25 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/OpenTelemetryApiInstrumentationModule.java
  18. 40 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/OpenTelemetryInstrumentation.java
  19. 21 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/ApplicationBatchCallback.java
  20. 60 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/ApplicationMeter115.java
  21. 18 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/ApplicationMeterFactory115.java
  22. 318 0
      instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/MeterTest.java
  23. 20 0
      opentelemetry-api-shaded-for-instrumenting/build.gradle.kts
  24. 1 0
      settings.gradle.kts

+ 21 - 1
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/ApplicationOpenTelemetry110.java

@@ -10,8 +10,11 @@ import application.io.opentelemetry.api.metrics.MeterProvider;
 import application.io.opentelemetry.api.trace.TracerProvider;
 import application.io.opentelemetry.context.propagation.ContextPropagators;
 import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.propagation.ApplicationContextPropagators;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ApplicationMeterFactory;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ApplicationMeterFactory14;
 import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ApplicationMeterProvider;
 import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_4.trace.ApplicationTracerProvider14;
+import java.lang.reflect.InvocationTargetException;
 
 public final class ApplicationOpenTelemetry110 implements OpenTelemetry {
 
@@ -31,7 +34,8 @@ public final class ApplicationOpenTelemetry110 implements OpenTelemetry {
         new ApplicationTracerProvider14(agentOpenTelemetry.getTracerProvider());
     applicationContextPropagators =
         new ApplicationContextPropagators(agentOpenTelemetry.getPropagators());
-    applicationMeterProvider = new ApplicationMeterProvider(agentOpenTelemetry.getMeterProvider());
+    applicationMeterProvider =
+        new ApplicationMeterProvider(getMeterFactory(), agentOpenTelemetry.getMeterProvider());
   }
 
   @Override
@@ -48,4 +52,20 @@ public final class ApplicationOpenTelemetry110 implements OpenTelemetry {
   public ContextPropagators getPropagators() {
     return applicationContextPropagators;
   }
+
+  private static ApplicationMeterFactory getMeterFactory() {
+    try {
+      // this class is defined in opentelemetry-api-1.15
+      Class<?> clazz =
+          Class.forName(
+              "io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15.metrics.ApplicationMeterFactory115");
+      return (ApplicationMeterFactory) clazz.getConstructor().newInstance();
+    } catch (ClassNotFoundException
+        | NoSuchMethodException
+        | InstantiationException
+        | IllegalAccessException
+        | InvocationTargetException exception) {
+      return new ApplicationMeterFactory14();
+    }
+  }
 }

+ 5 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleCounterBuilder.java

@@ -48,4 +48,9 @@ final class ApplicationDoubleCounterBuilder implements DoubleCounterBuilder {
                 applicationCallback.accept(
                     new ApplicationObservableDoubleMeasurement(agentMeasurement))));
   }
+
+  // added in 1.15.0
+  public ObservableDoubleMeasurement buildObserver() {
+    return new ApplicationObservableDoubleMeasurement(agentBuilder.buildObserver());
+  }
 }

+ 5 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleGaugeBuilder.java

@@ -48,4 +48,9 @@ final class ApplicationDoubleGaugeBuilder implements DoubleGaugeBuilder {
                 applicationCallback.accept(
                     new ApplicationObservableDoubleMeasurement(agentMeasurement))));
   }
+
+  // added in 1.15.0
+  public ObservableDoubleMeasurement buildObserver() {
+    return new ApplicationObservableDoubleMeasurement(agentBuilder.buildObserver());
+  }
 }

+ 5 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationDoubleUpDownCounterBuilder.java

@@ -49,4 +49,9 @@ final class ApplicationDoubleUpDownCounterBuilder implements DoubleUpDownCounter
                 applicationCallback.accept(
                     new ApplicationObservableDoubleMeasurement(agentMeasurement))));
   }
+
+  // added in 1.15.0
+  public ObservableDoubleMeasurement buildObserver() {
+    return new ApplicationObservableDoubleMeasurement(agentBuilder.buildObserver());
+  }
 }

+ 5 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongCounterBuilder.java

@@ -54,4 +54,9 @@ final class ApplicationLongCounterBuilder implements LongCounterBuilder {
                 applicationCallback.accept(
                     new ApplicationObservableLongMeasurement(agentMeasurement))));
   }
+
+  // added in 1.15.0
+  public ObservableLongMeasurement buildObserver() {
+    return new ApplicationObservableLongMeasurement(agentBuilder.buildObserver());
+  }
 }

+ 5 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongGaugeBuilder.java

@@ -42,4 +42,9 @@ final class ApplicationLongGaugeBuilder implements LongGaugeBuilder {
                 applicationCallback.accept(
                     new ApplicationObservableLongMeasurement(agentMeasurement))));
   }
+
+  // added in 1.15.0
+  public ObservableLongMeasurement buildObserver() {
+    return new ApplicationObservableLongMeasurement(agentBuilder.buildObserver());
+  }
 }

+ 5 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationLongUpDownCounterBuilder.java

@@ -55,4 +55,9 @@ final class ApplicationLongUpDownCounterBuilder implements LongUpDownCounterBuil
                 applicationCallback.accept(
                     new ApplicationObservableLongMeasurement(agentMeasurement))));
   }
+
+  // added in 1.15.0
+  public ObservableLongMeasurement buildObserver() {
+    return new ApplicationObservableLongMeasurement(agentBuilder.buildObserver());
+  }
 }

+ 2 - 2
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeter.java

@@ -11,11 +11,11 @@ import application.io.opentelemetry.api.metrics.LongCounterBuilder;
 import application.io.opentelemetry.api.metrics.LongUpDownCounterBuilder;
 import application.io.opentelemetry.api.metrics.Meter;
 
-class ApplicationMeter implements Meter {
+public class ApplicationMeter implements Meter {
 
   private final io.opentelemetry.api.metrics.Meter agentMeter;
 
-  ApplicationMeter(io.opentelemetry.api.metrics.Meter agentMeter) {
+  protected ApplicationMeter(io.opentelemetry.api.metrics.Meter agentMeter) {
     this.agentMeter = agentMeter;
   }
 

+ 6 - 2
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterBuilder.java

@@ -11,9 +11,13 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
 
 final class ApplicationMeterBuilder implements MeterBuilder {
 
+  private final ApplicationMeterFactory meterFactory;
   private final io.opentelemetry.api.metrics.MeterBuilder agentBuilder;
 
-  ApplicationMeterBuilder(io.opentelemetry.api.metrics.MeterBuilder agentBuilder) {
+  ApplicationMeterBuilder(
+      ApplicationMeterFactory meterFactory,
+      io.opentelemetry.api.metrics.MeterBuilder agentBuilder) {
+    this.meterFactory = meterFactory;
     this.agentBuilder = agentBuilder;
   }
 
@@ -33,6 +37,6 @@ final class ApplicationMeterBuilder implements MeterBuilder {
 
   @Override
   public Meter build() {
-    return new ApplicationMeter(agentBuilder.build());
+    return meterFactory.newMeter(agentBuilder.build());
   }
 }

+ 11 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterFactory.java

@@ -0,0 +1,11 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics;
+
+public interface ApplicationMeterFactory {
+
+  ApplicationMeter newMeter(io.opentelemetry.api.metrics.Meter agentMeter);
+}

+ 15 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterFactory14.java

@@ -0,0 +1,15 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics;
+
+import io.opentelemetry.api.metrics.Meter;
+
+public final class ApplicationMeterFactory14 implements ApplicationMeterFactory {
+  @Override
+  public ApplicationMeter newMeter(Meter agentMeter) {
+    return new ApplicationMeter(agentMeter);
+  }
+}

+ 7 - 2
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationMeterProvider.java

@@ -12,14 +12,19 @@ import application.io.opentelemetry.api.metrics.MeterProvider;
 @SuppressWarnings("UnnecessarilyFullyQualified")
 public class ApplicationMeterProvider implements MeterProvider {
 
+  private final ApplicationMeterFactory meterFactory;
   private final io.opentelemetry.api.metrics.MeterProvider agentMeterProvider;
 
-  public ApplicationMeterProvider(io.opentelemetry.api.metrics.MeterProvider agentMeterProvider) {
+  public ApplicationMeterProvider(
+      ApplicationMeterFactory meterFactory,
+      io.opentelemetry.api.metrics.MeterProvider agentMeterProvider) {
+    this.meterFactory = meterFactory;
     this.agentMeterProvider = agentMeterProvider;
   }
 
   @Override
   public MeterBuilder meterBuilder(String instrumentationName) {
-    return new ApplicationMeterBuilder(agentMeterProvider.meterBuilder(instrumentationName));
+    return new ApplicationMeterBuilder(
+        meterFactory, agentMeterProvider.meterBuilder(instrumentationName));
   }
 }

+ 8 - 1
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationObservableDoubleMeasurement.java

@@ -9,7 +9,9 @@ import application.io.opentelemetry.api.common.Attributes;
 import application.io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
 import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.trace.Bridging;
 
-final class ApplicationObservableDoubleMeasurement implements ObservableDoubleMeasurement {
+final class ApplicationObservableDoubleMeasurement
+    implements ObservableDoubleMeasurement,
+        ObservableMeasurementWrapper<io.opentelemetry.api.metrics.ObservableDoubleMeasurement> {
 
   private final io.opentelemetry.api.metrics.ObservableDoubleMeasurement agentMeasurement;
 
@@ -27,4 +29,9 @@ final class ApplicationObservableDoubleMeasurement implements ObservableDoubleMe
   public void record(double v, Attributes attributes) {
     agentMeasurement.record(v, Bridging.toAgent(attributes));
   }
+
+  @Override
+  public io.opentelemetry.api.metrics.ObservableDoubleMeasurement unwrap() {
+    return agentMeasurement;
+  }
 }

+ 8 - 1
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ApplicationObservableLongMeasurement.java

@@ -9,7 +9,9 @@ import application.io.opentelemetry.api.common.Attributes;
 import application.io.opentelemetry.api.metrics.ObservableLongMeasurement;
 import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.trace.Bridging;
 
-final class ApplicationObservableLongMeasurement implements ObservableLongMeasurement {
+final class ApplicationObservableLongMeasurement
+    implements ObservableLongMeasurement,
+        ObservableMeasurementWrapper<io.opentelemetry.api.metrics.ObservableLongMeasurement> {
 
   private final io.opentelemetry.api.metrics.ObservableLongMeasurement agentMeasurement;
 
@@ -27,4 +29,9 @@ final class ApplicationObservableLongMeasurement implements ObservableLongMeasur
   public void record(long v, Attributes attributes) {
     agentMeasurement.record(v, Bridging.toAgent(attributes));
   }
+
+  @Override
+  public io.opentelemetry.api.metrics.ObservableLongMeasurement unwrap() {
+    return agentMeasurement;
+  }
 }

+ 11 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.10/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_10/metrics/ObservableMeasurementWrapper.java

@@ -0,0 +1,11 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics;
+
+public interface ObservableMeasurementWrapper<T> {
+
+  T unwrap();
+}

+ 10 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/build.gradle.kts

@@ -0,0 +1,10 @@
+plugins {
+  id("otel.javaagent-instrumentation")
+}
+
+dependencies {
+  compileOnly(project(":opentelemetry-api-shaded-for-instrumenting", configuration = "v1_15"))
+  implementation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent"))
+  implementation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent"))
+  implementation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent"))
+}

+ 25 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/OpenTelemetryApiInstrumentationModule.java

@@ -0,0 +1,25 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15;
+
+import static java.util.Collections.singletonList;
+
+import com.google.auto.service.AutoService;
+import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
+import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
+import java.util.List;
+
+@AutoService(InstrumentationModule.class)
+public class OpenTelemetryApiInstrumentationModule extends InstrumentationModule {
+  public OpenTelemetryApiInstrumentationModule() {
+    super("opentelemetry-api-1.15", "opentelemetry-api");
+  }
+
+  @Override
+  public List<TypeInstrumentation> typeInstrumentations() {
+    return singletonList(new OpenTelemetryInstrumentation());
+  }
+}

+ 40 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/OpenTelemetryInstrumentation.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.none;
+
+import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
+import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15.metrics.ApplicationMeterFactory115;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+public class OpenTelemetryInstrumentation implements TypeInstrumentation {
+
+  @Override
+  public ElementMatcher<TypeDescription> typeMatcher() {
+    return named("application.io.opentelemetry.api.GlobalOpenTelemetry");
+  }
+
+  @Override
+  public void transform(TypeTransformer transformer) {
+    transformer.applyAdviceToMethod(
+        none(), OpenTelemetryInstrumentation.class.getName() + "$InitAdvice");
+  }
+
+  @SuppressWarnings({"ReturnValueIgnored", "unused"})
+  public static class InitAdvice {
+    @Advice.OnMethodEnter
+    public static void init() {
+      // the sole purpose of this advice is to ensure that ApplicationMeterFactory115 is recognized
+      // as helper class and injected into class loader
+      ApplicationMeterFactory115.class.getName();
+    }
+  }
+}

+ 21 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/ApplicationBatchCallback.java

@@ -0,0 +1,21 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15.metrics;
+
+import application.io.opentelemetry.api.metrics.BatchCallback;
+
+final class ApplicationBatchCallback implements BatchCallback {
+  private final io.opentelemetry.api.metrics.BatchCallback agentCallback;
+
+  ApplicationBatchCallback(io.opentelemetry.api.metrics.BatchCallback agentCallback) {
+    this.agentCallback = agentCallback;
+  }
+
+  @Override
+  public void close() {
+    agentCallback.close();
+  }
+}

+ 60 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/ApplicationMeter115.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15.metrics;
+
+import application.io.opentelemetry.api.metrics.BatchCallback;
+import application.io.opentelemetry.api.metrics.ObservableMeasurement;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ApplicationMeter;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ObservableMeasurementWrapper;
+
+class ApplicationMeter115 extends ApplicationMeter {
+
+  private final io.opentelemetry.api.metrics.Meter agentMeter;
+
+  ApplicationMeter115(io.opentelemetry.api.metrics.Meter agentMeter) {
+    super(agentMeter);
+    this.agentMeter = agentMeter;
+  }
+
+  @Override
+  public BatchCallback batchCallback(
+      Runnable callback,
+      ObservableMeasurement observableMeasurement,
+      ObservableMeasurement... additionalMeasurements) {
+    return new ApplicationBatchCallback(
+        agentMeter.batchCallback(
+            callback, unwrap(observableMeasurement), unwrap(additionalMeasurements)));
+  }
+
+  @SuppressWarnings("unchecked")
+  private static io.opentelemetry.api.metrics.ObservableMeasurement unwrap(
+      ObservableMeasurement observableMeasurement) {
+    if (observableMeasurement == null) {
+      return null;
+    }
+    if (!(observableMeasurement instanceof ObservableMeasurementWrapper)) {
+      // unwrap instruments that weren't created by us into a dummy instrument
+      // sdk ignores instruments that it didn't create
+      return new io.opentelemetry.api.metrics.ObservableMeasurement() {};
+    }
+    return ((ObservableMeasurementWrapper<io.opentelemetry.api.metrics.ObservableMeasurement>)
+            observableMeasurement)
+        .unwrap();
+  }
+
+  private static io.opentelemetry.api.metrics.ObservableMeasurement[] unwrap(
+      ObservableMeasurement[] observableMeasurements) {
+    if (observableMeasurements == null) {
+      return null;
+    }
+    io.opentelemetry.api.metrics.ObservableMeasurement[] result =
+        new io.opentelemetry.api.metrics.ObservableMeasurement[observableMeasurements.length];
+    for (int i = 0; i < observableMeasurements.length; i++) {
+      result[i] = unwrap(observableMeasurements[i]);
+    }
+    return result;
+  }
+}

+ 18 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/ApplicationMeterFactory115.java

@@ -0,0 +1,18 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15.metrics;
+
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ApplicationMeter;
+import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_10.metrics.ApplicationMeterFactory;
+
+// this class is used from opentelemetry-api-1.10 via reflection
+public final class ApplicationMeterFactory115 implements ApplicationMeterFactory {
+  @Override
+  public ApplicationMeter newMeter(Meter agentMeter) {
+    return new ApplicationMeter115(agentMeter);
+  }
+}

+ 318 - 0
instrumentation/opentelemetry-api/opentelemetry-api-1.15/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/v1_15/metrics/MeterTest.java

@@ -0,0 +1,318 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.opentelemetryapi.v1_15.metrics;
+
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.BatchCallback;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
+import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
+import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
+import org.assertj.core.api.AbstractIterableAssert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class MeterTest {
+
+  @RegisterExtension
+  static final AgentInstrumentationExtension testing = AgentInstrumentationExtension.create();
+
+  private String instrumentationName;
+  private Meter meter;
+
+  @BeforeEach
+  void setupMeter(TestInfo test) {
+    instrumentationName = "test-" + test.getDisplayName();
+    meter =
+        testing
+            .getOpenTelemetry()
+            .getMeterProvider()
+            .meterBuilder(instrumentationName)
+            .setInstrumentationVersion("1.2.3")
+            .setSchemaUrl("http://schema.org")
+            .build();
+  }
+
+  @Test
+  void batchLongCounter() throws InterruptedException {
+    ObservableLongMeasurement observableMeasurement =
+        meter.counterBuilder("test").setDescription("d").setUnit("u").buildObserver();
+
+    BatchCallback callback =
+        meter.batchCallback(
+            () -> {
+              observableMeasurement.record(11, Attributes.of(AttributeKey.stringKey("q"), "r"));
+            },
+            observableMeasurement);
+
+    testing.waitAndAssertMetrics(
+        instrumentationName,
+        "test",
+        metrics ->
+            metrics.anySatisfy(
+                metric ->
+                    assertThat(metric)
+                        .hasDescription("d")
+                        .hasUnit("u")
+                        .hasInstrumentationScope(
+                            InstrumentationScopeInfo.builder(instrumentationName)
+                                .setVersion("1.2.3")
+                                .build())
+                        .hasLongSumSatisfying(
+                            sum ->
+                                sum.isMonotonic()
+                                    .hasPointsSatisfying(
+                                        point ->
+                                            point
+                                                .hasValue(11)
+                                                .hasAttributesSatisfying(
+                                                    equalTo(AttributeKey.stringKey("q"), "r"))))));
+
+    callback.close();
+
+    // sleep exporter interval
+    Thread.sleep(100);
+    testing.clearData();
+    Thread.sleep(100);
+
+    testing.waitAndAssertMetrics(instrumentationName, "test", AbstractIterableAssert::isEmpty);
+  }
+
+  @Test
+  void batchDoubleCounter() throws InterruptedException {
+    ObservableDoubleMeasurement observableMeasurement =
+        meter.counterBuilder("test").ofDoubles().setDescription("d").setUnit("u").buildObserver();
+
+    BatchCallback callback =
+        meter.batchCallback(
+            () -> {
+              observableMeasurement.record(12.1, Attributes.of(AttributeKey.stringKey("q"), "r"));
+            },
+            observableMeasurement);
+
+    testing.waitAndAssertMetrics(
+        instrumentationName,
+        "test",
+        metrics ->
+            metrics.anySatisfy(
+                metric ->
+                    assertThat(metric)
+                        .hasDescription("d")
+                        .hasUnit("u")
+                        .hasInstrumentationScope(
+                            InstrumentationScopeInfo.builder(instrumentationName)
+                                .setVersion("1.2.3")
+                                .build())
+                        .hasDoubleSumSatisfying(
+                            sum ->
+                                sum.isMonotonic()
+                                    .hasPointsSatisfying(
+                                        point ->
+                                            point
+                                                .hasValue(12.1)
+                                                .hasAttributesSatisfying(
+                                                    equalTo(AttributeKey.stringKey("q"), "r"))))));
+
+    callback.close();
+
+    // sleep exporter interval
+    Thread.sleep(100);
+    testing.clearData();
+    Thread.sleep(100);
+
+    testing.waitAndAssertMetrics(instrumentationName, "test", AbstractIterableAssert::isEmpty);
+  }
+
+  @Test
+  void batchLongUpDownCounter() throws InterruptedException {
+    ObservableLongMeasurement observableMeasurement =
+        meter.upDownCounterBuilder("test").setDescription("d").setUnit("u").buildObserver();
+
+    BatchCallback callback =
+        meter.batchCallback(
+            () -> {
+              observableMeasurement.record(11, Attributes.of(AttributeKey.stringKey("q"), "r"));
+            },
+            observableMeasurement);
+
+    testing.waitAndAssertMetrics(
+        instrumentationName,
+        "test",
+        metrics ->
+            metrics.anySatisfy(
+                metric ->
+                    assertThat(metric)
+                        .hasDescription("d")
+                        .hasUnit("u")
+                        .hasInstrumentationScope(
+                            InstrumentationScopeInfo.builder(instrumentationName)
+                                .setVersion("1.2.3")
+                                .build())
+                        .hasLongSumSatisfying(
+                            sum ->
+                                sum.isNotMonotonic()
+                                    .hasPointsSatisfying(
+                                        point ->
+                                            point
+                                                .hasValue(11)
+                                                .hasAttributesSatisfying(
+                                                    equalTo(AttributeKey.stringKey("q"), "r"))))));
+
+    callback.close();
+
+    // sleep exporter interval
+    Thread.sleep(100);
+    testing.clearData();
+    Thread.sleep(100);
+
+    testing.waitAndAssertMetrics(instrumentationName, "test", AbstractIterableAssert::isEmpty);
+  }
+
+  @Test
+  void batchDoubleUpDownCounter() throws InterruptedException {
+    ObservableDoubleMeasurement observableMeasurement =
+        meter
+            .upDownCounterBuilder("test")
+            .ofDoubles()
+            .setDescription("d")
+            .setUnit("u")
+            .buildObserver();
+
+    BatchCallback callback =
+        meter.batchCallback(
+            () -> {
+              observableMeasurement.record(12.1, Attributes.of(AttributeKey.stringKey("q"), "r"));
+            },
+            observableMeasurement);
+
+    testing.waitAndAssertMetrics(
+        instrumentationName,
+        "test",
+        metrics ->
+            metrics.anySatisfy(
+                metric ->
+                    assertThat(metric)
+                        .hasDescription("d")
+                        .hasUnit("u")
+                        .hasInstrumentationScope(
+                            InstrumentationScopeInfo.builder(instrumentationName)
+                                .setVersion("1.2.3")
+                                .build())
+                        .hasDoubleSumSatisfying(
+                            sum ->
+                                sum.isNotMonotonic()
+                                    .hasPointsSatisfying(
+                                        point ->
+                                            point
+                                                .hasValue(12.1)
+                                                .hasAttributesSatisfying(
+                                                    equalTo(AttributeKey.stringKey("q"), "r"))))));
+
+    callback.close();
+
+    // sleep exporter interval
+    Thread.sleep(100);
+    testing.clearData();
+    Thread.sleep(100);
+
+    testing.waitAndAssertMetrics(instrumentationName, "test", AbstractIterableAssert::isEmpty);
+  }
+
+  @Test
+  void batchLongGauge() throws InterruptedException {
+    ObservableLongMeasurement observableMeasurement =
+        meter.gaugeBuilder("test").ofLongs().setDescription("d").setUnit("u").buildObserver();
+
+    BatchCallback callback =
+        meter.batchCallback(
+            () -> {
+              observableMeasurement.record(123, Attributes.of(AttributeKey.stringKey("q"), "r"));
+            },
+            observableMeasurement);
+
+    testing.waitAndAssertMetrics(
+        instrumentationName,
+        "test",
+        metrics ->
+            metrics.anySatisfy(
+                metric ->
+                    assertThat(metric)
+                        .hasDescription("d")
+                        .hasUnit("u")
+                        .hasInstrumentationScope(
+                            InstrumentationScopeInfo.builder(instrumentationName)
+                                .setVersion("1.2.3")
+                                .build())
+                        .hasLongGaugeSatisfying(
+                            gauge ->
+                                gauge.hasPointsSatisfying(
+                                    point ->
+                                        point
+                                            .hasValue(123)
+                                            .hasAttributesSatisfying(
+                                                equalTo(AttributeKey.stringKey("q"), "r"))))));
+
+    callback.close();
+
+    // sleep exporter interval
+    Thread.sleep(100);
+    testing.clearData();
+    Thread.sleep(100);
+
+    testing.waitAndAssertMetrics(instrumentationName, "test", AbstractIterableAssert::isEmpty);
+  }
+
+  @Test
+  void batchDoubleGauge() throws InterruptedException {
+    ObservableDoubleMeasurement observableMeasurement =
+        meter.gaugeBuilder("test").setDescription("d").setUnit("u").buildObserver();
+
+    BatchCallback callback =
+        meter.batchCallback(
+            () -> {
+              observableMeasurement.record(1.23, Attributes.of(AttributeKey.stringKey("q"), "r"));
+            },
+            observableMeasurement);
+
+    testing.waitAndAssertMetrics(
+        instrumentationName,
+        "test",
+        metrics ->
+            metrics.anySatisfy(
+                metric ->
+                    assertThat(metric)
+                        .hasDescription("d")
+                        .hasUnit("u")
+                        .hasInstrumentationScope(
+                            InstrumentationScopeInfo.builder(instrumentationName)
+                                .setVersion("1.2.3")
+                                .build())
+                        .hasDoubleGaugeSatisfying(
+                            gauge ->
+                                gauge.hasPointsSatisfying(
+                                    point ->
+                                        point
+                                            .hasValue(1.23)
+                                            .hasAttributesSatisfying(
+                                                equalTo(AttributeKey.stringKey("q"), "r"))))));
+
+    callback.close();
+
+    // sleep exporter interval
+    Thread.sleep(100);
+    testing.clearData();
+    Thread.sleep(100);
+
+    testing.waitAndAssertMetrics(instrumentationName, "test", AbstractIterableAssert::isEmpty);
+  }
+}

+ 20 - 0
opentelemetry-api-shaded-for-instrumenting/build.gradle.kts

@@ -19,12 +19,22 @@ val v1_10Deps by configurations.creating {
   // exclude the bom added by dependencyManagement
   exclude("io.opentelemetry", "opentelemetry-bom")
 }
+val v1_15Deps by configurations.creating {
+  isCanBeResolved = true
+  isCanBeConsumed = false
+  // exclude the bom added by dependencyManagement
+  exclude("io.opentelemetry", "opentelemetry-bom")
+}
 
 // configuration for publishing the shadowed artifact
 val v1_10 by configurations.creating {
   isCanBeConsumed = true
   isCanBeResolved = false
 }
+val v1_15 by configurations.creating {
+  isCanBeConsumed = true
+  isCanBeResolved = false
+}
 
 dependencies {
   latestDeps("io.opentelemetry:opentelemetry-api")
@@ -36,6 +46,11 @@ dependencies {
         strictly("1.10.0")
       }
     }
+    v1_15Deps("io.opentelemetry:$it") {
+      version {
+        strictly("1.15.0")
+      }
+    }
   }
 }
 
@@ -55,8 +70,13 @@ tasks {
     configurations = listOf(v1_10Deps)
     archiveClassifier.set("v1_10")
   }
+  val v1_15Shadow by registering(ShadowJar::class) {
+    configurations = listOf(v1_15Deps)
+    archiveClassifier.set("v1_15")
+  }
 
   artifacts {
     add(v1_10.name, v1_10Shadow)
+    add(v1_15.name, v1_15Shadow)
   }
 }

+ 1 - 0
settings.gradle.kts

@@ -361,6 +361,7 @@ hideFromDependabot(":instrumentation:okhttp:okhttp-3.0:testing")
 hideFromDependabot(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent")
 hideFromDependabot(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent")
 hideFromDependabot(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent")
+hideFromDependabot(":instrumentation:opentelemetry-api:opentelemetry-api-1.15:javaagent")
 hideFromDependabot(":instrumentation:opentelemetry-api:opentelemetry-api-logs-1.19:javaagent")
 hideFromDependabot(":instrumentation:opentelemetry-extension-annotations-1.0:javaagent")
 hideFromDependabot(":instrumentation:opentelemetry-extension-kotlin-1.0:javaagent")