2.2 AEM Custom Logging, Logback Configurations & Request Tracing
Key Takeaways
- Apache Sling Logging delegates directly to Logback Classic, allowing dynamic OSGi configuration through Sling LogManager factory configurations without JVM restarts.
- Custom application loggers must be configured via OSGi .cfg.json files named org.apache.sling.commons.log.LogManager.factory.config~identifier.cfg.json stored in the ui.config module.
- Setting org.apache.sling.commons.log.additiv to false isolates custom loggers and prevents log entries from bubbling up and duplicating inside the root error.log.
- Sling request.log records incoming requests with '->' and outgoing responses with '<-', correlating transactions via request IDs and displaying exact processing duration in milliseconds.
- SLF4J parameterized logging with placeholders ({}) prevents unnecessary String concatenation overhead, and expensive parameter evaluations must be guarded with log.isDebugEnabled().
2.2 AEM Custom Logging, Logback Configurations & Request Tracing
Core Principle: Robust logging and request tracing form the foundation of enterprise observability in Adobe Experience Manager. By leveraging the Apache Sling Commons Log framework and Logback Classic, developers configure isolated project log files, monitor request processing lifecycles via
request.log, and diagnose performance bottlenecks using diagnostic consoles and SLF4J best practices.
1. Sling Logging Architecture & Logback Integration
In Adobe Experience Manager, logging is governed by Apache Sling Commons Log, which embeds and extends the industry-standard Logback Classic and Logback Core libraries.
+----------------------------------------------------------------------+
| Application Layer (Java / OSGi) |
| org.slf4j.Logger LOG = LoggerFactory.getLogger(...) |
+----------------------------------------------------------------------+
|
v
+----------------------------------------------------------------------+
| SLF4J API (Simple Logging Facade for Java) |
+----------------------------------------------------------------------+
|
v
+----------------------------------------------------------------------+
| Sling Commons Log & Logback Core |
| (Dynamic OSGi Configuration Management via LogManager) |
+----------------------------------------------------------------------+
| |
v v
+------------------------------------+ +-----------------------------+
| OSGi Factory Configurations | | Built-in Log Appenders |
| LogManager.factory.config~*.cfg.json| | error.log, stdout.log |
+------------------------------------+ +-----------------------------+
| |
+---------------------+---------------------+
|
v
+----------------------------------------------------------------------+
| Target Log Files under crx-quickstart/logs/ |
| error.log | request.log | access.log | myproject.log |
+----------------------------------------------------------------------+
The SLF4J Abstraction
All AEM code (OSGi services, Sling Models, workflow processes, servlets) interacts solely with the SLF4J interface (org.slf4j.Logger, org.slf4j.LoggerFactory). Code remains completely decoupled from the underlying Logback implementation.
Dynamic OSGi Configuration
Unlike traditional standalone Java applications that require editing static logback.xml files on disk, Sling exposes the Sling LogManager (org.apache.sling.commons.log.LogManager) as an OSGi service. Loggers, appenders, log levels, and file destinations are managed dynamically via OSGi configuration admin. Modifications take effect immediately in the running JVM without restarting the AEM instance or redeploying bundles.
2. Sling LogManager Factory Configurations (.cfg.json)
In modern AEM projects, custom logging configurations are authored as OSGi factory configurations using the .cfg.json format and deployed via the ui.config Maven module.
File Naming Convention & Path
OSGi factory configurations require a tilde (~) separator followed by a unique semantic identifier:
ui.config/src/main/content/jcr_root/apps/myproject/osgiconfig/config/org.apache.sling.commons.log.LogManager.factory.config~myproject.cfg.json
Run-mode specific configurations can be targeted to distinct environments by placing files in run-mode folders:
config.author(applied only to author instances)config.publish(applied only to publish instances)config.stageorconfig.prod(Cloud Service environment tiers)
Complete Configuration Example
Below is a production-ready .cfg.json configuration for a custom application log:
{
"org.apache.sling.commons.log.file": "logs/myproject.log",
"org.apache.sling.commons.log.level": "INFO",
"org.apache.sling.commons.log.pattern": "%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n",
"org.apache.sling.commons.log.names": [
"com.myproject",
"com.myproject.core.services",
"com.myproject.core.models"
],
"org.apache.sling.commons.log.additiv": false,
"org.apache.sling.commons.log.file.size": "20MB",
"org.apache.sling.commons.log.file.number": 5
}
Configuration Property Breakdown
| Property Key | Type | Description & Production Guidance |
|---|---|---|
org.apache.sling.commons.log.file | String | File path for log output. Relative paths are resolved against crx-quickstart/ (e.g., logs/myproject.log). |
org.apache.sling.commons.log.level | String | Minimum logging threshold. Allowed values: TRACE, DEBUG, INFO, WARN, ERROR. Production default: INFO or WARN. |
org.apache.sling.commons.log.names | Array[String] | Array of Java package names or fully qualified class names routed to this appender. All sub-packages are automatically included. |
org.apache.sling.commons.log.pattern | String | Logback formatting pattern. Standard pattern: %d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n. |
org.apache.sling.commons.log.additiv | Boolean | Crucial Setting. Controls logger additivity. When false, messages written to myproject.log are not duplicated in error.log. Default is false in modern configs. |
org.apache.sling.commons.log.file.size | String | Maximum size of an individual log file before triggering rotation (e.g., 10MB, 50MB). |
org.apache.sling.commons.log.file.number | Integer | Number of rotated historical archive files to retain on disk before oldest files are purged. |
3. Log Appenders, Rolling Policies & MDC Tracing
Log File Rolling Policies
Sling Commons Log supports both size-based and time-based rolling policies:
- Size-Based Rolling: Configured via
org.apache.sling.commons.log.file.sizeandorg.apache.sling.commons.log.file.number. Whenmyproject.logreaches 20MB, Sling renames it tomyproject.log.0, shifting older files up tomyproject.log.4, and purges older files. - Time-Based (Date) Rolling: Configured by specifying a date pattern in quotes as the file property or appending a date pattern:
logs/myproject.log.yyyy-MM-dd. Files rotate daily at midnight.
Mapped Diagnostic Context (MDC) Tracing
In high-volume concurrent environments, multiple threads execute simultaneously, interleaving log statements across different user requests. Mapped Diagnostic Context (MDC) allows developers to trace an entire user journey across multiple OSGi services.
MDC values are available only when the request pipeline or application code populates them. Do not assume every AEM log pattern automatically exposes %X{request.id} or %X{sling.userId}. If end-to-end correlation is required, define and propagate a correlation value deliberately and include it in the project logger pattern.
Configuring MDC in the log pattern:
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [req:%X{request.id}] [user:%X{sling.userId}] %-5level %logger{36} - %msg%n
Log output with MDC:
2026-09-23 14:10:05.122 [127.0.0.1 [1695478205122] GET /content/wknd/us/en.html HTTP/1.1] [req:1042] [user:anonymous] INFO c.m.c.s.CatalogService - Cache miss for product 4421
4. Request Tracing: request.log and access.log Architecture
AEM maintains two distinct request tracing log files under crx-quickstart/logs/:
+------------------------------------------------------------------------+
| Incoming Client Request |
+------------------------------------------------------------------------+
| |
v v
+-------------------------------+ +-------------------------------+
| access.log | | request.log |
| (NCSA Combined Access Log) | | (Sling Engine Internal Trace)|
+-------------------------------+ +-------------------------------+
| - Remote Client IP Address | | - Inbound Marker (->) |
| - HTTP Method & Target URL | | - Request ID (e.g., [1042]) |
| - HTTP Status Code | | - Outbound Marker (<-) |
| - Bytes Transferred | | - Response Status & MIME Type |
| - User-Agent & Referer | | - Internal Duration in ms |
+-------------------------------+ +-------------------------------+
Anatomy of request.log Entries
The Sling Request Logger records two distinct lines for every single HTTP transaction: an inbound entry upon request arrival, and an outbound entry upon response completion.
1. Inbound Entry (->)
23/Sep/2026:14:10:05 +0000 [1042] -> GET /content/wknd/us/en.html HTTP/1.1
- Timestamp:
23/Sep/2026:14:10:05 +0000 - Request ID:
[1042](unique integer assigned to this request thread) - Direction:
->(inbound request arriving at Sling) - HTTP Details:
GET /content/wknd/us/en.html HTTP/1.1
2. Outbound Entry (<-)
23/Sep/2026:14:10:05 +0000 [1042] <- 200 text/html;charset=UTF-8 84ms
- Timestamp:
23/Sep/2026:14:10:05 +0000 - Request ID:
[1042](matches the inbound entry ID) - Direction:
<-(outbound response returned to client/Dispatcher) - HTTP Status Code:
200 - Content-Type:
text/html;charset=UTF-8 - Elapsed Time:
84ms(exact total processing time inside the AEM JVM)
Diagnostic Techniques with request.log
- Identifying Slow Requests: Filtering for durations exceeding performance budgets (e.g., > 500ms or 1000ms):
grep '<-' request.log | awk '$NF > 500 {print $0}' - Detecting Hung or Crashed Threads: If an inbound request (
->) appears inrequest.logwithout a corresponding outbound entry (<-), the thread either encountered an infinite loop, hung waiting on a deadlocked JCR session lock, or was killed by a JVM crash. - Analyzing Concurrent Load: Matching timestamps and request ID gaps indicates peak concurrency and request queues.
access.log vs. request.log
access.log: Formatted according to the standard NCSA Combined Log Format. Records external client IP, authenticated user, byte volume, and browser User-Agent. Ideal for traffic analysis and external DDoS detection.request.log: Sling-specific operational log. Records internal millisecond execution time and ties directly to Sling script resolution and OSGi thread execution.
5. Runtime Logger Management & Web Console Diagnostics
The Apache Felix Web Console (/system/console/slinglog)
On local SDK environments and on-premise/AMS AEM instances, developers can inspect and manipulate loggers in real time via the Sling Log Support console:
- Navigate to
http://localhost:4502/system/console/slinglog. - Review all active logger configurations, their assigned log levels, appenders, and package categories.
- Click Add new Logger to configure a temporary logger:
- Log Level:
DEBUG - Log File:
logs/debug-workflow.log - Logger:
com.myproject.core.workflow
- Log Level:
- The new logger activates instantly in memory without restarting bundles.
[!WARNING] Operational Guardrail: Changes made through
/system/console/slinglogare stored either in transient memory or in the local OSGi ConfigAdmin repository. In enterprise CI/CD workflows and AEM as a Cloud Service, any manual Web Console change will be wiped upon the next deployment. All durable loggers must be committed to Git as.cfg.jsonfiles.
Logging in AEM as a Cloud Service
In AEM as a Cloud Service, direct access to /system/console/slinglog is restricted on cloud environments. Developers manage and inspect logs through two primary mechanisms:
- Adobe I/O CLI (
aio): Developers stream live logs directly to their local terminal:aio cloudmanager:tail-logs 12345 publish aemerror aio cloudmanager:tail-logs 12345 author aemrequest - Cloud Manager Log Downloads: Download full compressed archives of
aemerror,aemrequest, andhttpdaccesslogs from the Cloud Manager Environment details page.
6. SLF4J Best Practices in OSGi Services & Sling Models
Writing clean, efficient logging code is critical to preventing CPU bottlenecks and memory allocation pressure in high-throughput AEM applications.
1. Logger Initialization Pattern
Always declare the logger as a private static final field at the class level:
package com.myproject.core.services.impl;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(service = CatalogService.class)
public class CatalogServiceImpl implements CatalogService {
// CORRECT: Class-level static final logger
private static final Logger LOG = LoggerFactory.getLogger(CatalogServiceImpl.class);
// ...
}
Avoid creating non-static logger instances in Sling Models; Sling Models are instantiated frequently, and recreating logger instances consumes unnecessary heap allocations.
2. Parameterized Logging (Avoid String Concatenation)
Never use String concatenation (+) inside log statements:
// INCORRECT (ANTI-PATTERN): Allocates StringBuilder even if DEBUG is disabled!
LOG.debug("Fetching product details for SKU " + sku + " in category " + category);
// CORRECT: Defers string formatting until level check passes
LOG.debug("Fetching product details for SKU {} in category {}", sku, category);
3. Guarding Expensive Parameter Construction
When computing a log argument requires significant processing (such as serializing a large object to JSON or iterating a JCR collection), wrap the statement in log.isDebugEnabled() or log.isTraceEnabled():
// CORRECT: Prevents expensive JSON serialization when DEBUG is disabled
if (LOG.isDebugEnabled()) {
LOG.debug("Serialized payload response: {}", complexObjectSerializer.toJson(order));
}
4. Proper Exception Logging
When catching exceptions, pass the Throwable object as the final unparameterized argument to allow SLF4J to print the complete stack trace:
try {
remoteService.syncInventory(storeId);
} catch (RemoteConnectionException ex) {
// CORRECT: Automatically appends complete stack trace to error.log
LOG.error("Failed to sync inventory for store {}", storeId, ex);
// INCORRECT: Destroys the stack trace and root cause
LOG.error("Error occurred: " + ex.getMessage());
// INCORRECT: Bypasses logging framework and writes to stderr
ex.printStackTrace();
}
5. Loop Hygiene
Never log inside tight loops over repository nodes or collections. Logging thousands of lines per second saturates the disk write buffer and causes thread lock contention. Instead, aggregate metrics and log periodic summaries:
int count = 0;
for (Resource item : pageResource.getChildren()) {
processItem(item);
if (++count % 1000 == 0) {
LOG.info("Processed {} items so far...", count);
}
}
LOG.info("Completed processing total {} items.", count);
7. AEM Standard Log Files Quick Reference
| Log File | Default Path | Primary Contents | Diagnostic Use Case |
|---|---|---|---|
error.log | crx-quickstart/logs/error.log | Uncaught exceptions, system startup messages, OSGi bundle errors. | Primary troubleshooting log for Java runtime failures and syntax exceptions. |
request.log | crx-quickstart/logs/request.log | Inbound (->) and outbound (<-) HTTP requests with execution duration in ms. | Diagnosing slow rendering servlets, unindexed queries, and hung worker threads. |
access.log | crx-quickstart/logs/access.log | NCSA Combined HTTP access logs (client IP, HTTP status, bytes). | Traffic analysis, IP rate limiting, external security monitoring. |
stdout.log | crx-quickstart/logs/stdout.log | JVM standard output and unhandled native crash dumps. | Detecting JVM OutOfMemory errors and garbage collection pauses. |
history.log | crx-quickstart/logs/history.log | Replication agent transmissions and package activations. | Troubleshooting content distribution failures between Author and Publish. |
An AEM engineering team creates an OSGi configuration org.apache.sling.commons.log.LogManager.factory.config~myproject.cfg.json to write application logs to logs/myproject.log. However, developers notice that every log entry written to myproject.log is also being duplicated into the central error.log. Which configuration property in the .cfg.json file must be updated to resolve this issue?
An AEM developer is analyzing request.log on a publish instance to diagnose intermittent latency. The log displays the following two lines: 23/Sep/2026:14:10:05 +0000 [3892] -> GET /content/myproject/us/en/catalog.html HTTP/1.1 23/Sep/2026:14:10:08 +0000 [3892] <- 200 text/html;charset=UTF-8 3250ms What do these entries confirm about the request?
Which code snippet demonstrates the correct, production-grade SLF4J logging pattern in an OSGi service or Sling Model to log an exception with context parameters while avoiding memory allocation overhead?
A developer needs to temporarily enable DEBUG logging for com.myproject.core.workflow on a local development instance without modifying project code or redeploying OSGi bundles. Which approach should be used, and what is its operational implication?