Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Getting Your Own Log4j2 File for Mule via Spring

DZone's Guide to

Getting Your Own Log4j2 File for Mule via Spring

You can programmatically reconfigure a log manager via Spring and load your own log4j2.xml file from a defined path, forcing your Mule app to pick the file from any location.

Free Resource

Share, secure, distribute, control, and monetize your APIs with the platform built with performance, time-to-value, and growth in mind. Free 90 day trial 3Scale by Red Hat

We all know how important logging is for enterprise applications and Mule applications. Logging for an application helps the developers, system administrators, and support team monitor and resolve any issues associated with the application.

With Mule 3.6, applications were supported with new log4j2 which supports asynchronous logging.

As per Mule documentation, we can see that when an application is deployed, Mule looks for a log4j2 file in the location following a child-first pattern, as listed below:

  • Look for a file called log4j2-test.xml in the application classpath.

  • Look for a file called log4j2.xml in the application classpath.

  • Look for a file called log4j2-test.xml in the domain classpath.

  • Look for a file called log4j2.xml in the domain classpath.

  • Look for a file called log4j2-test.xml in MULE_HOME/conf.

  • Look for a file called log4j2.xml in MULE_HOME/conf.

  • Apply default configuration.

In this article, we will see how to programmatically reconfigure the log manager via Spring and load our own log4j2.xml file from our defined path. We can force our Mule application to pick our own log4j2 file from the location we want.

We will first design our own custom log4j2.xml file as follows:

<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="TRACE">

      <Properties>
           <Property name="fileSize">10 KB</Property>
           <Property name="numberOfFiles">10</Property>
      </Properties>

      <Appenders>

            <!-- This will print logs in console -->

      <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%-5p %d [%t] %c: %m%n" />
      </Console>

            <!-- This will create log files based on INFO -->

      <RollingFile name="RollingFile_INFO"

                 fileName="E:/backup/test/log/${sys:CustomapplicationName}_INFO.log"

                 filePattern="E:/backup/test/log/test/Common-Util-%d{yyyy-MM-dd}-INFO-%i.log"> <!-- CustomapplicationName is set at mule-app.properties or in VM argument

                  in Run As Configure as -->

       <PatternLayout>
              <pattern>%d %p %C{1.} [%t] %m%n</pattern>
       </PatternLayout>

       <Policies>
               <SizeBasedTriggeringPolicy size="${fileSize}" /> <!-- Value taken from the <Property/> above -->
       </Policies>

       <DefaultRolloverStrategy max="${numberOfFiles}" />
       </RollingFile>

            <!-- This will create log files based on ERROR -->

       <RollingFile name="RollingFile_ERROR"

             fileName="E:/backup/test/log/${sys:CustomapplicationName}_ERROR.log"

             filePattern="E:/backup/test/log/test/Common-Util-%d{yyyy-MM-dd}-ERROR-%i.log"> <!-- CustomapplicationName is set at mule-app.properties or in VM argument

     in Run As Configure as -->

       <PatternLayout>
               <pattern>%d %p %C{1.} [%t] %m%n</pattern>
       </PatternLayout>

       <Policies>
               <SizeBasedTriggeringPolicy size="${fileSize}" /> <!-- Value taken from the <Property/> above -->
       </Policies>

      <DefaultRolloverStrategy max="${numberOfFiles}" />
      </RollingFile>

       </Appenders>

       <Loggers>

          <!-- CXF is used heavily by Mule for web services -->

       <AsyncLogger name="org.apache.cxf" level="WARN" />

       <!-- Apache Commons tend to make a lot of noise which can clutter the log -->

       <AsyncLogger name="org.apache" level="WARN" />

        <!-- HTTP -->

       <AsyncLogger name="httpclient.wire" level="DEBUG" />
       <AsyncLogger name="com.ning.http" level="DEBUG" />
       <AsyncLogger name="org.mule.transport.http" level="DEBUG" />

       <!-- Reduce startup noise -->

       <AsyncLogger name="org.springframework.beans.factory" level="WARN" />

      <!-- Mule classes -->

       <AsyncLogger name="org.mule" level="INFO" />
       <AsyncLogger name="com.mulesoft" level="INFO" />

      <!-- Reduce DM verbosity -->

      <AsyncLogger name="org.jetel" level="WARN" />
      <AsyncLogger name="Tracking" level="WARN" />

      <AsyncRoot>
        <AppenderRef ref="Console" level="all" /> <!-- Console will print all the levels -->
        <AppenderRef ref="RollingFile_ERROR" level="error" /> <!-- This will only log error in the error log files -->
        <AppenderRef ref="RollingFile_INFO" level="info" /> <!-- This will log info in the info log files -->
      </AsyncRoot>

     </Loggers>

  </Configuration>

Let’s name this log4j2.xml as our own common-log4j2.xml and place it in some external location, say in a path like E:\common-log4j2.xml.

Here, in the above configuration, we have used two appenders: file appenders and console appenders.

The console appenders will display the logs in the console, whereas the file appenders will create external log files for our application.

Again, with the above configuration, two different and separate log level files will be created: one for INFO and another for ERROR.

Also, notice that in the XML file, we are dynamically passing our log files name from our Mule application environment variable ${sys:CustomapplicationName}, which we will mention in our mule-app.properties file as follows:

CustomapplicationName=Common-logging-Util

We have also mentioned in our common-log4j2.xml file the size of the log file and the number of each log file that should be created after the size if the current log file exceeds:

<Policies>

       <SizeBasedTriggeringPolicy size="${fileSize}" />

      <!-- Value taken from the <Property/> above -->

</Policies>

<DefaultRolloverStrategy max="${numberOfFiles}" />

Creating Spring Configuration

We now create an XML file and name it as application-context.xml where we configure our application Spring application context to pick our log4j2.xml as follows:

<spring:bean id="logContext"

       class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">

       <spring:property name="targetClass">

              <spring:value>org.apache.logging.log4j.LogManager</spring:value>

       </spring:property>

       <spring:property name="targetMethod">

              <spring:value>getContext</spring:value>

       </spring:property>

       <spring:property name="arguments">

              <spring:value>false</spring:value>

       </spring:property>

</spring:bean>

<spring:bean id="logContext1" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">

       <spring:property name="targetObject" ref="logContext" />

       <spring:property name="targetMethod">

              <spring:value>setConfigLocation</spring:value>

       </spring:property>

       <spring:property name="arguments">

                <!-- Reading an external log4j.xml file -->

         <spring:value>file:///E:/common-log4j2.xml</spring:value> 

       </spring:property>

</spring:bean>

<spring:bean id="logContext2"

       class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">

       <spring:property name="targetObject" ref="logContext" />

       <spring:property name="targetMethod">

              <spring:value>reconfigure</spring:value>

       </spring:property>

</spring:bean>

Finally, we will use <spring:import/> to import this context from your file Mule flow:

<spring:beans>
    <spring:import resource="classpath*:application-context.xml" /> <!-- loading the the config file from classpath -->
</spring:beans>

We will create a Mule flow to test our log files that will be created:

<flow name="logFileTestFlow1">
   <http:listener config-ref="HTTP_Listener_Configuration" path="/testlog" doc:name="HTTP"/>
   <set-payload value="Sample payload to test log content!" doc:name="Set Payload"/>
   <logger message="INFO level log from #[flow.name] #[payload]" level="INFO" doc:name="Logger"/>
   <logger message="ERROR level log from #[flow.name] #[payload]" level="ERROR" doc:name="Logger"/>
 </flow>

Now, all set and we will be testing our application!

Testing Our Application

When we start our Mule application, we will see in our console that our log4j2.xml file, which is called common-log4j2.xml, is selected from the external path that we have mentioned in our application context for logging our application:

Image title

Once the application is deployed successfully, we can find two logs files in the location that we had mentioned in our common-log4j2.xml file:

Image title

We will test our application with this URL and check the log file content after that. We will go back to where the log files are created, open the Common-logging-Util_INFO.log, and find the following content:

Image title

We can see the content at the INFO level designates informational messages that highlight the progress of the application at the coarse-grained level.

If we open our next log file, which is Common-logging-Util_ERROR.log, we will see only the ERROR-level information, which still allows the application to continue running:

Image title

So, we can see that it’s very easy to force our Mule application to pick our own log4j2.xml from the location we want via Spring configuration. There are other ways to force the application to pick a log4j file, but with the above method, we can programmatically select the log4j config file.

Discover how you can achielve enterpriese agility with microservices and API management

Topics:
mule ,spring ,log4j2 ,logging ,tutorial ,integration

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}