Logs and log levels
Where Bonita Process Designer writes its logs in each deployment mode, how to change the log level, what a log line contains, and how request correlation ids let you trace a single request end to end.
Bonita Process Designer writes human-readable logs to the standard output of each process. The standalone distribution also writes them to a rotating file, ready to collect, with nothing to configure — see Standalone. In Docker, the container’s output is the log, and no file is written inside the containers.
Two things make these logs useful when diagnosing a problem:
-
every log line of a given HTTP request carries the same correlation id, so you can follow one request across all the lines it produced;
-
that same id is shown to the end user as a Support ID when a server error occurs, which lets you find the exact failure in the logs from a user’s report. See Get support.
Where the logs are
The answer depends on how you installed the product.
Docker
Each container logs to its own standard output, collected by Docker. Follow them with docker compose logs:
# Everything, followed live
docker compose logs -f
# One service at a time
docker compose logs -f backend # application log (see the format below)
docker compose logs -f frontend # HTTP access log of the web server
docker compose logs -f caddy # TLS reverse proxy
docker compose logs -f postgres # database
To export the back-end log to a file for a support request:
docker compose logs --no-color --timestamps backend > backend.log
The backend service holds the application log — it is the one to collect first.
No log file is written inside the containers: the container’s output is the log, and docker compose logs is the single source. Cap the space it uses with the Docker logging driver — see Rotation and retention.
Standalone
The standalone distribution is a single process: one Java application serves both the REST API and the editor UI. It writes its log to two places at once — the console where you started it, and a file:
logs/process-designer.log
relative to the directory holding app.jar. The start script prints the exact path when it launches. Nothing needs configuring, and the file is bounded: see Rotation and retention.
This file is what to collect for a support request. Send the current file and the archives covering the incident:
# Everything the retention window still holds
tar czf process-designer-logs.tar.gz logs/
To follow it live:
tail -f logs/process-designer.log
On Windows, where there is no tail, PowerShell does the same:
Get-Content -Wait logs\process-designer.log
When you run the application as a systemd service, the console half of that output goes to the journal (journalctl -u process-designer). The file is written either way, so there is no need to capture the console separately.
|
Do not redirect the console output into the same |
Log levels
Two environment variables control verbosity. Both default to INFO.
| Variable | Scope | Default |
|---|---|---|
|
The application’s own code. Raise this first when diagnosing a problem — it adds the detail that is deliberately kept out of |
|
|
Everything else: the framework, the database driver, and the other libraries. Raise this only when the problem looks like it sits below the application — for example a connection or startup failure. |
|
Refused requests do not need DEBUG: a rejected permission check, an expired license or an exceeded rate limit is logged at WARN, so it is already in the log at the default level. See Notable warnings.
|
A few framework loggers that are unusually noisy — or that would write sensitive data — are pinned to their own level, independently of |
Accepted values are ERROR, WARN, INFO, DEBUG and TRACE.
|
These two variables are read at startup. Restart the application after changing either of them. If a restart is not acceptable — a production instance you cannot interrupt — a level can also be changed on a running instance, temporarily. See Changing a level without restarting. |
Setting the level in Docker
The variables are passed through to the container from your environment, so either export them for a single run:
LOG_LEVEL=DEBUG docker compose up -d
or add them to the .env file next to docker-compose.yml to make the change persist:
LOG_LEVEL=DEBUG
LOG_LEVEL_ROOT=WARN
Setting LOG_LEVEL=DEBUG together with LOG_LEVEL_ROOT=WARN is the usual combination: full application detail, quiet frameworks.
Setting the level in the standalone distribution
Add the variable to config.env and restart:
LOG_LEVEL=DEBUG
|
Remember to set the level back to |
Changing a level without restarting
An Admin can raise or lower the verbosity of a running instance. The change takes effect immediately and lasts until the next restart, which makes it the right tool for "turn it up, reproduce the problem, turn it back down" on an instance you cannot interrupt.
Finding the right URL
The management endpoints are served by the back-end under /actuator. Which URL reaches them depends on how you installed the product:
| How you reach the product | Base URL |
|---|---|
Docker, through the bundled TLS proxy ( |
|
Docker, with the back-end port published directly ( |
|
Standalone |
|
The recipes below refer to that base as $API, so the same commands work in every mode. Set it once, to the value matching your own installation — the example is a Docker install reached through the TLS proxy on the local host:
API=https://localhost/api
|
Behind the bundled TLS proxy, the certificate is self-signed — issued by the proxy’s own internal certificate authority.
That call proves the certificate is accepted, but it still answers Trusting that root certificate on the client machines also removes the browser’s "not trusted" warning. Neither flag is needed once you have replaced the bundled certificate with one signed by a CA your hosts already trust — and neither applies at all when you reach the back-end over plain HTTP, where you can drop the |
Signing in from the command line
The endpoints need an Admin session, carried by the connect.sid cookie. There are two ways to get one.
From the browser — sign in as an Admin, open the developer tools, and read the value of connect.sid under Application → Cookies. Then substitute -b 'connect.sid=<value>' for -b cookies.txt in the commands that follow.
From the command line — sign in with curl and let it keep the cookie in a jar:
curl -k -c cookies.txt \
-H 'Content-Type: application/json' \
-d '{"username":"<admin-user>","password":"<password>"}' \
"$API/auth/login"
Then pass -b cookies.txt on each subsequent call, as below.
|
A password on a command line ends up in your shell history. Prefer the browser route on a shared machine, and delete |
Changing the level
# Raise the application's own log to DEBUG
curl -k -b cookies.txt \
-H 'Content-Type: application/json' \
-d '{"configuredLevel":"DEBUG"}' \
"$API/actuator/loggers/com.bonitasoft.processdesigner"
# Check what a logger is set to
curl -k -b cookies.txt \
"$API/actuator/loggers/com.bonitasoft.processdesigner"
# Put it back
curl -k -b cookies.txt \
-H 'Content-Type: application/json' \
-d '{"configuredLevel":"INFO"}' \
"$API/actuator/loggers/com.bonitasoft.processdesigner"
Unlike LOG_LEVEL, this also lets you target one logger rather than the whole application package — useful for turning up a single component without the volume of a full DEBUG run.
|
The change is not persisted. After a restart the instance returns to whatever |
|
These management endpoints require the Admin role — no part of them is available anonymously or to a Reader or Creator. They are the only way to change a running instance’s behaviour over HTTP, so treat Admin credentials accordingly. See Roles and permissions. |
Two companion endpoints answer the other questions a support request usually starts with. They take the same base URL and the same Admin session:
| Endpoint | What it tells you |
|---|---|
|
The exact product version running, which is more reliable than reading the footer of a page that may be cached. |
|
Whether the application and its database connection are up. |
What a log line looks like
2026-07-15T10:00:00.000+00:00 INFO [my-trace-42] [http-nio-3000-exec-1] c.b.p.web.RequestLoggingFilter : GET /diagrams -> 200 (12 ms) user=walter
Reading left to right: the timestamp with its UTC offset, the level, the correlation id in square brackets, the thread, the shortened class name, then the message. Lines that do not belong to a request — startup, scheduled tasks — show an empty pair of brackets.
The access log
The back-end writes one INFO line per HTTP request once it completes, with the method, path, response status, duration, and the authenticated user (anonymous when the request carried no session). That single line is often enough to answer "did the request reach the server, and what did it return?".
The query string is not recorded at INFO, because query parameters can carry sensitive values. Set LOG_LEVEL=DEBUG to have it appended.
Browser OPTIONS preflight requests are not logged.
Following one request: the correlation id
Every request is assigned an id when it arrives. That id is:
-
written on every log line produced while handling the request, in square brackets;
-
returned to the client on the
X-Request-Idresponse header; -
shown to the user as the Support ID when the server returns an error (see Get support).
So a single value ties together the user’s report, the browser’s network trace, and the server logs. Given an id, find everything the server did:
# Docker
docker compose logs --no-color backend | grep '\[a1b2c3d4-...\]'
# Standalone — search the archives too, the id may predate the current file
zgrep -h '\[a1b2c3d4-...\]' logs/process-designer.log*
On Windows there is no zgrep, so expand each gzipped archive before searching it:
$id = '[a1b2c3d4-...]'
# The current file
Select-String -Path logs\process-designer.log -SimpleMatch $id
# The archives, one at a time
Get-ChildItem logs\*.gz | ForEach-Object {
$archive = $_.Name
$tmp = [System.IO.Path]::GetTempFileName()
$in = [System.IO.File]::OpenRead($_.FullName)
$gz = New-Object System.IO.Compression.GZipStream($in, [System.IO.Compression.CompressionMode]::Decompress)
$out = [System.IO.File]::Create($tmp)
$gz.CopyTo($out)
$out.Close(); $gz.Close(); $in.Close()
Select-String -Path $tmp -SimpleMatch $id | ForEach-Object { "${archive}: $($_.Line)" }
Remove-Item $tmp
}
Real-time collaboration
Collaborative editing does not use ordinary HTTP requests, so its log lines carry a different kind of id in the same place — the editing session and the user who owns it:
2026-08-04T13:00:01.000+02:00 WARN [ws-k3n9x1qz/walter] ... READER_OP_REJECTED
Read it as ws-<editing session>/<user>. That makes a collaboration problem searchable from either end: by the session, to follow one editor’s whole sitting, or by the user name, to find every session they opened.
|
An editing session’s id is independent of the ids on that user’s ordinary requests — there is no value linking the two. To investigate a collaboration problem, search by the user name and the time of the incident. |
You can also supply your own id to trace a call you make yourself. It is accepted when it is at most 64 characters of letters, digits, dot, underscore or hyphen; anything else is replaced by a generated id.
curl -k -H "X-Request-Id: my-trace-42" "$API/diagrams"
The response echoes X-Request-Id: my-trace-42, and the server’s log lines for that call carry [my-trace-42].
|
Requests rejected before authentication — an expired license, a payload over the size limit, an exceeded AI rate limit — are logged with a correlation id too, so those failures are traceable as well. |
What is never written to the logs
The logs are designed to be safe to send to Support. They never contain:
-
passwords, API keys, license file contents, or any other secret;
-
session identifiers or cookie values;
-
request or response bodies — including diagram content, BPMN payloads, and the prompts submitted to AI generation;
-
query strings, unless you deliberately raise the level to
DEBUG.
Identifiers are recorded instead of content: a path, a user name, a diagram id, a job id. Values that come from a client are stripped of control characters before being written, so a crafted request cannot forge or split a log line.
|
This guarantee covers the application log. It does not extend to a database dump or a diagram export, which do contain your process content — treat those separately when sharing files. |
One exception: network identifiers in the front-end access log
The list above is about the application log — the one this page describes, the one the backend container writes and the one logs/process-designer.log holds. It records no network identifier at all: no client IP address, no browser user agent.
In the Docker distribution the front-end web server keeps a log of its own: one HTTP access line per request. That one can record network identifiers, depending on the NODE_ENV it runs with:
NODE_ENV |
What the front-end access line records |
|---|---|
unset (the default) |
A concise development line: method, path, status, duration. No client IP, no user agent. |
|
The full Apache combined format, which adds the client IP address, the referrer and the browser user agent. |
If you set NODE_ENV=production, treat the front-end log as containing personal data and apply your own retention and privacy rules to it. The back-end application log is unaffected either way — which is why the collection recipes on this page and in Get support export the backend service only.
Notable warnings
WARN lines are rare by design: the application logs one when it refuses something, never on the normal path. Seeing them is the quickest way to spot a misconfiguration:
| Rejection | What it means |
|---|---|
License refused ( |
The license is missing, invalid, or expired. See Manage the license. |
AI rate limit ( |
The caller exceeded the AI request rate. The line records the user and how long to wait. |
Request too large ( |
The payload exceeded the configured cap. The line records the size and the limit. |
Access denied ( |
A user attempted something their role does not allow, or credentials were wrong. See Roles and permissions. |
Conflict ( |
Two operations collided on the same data — for example a duplicate name. |
An ERROR line with a stack trace means an unexpected server failure. That is the case where the user is shown a Support ID, and the one worth reporting.
Rotation and retention
- Standalone
-
The log file rotates on its own — no
logrotateconfiguration needed.Limit Default Meaning Size per file
10 MB
The current file rolls over when it reaches this, and also once a day. Keeping it small is deliberate: this is the file you open in an editor or
grepduring an incident.History
30 days
Archives older than this are deleted. Chosen so a problem reported a few days late can still be investigated.
Total size
1 GB
Once the archives reach this, the oldest are deleted first, whatever their age.
Archives sit next to the current file, gzipped, one per rollover:
logs/process-designer.log (1) logs/process-designer.log.2026-08-03.0.gz (2) logs/process-designer.log.2026-08-02.0.gz1 the current file 2 archives, oldest deleted first The total-size limit is applied at each rollover, so the directory can briefly hold slightly more than 1 GB — about one archive’s worth. Provision for the limit as a bound, not as an exact ceiling.
To change any of them, set the matching variable in
config.envand restart:LOGGING_LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE=10MB LOGGING_LOGBACK_ROLLINGPOLICY_MAX_HISTORY=30 LOGGING_LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP=1GB - Docker
-
Rotation is the Docker daemon’s business, not the application’s — nothing is written to a file inside the containers. Configure the logging driver’s
max-sizeandmax-fileoptions, otherwise container logs grow until the host disk is full:services: backend: logging: driver: json-file options: max-size: "10m" max-file: "30"Read those two options carefully:
max-sizebounds each file, andmax-filecounts files, not days. The pair above therefore caps the container’s log at roughly 300 MB (30 x 10 MB) and imposes no time limit — how many days of history that buys depends entirely on your request volume, and nothing expires on age. Docker’s rotation has no equivalent of the standalonemax-history.
|
Raising the level to |
See also
-
Get support — what to collect before opening a support request.
-
Configuration reference — every environment variable, including the two on this page.