Skip to content

Commit

Permalink
Library instrumentation should read its version from a file
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Rzeszutek committed Mar 25, 2022
1 parent f2587ba commit 4972634
Show file tree
Hide file tree
Showing 19 changed files with 283 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import io.opentelemetry.instrumentation.gradle.OtelInstrumentationExtension

/** Common setup for manual instrumentation of libraries and javaagent instrumentation. */

plugins {
Expand Down Expand Up @@ -119,3 +121,30 @@ if (testLatestDeps) {
}
}
}

val otelInstrumentation = extensions.create<OtelInstrumentationExtension>("otelInstrumentation")

tasks {
val generateInstrumentationVersionFile by registering {
val propertiesDir = File(project.buildDir, "generated/instrumentationVersion")
outputs.dir(propertiesDir)

doLast {
if (!otelInstrumentation.name.isPresent) {
return@doLast
}
val name = otelInstrumentation.name.get()
val version = otelInstrumentation.version.getOrElse(project.version as String)
val metaInfDir = File(propertiesDir, "META-INF/io/opentelemetry/")
metaInfDir.mkdirs()
File(metaInfDir, "instrumentation.version")
.writeText("$name=$version")
}
}
}

sourceSets {
main {
output.dir("build/generated/instrumentationVersion", "builtBy" to "generateInstrumentationVersionFile")
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer

plugins {
id("com.github.johnrengelman.shadow")
Expand All @@ -12,6 +13,11 @@ tasks.withType<ShadowJar>().configureEach {
// service loader...)
mergeServiceFiles("software/amazon/awssdk/global/handlers")

// merge the instrumentation.version files
transform(PropertiesFileTransformer::class.java) {
paths = listOf("META-INF/io/opentelemetry/instrumentation.version")
}

exclude("**/module-info.class")

// Prevents conflict with other SLF4J instances. Important for premain.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.opentelemetry.instrumentation.gradle

import org.gradle.api.provider.Property

abstract class OtelInstrumentationExtension {

abstract val name : Property<String>

abstract val version : Property<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@

package io.opentelemetry.instrumentation.api;

/**
* This class will be removed.
*
* @deprecated This class will be removed.
*/
@Deprecated
public final class InstrumentationVersion {
public static final String VERSION =
InstrumentationVersion.class.getPackage().getImplementationVersion();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.InstrumentationVersion;
import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationVersions;
import io.opentelemetry.instrumentation.api.internal.SupportabilityMetrics;
import java.time.Instant;
import java.util.ArrayList;
Expand Down Expand Up @@ -59,7 +59,10 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder
String instrumentationName,
SpanNameExtractor<? super REQUEST> spanNameExtractor) {
return new InstrumenterBuilder<>(
openTelemetry, instrumentationName, InstrumentationVersion.VERSION, spanNameExtractor);
openTelemetry,
instrumentationName,
EmbeddedInstrumentationVersions.findVersion(instrumentationName),
spanNameExtractor);
}

/**
Expand All @@ -79,6 +82,7 @@ public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder
* different library versions it's easy to find out which instrumentations produced the telemetry
* data.
*/
// TODO: add a setInstrumentationVersion method to the builder instead
public static <REQUEST, RESPONSE> InstrumenterBuilder<REQUEST, RESPONSE> builder(
OpenTelemetry openTelemetry,
String instrumentationName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.api.internal;

import static java.util.Collections.emptyMap;
import static java.util.Collections.list;
import static java.util.Collections.unmodifiableMap;
import static java.util.logging.Level.FINE;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class EmbeddedInstrumentationVersions {

private static final Map<String, String> versions = loadVersions();

private static Map<String, String> loadVersions() {
Map<String, String> versions = new HashMap<>();

// preload this once
try {
List<URL> urls =
list(
new ResourceLoaderProxy()
.getResources("META-INF/io/opentelemetry/instrumentation.version"));
Properties parsed = new Properties();

for (URL url : urls) {
try (InputStream in = url.openStream()) {
parsed.clear();
parsed.load(in);

for (String name : parsed.stringPropertyNames()) {
versions.put(name, parsed.getProperty(name));
}
}
}

} catch (IOException e) {
Logger.getLogger(EmbeddedInstrumentationVersions.class.getName())
.log(FINE, "Failed to load embedded instrumentation version file(s)", e);
return emptyMap();
}

return unmodifiableMap(versions);
}

@Nullable
public static String findVersion(String instrumentationName) {
return versions.get(instrumentationName);
}

// works as a proxy for the bootstrap classloader (if agent)
private static final class ResourceLoaderProxy extends ClassLoader {
ResourceLoaderProxy() {
super(ResourceLoaderProxy.class.getClassLoader());
}
}

private EmbeddedInstrumentationVersions() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.propagation.TextMapGetter;
import io.opentelemetry.instrumentation.api.InstrumentationVersion;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerAttributesExtractor;
Expand Down Expand Up @@ -735,30 +734,6 @@ void instrumentationTypeDetected_generic() {
assertThat(SpanKey.PRODUCER.fromContextOrNull(context)).isNull();
}

@Test
void instrumentationVersion_default() {
InstrumenterBuilder<Map<String, String>, Map<String, String>> builder =
Instrumenter.builder(otelTesting.getOpenTelemetry(), "test", name -> "span");

Instrumenter<Map<String, String>, Map<String, String>> instrumenter = builder.newInstrumenter();

Context context = instrumenter.start(Context.root(), Collections.emptyMap());
assertThat(Span.fromContext(context)).isNotNull();

instrumenter.end(context, Collections.emptyMap(), Collections.emptyMap(), null);

otelTesting
.assertTraces()
.hasTracesSatisfyingExactly(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("span")
.hasInstrumentationLibraryInfo(
InstrumentationLibraryInfo.create(
"test", InstrumentationVersion.VERSION))));
}

@Test
void instrumentationVersion_custom() {
InstrumenterBuilder<Map<String, String>, Map<String, String>> builder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static void onExit(
@Advice.Argument(0) String name,
@Advice.Return(readOnly = false) URL resource) {

URL helper = HelperResources.load(classLoader, name);
URL helper = HelperResources.loadOne(classLoader, name);
if (helper != null) {
resource = helper;
}
Expand All @@ -73,26 +73,30 @@ public static void onExit(
@Advice.This ClassLoader classLoader,
@Advice.Argument(0) String name,
@Advice.Return(readOnly = false) Enumeration<URL> resources) {
URL helper = HelperResources.load(classLoader, name);
if (helper == null) {
List<URL> helpers = HelperResources.loadAll(classLoader, name);
if (helpers.isEmpty()) {
return;
}

if (!resources.hasMoreElements()) {
resources = Collections.enumeration(Collections.singleton(helper));
resources = Collections.enumeration(helpers);
return;
}

List<URL> result = Collections.list(resources);
boolean duplicate = false;
for (URL loadedUrl : result) {
if (helper.sameFile(loadedUrl)) {
duplicate = true;
break;

// TODO: not sure if this is correct
for (URL helperUrl : helpers) {
boolean duplicate = false;
for (URL loadedUrl : result) {
if (helperUrl.sameFile(loadedUrl)) {
duplicate = true;
break;
}
}
if (!duplicate) {
result.add(helperUrl);
}
}
if (!duplicate) {
result.add(helper);
}

resources = Collections.enumeration(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

package io.opentelemetry.instrumentation.jdbc;

import io.opentelemetry.instrumentation.api.InstrumentationVersion;
import static io.opentelemetry.instrumentation.jdbc.internal.JdbcSingletons.INSTRUMENTATION_NAME;

import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationVersions;
import io.opentelemetry.instrumentation.jdbc.internal.DbInfo;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcConnectionUrlParser;
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection;
Expand Down Expand Up @@ -122,7 +124,12 @@ private static String extractRealUrl(String url) {
}

private static int[] parseInstrumentationVersion() {
String[] parts = InstrumentationVersion.VERSION.split("\\.");
String version = EmbeddedInstrumentationVersions.findVersion(INSTRUMENTATION_NAME);
if (version == null) {
// return 0.0 as a fallback
return new int[] {0, 0};
}
String[] parts = version.split("\\.");
if (parts.length >= 2) {
try {
int majorVersion = Integer.parseInt(parts[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* any time.
*/
public final class JdbcSingletons {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";
public static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";

private static final Instrumenter<DbRequest, Void> INSTRUMENTER;

Expand Down
4 changes: 4 additions & 0 deletions instrumentation/okhttp/okhttp-2.2/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ muzzle {
}
}

otelInstrumentation {
name.set("io.opentelemetry.okhttp-2.2")
}

dependencies {
library("com.squareup.okhttp:okhttp:2.2.0")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.okhttp.v3_0

import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.instrumentation.okhttp.v3_0.AbstractOkHttp3Test
import io.opentelemetry.instrumentation.test.AgentTestTrait
import okhttp3.Call
Expand Down Expand Up @@ -38,4 +39,23 @@ class OkHttp3Test extends AbstractOkHttp3Test implements AgentTestTrait {
newClient.interceptors().size() == 1
}

// just a sample test case that verifies that version is correctly set
def "test version"() {
when:
def request = buildRequest("GET", resolveAddress("/success"), [:])
sendRequest(request, "GET", resolveAddress("/success"), [:])

then:
assertTraces(1) {
trace(0, 2) {
span(0) {
kind(SpanKind.CLIENT)
instrumentationLibraryVersion("42-test")
}
span(1) {
kind(SpanKind.SERVER)
}
}
}
}
}
6 changes: 6 additions & 0 deletions instrumentation/okhttp/okhttp-3.0/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ plugins {
id("otel.animalsniffer-conventions")
}

otelInstrumentation {
name.set("io.opentelemetry.okhttp-3.0")
// just for test
version.set("42-test")
}

dependencies {
library("com.squareup.okhttp3:okhttp:3.0.0")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import static java.util.logging.Level.FINE;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;

import io.opentelemetry.instrumentation.api.InstrumentationVersion;
import io.opentelemetry.javaagent.bootstrap.InstrumentationHolder;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
Expand Down Expand Up @@ -57,9 +57,9 @@ public void transform(TypeTransformer transformer) {
transformer.applyTransformer(
(builder, typeDescription, classLoader, module) -> {
if (module != null && module.isNamed()) {
// using InstrumentationVersion because it's in the unnamed module in the bootstrap
// using Java8BytecodeBridge because it's in the unnamed module in the bootstrap
// loader, and that's where the rmi instrumentation helper classes will end up
JavaModule helperModule = JavaModule.ofType(InstrumentationVersion.class);
JavaModule helperModule = JavaModule.ofType(Java8BytecodeBridge.class);
// expose sun.rmi.server package to unnamed module
ClassInjector.UsingInstrumentation.redefineModule(
InstrumentationHolder.getInstrumentation(),
Expand Down
Loading

0 comments on commit 4972634

Please sign in to comment.