From a631796c6aa21a1e915a8d844f87e6077c77c0e3 Mon Sep 17 00:00:00 2001 From: "Boslet, Cory (cb645j)" Date: Tue, 3 Dec 2024 15:10:56 -0500 Subject: [PATCH] Added initial commit of camunda instrumentation --- .../camunda-7.18/javaagent/build.gradle.kts | 22 ++++ .../CamundaActivityExecutionGetter.java | 20 +++ .../CamundaActivityExecutionLocalSetter.java | 14 ++ .../CamundaActivityExecutionSetter.java | 14 ++ .../behavior/CamundaBehaviorSingletons.java | 41 ++++++ .../CamundaBehaviorSpanNameExtractor.java | 24 ++++ ...lementActivityBehaviorInstrumentation.java | 116 +++++++++++++++++ ...CallableElementActivityBehaviorModule.java | 46 +++++++ ...dEventActivityBehaviorInstrumentation.java | 121 ++++++++++++++++++ ...CamundaEndEventActivityBehaviorModule.java | 48 +++++++ ...alTaskActivityBehaviorInstrumentation.java | 121 ++++++++++++++++++ ...ndaExternalTaskActivityBehaviorModule.java | 46 +++++++ ...daTaskActivityBehaviorInstrumentation.java | 101 +++++++++++++++ .../CamundaTaskActivityBehaviorModule.java | 46 +++++++ .../behavior/CamundaVariableMapSetter.java | 14 ++ .../v7_18/common/CamundaCommonRequest.java | 71 ++++++++++ .../CamundaVariableAttributeExtractor.java | 28 ++++ ...ContinuationJobHandlerInstrumentation.java | 112 ++++++++++++++++ ...undaAsyncContinuationJobHandlerModule.java | 46 +++++++ .../jobs/CamundaExecutionEntityGetter.java | 20 +++ .../v7_18/jobs/CamundaJobSingletons.java | 41 ++++++ .../jobs/CamundaJobSpanNameExtractor.java | 14 ++ ...ndaActivityInstantiationBuilderSetter.java | 16 +++ ...sInstanceModificationBuilderImpSetter.java | 17 +++ ...ssInstantiationBuilderInstrumentation.java | 93 ++++++++++++++ ...undaProcessInstantiationBuilderModule.java | 48 +++++++ .../processes/CamundaProcessSingletons.java | 41 ++++++ .../CamundaProcessSpanNameExtractor.java | 14 ++ .../v7_18/task/CamundaExternalTaskGetter.java | 20 +++ .../v7_18/task/CamundaTaskSingletons.java | 38 ++++++ .../task/CamundaTaskSpanNameExtractor.java | 14 ++ ...CamundaTopicSubscriptionManagerModule.java | 46 +++++++ ...opicSubscriptionMangerInstrumentation.java | 111 ++++++++++++++++ 33 files changed, 1584 insertions(+) create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/build.gradle.kts create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionGetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionLocalSetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionSetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSingletons.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSpanNameExtractor.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorInstrumentation.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorInstrumentation.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorInstrumentation.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaVariableMapSetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaCommonRequest.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaVariableAttributeExtractor.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaExecutionEntityGetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSingletons.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSpanNameExtractor.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaActivityInstantiationBuilderSetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstanceModificationBuilderImpSetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderInstrumentation.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSingletons.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSpanNameExtractor.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaExternalTaskGetter.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSingletons.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSpanNameExtractor.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionManagerModule.java create mode 100644 instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionMangerInstrumentation.java diff --git a/instrumentation/camunda/camunda-7.18/javaagent/build.gradle.kts b/instrumentation/camunda/camunda-7.18/javaagent/build.gradle.kts new file mode 100644 index 000000000000..66c0ea3c8e0f --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/build.gradle.kts @@ -0,0 +1,22 @@ +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,)") + } +} + + +dependencies { + implementation("org.camunda.bpm:camunda-engine:7.18.0") + implementation("org.camunda.bpm:camunda-external-task-client:7.18.0") + + api("com.google.auto.value:auto-value-annotations:1.6") + annotationProcessor("com.google.auto.value:auto-value:1.6") +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionGetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionGetter.java new file mode 100644 index 000000000000..c6eaf8cbfbac --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionGetter.java @@ -0,0 +1,20 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +import io.opentelemetry.context.propagation.TextMapGetter; + +public class CamundaActivityExecutionGetter implements TextMapGetter { + + @Override + public Iterable keys(ActivityExecution carrier) { + return carrier.getVariableNames(); + } + + @Override + public String get(ActivityExecution carrier, String key) { + Object variable = carrier.getVariables().get(key); + return variable == null ? null : variable.toString(); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionLocalSetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionLocalSetter.java new file mode 100644 index 000000000000..36bf109d5158 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionLocalSetter.java @@ -0,0 +1,14 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +import io.opentelemetry.context.propagation.TextMapSetter; + +public class CamundaActivityExecutionLocalSetter implements TextMapSetter { + + @Override + public void set(ActivityExecution carrier, String key, String value) { + carrier.setVariableLocal(key, value); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionSetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionSetter.java new file mode 100644 index 000000000000..7e5229060be4 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaActivityExecutionSetter.java @@ -0,0 +1,14 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +import io.opentelemetry.context.propagation.TextMapSetter; + +public class CamundaActivityExecutionSetter implements TextMapSetter { + + @Override + public void set(ActivityExecution carrier, String key, String value) { + carrier.setVariable(key, value); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSingletons.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSingletons.java new file mode 100644 index 000000000000..1a745dab6eb3 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSingletons.java @@ -0,0 +1,41 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaBehaviorSpanNameExtractor; +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaVariableAttributeExtractor; + +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.javaagent.bootstrap.internal.InstrumentationConfig; + +public class CamundaBehaviorSingletons { + + private static final Instrumenter instrumenter; + + private static final OpenTelemetry opentelemetry; + + private static final boolean propagationEnabled; + + static { + + opentelemetry = GlobalOpenTelemetry.get(); + + InstrumenterBuilder builder = Instrumenter + .builder(opentelemetry, "io.opentelemetry.camunda-behavior", + new CamundaBehaviorSpanNameExtractor()) + .addAttributesExtractor(new CamundaVariableAttributeExtractor()); + + instrumenter = builder.buildInstrumenter(); + } + + public static OpenTelemetry getOpentelemetry() { + return opentelemetry; + } + + public static Instrumenter getInstumenter() { + return instrumenter; + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSpanNameExtractor.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSpanNameExtractor.java new file mode 100644 index 000000000000..fa99d53e9228 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaBehaviorSpanNameExtractor.java @@ -0,0 +1,24 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import java.util.Arrays; + +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; + +public class CamundaBehaviorSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(CamundaCommonRequest request) { + String[] eventTypes = { "Start", "End" }; + String type = "Task"; + if (request.getActivityName().isPresent() + && Arrays.stream(eventTypes).anyMatch(request.getActivityName().get()::equals)) { + type = "Event"; + + } + return String.format("%s %s", request.getActivityName().orElse("Plugin"), type); + + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java new file mode 100644 index 000000000000..326b85da7dfa --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java @@ -0,0 +1,116 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getOpentelemetry; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import java.util.Optional; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; +import org.camunda.bpm.engine.variable.VariableMap; + +import java.util.logging.Level; + +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionGetter; +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaVariableMapSetter; +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class CamundaCallableElementActivityBehaviorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior"); + } + + @Override + public ElementMatcher 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"); + } + + 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())); + + String processInstanceId = execution.getProcessInstanceId(); + + if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { + //log + } + + 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(); + } + + } + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorModule.java new file mode 100644 index 000000000000..5acddb198dc6 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaCallableElementActivityBehaviorModule.java @@ -0,0 +1,46 @@ +package com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaCallableElementActivityBehaviorModule extends InstrumentationModule { + + public CamundaCallableElementActivityBehaviorModule() { + super("camunda", "camunda-behavior", "camunda-behavior-7_18"); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaCallableElementActivityBehaviorInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.CallableElementActivityBehavior"); + } + + String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior", + "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common" }; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorInstrumentation.java new file mode 100644 index 000000000000..067c42c67396 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorInstrumentation.java @@ -0,0 +1,121 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getOpentelemetry; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.anyOf; + +import java.util.Optional; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; +import org.camunda.bpm.model.bpmn.instance.CompensateEventDefinition; +import org.camunda.bpm.model.bpmn.instance.EndEvent; +import org.camunda.bpm.model.bpmn.instance.ErrorEventDefinition; +import org.camunda.bpm.model.bpmn.instance.EventDefinition; +import org.camunda.bpm.model.bpmn.instance.TerminateEventDefinition; + +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionGetter; +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class CamundaEndEventActivityBehaviorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.TerminateEndEventActivityBehavior"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.camunda.bpm.engine.impl.bpmn.behavior.TerminateEndEventActivityBehavior") + .or(named("org.camunda.bpm.engine.impl.bpmn.behavior.NoneEndEventActivityBehavior") + .or(named("org.camunda.bpm.engine.impl.bpmn.behavior.ErrorEndEventActivityBehavior"))); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(ElementMatchers.isMethod().and(ElementMatchers.named("execute")), + this.getClass().getName() + "$CamundaEndEventActivityBehaviorAdvice"); + } + + public static class CamundaEndEventActivityBehaviorAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter(@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) { + + if (execution == null) { + return; + } + + request = new CamundaCommonRequest(); + request.setProcessDefinitionId(Optional.ofNullable(execution.getProcessDefinitionId())); + request.setProcessInstanceId(Optional.ofNullable(execution.getProcessInstanceId())); + request.setActivityId(Optional.ofNullable(execution.getCurrentActivityId())); + + if (execution.getBpmnModelElementInstance() != null) { + //TODO lambda does not work due to access modifier + + // TODO add other events execution.getBpmnModelElementInstance() instanceof + // EndEvent + EndEvent e = (EndEvent) execution.getBpmnModelElementInstance(); + + for (EventDefinition ed : e.getEventDefinitions()) { + if (ed instanceof TerminateEventDefinition) { + request.setActivityName(Optional.of("End")); + }else if(ed instanceof ErrorEventDefinition) { + request.setActivityName(Optional.of("Error End")); + }else if(ed instanceof CompensateEventDefinition) { + request.setActivityName(Optional.of("Compensation End")); + }else { + request.setActivityName(Optional.of("End")); + } + } + } + + String processInstanceId = execution.getProcessInstanceId(); + + 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(); + + } + + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeSpan(@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(); + } + + } + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorModule.java new file mode 100644 index 000000000000..26a81dbcd2ed --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaEndEventActivityBehaviorModule.java @@ -0,0 +1,48 @@ +package com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaEndEventActivityBehaviorModule extends InstrumentationModule { + + public CamundaEndEventActivityBehaviorModule() { + super("camunda", "camunda-behavior", "camunda-behavior-7_18"); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaEndEventActivityBehaviorInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.TerminateEndEventActivityBehavior", + "org.camunda.bpm.engine.impl.bpmn.behavior.NoneEndEventActivityBehavior", + "org.camunda.bpm.engine.impl.bpmn.behavior.ErrorEndEventActivityBehavior"); + } + + String[] helperClassnames = { "com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior", + "com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.common" }; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorInstrumentation.java new file mode 100644 index 000000000000..83f6c29175ba --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorInstrumentation.java @@ -0,0 +1,121 @@ +package com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior; + +import static com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getInstumenter; +import static com.att.sre.otel.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getOpentelemetry; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionGetter; +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionLocalSetter; +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionSetter; +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaVariableMapSetter; +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class CamundaExternalTaskActivityBehaviorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.ExternalTaskActivityBehavior"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.camunda.bpm.engine.impl.bpmn.behavior.ExternalTaskActivityBehavior"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(ElementMatchers.isMethod().and(ElementMatchers.named("execute")), + this.getClass().getName() + "$CamundaExternalTaskActivityBehaviorAdvice"); + + } + + public static class CamundaExternalTaskActivityBehaviorAdvice { + + /** + * Sets parent context to process instance. Creates new context and span. + * propagates current context to variable set for topic to retrieve. + * + * @param execution + * @param request + * @param parentScope + * @param parentContext + * @param context + * @param scope + */ + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter(@Advice.Argument(0) ActivityExecution execution, + @Advice.Local("request") CamundaCommonRequest request, + @Advice.Local("otelParentScope") Scope parentScope, + @Advice.Local("otelParentContext") Context parentContext, @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + + if (execution == null) { + 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())); + + String processInstanceId = execution.getProcessInstanceId(); + + if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { + } + + 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(); + + getOpentelemetry().getPropagators().getTextMapPropagator().inject(context, execution, + new CamundaActivityExecutionLocalSetter()); + } + + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeTrace(@Advice.Local("request") CamundaCommonRequest request, + @Advice.Local("otelParentScope") Scope parentScope, + @Advice.Local("otelParentContext") Context parentContext, @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(); + } + + } + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorModule.java new file mode 100644 index 000000000000..31cd2bcc2303 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaExternalTaskActivityBehaviorModule.java @@ -0,0 +1,46 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaExternalTaskActivityBehaviorModule extends InstrumentationModule { + + public CamundaExternalTaskActivityBehaviorModule() { + super("camunda", "camunda-behavior", "camunda-behavior-7_18"); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaExternalTaskActivityBehaviorInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.ExternalTaskActivityBehavior"); + } + + String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior", + "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common" }; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorInstrumentation.java new file mode 100644 index 000000000000..1c7dd80130da --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorInstrumentation.java @@ -0,0 +1,101 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior.CamundaBehaviorSingletons.getOpentelemetry; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import java.util.Optional; +import java.util.logging.Level; + +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +import io.opentelemetry.javaagent.camunda.v7_18.behavior.CamundaActivityExecutionGetter; +import io.opentelemetry.javaagent.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class CamundaTaskActivityBehaviorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.TaskActivityBehavior"); + } + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("org.camunda.bpm.engine.impl.bpmn.behavior.TaskActivityBehavior")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(ElementMatchers.isMethod().and(ElementMatchers.named("performExecution")), + this.getClass().getName() + "$CamundaTaskActivityBehaviorAdvice"); + } + + public static class CamundaTaskActivityBehaviorAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter(@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) { + + + if (execution == null) { + 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())); + + String processInstanceId = execution.getProcessInstanceId(); + + if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { + } + + 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(); + + } + + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeTrace(@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(); + } + + } + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorModule.java new file mode 100644 index 000000000000..a29e4e766e42 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaTaskActivityBehaviorModule.java @@ -0,0 +1,46 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaTaskActivityBehaviorModule extends InstrumentationModule { + + public CamundaTaskActivityBehaviorModule() { + super("camunda", "camunda-behavior", "camunda-behavior-7_18"); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaTaskActivityBehaviorInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.camunda.bpm.engine.impl.bpmn.behavior.TaskActivityBehavior"); + } + + String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior", + "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common"}; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaVariableMapSetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaVariableMapSetter.java new file mode 100644 index 000000000000..d8db3a6aadf7 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/behavior/CamundaVariableMapSetter.java @@ -0,0 +1,14 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.behavior; + +import org.camunda.bpm.engine.variable.VariableMap; + +import io.opentelemetry.context.propagation.TextMapSetter; + +public class CamundaVariableMapSetter implements TextMapSetter { + + @Override + public void set(VariableMap carrier, String key, String value) { + carrier.put(key, value); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaCommonRequest.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaCommonRequest.java new file mode 100644 index 000000000000..18de18f8220c --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaCommonRequest.java @@ -0,0 +1,71 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common; + +import java.util.Optional; + +public class CamundaCommonRequest { + + private Optional processDefinitionId = Optional.empty(); + private Optional processDefinitionKey = Optional.empty(); + private Optional processInstanceId = Optional.empty(); + private Optional activityId = Optional.empty(); + private Optional activityName = Optional.empty(); + private Optional topicName = Optional.empty(); + private Optional topicWorkerId = Optional.empty(); + + public Optional getProcessDefinitionId() { + return processDefinitionId; + } + + public void setProcessDefinitionId(Optional processDefinitionId) { + this.processDefinitionId = processDefinitionId; + } + + public Optional getProcessDefinitionKey() { + return processDefinitionKey; + } + + public void setProcessDefinitionKey(Optional processDefinitionKey) { + this.processDefinitionKey = processDefinitionKey; + } + + public Optional getProcessInstanceId() { + return processInstanceId; + } + + public void setProcessInstanceId(Optional processInstanceId) { + this.processInstanceId = processInstanceId; + } + + public Optional getActivityId() { + return activityId; + } + + public void setActivityId(Optional activityId) { + this.activityId = activityId; + } + + public Optional getActivityName() { + return activityName; + } + + public void setActivityName(Optional activityName) { + this.activityName = activityName; + } + + public Optional getTopicName() { + return topicName; + } + + public void setTopicName(Optional topicName) { + this.topicName = topicName; + } + + public Optional getTopicWorkerId() { + return topicWorkerId; + } + + public void setTopicWorkerId(Optional topicWorkerId) { + this.topicWorkerId = topicWorkerId; + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaVariableAttributeExtractor.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaVariableAttributeExtractor.java new file mode 100644 index 000000000000..b1f3bc405503 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/common/CamundaVariableAttributeExtractor.java @@ -0,0 +1,28 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common; + +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; + +public class CamundaVariableAttributeExtractor implements AttributesExtractor { + + @Override + public void onStart(AttributesBuilder attributes, Context parentContext, CamundaCommonRequest request) { + + request.getProcessDefinitionKey().ifPresent(pdk -> attributes.put("camunda.processdefinitionkey", pdk)); + request.getProcessDefinitionId().ifPresent(pdi -> attributes.put("camunda.processdefinitionid", pdi)); + request.getProcessInstanceId().ifPresent(pid -> attributes.put("camunda.processinstanceid", pid)); + request.getActivityId().ifPresent(aid -> attributes.put("camunda.activityid", aid)); + request.getActivityName().ifPresent(an -> attributes.put("camunda.activityname", an)); + request.getTopicName().ifPresent(tn -> attributes.put("camunda.topicname", tn)); + request.getTopicWorkerId().ifPresent(twi -> attributes.put("camunda.topicworkerid", twi)); + } + + @Override + public void onEnd(AttributesBuilder attributes, Context context, CamundaCommonRequest request, String response, + Throwable error) { + // TODO Auto-generated method stub + + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java new file mode 100644 index 000000000000..2fc9b52e8216 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java @@ -0,0 +1,112 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Optional; + +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs.CamundaJobSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs.CamundaJobSingletons.getOpentelemetry; + +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs.CamundaExecutionEntityGetter; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class CamundaAsyncContinuationJobHandlerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler"); + } + + @Override + public ElementMatcher typeMatcher() { + return ElementMatchers.named("org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer + .applyAdviceToMethod( + ElementMatchers.isMethod().and(ElementMatchers.named("execute")) + .and(ElementMatchers.takesArgument(1, + ElementMatchers.named( + "org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity"))), + this.getClass().getName() + "$CamundaAsyncContinuationJobHandlerAdvice"); + } + + public static class CamundaAsyncContinuationJobHandlerAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter(@Advice.Argument(1) ExecutionEntity executionEntity, + @Advice.Local("request") CamundaCommonRequest request, + @Advice.Local("otelParentScope") Scope parentScope, @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + + if (executionEntity == null) { + return; + } + + request = new CamundaCommonRequest(); + request.setProcessDefinitionId(Optional.ofNullable(executionEntity.getProcessDefinitionId())); + if (executionEntity.getProcessDefinition() != null) { + request.setProcessDefinitionKey(Optional.ofNullable(executionEntity.getProcessDefinition().getKey())); + } + request.setProcessInstanceId(Optional.ofNullable(executionEntity.getProcessInstanceId())); + request.setActivityId(Optional.ofNullable(executionEntity.getActivityId())); + if (executionEntity.getActivity() != null) { + request.setActivityName(Optional.ofNullable(executionEntity.getActivity().getName())); + } + + String processInstanceId = executionEntity.getProcessInstanceId(); + + if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { + System.out.println("No initial span context for process instance " + processInstanceId); + } + + Context parentContext = getOpentelemetry().getPropagators().getTextMapPropagator() + .extract(Java8BytecodeBridge.currentContext(), executionEntity, new CamundaExecutionEntityGetter()); + + parentScope = parentContext.makeCurrent(); + + if (getInstumenter().shouldStart(Java8BytecodeBridge.currentContext(), request)) { + context = getInstumenter().start(Java8BytecodeBridge.currentContext(), request); + scope = context.makeCurrent(); + + } else { + System.out.println("Unable to start telemetry for process " + processInstanceId); + } + + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeTrace(@Advice.Argument(1) ExecutionEntity executionEntity, + @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(); + } + + } + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerModule.java new file mode 100644 index 000000000000..63b5f8e9a595 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaAsyncContinuationJobHandlerModule.java @@ -0,0 +1,46 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaAsyncContinuationJobHandlerModule extends InstrumentationModule { + + public CamundaAsyncContinuationJobHandlerModule() { + super("camunda", "camunda-job", "camunda-job-7_18"); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaAsyncContinuationJobHandlerInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler"); + } + + String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs", + "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common"}; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaExecutionEntityGetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaExecutionEntityGetter.java new file mode 100644 index 000000000000..dc04180f6c36 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaExecutionEntityGetter.java @@ -0,0 +1,20 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs; + +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; + +import io.opentelemetry.context.propagation.TextMapGetter; + +public class CamundaExecutionEntityGetter implements TextMapGetter { + + @Override + public Iterable keys(ExecutionEntity carrier) { + return carrier.getVariableNames(); + } + + @Override + public String get(ExecutionEntity carrier, String key) { + Object variable = carrier.getVariables().get(key); + return variable == null ? null : variable.toString(); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSingletons.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSingletons.java new file mode 100644 index 000000000000..f640c38b2d18 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSingletons.java @@ -0,0 +1,41 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs; + +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaVariableAttributeExtractor; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs.CamundaJobSpanNameExtractor; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; + +public class CamundaJobSingletons { + + private static final Instrumenter instrumenter; + + private static final OpenTelemetry opentelemetry; + + static { + + opentelemetry = GlobalOpenTelemetry.get(); + + InstrumenterBuilder builder = Instrumenter + .builder(opentelemetry, "io.opentelemetry.camunda-job", + new CamundaJobSpanNameExtractor()) + .addAttributesExtractor(new CamundaVariableAttributeExtractor()); + + instrumenter = builder.buildInstrumenter(); + } + + public static OpenTelemetry getOpentelemetry() { + return opentelemetry; + } + + public static Instrumenter getInstumenter() { + return instrumenter; + } + + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSpanNameExtractor.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSpanNameExtractor.java new file mode 100644 index 000000000000..943d6e7871f8 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/jobs/CamundaJobSpanNameExtractor.java @@ -0,0 +1,14 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.jobs; + +import com.att.sre.otel.instrumentation.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; + +public class CamundaJobSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(CamundaCommonRequest request) { + return String.format("%s AsyncContinuation", request.getActivityName().get()); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaActivityInstantiationBuilderSetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaActivityInstantiationBuilderSetter.java new file mode 100644 index 000000000000..f08b19de728a --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaActivityInstantiationBuilderSetter.java @@ -0,0 +1,16 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes; + +import org.camunda.bpm.engine.runtime.ActivityInstantiationBuilder; + +import io.opentelemetry.context.propagation.TextMapSetter; + +//TODO Bound it to ProcessInstanceModificationBuilder ?? +public class CamundaActivityInstantiationBuilderSetter implements TextMapSetter> { + + @Override + public void set(ActivityInstantiationBuilder carrier, String key, String value) { + carrier.setVariable(key, value); + + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstanceModificationBuilderImpSetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstanceModificationBuilderImpSetter.java new file mode 100644 index 000000000000..215278a1eea6 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstanceModificationBuilderImpSetter.java @@ -0,0 +1,17 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes; + +import org.camunda.bpm.engine.impl.ProcessInstanceModificationBuilderImpl; + +import io.opentelemetry.context.propagation.TextMapSetter; + +//TODO use this or activityinstanctiationbuildersetter ?? +public class CamundaProcessInstanceModificationBuilderImpSetter + implements TextMapSetter { + + @Override + public void set(ProcessInstanceModificationBuilderImpl carrier, String key, String value) { + carrier.setVariable(key, value); + + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderInstrumentation.java new file mode 100644 index 000000000000..55a916e9c071 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderInstrumentation.java @@ -0,0 +1,93 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import java.util.Optional; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes.CamundaActivityInstantiationBuilderSetter; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes.CamundaProcessSingletons; + +import org.camunda.bpm.engine.impl.ProcessInstanceModificationBuilderImpl; +import org.camunda.bpm.engine.runtime.ProcessInstance; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class CamundaProcessInstantiationBuilderInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.engine.runtime.ProcessInstantiationBuilder"); + } + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("org.camunda.bpm.engine.runtime.ProcessInstantiationBuilder")); + } + + @Override + public void transform(TypeTransformer transformer) { + // comment out for now because it causes duplicate spans + // transformer.applyAdviceToMethod(isMethod().and(named("execute")), + // this.getClass().getName() + + // "$CamundaProcessInstantiationBuilderExecuteAdvice"); + + transformer.applyAdviceToMethod(isMethod().and(named("executeWithVariablesInReturn")), + this.getClass().getName() + "$CamundaProcessInstantiationBuilderExecuteWithVariablesAdvice"); + + } + + public static class CamundaProcessInstantiationBuilderExecuteWithVariablesAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter( + @Advice.FieldValue("modificationBuilder") ProcessInstanceModificationBuilderImpl modificationBuilder, + @Advice.FieldValue("processDefinitionKey") String processDefinitionKey, + @Advice.FieldValue("businessKey") String businessKey, + @Advice.Local("request") CamundaCommonRequest request, @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + request = new CamundaCommonRequest(); + request.setProcessDefinitionKey(Optional.ofNullable(processDefinitionKey)); + + Context parentContext = Java8BytecodeBridge.currentContext(); + + if (CamundaProcessSingletons.getInstumenter().shouldStart(parentContext, request)) { + context = CamundaProcessSingletons.getInstumenter().start(parentContext, request); + scope = context.makeCurrent(); + + SpanContext currentSpanContext = Java8BytecodeBridge.spanFromContext(context).getSpanContext(); + if (currentSpanContext.isValid()) { + CamundaProcessSingletons.getOpentelemetry().getPropagators().getTextMapPropagator() + .inject(context, modificationBuilder, new CamundaActivityInstantiationBuilderSetter()); + } + + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeTrace(@Advice.FieldValue("processDefinitionKey") String processDefinitionKey, + @Advice.Local("request") CamundaCommonRequest request, @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope, @Advice.Thrown Throwable throwable, + @Advice.Return ProcessInstance pi) { + + if (context != null && scope != null) { + CamundaProcessSingletons.getInstumenter().end(context, request, "NA", throwable); + scope.close(); + } + + } + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderModule.java new file mode 100644 index 000000000000..7472cb4fe233 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessInstantiationBuilderModule.java @@ -0,0 +1,48 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaProcessInstantiationBuilderModule extends InstrumentationModule { + + public CamundaProcessInstantiationBuilderModule() { + super("camunda", "camunda-process", "camunda-process-7_18"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaProcessInstantiationBuilderInstrumentation()); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + + return hasClassesNamed("org.camunda.bpm.engine.runtime.ProcessInstantiationBuilder"); + } + + String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes", + "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common" }; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSingletons.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSingletons.java new file mode 100644 index 000000000000..a1e6f4e0b175 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSingletons.java @@ -0,0 +1,41 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaVariableAttributeExtractor; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes.CamundaProcessSpanNameExtractor; + +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.javaagent.bootstrap.internal.InstrumentationConfig; + +public class CamundaProcessSingletons { + + private static final Instrumenter instrumenter; + + private static final OpenTelemetry opentelemetry; + + private static final boolean propagationEnabled; + + static { + + opentelemetry = GlobalOpenTelemetry.get(); + + InstrumenterBuilder builder = Instrumenter + .builder(opentelemetry, "io.opentelemetry.camunda-process", + new CamundaProcessSpanNameExtractor()) + .addAttributesExtractor(new CamundaVariableAttributeExtractor()); + + instrumenter = builder.buildInstrumenter(); + } + + public static OpenTelemetry getOpentelemetry() { + return opentelemetry; + } + + public static Instrumenter getInstumenter() { + return instrumenter; + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSpanNameExtractor.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSpanNameExtractor.java new file mode 100644 index 000000000000..e2ebe4346c59 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/processes/CamundaProcessSpanNameExtractor.java @@ -0,0 +1,14 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.processes; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; + +public class CamundaProcessSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(CamundaCommonRequest request) { + return request.getProcessDefinitionKey().get(); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaExternalTaskGetter.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaExternalTaskGetter.java new file mode 100644 index 000000000000..a33a80809d78 --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaExternalTaskGetter.java @@ -0,0 +1,20 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task; + +import org.camunda.bpm.client.task.ExternalTask; + +import io.opentelemetry.context.propagation.TextMapGetter; + +public class CamundaExternalTaskGetter implements TextMapGetter { + + @Override + public Iterable keys(ExternalTask carrier) { + return carrier.getAllVariables().keySet(); + } + + @Override + public String get(ExternalTask carrier, String key) { + Object variable = carrier.getAllVariables().get(key); + return variable == null ? null : variable.toString(); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSingletons.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSingletons.java new file mode 100644 index 000000000000..fffbc1f9a4dc --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSingletons.java @@ -0,0 +1,38 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaVariableAttributeExtractor; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task.CamundaTaskSpanNameExtractor; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; + +public class CamundaTaskSingletons { + + private static final Instrumenter instrumenter; + + private static final OpenTelemetry opentelemetry; + + static { + + opentelemetry = GlobalOpenTelemetry.get(); + + InstrumenterBuilder builder = Instrumenter + .builder(opentelemetry, "io.opentelemetry.camunda-task", + new CamundaTaskSpanNameExtractor()) + .addAttributesExtractor(new CamundaVariableAttributeExtractor()); + + instrumenter = builder.buildInstrumenter(); + } + + public static OpenTelemetry getOpentelemetry() { + return opentelemetry; + } + + public static Instrumenter getInstumenter() { + return instrumenter; + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSpanNameExtractor.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSpanNameExtractor.java new file mode 100644 index 000000000000..be234ae6fbda --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTaskSpanNameExtractor.java @@ -0,0 +1,14 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; + +public class CamundaTaskSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(CamundaCommonRequest request) { + return String.format("%s Topic", request.getTopicName().get()); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionManagerModule.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionManagerModule.java new file mode 100644 index 000000000000..1d214cba5f9b --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionManagerModule.java @@ -0,0 +1,46 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +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 net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaTopicSubscriptionManagerModule extends InstrumentationModule { + + public CamundaTopicSubscriptionManagerModule() { + super("camunda", "camunda-task", "camunda-task-7_18"); + } + + @Override + public boolean defaultEnabled(ConfigProperties config) { + return config.getBoolean("otel.instrumentation.common.default-enabled", true); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaTopicSubscriptionMangerInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.camunda.bpm.client.topic.impl.TopicSubscriptionManager"); + } + + String[] helperClassnames = { "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task", + "io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common" }; + + @Override + public boolean isHelperClass(String classname) { + return super.isHelperClass(classname) || Arrays.stream(helperClassnames).anyMatch(c -> classname.startsWith(c)); + } + +} diff --git a/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionMangerInstrumentation.java b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionMangerInstrumentation.java new file mode 100644 index 000000000000..f4cee3721c9e --- /dev/null +++ b/instrumentation/camunda/camunda-7.18/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_18/task/CamundaTopicSubscriptionMangerInstrumentation.java @@ -0,0 +1,111 @@ +package io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task; + +import static io.opentelemetry.javaagent.instrumentationn.camunda.v7_18.task.CamundaTaskSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentationn.camunda.v7_18.task.CamundaTaskSingletons.getOpentelemetry; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import java.util.Map; +import java.util.Optional; + +import org.camunda.bpm.client.task.ExternalTask; +import org.camunda.bpm.client.task.impl.ExternalTaskImpl; +import org.camunda.bpm.client.variable.impl.TypedValueField; +import org.camunda.bpm.client.variable.impl.TypedValues; +import org.camunda.bpm.client.variable.impl.VariableValue; + +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.common.CamundaCommonRequest; +import io.opentelemetry.javaagent.instrumentation.camunda.v7_18.task.CamundaExternalTaskGetter; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class CamundaTopicSubscriptionMangerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.camunda.bpm.client.topic.impl.TopicSubscriptionManager"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.camunda.bpm.client.topic.impl.TopicSubscriptionManager"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(ElementMatchers.isMethod().and(named("handleExternalTask")), + this.getClass().getName() + "$CamundaTopicSubscriptionMangerAdvice"); + } + + public static class CamundaTopicSubscriptionMangerAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter(@Advice.FieldValue("typedValues") TypedValues typedValues, + @Advice.Argument(0) ExternalTask externalTask, @Advice.Local("request") CamundaCommonRequest request, + @Advice.Local("otelParentScope") Scope parentScope, @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + + if (externalTask == null) { + return; + } + + request = new CamundaCommonRequest(); + request.setProcessDefinitionKey(Optional.ofNullable(externalTask.getProcessDefinitionKey())); + request.setProcessInstanceId(Optional.ofNullable(externalTask.getProcessInstanceId())); + request.setTopicName(Optional.ofNullable(externalTask.getTopicName())); + request.setTopicWorkerId(Optional.ofNullable(externalTask.getWorkerId())); + + String id = externalTask.getTopicName() + " " + externalTask.getWorkerId(); + + if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { + //log + } + + ExternalTaskImpl task = (ExternalTaskImpl) externalTask; + + Map variables = task.getVariables(); + + Map wrappedVariables = typedValues.wrapVariables(externalTask, variables); + + task.setReceivedVariableMap(wrappedVariables); + + Context parentContext = getOpentelemetry().getPropagators().getTextMapPropagator() + .extract(Java8BytecodeBridge.currentContext(), externalTask, new CamundaExternalTaskGetter()); + + parentScope = parentContext.makeCurrent(); + + if (getInstumenter().shouldStart(Java8BytecodeBridge.currentContext(), request)) { + context = getInstumenter().start(Java8BytecodeBridge.currentContext(), request); + scope = context.makeCurrent(); + + } + + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void closeTrace(@Advice.Local("otelParentScope") Scope parentScope, + @Advice.Local("request") CamundaCommonRequest request, @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(); + } + + } + } +}