Camunda 8 audit logging

Hello.

I would like to understand better about Camunda/Zeebe engine audit logging. As I understand, the audit logs can be exported via exporters, and Camunda Kubernetes Helm charts come prebundled with Elasticsearch exporter, from which the documents can be searched.

I tried looking into Zeebe’s log4j2 configuration, but couldn’t find a similarly formatted logs. When changing Zeebe logs to TRACE, I was able to get some information, but it also includes a lot of unneeded logs.

My question is, can these audit trails about user actions be logged to standard console as well? I would ideally like to preserve the same information.

@rudolf To answer your question about logging audit trails to the console.

1. Zeebe and Audit Trails:

In Camunda 8 (Zeebe), the engine itself does not generate audit trails like Camunda 7 did (e.g., ACT_HI_* tables in the database). Instead:

  • Zeebe is event-driven.
  • Audit-style data is captured through exporters, with Elasticsearch exporter being the default and most complete one.
  • These exporters pull from the Zeebe event stream, not from log files.

So, most of the meaningful audit data (process started, task completed, variable updated, etc.) ends up in Elasticsearch if that exporter is enabled.


2. Zeebe Log4j2 Logging:

Zeebe uses log4j2.xml for its logging configuration.

  • Turning on TRACE or DEBUG will flood the logs, as you experienced.
  • These logs are not structured audit logs but rather for internal diagnostics (e.g., actor behavior, state transitions).

Can audit trails about user actions be logged to the standard console?**
Not directly by default, but here are some ways to do it:**

Option 1: Custom Exporter

You can write a custom exporter that reads events from Zeebe and logs selected ones to the console.

  • You’d filter for events of interest: ELEMENT_ACTIVATED, ELEMENT_COMPLETED, etc.
  • Then log them in a structured or human-readable way.

Example:

@Override
public void export(Record<?> record) {
    if (record.getIntent() == WorkflowInstanceIntent.ELEMENT_COMPLETED) {
        System.out.println("Element completed: " + record.getValue());
    }
}

This exporter can run alongside the Elasticsearch exporter.


Option 2: Use Operate or Elasticsearch APIs

If you’re looking for user-facing audit logs:

  • Camunda 8’s Operate UI already provides an “audit trail” experience for processes.
  • You can query Elasticsearch directly (via REST or Kibana) for specific audit logs.
  • Alternatively, write a small service that queries Elasticsearch and writes filtered events to logs or another sink.

Important Clarification:

  • User actions like completing a task are actually done by external workers (your Java code).
  • These actions do not inherently have “user” identity in Zeebe—unless you capture and log the user yourself (e.g., in variables or headers).
  • Zeebe doesn’t know about “users” in the same way Camunda 7 did.

So, if you want to track which user did what, you’d need to:

  1. Capture the user in your external worker.
  2. Log it yourself or push it as a process variable.
  3. Optionally export or log that information through custom exporters or Elasticsearch queries.

@rudolf Here’s an example for custom exporter.

package com.mycomp.camunda.consoleexporter;

import io.camunda.zeebe.exporter.api.context.Context;
import io.camunda.zeebe.exporter.api.context.ExporterContext;
import io.camunda.zeebe.exporter.api.Exporter;
import io.camunda.zeebe.protocol.record.Record;
import io.camunda.zeebe.protocol.record.intent.Intent;

public class ZeebeConsoleExporter implements Exporter {

    @Override
    public void configure(Context context) {
        // optional config
    }

    @Override
    public void open(ExporterContext exporterContext) {
        System.out.println("[ZeebeConsoleExporter] Opened.");
    }

    @Override
    public void export(Record<?> record) {
        Intent intent = record.getIntent();
        String recordType = record.getRecordType().name();

        if ("ELEMENT_COMPLETED".equals(intent.name())) {
            System.out.println("[AuditLog] " + recordType + " - " + intent + " - " + record.getValue());
        }
    }

    @Override
    public void close() {
        System.out.println("[ZeebeConsoleExporter] Closed.");
    }
}

1 Like

Thank you for the answer, looks like a custom exporter is the way to go in this case!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.