Skip to content

Commit a631796

Browse files
committed
Added initial commit of camunda instrumentation
1 parent f92e3fb commit a631796

File tree

33 files changed

+1584
-0
lines changed

33 files changed

+1584
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("org.camunda.bpm")
8+
module.set("camunda-engine")
9+
10+
// have not tested with versions prior to 7.18.0
11+
versions.set("[7.18.0,)")
12+
}
13+
}
14+
15+
16+
dependencies {
17+
implementation("org.camunda.bpm:camunda-engine:7.18.0")
18+
implementation("org.camunda.bpm:camunda-external-task-client:7.18.0")
19+
20+
api("com.google.auto.value:auto-value-annotations:1.6")
21+
annotationProcessor("com.google.auto.value:auto-value:1.6")
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution;
4+
5+
import io.opentelemetry.context.propagation.TextMapGetter;
6+
7+
public class CamundaActivityExecutionGetter implements TextMapGetter<ActivityExecution> {
8+
9+
@Override
10+
public Iterable<String> keys(ActivityExecution carrier) {
11+
return carrier.getVariableNames();
12+
}
13+
14+
@Override
15+
public String get(ActivityExecution carrier, String key) {
16+
Object variable = carrier.getVariables().get(key);
17+
return variable == null ? null : variable.toString();
18+
}
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution;
4+
5+
import io.opentelemetry.context.propagation.TextMapSetter;
6+
7+
public class CamundaActivityExecutionLocalSetter implements TextMapSetter<ActivityExecution> {
8+
9+
@Override
10+
public void set(ActivityExecution carrier, String key, String value) {
11+
carrier.setVariableLocal(key, value);
12+
}
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution;
4+
5+
import io.opentelemetry.context.propagation.TextMapSetter;
6+
7+
public class CamundaActivityExecutionSetter implements TextMapSetter<ActivityExecution> {
8+
9+
@Override
10+
public void set(ActivityExecution carrier, String key, String value) {
11+
carrier.setVariable(key, value);
12+
}
13+
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaBehaviorSpanNameExtractor;
4+
import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest;
5+
import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaVariableAttributeExtractor;
6+
7+
import io.opentelemetry.api.GlobalOpenTelemetry;
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
10+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
11+
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
12+
13+
public class CamundaBehaviorSingletons {
14+
15+
private static final Instrumenter<CamundaCommonRequest, String> instrumenter;
16+
17+
private static final OpenTelemetry opentelemetry;
18+
19+
private static final boolean propagationEnabled;
20+
21+
static {
22+
23+
opentelemetry = GlobalOpenTelemetry.get();
24+
25+
InstrumenterBuilder<CamundaCommonRequest, String> builder = Instrumenter
26+
.<CamundaCommonRequest, String>builder(opentelemetry, "io.opentelemetry.camunda-behavior",
27+
new CamundaBehaviorSpanNameExtractor())
28+
.addAttributesExtractor(new CamundaVariableAttributeExtractor());
29+
30+
instrumenter = builder.buildInstrumenter();
31+
}
32+
33+
public static OpenTelemetry getOpentelemetry() {
34+
return opentelemetry;
35+
}
36+
37+
public static Instrumenter<CamundaCommonRequest, String> getInstumenter() {
38+
return instrumenter;
39+
}
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import java.util.Arrays;
4+
5+
import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest;
6+
7+
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
8+
9+
public class CamundaBehaviorSpanNameExtractor implements SpanNameExtractor<CamundaCommonRequest> {
10+
11+
@Override
12+
public String extract(CamundaCommonRequest request) {
13+
String[] eventTypes = { "Start", "End" };
14+
String type = "Task";
15+
if (request.getActivityName().isPresent()
16+
&& Arrays.stream(eventTypes).anyMatch(request.getActivityName().get()::equals)) {
17+
type = "Event";
18+
19+
}
20+
return String.format("%s %s", request.getActivityName().orElse("Plugin"), type);
21+
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getInstumenter;
4+
import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getOpentelemetry;
5+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
6+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType;
7+
import static net.bytebuddy.matcher.ElementMatchers.named;
8+
9+
import java.util.Optional;
10+
11+
import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution;
12+
import org.camunda.bpm.engine.variable.VariableMap;
13+
14+
import java.util.logging.Level;
15+
16+
import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionGetter;
17+
import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaVariableMapSetter;
18+
import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest;
19+
20+
import io.opentelemetry.api.trace.SpanContext;
21+
import io.opentelemetry.context.Context;
22+
import io.opentelemetry.context.Scope;
23+
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
24+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
25+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
26+
import net.bytebuddy.asm.Advice;
27+
import net.bytebuddy.description.type.TypeDescription;
28+
import net.bytebuddy.matcher.ElementMatcher;
29+
import net.bytebuddy.matcher.ElementMatchers;
30+
31+
public class CamundaCallableElementActivityBehaviorInstrumentation implements TypeInstrumentation {
32+
33+
@Override
34+
public ElementMatcher<ClassLoader> classLoaderOptimization() {
35+
return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior");
36+
}
37+
38+
@Override
39+
public ElementMatcher<TypeDescription> typeMatcher() {
40+
return hasSuperType(named("org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior"));
41+
}
42+
43+
@Override
44+
public void transform(TypeTransformer transformer) {
45+
transformer.applyAdviceToMethod(ElementMatchers.isMethod().and(ElementMatchers.named("startInstance")),
46+
this.getClass().getName() + "$CamundaCallableElementActivityBehaviorAdvice");
47+
}
48+
49+
public static class CamundaCallableElementActivityBehaviorAdvice {
50+
51+
@Advice.OnMethodEnter(suppress = Throwable.class)
52+
public static void addTracingEnter(@Advice.Argument(0) ActivityExecution execution,
53+
@Advice.Argument(1) VariableMap variables, @Advice.Local("request") CamundaCommonRequest request,
54+
@Advice.Local("otelParentScope") Scope parentScope, @Advice.Local("otelContext") Context context,
55+
@Advice.Local("otelScope") Scope scope) {
56+
57+
58+
if (execution == null) {
59+
//log warning
60+
return;
61+
}
62+
63+
request = new CamundaCommonRequest();
64+
request.setProcessDefinitionId(Optional.ofNullable(execution.getProcessDefinitionId()));
65+
request.setProcessInstanceId(Optional.ofNullable(execution.getProcessInstanceId()));
66+
request.setActivityId(Optional.ofNullable(execution.getCurrentActivityId()));
67+
request.setActivityName(Optional.ofNullable(execution.getCurrentActivityName()));
68+
69+
String processInstanceId = execution.getProcessInstanceId();
70+
71+
if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) {
72+
//log
73+
}
74+
75+
Context parentContext = getOpentelemetry().getPropagators().getTextMapPropagator()
76+
.extract(Java8BytecodeBridge.currentContext(), execution, new CamundaActivityExecutionGetter());
77+
78+
parentScope = parentContext.makeCurrent();
79+
80+
if (getInstumenter().shouldStart(Java8BytecodeBridge.currentContext(), request)) {
81+
context = getInstumenter().start(Java8BytecodeBridge.currentContext(), request);
82+
scope = context.makeCurrent();
83+
84+
// Inject subflow trace context as pi variables so they are propagated and
85+
// accessible
86+
87+
SpanContext currentSpanContext = Java8BytecodeBridge.spanFromContext(context).getSpanContext();
88+
if (currentSpanContext.isValid()) {
89+
getOpentelemetry().getPropagators().getTextMapPropagator().inject(context, variables,
90+
new CamundaVariableMapSetter());
91+
}
92+
93+
}
94+
95+
}
96+
97+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
98+
public static void closeTrace(@Advice.Argument(0) ActivityExecution execution,
99+
@Advice.Local("request") CamundaCommonRequest request,
100+
@Advice.Local("otelParentScope") Scope parentScope, @Advice.Local("otelContext") Context context,
101+
@Advice.Local("otelScope") Scope scope, @Advice.Thrown Throwable throwable) {
102+
103+
104+
if (context != null && scope != null) {
105+
getInstumenter().end(context, request, "NA", throwable);
106+
scope.close();
107+
}
108+
109+
if (parentScope != null) {
110+
parentScope.close();
111+
}
112+
113+
}
114+
}
115+
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior;
2+
3+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
4+
5+
import java.util.Arrays;
6+
import java.util.Collections;
7+
import java.util.List;
8+
9+
import com.google.auto.service.AutoService;
10+
11+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
14+
import net.bytebuddy.matcher.ElementMatcher;
15+
16+
@AutoService(InstrumentationModule.class)
17+
public class CamundaCallableElementActivityBehaviorModule extends InstrumentationModule {
18+
19+
public CamundaCallableElementActivityBehaviorModule() {
20+
super("camunda", "camunda-behavior", "camunda-behavior-7_18");
21+
}
22+
23+
@Override
24+
public boolean defaultEnabled(ConfigProperties config) {
25+
return config.getBoolean("otel.instrumentation.common.default-enabled", true);
26+
}
27+
28+
@Override
29+
public List<TypeInstrumentation> typeInstrumentations() {
30+
return Collections.singletonList(new CamundaCallableElementActivityBehaviorInstrumentation());
31+
}
32+
33+
@Override
34+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
35+
return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior");
36+
}
37+
38+
String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior",
39+
"io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common" };
40+
41+
@Override
42+
public boolean isHelperClass(String classname) {
43+
return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c));
44+
}
45+
46+
}

0 commit comments

Comments
 (0)