[JAVA] I just wanted to logrotate with log4j 2

Introduction

This article is the 16th day article of Hamee Advent Calendar 2020. It's my first time to participate in ad-care, I'm really nervous (lie)

What is log4j 2?

From Official

Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture.

It's an improved version of log4j. The harmful effects of taking over the structure of Logback have also been resolved nicely.

And that.

What is log4j in the first place? If you explain in one line to those who say log4j is a logger API for Java.

So what is log rotation?

It's also called log rotation.

I think that any system outputs logs, but the amount of output will increase over time and will devour storage. That's not good, so you need to automatically delete logs after a certain period of time, or delete old ones when the size of the log reaches a certain amount.

This is log rotation.

Try to set

First, to define the logger, write log4j2.xml as follows.

log4j2.xml


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF">
    <Properties>
        <Property name="standard">[%d{yyyy/MM/dd HH:mm:ss.SSS}] %m%n</Property>
        <Property name="log_path">logs</Property>
        <Property name="log_filename">app</Property>
    </Properties>
    <Appenders>
        <RollingFile name="app" fileName="${log_path}/${log_filename}.log" filePattern="${log_path}/${log_filename}.%d{yyyy-MM-dd}.log.gz">
            <PatternLayout pattern="[%d{yyyy.MM.dd HH:mm:ss.SSS}] %p - %m%n" />
            <TimeBasedTriggeringPolicy />
            <DefaultRolloverStrategy>
                <Delete basePath="${log_path}" maxDepth="1">
                    <IfFileName glob="${log_filename}*.log.gz" />
                    <IfLastModified age="7d" />
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Root name="app_logger" level="info">
            <AppenderRef ref="app" />
        </Root>
    </Loggers>
</Configuration>

I will explain the settings a little bit.

<Properties>

<Properties>
    <Property name="standard">[%d{yyyy/MM/dd HH:mm:ss.SSS}] %m%n</Property>
    <Property name="log_path">logs</Property>
    <Property name="log_filename">app</Property>
</Properties>

A tag that can be used when you want to use a constant in log4j2.xml. For example, with the above settings, you can use the format enclosed in <Property name =" standard "> by writing $ {standard} in one shot.

After all, this setting

 <RollingFile name="app" fileName="${log_path}/${log_filename}.log" filePattern="${log_path}/${log_filename}.%d{yyyy-MM-dd}.log.gz">

It is converted like this.

 <RollingFile name="app" fileName="logs/app.log" filePattern="logs/app.%d{yyyy-MM-dd}.log.gz">

<Loggers>

<Loggers>
    <Root name="app_logger" level="info">
        <AppenderRef ref="app" />
    </Root>
</Loggers>

I will write the definition of logger here. <Root> defines the logger to be used by default, but this time there is only one, so this is OK. ref =" app " receives the output from the name =" app " guy defined in <Appenders>, meaning.

Now you have defined the interface that outputs the log (I think it is selfish).

<RollingFile>

<Appenders>
    <RollingFile name="app" fileName="${log_path}/${log_filename}.log" filePattern="${log_path}/${log_filename}.%d{yyyy-MM-dd}.log.gz">
        <PatternLayout pattern="[%d{yyyy.MM.dd HH:mm:ss.SSS}] %p - %m%n" />
            <TimeBasedTriggeringPolicy />
            <DefaultRolloverStrategy>
            <Delete basePath="${log_path}" maxDepth="1">
                <IfFileName glob="${log_filename}*.log.gz" />
                <IfLastModified age="7d" />
            </Delete>
        </DefaultRolloverStrategy>
    </RollingFile>
</Appenders>

Define the format that is actually output as a log in <Appenders>. <RollingFile> is one of them.

I will omit the details, ** Log rotation is done on a daily basis, and logs older than 7 days are deleted! ** ** The settings like this are made here.

Operation check

On the server side, add some kind of log output operation, deploy, and wait for the date to change. When I ssh and check it with excitement, is that?

$ ls -la
drwxr-xr-x 3 tomcat tomcat 4096 December 12 23:39 .
drwxr-xr-x 3 tomcat tomcat 4096 December 12 16:44 ..
-rw-r--r--1 tomcat tomcat 207 December 13 00:00 app.log

** Are? ** **

No matter how many times I check today's date, 12/13 ... Normally, log rotation should be done and 12/12 days worth of logs should remain ...

Originally I want it to be ↓.

$ ls -la
drwxr-xr-x 3 tomcat tomcat 4096 December 12 23:39 .
drwxr-xr-x 3 tomcat tomcat 4096 December 12 16:44 ..
-rw-r--r--1 tomcat tomcat 20507 December 13 00:00 app.2020-12-12.log
-rw-r--r--1 tomcat tomcat 207 December 13 00:00 app.log

Elucidation of the cause

(I'll write it lightly here, but it usually takes about a week to fix it.)

First, take a look at my crontab.

*/10 * * * * sh ./batch1

0 0 1 * * sh ./batch2

I'm just setting up a periodic batch with cron.

At first glance, it's a normal setting, but the point here is that ** both batches are executed at 0:00:00 **. This is the cause of everything.

Apparently in log4j2

--** Simultaneous ** writing to logs by multiple processes --Target for log rotation

When the above two conditions are met, log rotation is not performed normally and the log of the previous day disappears. (Is that okay ...?)

It doesn't matter if batch2 doesn't run at 0:00:00, so I tried logrotate with a slight time lag and it worked fine.

(By the way, there seems to be a ScoketAppender to solve this? I'm sorry if I make a mistake.)

Summary

Despite the framework specifications, I didn't think I would be bothered by log output so far. Have a good log life!

Recommended Posts

I just wanted to logrotate with log4j 2
I wanted to gradle spring boot with multi-project
I tried what I wanted to try with Stream softly.
I wanted to develop PHP with vscode remote container
I wanted to start the AP server and debug with just the Maven command
I just wanted to make a Reactive Property in Java
I tried to interact with Java
The story I wanted to unzip
I wanted to make JavaFX programming easier with the Spring Framework
I tried to get started with WebAssembly
I want to use DBViewer with Eclipse 2018-12! !!
I wanted to add @VisibleForTesting to the method
I tried to implement ModanShogi with Kinx
I wanted to implement a slide show in a fashionable way with slick.
After all I wanted to preview the contents of mysql with Docker ...
[Kotlin] I wanted to generate a png with a large capacity per area [Java]
I want to test Action Cable with RSpec test
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
I tried to make Basic authentication with Java
I tried to manage struts configuration with Coggle
I tried to manage login information with JMX
I want to use java8 forEach with index
I wanted to make (a == 1 && a == 2 && a == 3) true in Java
I was addicted to doing onActivityResult () with DialogFragment
I want to play with Firestore from Rails
I tried to break a block with java (1)
I want to perform aggregation processing with spring-batch
[Rails] I want to load CSS with webpacker
I tried to implement file upload with Spring MVC
I tried to read and output CSV with Outsystems
[Java 11] I tried to execute Java without compiling with javac
I started MySQL 5.7 with docker-compose and tried to connect
I tried to get started with Spring Data JPA
I want to dark mode with the SWT app
I want to monitor a specific file with WatchService
I want to authenticate users to Rails with Devise + OmniAuth
I tried to draw animation with Blazor + canvas API
I tried to implement Stalin sort with Java Collector
I want to transition screens with kotlin and java!
I want to get along with Map [Java beginner]
I used to make nc (netcat) with JAVA normally
I want to redirect sound from Ubuntu with xrdp
roman numerals (I tried to simplify it with hash)
I played with Refinements
I tried to create a java8 development environment with Chocolatey
I tried to increase the processing speed with spiritual engineering
[Rails] I tried to create a mini app with FullCalendar
I want to push an app made with Rails 6 to GitHub
I made a plugin to execute jextract with Gradle task
I was addicted to setting default_url_options with Rails devise introduction
I want to make a list with kotlin and java!
I want to make a function with kotlin and java!
I tried to link chat with Minecraft server with Discord API
[Rails] I tried to implement batch processing with Rake task
What I was addicted to with the Redmine REST API
Even in Java, I want to output true with a == 1 && a == 2 && a == 3
I tried to automate LibreOffice Calc with Ruby + PyCall.rb (Ubuntu 18.04)
I want to manually send an authorization email with Devise
I want to distinct the duplicated data with has_many through
I want to implement various functions with kotlin and java!
I tried to create a padrino development environment with Docker