diff --git a/instrumentation/camunda/camunda-7.0/javaagent/build.gradle.kts b/instrumentation/camunda/camunda-7.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..566beb300d0f --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/build.gradle.kts @@ -0,0 +1,23 @@ +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") +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaBehaviorSingletons.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaBehaviorSingletons.java new file mode 100644 index 000000000000..2f2f04db16b1 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaBehaviorSingletons.java @@ -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 instrumenter; + + private static final OpenTelemetry opentelemetry; + + 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; + } + + private CamundaBehaviorSingletons() {} +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java new file mode 100644 index 000000000000..7756fafd796f --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCallableElementActivityBehaviorInstrumentation.java @@ -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 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"); + } + + @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 + } + + 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.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCallableElementActivityBehaviorModule.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCallableElementActivityBehaviorModule.java new file mode 100644 index 000000000000..0e131ad4c83a --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCallableElementActivityBehaviorModule.java @@ -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-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_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)); + } +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCommonBehaviorInstrumentation.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCommonBehaviorInstrumentation.java new file mode 100644 index 000000000000..39eabce31583 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCommonBehaviorInstrumentation.java @@ -0,0 +1,158 @@ +/* + * 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.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.camunda.v7_0.behavior.CamundaActivityExecutionGetter; +import io.opentelemetry.instrumentation.camunda.v7_0.behavior.CamundaActivityExecutionLocalSetter; +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.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.Gateway; +import org.camunda.bpm.model.bpmn.instance.TerminateEventDefinition; + +public class CamundaCommonBehaviorInstrumentation 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")) + .or(named("org.camunda.bpm.engine.impl.bpmn.behavior.ExternalTaskActivityBehavior")) + .or(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")); + // elements that have been tested with instrumentation, eventually this can be + // replaced by the supertype AbstractBpmnActivityBehavior, once all elements + // instrumentations have been certified and instrumented, but will need to make + // sure its not callable element as the instrumentation should remain separate + // due to logic + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + ElementMatchers.isMethod().and(ElementMatchers.named("execute")), + this.getClass().getName() + "$CamundaCommonBehaviorAdvice"); + } + + @SuppressWarnings("unused") + public static class CamundaCommonBehaviorAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter( + @Advice.Argument(0) ActivityExecution execution, + @Advice.This Object target, + @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.setBusinessKey(Optional.ofNullable(execution.getProcessBusinessKey())); + + if (execution.getBpmnModelElementInstance() != null) { + // TODO lambda does not work due to access modifier + if (execution.getBpmnModelElementInstance() instanceof EndEvent) { + EndEvent e = (EndEvent) execution.getBpmnModelElementInstance(); + + if (e.getEventDefinitions() == null || e.getEventDefinitions().isEmpty()) { + request.setActivityName(Optional.of("End")); + } + 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")); + } + } + } else if (execution.getBpmnModelElementInstance() instanceof Gateway) { + // TODO + } else { + request.setActivityName(Optional.ofNullable(execution.getCurrentActivityName())); + } + } else { + request.setActivityName(Optional.ofNullable(execution.getCurrentActivityName())); + } + + 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(); + + if (target.getClass() + == org.camunda.bpm.engine.impl.bpmn.behavior.ExternalTaskActivityBehavior.class) { + + 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.This Object target, + @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.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCommonBehaviorModule.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCommonBehaviorModule.java new file mode 100644 index 000000000000..aaed45e34f60 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/behavior/CamundaCommonBehaviorModule.java @@ -0,0 +1,49 @@ +/* + * 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 java.util.Arrays; +import java.util.Collections; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CamundaCommonBehaviorModule extends InstrumentationModule { + + public CamundaCommonBehaviorModule() { + super("camunda", "camunda-behavior", "camunda-behavior-7_18"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CamundaCommonBehaviorInstrumentation()); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed( + "org.camunda.bpm.engine.impl.bpmn.behavior.TaskActivityBehavior", + "org.camunda.bpm.engine.impl.bpmn.behavior.NoneEndEventActivityBehavior", + "org.camunda.bpm.engine.impl.bpmn.behavior.ErrorEndEventActivityBehavior"); + } + + 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)); + } +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java new file mode 100644 index 000000000000..f89d74402b86 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaAsyncContinuationJobHandlerInstrumentation.java @@ -0,0 +1,118 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.jobs; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_0.jobs.CamundaJobSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_0.jobs.CamundaJobSingletons.getOpentelemetry; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; +import io.opentelemetry.instrumentation.camunda.v7_0.jobs.CamundaExecutionEntityGetter; +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.persistence.entity.ExecutionEntity; + +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"); + } + + @SuppressWarnings("unused") + 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())); + } + + if (Java8BytecodeBridge.currentContext() == Java8BytecodeBridge.rootContext()) { + // log + } + + 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(); + } + } + + @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.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaAsyncContinuationJobHandlerModule.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaAsyncContinuationJobHandlerModule.java new file mode 100644 index 000000000000..6f3f83db7794 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaAsyncContinuationJobHandlerModule.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.jobs; + +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 java.util.Arrays; +import java.util.Collections; +import java.util.List; +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 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_0.jobs", + "io.opentelemetry.instrumentation.camunda.v7_0.jobs", + "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)); + } +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaJobSingletons.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaJobSingletons.java new file mode 100644 index 000000000000..1dcb81c74f0b --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/jobs/CamundaJobSingletons.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.jobs; + +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.common.CamundaCommonRequest; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaVariableAttributeExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.jobs.CamundaJobSpanNameExtractor; + +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; + } + + private CamundaJobSingletons() {} +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessInstantiationBuilderInstrumentation.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessInstantiationBuilderInstrumentation.java new file mode 100644 index 000000000000..bff245476d99 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessInstantiationBuilderInstrumentation.java @@ -0,0 +1,102 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.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 io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; +import io.opentelemetry.instrumentation.camunda.v7_0.processes.CamundaActivityInstantiationBuilderSetter; +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 org.camunda.bpm.engine.impl.ProcessInstanceModificationBuilderImpl; +import org.camunda.bpm.engine.runtime.ProcessInstance; + +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"); + } + + @SuppressWarnings("unused") + 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.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessInstantiationBuilderModule.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessInstantiationBuilderModule.java new file mode 100644 index 000000000000..3a96cada2b63 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessInstantiationBuilderModule.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.processes; + +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 java.util.Arrays; +import java.util.Collections; +import java.util.List; +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 ElementMatcher.Junction classLoaderMatcher() { + + return hasClassesNamed("org.camunda.bpm.engine.runtime.ProcessInstantiationBuilder"); + } + + String[] helperClassnames = { + "io.opentelemetry.javaagent.instrumentation.camunda.v7_0.processes", + "io.opentelemetry.instrumentation.camunda.v7_0.processes", + "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)); + } +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessSingletons.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessSingletons.java new file mode 100644 index 000000000000..e8437e0d9640 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/processes/CamundaProcessSingletons.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.processes; + +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.common.CamundaCommonRequest; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaVariableAttributeExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.processes.CamundaProcessSpanNameExtractor; + +public class CamundaProcessSingletons { + + private static final Instrumenter instrumenter; + + private static final OpenTelemetry opentelemetry; + + 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; + } + + private CamundaProcessSingletons() {} +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTaskSingletons.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTaskSingletons.java new file mode 100644 index 000000000000..4cb4b76777b2 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTaskSingletons.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.task; + +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.common.CamundaCommonRequest; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaVariableAttributeExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.task.CamundaTaskSpanNameExtractor; + +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; + } + + private CamundaTaskSingletons() {} +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTopicSubscriptionManagerModule.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTopicSubscriptionManagerModule.java new file mode 100644 index 000000000000..659737ae4a54 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTopicSubscriptionManagerModule.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.task; + +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 java.util.Arrays; +import java.util.Collections; +import java.util.List; +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 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_0.task", + "io.opentelemetry.instrumentation.camunda.v7_0.task", + "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)); + } +} diff --git a/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTopicSubscriptionMangerInstrumentation.java b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTopicSubscriptionMangerInstrumentation.java new file mode 100644 index 000000000000..e58c19e793b3 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/camunda/v7_0/task/CamundaTopicSubscriptionMangerInstrumentation.java @@ -0,0 +1,123 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.camunda.v7_0.task; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_0.task.CamundaTaskSingletons.getInstumenter; +import static io.opentelemetry.javaagent.instrumentation.camunda.v7_0.task.CamundaTaskSingletons.getOpentelemetry; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; +import io.opentelemetry.instrumentation.camunda.v7_0.task.CamundaExternalTaskGetter; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.Map; +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.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; + +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 { + + @SuppressWarnings({"rawtypes", "unused"}) + @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(); + } + } + } +} diff --git a/instrumentation/camunda/camunda-7.0/library/build.gradle.kts b/instrumentation/camunda/camunda-7.0/library/build.gradle.kts new file mode 100644 index 000000000000..8ac3e9ba95e4 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + 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") +} + +tasks.withType().configureEach { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) +} diff --git a/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionGetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionGetter.java new file mode 100644 index 000000000000..07215e0ec122 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.behavior; + +import io.opentelemetry.context.propagation.TextMapGetter; +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionLocalSetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionLocalSetter.java new file mode 100644 index 000000000000..5b220abce7e4 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionLocalSetter.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.behavior; + +import io.opentelemetry.context.propagation.TextMapSetter; +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionSetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionSetter.java new file mode 100644 index 000000000000..075a7c7c991f --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaActivityExecutionSetter.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.behavior; + +import io.opentelemetry.context.propagation.TextMapSetter; +import org.camunda.bpm.engine.impl.pvm.delegate.ActivityExecution; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaBehaviorSpanNameExtractor.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaBehaviorSpanNameExtractor.java new file mode 100644 index 000000000000..96a530793df3 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaBehaviorSpanNameExtractor.java @@ -0,0 +1,24 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.behavior; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; +import java.util.Arrays; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaVariableMapSetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaVariableMapSetter.java new file mode 100644 index 000000000000..838dcf9f13f3 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/behavior/CamundaVariableMapSetter.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.behavior; + +import io.opentelemetry.context.propagation.TextMapSetter; +import org.camunda.bpm.engine.variable.VariableMap; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/common/CamundaCommonRequest.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/common/CamundaCommonRequest.java new file mode 100644 index 000000000000..e5265dd672fc --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/common/CamundaCommonRequest.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.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 businessKey = 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 getBusinessKey() { + return businessKey; + } + + public void setBusinessKey(Optional businessKey) { + this.businessKey = businessKey; + } + + 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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/common/CamundaVariableAttributeExtractor.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/common/CamundaVariableAttributeExtractor.java new file mode 100644 index 000000000000..441489999084 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/common/CamundaVariableAttributeExtractor.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/jobs/CamundaExecutionEntityGetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/jobs/CamundaExecutionEntityGetter.java new file mode 100644 index 000000000000..38ee5fe76d4d --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/jobs/CamundaExecutionEntityGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.jobs; + +import io.opentelemetry.context.propagation.TextMapGetter; +import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/jobs/CamundaJobSpanNameExtractor.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/jobs/CamundaJobSpanNameExtractor.java new file mode 100644 index 000000000000..52ac6055cb0f --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/jobs/CamundaJobSpanNameExtractor.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.jobs; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaActivityInstantiationBuilderSetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaActivityInstantiationBuilderSetter.java new file mode 100644 index 000000000000..9999eaca0c7a --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaActivityInstantiationBuilderSetter.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.processes; + +import io.opentelemetry.context.propagation.TextMapSetter; +import org.camunda.bpm.engine.runtime.ActivityInstantiationBuilder; + +// 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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaProcessInstanceModificationBuilderImpSetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaProcessInstanceModificationBuilderImpSetter.java new file mode 100644 index 000000000000..5f0834858ffc --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaProcessInstanceModificationBuilderImpSetter.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.processes; + +import io.opentelemetry.context.propagation.TextMapSetter; +import org.camunda.bpm.engine.impl.ProcessInstanceModificationBuilderImpl; + +// 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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaProcessSpanNameExtractor.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaProcessSpanNameExtractor.java new file mode 100644 index 000000000000..9fea08620cc3 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/processes/CamundaProcessSpanNameExtractor.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.processes; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; + +public class CamundaProcessSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(CamundaCommonRequest request) { + return request.getProcessDefinitionKey().get(); + } +} diff --git a/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/task/CamundaExternalTaskGetter.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/task/CamundaExternalTaskGetter.java new file mode 100644 index 000000000000..b3030d6cae41 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/task/CamundaExternalTaskGetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.task; + +import io.opentelemetry.context.propagation.TextMapGetter; +import org.camunda.bpm.client.task.ExternalTask; + +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.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/task/CamundaTaskSpanNameExtractor.java b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/task/CamundaTaskSpanNameExtractor.java new file mode 100644 index 000000000000..4f9e53eefeb2 --- /dev/null +++ b/instrumentation/camunda/camunda-7.0/library/src/main/java/io/opentelemetry/instrumentation/camunda/v7_0/task/CamundaTaskSpanNameExtractor.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.camunda.v7_0.task; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.camunda.v7_0.common.CamundaCommonRequest; + +public class CamundaTaskSpanNameExtractor implements SpanNameExtractor { + + @Override + public String extract(CamundaCommonRequest request) { + return String.format("%s Topic", request.getTopicName().get()); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 4460bf268d1d..ce6a72204c3f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -217,6 +217,9 @@ include(":instrumentation:c3p0-0.9:library") include(":instrumentation:c3p0-0.9:testing") include(":instrumentation:camel-2.20:javaagent") include(":instrumentation:camel-2.20:javaagent-unit-tests") +include(":instrumentation:camunda:camunda-7.0:javaagent") +include(":instrumentation:camunda:camunda-7.0:library") +include(":instrumentation:camunda:camunda-7.0:testing") include(":instrumentation:cassandra:cassandra-3.0:javaagent") include(":instrumentation:cassandra:cassandra-4.0:javaagent") include(":instrumentation:cassandra:cassandra-4.4:javaagent")