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().
Last updated: September 2026

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.stage or config.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 KeyTypeDescription & Production Guidance
org.apache.sling.commons.log.fileStringFile path for log output. Relative paths are resolved against crx-quickstart/ (e.g., logs/myproject.log).
org.apache.sling.commons.log.levelStringMinimum logging threshold. Allowed values: TRACE, DEBUG, INFO, WARN, ERROR. Production default: INFO or WARN.
org.apache.sling.commons.log.namesArray[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.patternStringLogback formatting pattern. Standard pattern: %d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n.
org.apache.sling.commons.log.additivBooleanCrucial 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.sizeStringMaximum size of an individual log file before triggering rotation (e.g., 10MB, 50MB).
org.apache.sling.commons.log.file.numberIntegerNumber 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:

  1. Size-Based Rolling: Configured via org.apache.sling.commons.log.file.size and org.apache.sling.commons.log.file.number. When myproject.log reaches 20MB, Sling renames it to myproject.log.0, shifting older files up to myproject.log.4, and purges older files.
  2. 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

  1. Identifying Slow Requests: Filtering for durations exceeding performance budgets (e.g., > 500ms or 1000ms):
    grep '<-' request.log | awk '$NF > 500 {print $0}'
    
  2. Detecting Hung or Crashed Threads: If an inbound request (->) appears in request.log without 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.
  3. 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:

  1. Navigate to http://localhost:4502/system/console/slinglog.
  2. Review all active logger configurations, their assigned log levels, appenders, and package categories.
  3. Click Add new Logger to configure a temporary logger:
    • Log Level: DEBUG
    • Log File: logs/debug-workflow.log
    • Logger: com.myproject.core.workflow
  4. The new logger activates instantly in memory without restarting bundles.

[!WARNING] Operational Guardrail: Changes made through /system/console/slinglog are 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.json files.

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:

  1. 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
    
  2. Cloud Manager Log Downloads: Download full compressed archives of aemerror, aemrequest, and httpdaccess logs 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 FileDefault PathPrimary ContentsDiagnostic Use Case
error.logcrx-quickstart/logs/error.logUncaught exceptions, system startup messages, OSGi bundle errors.Primary troubleshooting log for Java runtime failures and syntax exceptions.
request.logcrx-quickstart/logs/request.logInbound (->) and outbound (<-) HTTP requests with execution duration in ms.Diagnosing slow rendering servlets, unindexed queries, and hung worker threads.
access.logcrx-quickstart/logs/access.logNCSA Combined HTTP access logs (client IP, HTTP status, bytes).Traffic analysis, IP rate limiting, external security monitoring.
stdout.logcrx-quickstart/logs/stdout.logJVM standard output and unhandled native crash dumps.Detecting JVM OutOfMemory errors and garbage collection pauses.
history.logcrx-quickstart/logs/history.logReplication agent transmissions and package activations.Troubleshooting content distribution failures between Author and Publish.
Test Your Knowledge

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?

A
B
C
D
Test Your Knowledge

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?

A
B
C
D
Test Your Knowledge

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
B
C
D
Test Your Knowledge

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?

A
B
C
D