Logging using Logback in Spring Boot Applications

Logging is mandatory for enterprise applications, which helps you to debug your application's output. We have lot of logging framework to use, in which, Logback is one of the famous and excellent logging framework for enterprise applications. 

In this post, I will discuss and demonstrate how to implement Logback in Spring Boot Applications. Beside, Spring Boot uses Logback logging framework as a default logger. If you are using Spring Boot, then you don't need to add any additional libraries or maven/gradle dependencies.

We will be using:

1. Spring Boot v2.0.3.RELEASE (You can choose your own)

2. Gradle 4.x or Maven 3.x (I will be using Gradle)

3. Lombok v1.18.0

4. WebLogic v12.2.1.3 (Well tested 🙂 )

Please refer to my first article to create and download project from Spring's Official Website.

Create Loggers

I will be using the Spring Boot's main application for this demo. Code for main application is:

package com.app.boot.logback.api;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@Slf4j
public class BootLogbackApplication {

    public static void main(String[] args) {
        SpringApplication.run(BootLogbackApplication.class, args);

        log.debug("Hello, I'm DEBUG message.");
        log.info("Hello, I'm INFO message.");
        log.warn("Hello, I'm WARN message.");
        log.error("Hello, I'm ERROR message.");
    }
}

In this post, I'm using Lombok library to remove boiler plate code. Lombok library helps us to remove most of the boiler plate code like, setters, getter, constructors, hash code, toString(), etc. Please check full documentation on how to use Lombok. I will create a separate post on "How to Lombok". I'm using @Slf4j annotation provided by Lombok library. 

@Slf4j annotation actual implementation is equivalent to:

private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(BootLogbackApplication.class.getName());

If you want to work with Log4j or Log4j2, you can use @Log4j and @Log4j2 respectively, where @Log4j and @Log4j2 are the implemetation of Apache Logger. 

logback-spring.xml

Spring Framework team recommends to call logback-spring.xml as a file name for Spring Framework applications so that profile based logging can be triggered easily.

Let's look on simple implementation, logging basic loggers for console. Consider below code:

<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
        </Pattern>
    </encoder>
</appender>
<root>
    <level value="DEBUG"/>
    <appender-ref ref="consoleAppender"/>
</root>

In the above code snippet, we are defining appender name as "consoleAppender" and the class to be use for this appender is "ConsoleAppender". This appender will print the log in container's console. We are also defining the log pattern to be printed in console. We are setting our root logger level to DEBUG and referencing to our "consoleAppender". Now, if you run your application, you should see all the logs from debug level and also the logs that we have printed:

[DEBUG] 2018-07-30 22:36:24.110 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm DEBUG message.
[INFO ] 2018-07-30 22:36:24.110 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm INFO message.
[WARN ] 2018-07-30 22:36:24.110 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm WARN message.
[ERROR] 2018-07-30 22:36:24.110 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm ERROR message.

Filtering Log Levels

Now, lets say, you only want to print INFO level log in console. For this, you can set filter using ch.qos.logback.classic.filter.LevelFilter, where you can define filter and its level to ALLOW or DENY. Consider below code snippet:

<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
        </Pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>DEBUG</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>INFO</level>
        <onMatch>ACCEPT</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>TRACE</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>WARN</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>DENY</onMatch>
    </filter>
</appender>

Here, we are setting filters to ACCEPT only INFO level logs. We are now denying to log DEBUG, WARN and ERROR levels. If you run the application, you should see only INFO level logs.

[INFO ] 2018-07-30 23:18:20.610 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm INFO message.

This is cool right? 🙂 Now, you can log what level you want to log.

Spring Boot's Profile based Logging

Let's create a scenario to see how loggers can work well with Spring Profile based logging. For a realtime scenario, we will have profiles as "local", "dev", "test" and "prod". I'm using these 4 profiles. I will be logging any INFO and WARN level logs to log on file named as "boot-logback-access.log" and ERROR level logs to log on file named as "boot-logback-error.log". I will be picking up the log directory from application.properties files which will be based on profile. Following will be my conclusions:

1. local,dev: Log levels will set to DEBUG, thus allowing all log levels to log in file. We will ALLOW all log levels using LevelFilter class to log into "**.access.log" file
2. test,prod: Log levels will set to INFO, thus allowing to log only INFO and WARN to log in file. That means, we will allow INFO and WARN levels using LevelFilter to log into "**.access.log" file.
3. All ERROR log will be logged into "**.error.log" file.

First, I'm going to create 4 different properties files which will be picked up upon application deployment. This pickup will be based on:  spring.profiles.active=local which is defined under application.properties

application-local.properties

server.port=8082
log.path=/Users/anish/Desktop/logs/local

application-dev.properties

server.port=8083
log.path=/Users/anish/Desktop/logs/dev

application-test.properties

server.port=8084
log.path=/Users/anish/Desktop/logs/test

application-prod.properties

server.port=8085
log.path=/Users/anish/Desktop/logs/prod

If you notice above, the application will be running on different port based on your profile set, and the logs will be stored on different folder based on profile names.

Now, lets see the code snippets below for profile "local,dev":

<springProfile name="local,dev">
    <appender name="accessLogFileAppender4Dev" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/boot-logback-access.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/boot-logback-access.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </Pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
    </appender>
</springProfile>

Here, we are creating an appender based on profile. The above appender will be used if profile is set to either local or dev and all the level filters are set to ACCEPT, which means all the log levels will be stored to a file specified. The appender class name is set to RollingFileAppender, which can be use to roll the new file based on date and time. Also, for rolling policy, we are using TimeBasedRollingPolicy. Check the below code:

<file>${LOG_PATH}/boot-logback-access.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>${LOG_PATH}/boot-logback-access.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <maxFileSize>5MB</maxFileSize>
    </timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>

First, we are defining the log file name to be created using <file> … </file>. Second, look inside <timeBasedFileNamingAndTriggeringPolicy>…</timeBasedFileNamingAndTriggeringPolicy>. We are defining <maxFileSize> as 5MB which means, once this file size crosses 5MB limit, then backup the existing log file with name as boot-logback-access.%d{yyyy-MM-dd}.%i.log (where %i is the counter starting from 0) and then again create a new file with the name as boot-logback-access.log and continue logging. Which means, if in a same day, 26 MB log file is genereted, it will be splitted into following:

boot-logback-access.2018-07-31.0.log (0 – 5MB)
boot-logback-access.2018-07-31.1.log (5.1 – 10MB)
boot-logback-access.2018-07-31.2.log (10.1 – 15MB)
boot-logback-access.2018-07-31.3.log (15.1 – 20MB)
boot-logback-access.{2018-07-31.4.log (20.1 – 25MB)

boot-logback-access.log (25.1 to <= 30MB)

This is same for profile test and prod, but only difference is test and prod logs level will accepting INFOs and WARNs to be logged into file. See the code snippet below:

<springProfile name="test,prod">
    <appender name="accessLogFileAppender4TestAndProd" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/boot-logback-access.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/boot-logback-access.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </Pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>DENY</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>DENY</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
        </filter>
    </appender>
</springProfile>

And now, we will be logging all the ERROR level logs into separate error log file, boot-logback-error.log. See code snippet below:

<appender name="errorLogFileAppender4All" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_PATH}/boot-logback-error.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/boot-logback-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>5MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
    </rollingPolicy>
    <encoder>
        <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
        </Pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>DEBUG</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>INFO</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>TRACE</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>WARN</level>
        <onMatch>DENY</onMatch>
    </filter>
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
    </filter>
</appender>

Now, above appender will be used to log errors in separate log file. All the TimeBasedRollingPolicy and SizeAndTimeBasedFNATP are same with logging access files.

Last part is to set root log level to DEBUG mode. Root level is required, but we will be controlling its logging using LevelFilter

<root>
    <level value="DEBUG"/>
    <appender-ref ref="consoleAppender"/>
</root>

Now, it's time to run our application. Let's do step by step changing the profile name and see what happens:

1. With profile local: Set spring.profiles.active=local in application.properties, and run the application. You should see below log printed in your console.

[DEBUG] 2018-07-31 00:38:24.107 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm DEBUG message.
[INFO ] 2018-07-31 00:38:24.107 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm INFO message.
[WARN ] 2018-07-31 00:38:24.107 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm WARN message.
[ERROR] 2018-07-31 00:38:24.107 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm ERROR message.

You should not see any logs printed in you log files, although log files will be generated at the time of deployment. We are not printing any logs into file when profile is set to local.

2. With profile dev: Set spring.profiles.active=dev in application.properties, delete log files if exists, and run the application. You will not see any application specific logs printed in your console, because for dev profile we are logging them into log files. Check your boot-logback-error.log and boot-logback-access.log. Your logs will be printed here. Since, this is dev profile, so you should see all DEBUG, INFO, WARN & ERROR level logs printed in boot-logback-access.log and only ERROR logs printed in boot-logback-error.log

From boot-logback-access.log file:

[INFO ] 2018-07-31 00:52:20.322 [restartedMain] c.a.b.l.a.BootLogbackApplication - Starting BootLogbackApplication on Anishs-MacBook-Pro.local with PID 3218 (/Users/anish/IdeaProjects/spring-boot-apps/boot-logback/build/classes/java/main started by anish in /Users/anish/IdeaProjects/spring-boot-apps/boot-logback)
[DEBUG] 2018-07-31 00:52:20.322 [restartedMain] c.a.b.l.a.BootLogbackApplication - Running with Spring Boot v2.0.3.RELEASE, Spring v5.0.7.RELEASE
[INFO ] 2018-07-31 00:52:20.322 [restartedMain] c.a.b.l.a.BootLogbackApplication - The following profiles are active: dev
[INFO ] 2018-07-31 00:52:22.411 [restartedMain] c.a.b.l.a.BootLogbackApplication - Started BootLogbackApplication in 2.427 seconds (JVM running for 3.078)
[DEBUG] 2018-07-31 00:52:22.416 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm DEBUG message.
[INFO ] 2018-07-31 00:52:22.416 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm INFO message.
[WARN ] 2018-07-31 00:52:22.416 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm WARN message.
[ERROR] 2018-07-31 00:52:22.416 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm ERROR message.

From boot-logback-error.log file:

[ERROR] 2018-07-31 00:52:22.416 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm ERROR message.

3. With profile test/prod: For, test and prod profiles we are using same configurations, log levels and LevelFilter. So, we will test for test profile only. You can go ahead and test for prod, but result will be same. Only difference will be, the location of prod logs. It will be under ../prod/.. location. Or, you can set both profiles at the same time. Set spring.profiles.active=test,prod in application.properties, delete log files if exists, and run the application. Again, you will not see any application specific logs printed in your console, because for test,prod profile we are logging them into log files. Check your boot-logback-error.log and boot-logback-access.log.

From boot-logback-access.log file:

[INFO ] 2018-07-31 01:06:12.964 [restartedMain] c.a.b.l.a.BootLogbackApplication - Starting BootLogbackApplication on Anishs-MacBook-Pro.local with PID 3230 (/Users/anish/IdeaProjects/spring-boot-apps/boot-logback/build/classes/java/main started by anish in /Users/anish/IdeaProjects/spring-boot-apps/boot-logback)
[INFO ] 2018-07-31 01:06:12.965 [restartedMain] c.a.b.l.a.BootLogbackApplication - The following profiles are active: test
[INFO ] 2018-07-31 01:06:15.225 [restartedMain] c.a.b.l.a.BootLogbackApplication - Started BootLogbackApplication in 2.609 seconds (JVM running for 3.193)
[INFO ] 2018-07-31 01:06:15.228 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm INFO message.
[WARN ] 2018-07-31 01:06:15.228 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm WARN message.

From boot-logback-error.log file:

[ERROR] 2018-07-31 01:06:15.228 [restartedMain] c.a.b.l.a.BootLogbackApplication - Hello, I'm ERROR message.

Now, that you have learned how to use logback in Spring Boot Applications, how to filter log levels and how to use Spring Profile based logging. But, everything that we logged is using Synchronous. What if we want to log in Asynchronous? No problem, Logback has a solution for this and is very simple to implement. We can just modify above loggers and can implement Asynchronous logging in our applications.

Asynchronous Logging using AsyncAppender

We should consider using asynchronous logging where there are heavy used of loggers. Asynchronous loggers helps us to acheive higher performance in logging perspectives. Logback executes an async appender in a separate theread where we can decouple the logging overhead from the thread. To achieve this, we can use AsyncAppender. Take a look on below code snippet. I'm taking example for only one appender. 

<springProfile name="local,dev">
    ....
    ....
    ....
    <appender name="Async-accessLogFileAppender4Dev" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="accessLogFileAppender4Dev"/>
    </appender>
</springProfile>

Here, we are defining AsyncAppender for "accessLogFileAppender4Dev". Remember, "accessLogFileAppender4Dev" is synchronous logger. Now, where ever you refer, "Async-accessLogFileAppender4Dev", your log will be logging asynchronously. This is awesome, right? 🙂 

Complete logback-spring.xml

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

    <springProperty scope="context" name="LOG_PATH" source="log.path" defaultValue="/Users/anish/Desktop"/>

    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </Pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
    </appender>

    <appender name="Local-ConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </Pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
    </appender>

    <springProfile name="local,dev">
        <appender name="accessLogFileAppender4Dev" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/boot-logback-access.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_PATH}/boot-logback-access.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>5MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder>
                <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
                </Pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>DEBUG</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>TRACE</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>WARN</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
        </appender>

        <appender name="Async-accessLogFileAppender4Dev" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="accessLogFileAppender4Dev"/>
        </appender>
    </springProfile>

    <springProfile name="test,prod">
        <appender name="accessLogFileAppender4TestAndProd" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/boot-logback-access.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_PATH}/boot-logback-access.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>5MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder>
                <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
                </Pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>DEBUG</level>
                <onMatch>DENY</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>INFO</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>TRACE</level>
                <onMatch>DENY</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>WARN</level>
                <onMatch>ACCEPT</onMatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>DENY</onMatch>
            </filter>
        </appender>

        <appender name="Async-accessLogFileAppender4TestAndProd" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="accessLogFileAppender4TestAndProd"/>
        </appender>

    </springProfile>

    <appender name="errorLogFileAppender4All" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/boot-logback-error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/boot-logback-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <Pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
            </Pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>DENY</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>DENY</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>TRACE</level>
            <onMatch>DENY</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>WARN</level>
            <onMatch>DENY</onMatch>
        </filter>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
        </filter>
    </appender>

    <root>
        <level value="DEBUG"/>
        <appender-ref ref="consoleAppender"/>
    </root>

    <appender name="Async-errorLogFileAppender4All" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="errorLogFileAppender4All"/>
    </appender>

    <springProfile name="local">
        <logger name="com.app.boot.logback" level="DEBUG" additivity="false">
            <appender-ref ref="consoleAppender"/>
            <appender-ref ref="Async-accessLogFileAppender4Dev"/>
            <appender-ref ref="Async-errorLogFileAppender4All"/>
        </logger>
    </springProfile>

    <springProfile name="dev">
        <logger name="com.app.boot.logback" level="DEBUG" additivity="false">
            <appender-ref ref="Async-accessLogFileAppender4Dev"/>
            <appender-ref ref="Async-errorLogFileAppender4All"/>
        </logger>
    </springProfile>

    <springProfile name="test,prod">
        <logger name="com.app.boot.logback" level="INFO" additivity="false">
            <appender-ref ref="Async-accessLogFileAppender4TestAndProd"/>
            <appender-ref ref="Async-errorLogFileAppender4All"/>
        </logger>
    </springProfile>
</configuration>

That's it. You can run your application and can see the effect. Please, let me know if you have any issues implementing this example.

WebLogic Compitable:

As I said earlier, you have to refer to my earlier post to make it compatible working for WebLogic. If you have any problem making this application workable in WebLogic, please let me know. I will help you.

Complete project is available in Github.

Thanks and Cheers!!!

Implementing Swagger2 in Spring Boot Application for API Documentation