Deep dive into Log4j Loggers for Mule


Loggers
 are the main components responsible for capturing log messages. A logger is associated with a specific part of the application, usually identified by a name (which typically corresponds to the package or class it logs messages for).

In Java, developers create loggers using Logger.getLogger(ClassName.class). Once created, these loggers can be used to record log messages at different severity levels, such as DEBUG, INFO, WARN, ERROR, and FATAL.

In Mule, even if Mule is java-based, we don’t write the underlying java code. We design our flows in Anypoint Studio where each processor normally has a logger associated or we can create a new one with the logger processor.

For example, in a Mule application, we can utilize the Logger component to explicitly log messages within a flow. The Loggercomponent is configured directly within the Mule flow, and it can leverage Log4j’s logging capabilities. This would be the closest to create a Logger directly. But in other processors, like the connectors, each connector encapsulates the calls to create these loggers when the connector requires to log info.

Loggers are organized in a hierarchical tree structure, where each logger inherits properties from its parent logger unless overridden. Every logger can define:
  • A Layout
  • An Appender
  • Log level
Every app has a root logger, which defines the appenders (with the associated layout) and the log level for all the application. The root logger is mandatory so that we can have at least one configuration defined for logging by default.

But we can have multiple loggers with different configurations. This means we can have the general config in the root logger and, if needed, we could add a different configuration for a specific part of our application.

How to configure Loggers in Log4j

All the configuration of the Log4j is managed by the log4j2.xml file located in the src/main/resources folder of our mule project. Within that file, Loggers have its own section. Each logger that we add is represented by a Logger element in the xml file. 
MuleSoft organizes its loggers in a hierarchical structure that reflects the architecture of the Mule runtime. For instance:
  • org.mule: The root logger for the Mule runtime. It logs high-level events that occur in the runtime.
  • org.mule.api: Logs messages related to the Mule API, including the execution of flows and other API-related activities.
  • org.mule.transport: Logs messages related to various transport layers (HTTP, JMS, FTP, etc.).

Default configuration

Let’s see the default log4j2.xml file of a mule app - this is the log4j2.xml file that you’ll see right after you create a new mule project:


Notice that mule provides a configuration with asynchronous loggers so that, if nothing is changed in the log4j, our application will log asynchronously. Asynchronous logging is a best practice, so if we need to customize our logging adding more loggers we should add asynchronous loggers.

In the default configuration we can see that the root level (AsyncRoot element) sets: 
  • the log level to INFO
  • the default appender to the file
The default configuration also includes a specific logger for the mule logger processor (org.mule.runtime.core.internal.processor.LoggerMessageProcessor). The default values don’t change anything, because the log level is the same as the root (INFO) and it does not specify an appender, so it will use the default one in the root. But it’s useful to have it in the initial file, so that we can use it during debugging.

Lastly, the default configuration includes:
  • Two extra asynchronous loggers, for the HTTP connector, to enable the WARN level on it.
  • An extra line, commented, with the package to use enable wire logging (DEBUG level) in HTTP for debugging purposes. Again, very useful for us, this way we don’t have to look for the right package name.

So, in summary, to add a new Logger we need to create a new logger element (preferably async) within the Loggers section of the log4j2.xml file specifying:
  • The name attribute of the logger corresponds to the package of the part of the application we want to have a specific logging configuration. At the end of this doc you’ll find how to identify the package name.
  • Log level - as an attribute of the logger element
  • Appender - With the attribute ref indicating the name attribute of your appender

Adding Loggers

MuleSoft loggers inherit configurations from parent loggers unless explicitly overridden. This hierarchical structure allows fine-grained control over logging. For example, you might set the root logger to INFO but enable DEBUG for a specific connector or component to troubleshoot an issue without flooding logs with too much data.

For example, we could define a specific logger for a connector that uses a different log level (let’s say DEBUG, which would create more verbose logs) and send, only those logs, to a different location. Each appender defines its own patternLayout so we could also have different log formats in both appenders. 

In the example below, all the logs are sent to the file appender (including the HTTP connector logs) as specified in the root:

<Loggers>
<!-- HTTP Connector logger -->
<AsyncLogger name="org.mule.service.http.impl.service.HttpMessageLogger" level="DEBUG">
<AppenderRef ref="Splunk" />
</AsyncLogger>

<AsyncRoot level="INFO">
<AppenderRef ref="file"/>
</AsyncRoot>
</Loggers>

We can be even more specific and change the log level not only for the http connector (all listeners and requesters) but even for one specific listener or requester. This would be very useful when we wanted to debug or troubleshoot something in an app with multiple listeners/requesters. Enabling wire logging for all endpoints might create too many logs for troubleshooting. But if we know for sure the issue is happening on a specific listener of requester, we could enable the wire logging only on that.

This way, for an HTTP listener we could create an HTTP Listener configuration called MY_LISTENER and we would set the log level to DEBUG in this specific package:

org.mule.service.http.impl.service.HttpMessageLogger.MY_LISTENER

And for an specific HTTP requester, we could create an HTTP Requester configuration called MY_REQUESTER and we would set the log level to DEBUG in this specific package:

org.mule.service.http.impl.service.HttpMessageLogger.http.requester.MY_REQUESTER

Notice that, MY_LISTENER and MY_REQUESTER correspond to the 
http:listener-config” or “http:request-config items in the XML items of the mule project.


How do I know the package name of my connector?

Lastly, if we wanted to change the log level for an specific connector, after everything we saw in this post, it would be as easy as adding the package name of the connector to our log4j2.xml and set the log level. But how do we know that package name? 

For that, we just need to go to our project in Anypoint Studio, find our connector in the Package library and expand the list of packages the connector includes. Those are the names we can use. Here’s an example of the DB connector:

Previous Post Next Post