diff --git a/spring-boot/README.md b/spring-boot/README.md index 20ed3cc..791511c 100644 --- a/spring-boot/README.md +++ b/spring-boot/README.md @@ -136,6 +136,13 @@ vanillabp: task-timeout: PT3S # overrides vanillabp.workflow-modules.Demo.workflows.DemoWorkflow.adapters.camunda8.task-timeout ``` +### Logging + +These attributes are set in addition to the [default logging context](https://github.com/vanillabp/spring-boot-support#logging) +(defined in class `io.vanillabp.camunda8.LoggingContext`): + +* Tenant-ID - The tenant ID used accessing Camunda 8 cluster. + ## Multi-instance Since Camunda 8 is a remote engine the workflow is processed in a different runtime environment. Due to this fact the Blueprint adapter cannot do the entire binding of multi-instance context information under the hood. In the BPMN the multi-instance aspects like the input element, the element's index and the total number of elements have to be defined according to a certain naming convention: diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index a990186..07d9bf9 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -35,7 +35,7 @@ io.vanillabp spring-boot-support - 1.1.1 + 1.1.2-SNAPSHOT io.camunda.spring diff --git a/spring-boot/src/main/java/io/vanillabp/camunda8/Camunda8AdapterConfiguration.java b/spring-boot/src/main/java/io/vanillabp/camunda8/Camunda8AdapterConfiguration.java index c947d8d..9a698d1 100644 --- a/spring-boot/src/main/java/io/vanillabp/camunda8/Camunda8AdapterConfiguration.java +++ b/spring-boot/src/main/java/io/vanillabp/camunda8/Camunda8AdapterConfiguration.java @@ -155,7 +155,10 @@ public Camunda8TaskHandler camunda8TaskHandler( final Object bean, final Method method, final List parameters, - final String idPropertyName) { + final String idPropertyName, + final String tenantId, + final String workflowModuleId, + final String bpmnProcessId) { return new Camunda8TaskHandler( taskType, @@ -163,7 +166,10 @@ public Camunda8TaskHandler camunda8TaskHandler( bean, method, parameters, - idPropertyName); + idPropertyName, + tenantId, + workflowModuleId, + bpmnProcessId); } diff --git a/spring-boot/src/main/java/io/vanillabp/camunda8/LoggingContext.java b/spring-boot/src/main/java/io/vanillabp/camunda8/LoggingContext.java new file mode 100644 index 0000000..0a292ce --- /dev/null +++ b/spring-boot/src/main/java/io/vanillabp/camunda8/LoggingContext.java @@ -0,0 +1,65 @@ +package io.vanillabp.camunda8; + +import org.slf4j.MDC; + +public class LoggingContext extends io.vanillabp.springboot.adapter.LoggingContext { + + /** + * The current workflow's tenant. Per default the same value as the workflow module's ID + * but may be overwritten by using Spring Boot properties. + * + * see Multi-tenancy + */ + public static final String WORKFLOW_TENANT_ID = "workflowTenantId"; + + public static void clearContext() { + + io.vanillabp.springboot.adapter.LoggingContext.clearContext(); + + MDC.remove(WORKFLOW_ADAPTER_ID); + MDC.remove(WORKFLOW_AGGREGATE_ID); + MDC.remove(WORKFLOW_BPM_ID); + MDC.remove(WORKFLOW_BPMN_ID); + MDC.remove(WORKFLOW_TASK_NODE); + MDC.remove(WORKFLOW_TASK_ID); + MDC.remove(WORKFLOW_TASK_NODE_ID); + MDC.remove(WORKFLOW_MODULE_ID); + MDC.remove(WORKFLOW_TENANT_ID); + + } + + public static void setLoggingContext( + final String adapterId, + final String tenantId, + final String workflowModuleId, + final String aggregateId, + final String bpmnId, + final String taskId, + final String bpmId, + final String taskNode, + final String taskNodeId) { + + MDC.put(WORKFLOW_ADAPTER_ID, adapterId); + MDC.put(WORKFLOW_AGGREGATE_ID, aggregateId); + MDC.put(WORKFLOW_BPM_ID, bpmId); + MDC.put(WORKFLOW_BPMN_ID, bpmnId); + MDC.put(WORKFLOW_TASK_NODE, taskNode); + MDC.put(WORKFLOW_TASK_ID, taskId); + MDC.put(WORKFLOW_TASK_NODE_ID, taskNodeId); + MDC.put(WORKFLOW_MODULE_ID, workflowModuleId); + MDC.put(WORKFLOW_TENANT_ID, tenantId); + + final var context = io.vanillabp.springboot.adapter.LoggingContext.getWriteableContext(); + context.put(WORKFLOW_TENANT_ID, tenantId); + context.put(WORKFLOW_ADAPTER_ID, adapterId); + context.put(WORKFLOW_AGGREGATE_ID, aggregateId); + context.put(WORKFLOW_BPM_ID, bpmId); + context.put(WORKFLOW_BPMN_ID, bpmnId); + context.put(WORKFLOW_TASK_NODE, taskNode); + context.put(WORKFLOW_TASK_ID, taskId); + context.put(WORKFLOW_TASK_NODE_ID, taskNodeId); + context.put(WORKFLOW_MODULE_ID, workflowModuleId); + + } + +} diff --git a/spring-boot/src/main/java/io/vanillabp/camunda8/service/Camunda8ProcessService.java b/spring-boot/src/main/java/io/vanillabp/camunda8/service/Camunda8ProcessService.java index 0c7eda7..c773042 100644 --- a/spring-boot/src/main/java/io/vanillabp/camunda8/service/Camunda8ProcessService.java +++ b/spring-boot/src/main/java/io/vanillabp/camunda8/service/Camunda8ProcessService.java @@ -3,6 +3,7 @@ import io.camunda.zeebe.client.ZeebeClient; import io.vanillabp.camunda8.Camunda8AdapterConfiguration; import io.vanillabp.camunda8.Camunda8VanillaBpProperties; +import io.vanillabp.camunda8.LoggingContext; import io.vanillabp.springboot.adapter.AdapterAwareProcessService; import io.vanillabp.springboot.adapter.ProcessServiceImplementation; import java.time.Duration; @@ -102,6 +103,11 @@ public CrudRepository getWorkflowAggregateRepository() { } + @Override + public String getPrimaryBpmnProcessId() { + return parent.getPrimaryBpmnProcessId(); + } + @Override public DE startWorkflow( final DE workflowAggregate) throws Exception { @@ -322,34 +328,53 @@ private DE runInTransaction( final Consumer runnable, final String methodSignature) { - // persist to get ID in case of @Id @GeneratedValue - // or force optimistic locking exceptions before running - // the workflow if aggregate was already persisted before - final var attachedAggregate = workflowAggregateRepository - .save(workflowAggregate); - - if (TransactionSynchronizationManager.isActualTransactionActive()) { - if (taskIdToTestForAlreadyCompletedOrCancelled != null) { + try { + + // persist to get ID in case of @Id @GeneratedValue + // or force optimistic locking exceptions before running + // the workflow if aggregate was already persisted before + final var attachedAggregate = workflowAggregateRepository + .save(workflowAggregate); + + final var aggregateId = getWorkflowAggregateId.apply(attachedAggregate); + final var bpmnProcessId = parent.getPrimaryBpmnProcessId(); + LoggingContext.setLoggingContext( + Camunda8AdapterConfiguration.ADAPTER_ID, + camunda8Properties.getTenantId(parent.getWorkflowModuleId()), + parent.getWorkflowModuleId(), + aggregateId == null ? null : aggregateId.toString(), + bpmnProcessId, + taskIdToTestForAlreadyCompletedOrCancelled, + null, + null, + null); + + if (TransactionSynchronizationManager.isActualTransactionActive()) { + if (taskIdToTestForAlreadyCompletedOrCancelled != null) { + publisher.publishEvent( + new Camunda8TransactionProcessor.Camunda8TestForTaskAlreadyCompletedOrCancelled( + methodSignature, + () -> client + .newUpdateTimeoutCommand(Long.parseUnsignedLong(taskIdToTestForAlreadyCompletedOrCancelled, 16)) + .timeout(Duration.ofMinutes(10)) + .send() + .join(5, TimeUnit.MINUTES), // needs to run synchronously + () -> "aggregate: " + aggregateId + "; bpmn-process-id: " + bpmnProcessId)); + } publisher.publishEvent( - new Camunda8TransactionProcessor.Camunda8TestForTaskAlreadyCompletedOrCancelled( + new Camunda8TransactionProcessor.Camunda8CommandAfterTx( methodSignature, - () -> client - .newUpdateTimeoutCommand(Long.parseUnsignedLong(taskIdToTestForAlreadyCompletedOrCancelled, 16)) - .timeout(Duration.ofMinutes(10)) - .send() - .join(5, TimeUnit.MINUTES), // needs to run synchronously - () -> "aggregate: " + getWorkflowAggregateId.apply(attachedAggregate) + "; bpmn-process-id: " + parent.getPrimaryBpmnProcessId())); + () -> runnable.accept(attachedAggregate), + () -> "aggregate: " + aggregateId + "; bpmn-process-id: " + bpmnProcessId)); + } else { + runnable.accept(attachedAggregate); } - publisher.publishEvent( - new Camunda8TransactionProcessor.Camunda8CommandAfterTx( - methodSignature, - () -> runnable.accept(attachedAggregate), - () -> "aggregate: " + getWorkflowAggregateId.apply(attachedAggregate) + "; bpmn-process-id: " + parent.getPrimaryBpmnProcessId())); - } else { - runnable.accept(attachedAggregate); - } - return attachedAggregate; + return attachedAggregate; + + } finally { + LoggingContext.clearContext(); + } } diff --git a/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskHandler.java b/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskHandler.java index 57f7b1c..4b13060 100644 --- a/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskHandler.java +++ b/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskHandler.java @@ -4,6 +4,8 @@ import io.camunda.zeebe.client.api.response.ActivatedJob; import io.camunda.zeebe.client.api.worker.JobClient; import io.camunda.zeebe.client.api.worker.JobHandler; +import io.vanillabp.camunda8.Camunda8AdapterConfiguration; +import io.vanillabp.camunda8.LoggingContext; import io.vanillabp.camunda8.service.Camunda8TransactionAspect; import io.vanillabp.camunda8.service.Camunda8TransactionProcessor; import io.vanillabp.camunda8.wiring.Camunda8Connectable.Type; @@ -40,6 +42,12 @@ public class Camunda8TaskHandler extends TaskHandlerBase implements JobHandler, private final String idPropertyName; + private final String tenantId; + + private final String workflowModuleId; + + private final String bpmnProcessId; + private ZeebeClient zeebeClient; public Camunda8TaskHandler( @@ -48,11 +56,17 @@ public Camunda8TaskHandler( final Object bean, final Method method, final List parameters, - final String idPropertyName) { + final String idPropertyName, + final String tenantId, + final String workflowModuleId, + final String bpmnProcessId) { super(workflowAggregateRepository, bean, method, parameters); this.taskType = taskType; this.idPropertyName = idPropertyName; + this.tenantId = tenantId; + this.workflowModuleId = workflowModuleId; + this.bpmnProcessId = bpmnProcessId; } @@ -79,6 +93,18 @@ public void handle( try { final var businessKey = getVariable(job, idPropertyName); + final var taskId = Long.toHexString(job.getKey()); + + LoggingContext.setLoggingContext( + Camunda8AdapterConfiguration.ADAPTER_ID, + tenantId, + workflowModuleId, + businessKey == null ? null : businessKey.toString(), + bpmnProcessId, + taskId, + Long.toString(job.getProcessInstanceKey()), + job.getBpmnProcessId() + "#" + job.getElementId(), + Long.toString(job.getElementInstanceKey())); logger.trace("Will handle task '{}' (task-definition '{}‘) of workflow '{}' (instance-id '{}') as job '{}'", job.getElementId(), @@ -134,7 +160,7 @@ public void handle( param, () -> { taskIdRetrieved.set(true); - return Long.toHexString(job.getKey()); + return taskId; }), (args, param) -> processTaskEventParameter( args, @@ -167,6 +193,7 @@ public void handle( } finally { Camunda8TransactionProcessor.unregisterCallbacks(); Camunda8TransactionAspect.unregisterDeferredInTransaction(); + LoggingContext.clearContext(); } } diff --git a/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskWiring.java b/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskWiring.java index c346cee..9a73652 100644 --- a/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskWiring.java +++ b/spring-boot/src/main/java/io/vanillabp/camunda8/wiring/Camunda8TaskWiring.java @@ -22,10 +22,6 @@ import io.vanillabp.springboot.adapter.TaskWiringBase; import io.vanillabp.springboot.parameters.MethodParameter; import jakarta.persistence.Id; -import org.camunda.bpm.model.xml.instance.ModelElementInstance; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.context.ApplicationContext; - import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; @@ -37,6 +33,9 @@ import java.util.Set; import java.util.function.Consumer; import java.util.stream.Stream; +import org.camunda.bpm.model.xml.instance.ModelElementInstance; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.ApplicationContext; public class Camunda8TaskWiring extends TaskWiringBase, Camunda8MethodParameterFactory> implements Consumer { @@ -241,7 +240,10 @@ protected void connectToBpms( bean, method, parameters, - idPropertyName); + idPropertyName, + tenantId, + workflowModuleId, + processService.getPrimaryBpmnProcessId()); if (this.client != null) { taskHandler.accept(this.client); } else {