-
Notifications
You must be signed in to change notification settings - Fork 938
Added initial commit of camunda instrumentation #12830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a631796
979c377
4fbdbf8
7b268c0
c6aa4c2
80b0e67
b267316
fb19def
b44bc9d
26df3f1
9fb1aed
d7f179f
308f6a8
fd44b57
bdd9ea1
628d839
75f3fdc
0cd3185
3f9833a
38ff7f0
7fcfa7d
da9e851
4355c63
c5718bc
193cd63
d60eaf3
cc90160
6e797eb
e484aa7
f938b85
2aa9616
3277d7e
a3fd09c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("org.camunda.bpm") | ||
module.set("camunda-engine") | ||
|
||
// have not tested with versions prior to 7.18.0 | ||
versions.set("[7.18.0,)") | ||
extraDependency("org.camunda.bpm:camunda-external-task-client:7.18.0") | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation(project(":instrumentation:camunda:camunda-7.0:library")) | ||
|
||
library("org.camunda.bpm:camunda-engine:7.18.0") | ||
library("org.camunda.bpm:camunda-external-task-client:7.18.0") | ||
|
||
annotationProcessor("com.google.auto.value:auto-value:1.6") | ||
|
||
testImplementation(project(":instrumentation:camunda:camunda-7.0:testing")) | ||
testImplementation("com.h2database:h2:2.2.224") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.behavior; | ||
|
||
import io.opentelemetry.api.GlobalOpenTelemetry; | ||
import io.opentelemetry.api.OpenTelemetry; | ||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; | ||
import io.opentelemetry.instrumentation.camunda.v7_0.behavior.CamundaBehaviorSpanNameExtractor; | ||
import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; | ||
import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaVariableAttributeExtractor; | ||
|
||
public class CamundaBehaviorSingletons { | ||
|
||
private static final Instrumenter<CamundaCommonRequest, String> instrumenter; | ||
|
||
private static final OpenTelemetry opentelemetry; | ||
|
||
static { | ||
opentelemetry = GlobalOpenTelemetry.get(); | ||
|
||
InstrumenterBuilder<CamundaCommonRequest, String> builder = | ||
Instrumenter.<CamundaCommonRequest, String>builder( | ||
opentelemetry, | ||
"io.opentelemetry.camunda-behavior", | ||
new CamundaBehaviorSpanNameExtractor()) | ||
.addAttributesExtractor(new CamundaVariableAttributeExtractor()); | ||
|
||
instrumenter = builder.buildInstrumenter(); | ||
} | ||
|
||
public static OpenTelemetry getOpentelemetry() { | ||
return opentelemetry; | ||
} | ||
|
||
public static Instrumenter<CamundaCommonRequest, String> getInstumenter() { | ||
return instrumenter; | ||
} | ||
|
||
private CamundaBehaviorSingletons() {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.behavior; | ||
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; | ||
import static io.opentelemetry.javaagent.instrumentation.camunda.v7_0.behavior.CamundaBehaviorSingletons.getInstumenter; | ||
import static io.opentelemetry.javaagent.instrumentation.camunda.v7_0.behavior.CamundaBehaviorSingletons.getOpentelemetry; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
|
||
import io.opentelemetry.api.trace.SpanContext; | ||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.Scope; | ||
import io.opentelemetry.instrumentation.camunda.v7_0.behavior.CamundaActivityExecutionGetter; | ||
import io.opentelemetry.instrumentation.camunda.v7_0.behavior.CamundaVariableMapSetter; | ||
import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; | ||
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
import java.util.Optional; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import net.bytebuddy.matcher.ElementMatchers; | ||
import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; | ||
import org.camunda.bpm.engine.variable.VariableMap; | ||
|
||
public class CamundaCallableElementActivityBehaviorInstrumentation implements TypeInstrumentation { | ||
|
||
@Override | ||
public ElementMatcher<ClassLoader> classLoaderOptimization() { | ||
return hasClassesNamed( | ||
"org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior"); | ||
} | ||
|
||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return hasSuperType( | ||
named("org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior")); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
ElementMatchers.isMethod().and(ElementMatchers.named("startInstance")), | ||
this.getClass().getName() + "$CamundaCallableElementActivityBehaviorAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class CamundaCallableElementActivityBehaviorAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void addTracingEnter( | ||
@Advice.Argument(0) ActivityExecution execution, | ||
@Advice.Argument(1) VariableMap variables, | ||
@Advice.Local("request") CamundaCommonRequest request, | ||
@Advice.Local("otelParentScope") Scope parentScope, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
|
||
if (execution == null) { | ||
// log warning | ||
return; | ||
} | ||
|
||
request = new CamundaCommonRequest(); | ||
request.setProcessDefinitionId(Optional.ofNullable(execution.getProcessDefinitionId())); | ||
request.setProcessInstanceId(Optional.ofNullable(execution.getProcessInstanceId())); | ||
request.setActivityId(Optional.ofNullable(execution.getCurrentActivityId())); | ||
request.setActivityName(Optional.ofNullable(execution.getCurrentActivityName())); | ||
request.setBusinessKey(Optional.ofNullable(execution.getProcessBusinessKey())); | ||
|
||
if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { | ||
// log | ||
} | ||
Comment on lines
+76
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no-op |
||
|
||
Context parentContext = | ||
getOpentelemetry() | ||
.getPropagators() | ||
.getTextMapPropagator() | ||
.extract( | ||
Java8BytecodeBridge.currentContext(), | ||
execution, | ||
new CamundaActivityExecutionGetter()); | ||
|
||
parentScope = parentContext.makeCurrent(); | ||
|
||
if (getInstumenter().shouldStart(Java8BytecodeBridge.currentContext(), request)) { | ||
context = getInstumenter().start(Java8BytecodeBridge.currentContext(), request); | ||
scope = context.makeCurrent(); | ||
|
||
// Inject subflow trace context as pi variables so they are propagated and | ||
// accessible | ||
|
||
SpanContext currentSpanContext = | ||
Java8BytecodeBridge.spanFromContext(context).getSpanContext(); | ||
if (currentSpanContext.isValid()) { | ||
getOpentelemetry() | ||
.getPropagators() | ||
.getTextMapPropagator() | ||
.inject(context, variables, new CamundaVariableMapSetter()); | ||
} | ||
} | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void closeTrace( | ||
@Advice.Argument(0) ActivityExecution execution, | ||
@Advice.Local("request") CamundaCommonRequest request, | ||
@Advice.Local("otelParentScope") Scope parentScope, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope, | ||
@Advice.Thrown Throwable throwable) { | ||
|
||
if (context != null && scope != null) { | ||
getInstumenter().end(context, request, "NA", throwable); | ||
scope.close(); | ||
} | ||
|
||
if (parentScope != null) { | ||
parentScope.close(); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.behavior; | ||
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
@AutoService(InstrumentationModule.class) | ||
public class CamundaCallableElementActivityBehaviorModule extends InstrumentationModule { | ||
|
||
public CamundaCallableElementActivityBehaviorModule() { | ||
super("camunda", "camunda-7.0", "camunda-behavior", "camunda-behavior-7_18"); | ||
} | ||
|
||
@Override | ||
public boolean defaultEnabled(ConfigProperties config) { | ||
return config.getBoolean("otel.instrumentation.common.default-enabled", true); | ||
} | ||
Comment on lines
+26
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this needed? |
||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return Collections.singletonList(new CamundaCallableElementActivityBehaviorInstrumentation()); | ||
} | ||
|
||
@Override | ||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() { | ||
return hasClassesNamed( | ||
"org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior"); | ||
} | ||
|
||
String[] helperClassnames = { | ||
"io.opentelemetry.javaagent.instrumentation.camunda.v7_0.behavior", | ||
"io.opentelemetry.instrumentation.camunda.v7_0.behavior", | ||
"io.opentelemetry.instrumentation.camunda.v7_0.common" | ||
}; | ||
|
||
@Override | ||
public boolean isHelperClass(String classname) { | ||
return super.isHelperClass(classname) | ||
|| Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); | ||
} | ||
Comment on lines
+42
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this needed? |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually we also add
assertInverse.set(true)
. This verifies that the instrumentation does not apply to versions outside the given range. If the instrumentation applies to earlier versions you have the choice of either expanding the supported version range or add aclassLoaderMatcher
method that checks for a class that was added in the first version you intend to support (this will make the instrumentation not apply to earlier versions). If the first supported version is 7.18 then the module should be named accordingly.