Skip to content

Commit 0375613

Browse files
committed
Add otel4s instrumentation
1 parent 2fc47b5 commit 0375613

File tree

10 files changed

+765
-0
lines changed

10 files changed

+765
-0
lines changed

instrumentation/opentelemetry-api/opentelemetry-api-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opentelemetryapi/context/AgentContextStorage.java

+3
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ public static Context newContextWrapper(
143143
public Scope attach(Context toAttach) {
144144
io.opentelemetry.context.Context currentAgentContext =
145145
io.opentelemetry.context.Context.current();
146+
147+
logger.info("Current context: " + currentAgentContext + " " + currentAgentContext.getClass().getName());
146148
Context currentApplicationContext = currentAgentContext.get(APPLICATION_CONTEXT);
147149
if (currentApplicationContext == null) {
148150
currentApplicationContext = applicationRoot;
@@ -166,6 +168,7 @@ public Scope attach(Context toAttach) {
166168
@Override
167169
public Context current() {
168170
io.opentelemetry.context.Context agentContext = io.opentelemetry.context.Context.current();
171+
logger.info("Current context: " + agentContext + " " + agentContext.getClass().getName());
169172
Context applicationContext = agentContext.get(APPLICATION_CONTEXT);
170173
if (applicationContext == null) {
171174
applicationContext = applicationRoot;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
plugins {
2+
id("otel.javaagent-bootstrap")
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.bootstrap.otel4s;
7+
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.Scope;
10+
import java.util.concurrent.atomic.AtomicReference;
11+
import java.util.logging.Logger;
12+
import javax.annotation.Nullable;
13+
14+
/** The helper stores a reference to the IOLocal#unsafeThreadLocal. */
15+
public final class FiberLocalContextHelper {
16+
17+
private static final Logger logger = Logger.getLogger(FiberLocalContextHelper.class.getName());
18+
19+
private static final AtomicReference<ThreadLocal<Context>> fiberContextThreadLocal =
20+
new AtomicReference<>();
21+
22+
public static void setFiberThreadLocal(ThreadLocal<Context> fiberThreadLocal) {
23+
if (fiberContextThreadLocal.get() == null) {
24+
fiberContextThreadLocal.set(fiberThreadLocal);
25+
} else {
26+
logger.warning("The fiberContextThreadLocal is already configured");
27+
}
28+
}
29+
30+
@Nullable
31+
public static Context current() {
32+
ThreadLocal<Context> local = getFiberThreadLocal();
33+
return local != null ? local.get() : null;
34+
}
35+
36+
public static Scope attach(Context toAttach) {
37+
ThreadLocal<Context> local = fiberContextThreadLocal.get();
38+
if (toAttach == null || local == null) {
39+
return Scope.noop();
40+
} else {
41+
Context beforeAttach = current();
42+
if (toAttach == beforeAttach) {
43+
return Scope.noop();
44+
} else {
45+
local.set(toAttach);
46+
return () -> local.set(beforeAttach);
47+
}
48+
}
49+
}
50+
51+
@Nullable
52+
private static ThreadLocal<Context> getFiberThreadLocal() {
53+
return fiberContextThreadLocal.get();
54+
}
55+
56+
private FiberLocalContextHelper() {}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
id("otel.nullaway-conventions")
4+
id("otel.scala-conventions")
5+
}
6+
7+
val otel4sVersion = "0.12-c8138cf-20250312T184045Z-SNAPSHOT"
8+
val scalaVersion = "2.13"
9+
10+
muzzle {
11+
pass {
12+
group.set("org.typelevel")
13+
module.set("otel4s-oteljava-context-storage_2.13")
14+
extraDependency("io.opentelemetry:opentelemetry-api:1.0.0")
15+
versions.set("[$otel4sVersion,)")
16+
assertInverse.set(true)
17+
excludeInstrumentationName("opentelemetry-api")
18+
}
19+
pass {
20+
group.set("org.typelevel")
21+
module.set("otel4s-oteljava-context-storage_3")
22+
versions.set("[$otel4sVersion,)")
23+
extraDependency("io.opentelemetry:opentelemetry-api:1.0.0")
24+
assertInverse.set(true)
25+
excludeInstrumentationName("opentelemetry-api")
26+
}
27+
}
28+
29+
dependencies {
30+
bootstrap(project(":instrumentation:otel4s:otel4s-0.12:bootstrap"))
31+
32+
// we need access to the "application.io.opentelemetry.context.Context"
33+
// to properly bridge fiber and agent context storages
34+
compileOnly(project(":opentelemetry-api-shaded-for-instrumenting", configuration = "shadow"))
35+
implementation(project(":instrumentation:opentelemetry-api:opentelemetry-api-1.0:javaagent"))
36+
37+
// otel4s
38+
testImplementation("org.typelevel:otel4s-oteljava-context-storage_$scalaVersion:$otel4sVersion")
39+
40+
latestDepTestLibrary("org.typelevel:otel4s-oteljava-context-storage_$scalaVersion:+")
41+
}
42+
43+
tasks {
44+
withType<Test>().configureEach {
45+
jvmArgs("-Dio.opentelemetry.javaagent.shaded.io.opentelemetry.context.enableStrictContext=false")
46+
jvmArgs("-Dcats.effect.trackFiberContext=true")
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.otel4s.v0_12;
7+
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.ContextStorage;
10+
import io.opentelemetry.context.Scope;
11+
import io.opentelemetry.javaagent.bootstrap.otel4s.FiberLocalContextHelper;
12+
import io.opentelemetry.javaagent.instrumentation.opentelemetryapi.context.AgentContextStorage;
13+
import javax.annotation.Nullable;
14+
15+
public class Otel4sFiberContextBridge implements ContextStorage {
16+
17+
private final ContextStorage agentContextStorage;
18+
19+
public Otel4sFiberContextBridge(ContextStorage delegate) {
20+
this.agentContextStorage = delegate;
21+
}
22+
23+
@Override
24+
public Scope attach(Context context) {
25+
Scope fiberScope = FiberLocalContextHelper.attach(context);
26+
Scope agentScope = agentContextStorage.attach(context);
27+
return () -> {
28+
fiberScope.close();
29+
agentScope.close();
30+
};
31+
}
32+
33+
@Nullable
34+
@Override
35+
public Context current() {
36+
Context agentContext = agentContextStorage.current();
37+
Context fiberContext = FiberLocalContextHelper.current();
38+
39+
if (agentContext == null && fiberContext != null) {
40+
return fiberContext;
41+
}
42+
43+
// if (agentContext != null) {
44+
// logger.severe("Got conflicting context. Agent: " + agentContext + ". Fiber: " +
45+
// fiberContext);
46+
// }
47+
48+
return agentContext;
49+
}
50+
51+
public static ThreadLocal<Context> contextThreadLocal(
52+
ThreadLocal<application.io.opentelemetry.context.Context> fiberThreadLocal) {
53+
return new ThreadLocal<Context>() {
54+
@Override
55+
public Context get() {
56+
return AgentContextStorage.getAgentContext(fiberThreadLocal.get());
57+
}
58+
59+
@Override
60+
public void set(Context value) {
61+
fiberThreadLocal.set(AgentContextStorage.toApplicationContext(value));
62+
}
63+
};
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.otel4s.v0_12;
7+
8+
import com.google.auto.service.AutoService;
9+
import io.opentelemetry.context.ContextStorage;
10+
import io.opentelemetry.javaagent.extension.AgentListener;
11+
import io.opentelemetry.javaagent.tooling.BeforeAgentListener;
12+
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
13+
import java.util.logging.Logger;
14+
15+
/**
16+
* An {@link AgentListener} that enables oshi metrics during agent startup if oshi is present on the
17+
* system classpath.
18+
*/
19+
@AutoService(BeforeAgentListener.class)
20+
public class Otel4sFiberContextBridgeInstaller implements BeforeAgentListener {
21+
22+
private static final Logger logger =
23+
Logger.getLogger(Otel4sFiberContextBridgeInstaller.class.getName());
24+
25+
@Override
26+
public void beforeAgent(AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk) {
27+
ContextStorage.addWrapper(Otel4sFiberContextBridge::new);
28+
logger.info("Installed FiberContextBridge");
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.otel4s.v0_12;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
9+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
10+
import static net.bytebuddy.matcher.ElementMatchers.named;
11+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
12+
13+
import io.opentelemetry.javaagent.bootstrap.otel4s.FiberLocalContextHelper;
14+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
15+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
16+
import net.bytebuddy.asm.Advice;
17+
import net.bytebuddy.description.type.TypeDescription;
18+
import net.bytebuddy.matcher.ElementMatcher;
19+
20+
@SuppressWarnings("IdentifierName")
21+
public class Otel4sIOLocalContextStorageInstrumentation implements TypeInstrumentation {
22+
23+
@Override
24+
public ElementMatcher<TypeDescription> typeMatcher() {
25+
return named("org.typelevel.otel4s.oteljava.context.IOLocalContextStorage$");
26+
}
27+
28+
@Override
29+
@SuppressWarnings({"CatchingUnchecked", "CatchAndPrintStackTrace"})
30+
public void transform(TypeTransformer transformer) {
31+
transformer.applyAdviceToMethod(
32+
isPublic()
33+
.and(isMethod())
34+
.and(named("registerFiberThreadContext"))
35+
.and(takesArgument(0, ThreadLocal.class)),
36+
this.getClass().getName() + "$RegisterAdvice");
37+
}
38+
39+
@SuppressWarnings("unused")
40+
public static final class RegisterAdvice {
41+
42+
private RegisterAdvice() {}
43+
44+
@Advice.OnMethodEnter(suppress = Throwable.class)
45+
public static void onEnter(
46+
@Advice.Argument(0)
47+
ThreadLocal<application.io.opentelemetry.context.Context> _fiberThreadLocal) {
48+
FiberLocalContextHelper.setFiberThreadLocal(
49+
Otel4sFiberContextBridge.contextThreadLocal(_fiberThreadLocal));
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.otel4s.v0_12;
7+
8+
import com.google.auto.service.AutoService;
9+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
10+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
11+
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
12+
import java.util.Collections;
13+
import java.util.List;
14+
15+
@AutoService(InstrumentationModule.class)
16+
public class Otel4sInstrumentationModule extends InstrumentationModule
17+
implements ExperimentalInstrumentationModule {
18+
19+
public Otel4sInstrumentationModule() {
20+
super("otel4s", "otel4s-0.12");
21+
}
22+
23+
@Override
24+
public List<TypeInstrumentation> typeInstrumentations() {
25+
return Collections.singletonList(new Otel4sIOLocalContextStorageInstrumentation());
26+
}
27+
28+
// ensure it's the last one
29+
@Override
30+
public int order() {
31+
return Integer.MAX_VALUE;
32+
}
33+
34+
@Override
35+
public String getModuleGroup() {
36+
// This module uses the api context bridge helpers, therefore must be in the same classloader
37+
return "opentelemetry-api-bridge";
38+
}
39+
}

0 commit comments

Comments
 (0)