July 16, 2016

Configuring Spring Boot application logging outside from Docker container

Logging is an important part of the application. This is one of a few ways to get the visibility about how your application is doing in production. There is no single answer which says what you should log and what you should not. But the common sense (although there is nothing more uncommon than common sense :) says, errors obviously, info messages mostly to as milestones of the application processing flow, and debug for anything which might interesting to explore. Here I will not talk about that specific topic, but instead about how to configure the logger using external logging config file.

And everything below will be in the context of Java, Spring Boot, Logback and Docker.

Keep logging config file outside of the application

During development usually we keep the logging config file in src/main/resources/ directory. And so when building, and packaging the JAR (in case of Spring Boot - executable self contained JAR with embedded web container in it), it is a good idea to configure the application to read the logging config file outside of the classpath, so it would be possible to change logging details (like logging levels, or classes to log) without redeploying the application, but by simply restarting it.

logging.config property

By providing a logging.config or LOGGING_CONFIG environment property during application startup we can configure where the application will read the logging configuration.

If you run the application in Docker, as we do, and use Ansible to automate the infrastructure, the following environment docker-compose snippet can be used:

# docker-compose.yml
...
environment:
    LOGGING_CONFIG: {{ app_logging_config_dir }}/application.xml
    LOGGING_FILE: {{ app_logging_file_dir }}/application.log
...    

(we will discuss LOGGING_FILE later on in a minute)

Ansible variables app_logging_config_dir and app_logging_file_dir are simply:

# group_vars/all Ansible variables
...
app_home: /opt/application
app_logging_config_dir: "{{ app_home }}/logging.config"
app_logging_file_dir: "{{ app_home }}/logs"
...

One more bit for this to work is to have the corresponding directories created inside the container, and mapping the container directory into the host directory.

The former can be achieved by putting this into Dockerfile:

# Dockerfile
...
ENV APP_CONFIG_DIR /opt/application

# Logging
ENV LOGGING_CONFIG_DIR ${APP_CONFIG_DIR}/logging.config
RUN mkdir -p ${LOGGING_CONFIG_DIR}

ENV LOGGING_FILE_DIR ${APP_CONFIG_DIR}/logs
RUN mkdir -p ${LOGGING_FILE_DIR}

VOLUME $APP_CONFIG_DIR
...

And the later can be achieved by adding volumes into the docker-compose.yml:

# docker-compose.yml
...
volumes:
    - {{ app_home }}:{{ app_home }}
...

where app_home you've seen defined above in group_vars/all.

So, this is it. Having this setup and running the docker-compose up -d you will have the application to read the logging config files from host /opt/application/logging.config/application.xml.

And one last bit, because this is the Spring Boot application, and Spring Boot is the one who handles logging.config environment property, there are two options to make it work:

  1. either name your logging config file logback-spring.xml
  2. or if you have multiple applications each of them having a separate logging config file (as we do), then name your logging file whatever you want and place the following there:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <logger name="configure your application packages here" level="ERROR"/>
</configuration>

So, by including <include resource="org/springframework/boot/logging/logback/base.xml"/> you make Spring Boot to process those environment variables without file being named logback-spring.xml.

logging.file property

As you've seen before there is one more environment property used in docker-compose.yml: LOGGING_FILE: {{ app_logging_file_dir }}/application.log. As you probably could guess this is the one which controls where the application log file will be placed. So in our case in /opt/application/logs, so it can be available for inspection even after your containers were stopped (of course, in case of docker-compose while the applications are running docker-compose logs -f might be more handy).

For the all the details and other available properties Spring Boot supports consult Logging Section from Spring Boot reference guide.

Afterword

It is even possible to configure Logback to watch over the changes in the logging config file and reloading them even without the application restart using scan and scanPeriod configuration attributes, like this:

<configuration scan="true" scanPeriod="30 seconds" > 
...
</configuration> 

But, consider carefully whether you want to enable this feature in production. For the more details read here: Logback Configuration.

So, this is it folks! See you soon :)

Tags: practice