Listen to workflow events

Quarkus Flow allows you to hook into the lifecycle of workflows and tasks by registering custom execution listeners. While Quarkus Flow provides built-in observability features, custom listeners are highly useful for advanced use cases, such as implementing custom audit logging, triggering specific business logic, or integrating with proprietary tracking systems when a workflow or task changes state.

The WorkflowExecutionListener Interface

To listen to workflow events, implement the io.serverlessworkflow.impl.lifecycle.WorkflowExecutionListener interface.

The interface provides default empty implementations for all lifecycle methods. This means you only need to override the specific events you care about. Available event hooks include:

  • Workflow level: onWorkflowStarted, onWorkflowSuspended, onWorkflowResumed, onWorkflowCompleted, onWorkflowFailed, onWorkflowCancelled, onWorkflowStatusChanged

  • Task level: onTaskStarted, onTaskCompleted, onTaskFailed, onTaskCancelled, onTaskSuspended, onTaskResumed, onTaskRetried

Registering a Custom Listener

Registering a custom listener is as simple as exposing your implementation as a standard Quarkus CDI bean (for example, by annotating it with @ApplicationScoped).

The engine automatically discovers any beans implementing WorkflowExecutionListener at build time and wires them in safely. The extension ensures these beans are kept during the build process, so you don’t have to worry about Quarkus’s dead code elimination removing them even if they aren’t explicitly injected elsewhere in your codebase.

Example

Here is an example of a custom listener that logs a message every time a workflow starts and completes:

package org.acme;

import jakarta.enterprise.context.ApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.serverlessworkflow.impl.lifecycle.WorkflowExecutionListener;
import io.serverlessworkflow.impl.lifecycle.WorkflowStartedEvent;
import io.serverlessworkflow.impl.lifecycle.WorkflowCompletedEvent;

@ApplicationScoped
public class AuditLogExecutionListener implements WorkflowExecutionListener {

    private static final Logger LOG = LoggerFactory.getLogger(AuditLogExecutionListener.class);

    @Override
    public void onWorkflowStarted(WorkflowStartedEvent ev) {
        LOG.info("Audit: Workflow '{}' (ID: {}) has started.",
            ev.workflowContext().workflowId(),
            ev.workflowContext().instanceId());
    }

    @Override
    public void onWorkflowCompleted(WorkflowCompletedEvent ev) {
        LOG.info("Audit: Workflow '{}' (ID: {}) completed successfully.",
            ev.workflowContext().workflowId(),
            ev.workflowContext().instanceId());
    }

    @Override
    public int priority() {
        // Optional: override if you need to control the execution order
        // relative to other internal listeners. Default is 0.
        return 0;
    }
}

Built-in Observability

If your primary goal is to add metrics or tracing to your application, you likely do not need to write a custom execution listener.

Quarkus Flow already provides out-of-the-box observability features using internal listeners. For standard monitoring, please refer to:

Custom listeners should be reserved for advanced, domain-specific use cases (like the audit logger above) or when triggering specific side-effects.

See also