In {project_name}, logging goes beyond setting log levels — you can direct output to different handlers, use asynchronous logging for performance, capture HTTP access logs, and more.
These features make it possible to adapt logging to your operational needs and integrate with observability platforms.
{project_name} uses the JBoss Logging framework under the hood.
== Available log handlers
[cols="^1,^1,^1", frame=none, grid=all]
|===
| *Console* +
Logs to standard output
| *File* +
Persists logs to disk
| *Syslog* +
Sends logs to Syslog server
|===
To enable log handlers, enter the following command:
When no log level configuration exists for a more specific category logger, the enclosing category is used instead. When there is no enclosing category, the root logger level is used.
* If you were to accidentally set the log level twice, the last occurrence in the list becomes the log level. For example, if you included the syntax `--log-level="info,...,DEBUG,..."`, the root logger would be `DEBUG`.
You can set different log levels for specific areas in {project_name}. Use this command to provide a comma-separated list of categories for which you want a different log level:
* The hibernate log level in general is set to debug.
* To keep SQL abstract syntax trees from creating verbose log output, the specific subcategory `org.hibernate.hql.internal.ast` is set to info. As a result, the SQL abstract syntax trees are omitted instead of appearing at the `debug` level.
When configuring category-specific log levels, you can also set the log levels as individual `log-level-<category>` options instead of using the `log-level` option for that.
This is useful when you want to set the log levels for selected categories without overwriting the previously set `log-level` option.
you can then set an environmental variable `KC_LOG_LEVEL_ORG_KEYCLOAK=trace` to change the log level for the `org.keycloak` category.
The `log-level-<category>` options take precedence over `log-level`. This allows you to override what was set in the `log-level` option.
For instance if you set `KC_LOG_LEVEL_ORG_HIBERNATE=trace` for the CLI example above, the `org.hibernate` category will use the `trace` level instead of `debug`.
Bear in mind that when using the environmental variables, the category name must be in uppercase and the dots must be replaced with underscores.
When using other config sources, the category name must be specified "as is", for example:
It is necessary to understand that setting the log levels for each particular handler *does not override the root level* specified in the `log-level` property.
Log handlers respect the root log level, which represents the maximal verbosity for the whole logging system.
It means individual log handlers can be configured to be less verbose than the root logger, but not more.
Specifically, when an arbitrary log level is defined for the handler, it does not mean the log records with the log level will be present in the output.
In that case, the root `log-level` must also be assessed.
Log handler levels provide the *restriction for the root log level*, and the default log level for log handlers is `all` - without any restriction.
Every log handler provides the ability to have structured log output in JSON format.
It can be enabled by properties in the format `log-<handler>-output=json` (where `<handler>` is a log handler).
If you need a different format of the produced JSON, you can leverage the following JSON output formats:
* `default` (default)
* `ecs`
The `ecs` value refers to the https://www.elastic.co/guide/en/ecs-logging/overview/current/intro.html[ECS] (Elastic Common Schema).
ECS is an open-source, community-driven specification that defines a common set of fields to be used with Elastic solutions.
The ECS specification is being converged with https://opentelemetry.io/docs/concepts/semantic-conventions/[OpenTelemetry Semantic Conventions] with the goal of creating a single standard maintained by OpenTelemetry.
In order to change the JSON output format, properties in the format `log-<handler>-json-format` (where `<handler>` is a log handler) were introduced:
{"@timestamp":"2025-02-03T14:53:22.539484211+01:00","event.sequence":9608,"log.logger":"io.quarkus","log.level":"INFO","message":"Keycloak 999.0.0-SNAPSHOT on JVM (powered by Quarkus 3.17.8) started in 4.615s. Listening on: http://0.0.0.0:8080","process.thread.name":"main","process.thread.id":1,"mdc":{},"ndc":"","host.hostname":"host-name","process.name":"/usr/lib/jvm/jdk-21.0.3+9/bin/java","process.pid":77561,"data_stream.type":"logs","ecs.version":"1.12.2","service.environment":"prod","service.name":"Keycloak","service.version":"999.0.0-SNAPSHOT"}
* You need **lower latencies** for incoming requests
* You need **higher throughput**
* You have **small worker thread pool** and want to offload logging to separate threads
* You want to reduce the impact of **I/O-heavy log handlers**
* You are logging to **remote destinations** (e.g., network syslog servers) and want to avoid blocking worker threads
WARNING: Be aware that enabling asynchronous logging might bring some **additional memory overhead** due to the additional separate thread and the inner queue.
In that case, it is not recommended to use it for resource-constrained environments.
Additionally, unexpected server shutdowns create a risk of **losing log records**.
You can enable asynchronous logging globally for all log handlers by using `log-async` property as follows:
<@kc.start parameters="--log-async=true"/>
Or you can enable the asynchronous logging for every specific handler by using properties in the format `log-<handler>-async` (where `<handler>` is a log handler).
If the property for a specific handler is not set, the value from the parent `log-async` property is used.
{project_name} supports HTTP access logging to record details of incoming HTTP requests.
While access logs are often used for debugging and traffic analysis, they are also important for security auditing and compliance monitoring, helping administrators track access patterns, identify suspicious activity, and maintain audit trails.
These logs are written at the `INFO` level, so make sure your logging configuration includes this level — either globally (e.g. `log-level=info`) or specifically for the access log category (e.g. `log-level=org.keycloak.http.access-log:info`).
When HTTP access logs are enabled, they are shown by default, as `INFO` level is the default log level for {project_name}.
The `Authorization` header and selected sensitive cookies are automatically masked in the HTTP Access log. However, the list of masked items might not be complete. Be careful with using the `long` pattern or printing the headers by the custom format - you should use it only for development purposes. To extend the list of the masked items, see below.
Consult the https://quarkus.io/guides/http-reference#configuring-http-access-logs[Quarkus documentation] for the full list of variables that can be used.
Selected sensitive HTTP headers and cookies are automatically masked in the HTTP Access log.
Masked sensitive HTTP headers:
* `Authorization`
Masked sensitive {project_name} cookies:
* `AUTH_SESSION_ID`
* `KC_AUTH_SESSION_HASH`
* `KEYCLOAK_IDENTITY`
* `KEYCLOAK_SESSION`
* `AUTH_SESSION_ID_LEGACY`
* `KEYCLOAK_IDENTITY_LEGACY`
* `KEYCLOAK_SESSION_LEGACY`
In order to extend the list of the masked items (e.g. to accommodate for headers and cookies used by your custom extensions), configure the `http-access-log-masked-headers` and `http-access-log-masked-cookies` options.
By default, the HTTP access log file is rotated daily.
When the daily rotation occurs, a date-based suffix '.+{yyyy-MM-dd}+' is added to the log file name from the previous day, keeping access logs separated by date (for example, `keycloak-http-access.2052-02-29.log`).
To disable rotation, use the following configuration: