[Java log] P2: Log4j integrated into the software

Articles published from blog Tung Huynh, was the consent of the author.

Hi everybody, at all before ([Java log] P1: The importance of logging in software development) I've shared about the importance of logging and some rule when logging. This article I will guide integrating a basic module to the application log. You should get into the habit of logging into any integrated software from the smallest of the individual, will see its convenience and software becomes more professional, when the major software will find familiar and much easier.
Mình sẽ hướng dẫn cách tích hợp Log4j vào một chương trình Java nhỏ. Trong bài này mình sẽ hướng dẫn cấu hình bằng file XML, các bạn hoàn toàn có thể tìm hiểu và thử cấu hình bằng JSON, YAML, Properties cũng với cấu trúc tương tự. Trong phạm vi bài viết này, để đơn giản nhất có thể nên mình sẽ không sử dụng maven, thay vào đó sẽ dùng cách add lib vào project thông thường 😀

Trước tiên các bạn cần tải gói jar log4j, ở đây mình sẽ sử dụng phiên bản Log4j 2.7.
Link download: http://archive.apache.org/dist/logging/log4j/2.7/apache-log4j-2.7-bin.zip
Trong file nén này có rất nhiều gói jar, nhưng để ghi được log thì chỉ cần 2 gói core and api là đủ.
After 2 gói jar trên, các bạn add vào project và bắt đầu code. Nếu ai chưa biết các add lib .jar vào project thì tự google nhé, đây là kiến thức cơ bản 🙂

Trước tiên mình tạo 1 file Main.java cho ví dụ này và khai báo một đối tượng logger như đoạn code dưới.

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
 
public class Main {
    static Logger logger = LogManager.getLogger(Main.class);
    public static void main(String[] args) {
        logger.info("info");
        logger.warn("warn");
        logger.error("error");
    }
}

Sau đó tạo 1 filelog4j2.xml and put into foldersresources of project (must correct filename and directory nhé). This will be the configuration file for logging.
Structured files such as photos

Before my eyes will configure one appender The simplest log to record the screen console

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss a} %highlight{%-5level} [%15.15t] %style{%40C{1.}.%-20M}{cyan} : %msg%n"/>
        </Console>
    </Appenders>
 
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

Appenders to declare the connector helps define the log where the configuration saved, What log format.
Above I have declared 1 appender type Console, named Console the output destination is SYSTEM_OUTie screen command line. In which I have defined more formats 1 line when outputting log will include information.
You can see more details about this format at docs of Log4j
https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout
Loggers to declare the conditions of use appender. As on the condition that he is to log everything level log from Debug onwards will burn Console
Commissioning program and see results

2019-07-12 22:56:33 PM INFO  [           main]                               n.t.l.Main.main                 : info
2019-07-12 22:56:33 PM WARN  [           main]                               n.t.l.Main.main                 : warn
2019-07-12 22:56:33 PM ERROR [           main]                               n.t.l.Main.main                 : error

Although the code in the file Main.java I just call logging with the text as “info”, “warn”, “error”. But the log display is a full out execution time,log level (INFO, WARN, ERROR), package class, method, pretty convenient to see the log. That is due in part configuration PatternLayout
Similarly I will configure more 1 appender logging out file by adding appender below

<RollingFile name="File" fileName="logs/server.log"
             filePattern="logs/$${date:yyyy-MM}/%d{dd}/server.%i.log">
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss a} %-5level [%t] %logger{36}.%M : %msg%n"/>
    <Policies />
</RollingFile>

Result, with log files, often burn themselves to show full package class and not aligned spaces to avoid excess capacity increase

2019-07-12 23:08:58 PM INFO [main] net.tunghuynh.logging.Main.main : info
2019-07-12 23:08:58 PM WARN [main] net.tunghuynh.logging.Main.main : warn
2019-07-12 23:08:58 PM ERROR [main] net.tunghuynh.logging.Main.main : error

In appender for log file on, I've configured to automatically add filePattern cut logs by date
So just a few short steps, you can logging standards for its programs. If you want to clear, you can configure more appender to split into files log info, log error, log debug for easy control and replace the above general log.

<RollingFile name="FileInfo" fileName="logs/info.log"
             filePattern="logs/$${date:yyyy-MM}/%d{dd}/info.%i.log">
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss a} %-5level [%t] %logger{36}.%M : %msg%n"/>
    <LevelRangeFilter minLevel="INFO" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
    <Policies />
<RollingFile name="FileWarn" fileName="logs/warn.log"
             filePattern="logs/$${date:yyyy-MM}/%d{dd}/warn.%i.log">
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss a} %-5level [%t] %logger{36}.%M : %msg%n"/>
    <LevelRangeFilter minLevel="WARN" maxLevel="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
    <Policies />
<RollingFile name="FileError" fileName="logs/error.log"
             filePattern="logs/$${date:yyyy-MM}/%d{dd}/error.%i.log">
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss a} %-5level [%t] %logger{36}.%M : %msg%n"/>
    <LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
    <Policies />
</RollingFile>

Go here, begin to see appender have similar configurations such as the path to save log, pattern layout, if necessary repair time will be fixed each appender, so we can declare property defined value and reuse in appender

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Properties>
        <Property name="patternLayout">%d{yyyy-MM-dd HH:mm:ss a} %-5level [%t] %logger{36}.%M : %msg%n</Property>
        <Property name="logPath">logs</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout
                    pattern="%d{yyyy-MM-dd HH:mm:ss a} %highlight{%-5level} [%15.15t] %style{%40C{1.}.%-20M}{cyan} : %msg%n"/>
        </Console>
        <RollingFile name="FileInfo" fileName="${logPath}/info.log"
                     filePattern="${logPath}/$${date:yyyy-MM}/%d{dd}/info.%i.log">
            <PatternLayout pattern="${patternLayout}"/>
            <LevelRangeFilter minLevel="INFO" maxLevel="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
            <Policies />
        </RollingFile>
        <RollingFile name="FileWarn" fileName="${logPath}/warn.log"
                     filePattern="${logPath}/$${date:yyyy-MM}/%d{dd}/warn.%i.log">
            <PatternLayout pattern="${patternLayout}"/>
            <LevelRangeFilter minLevel="WARN" maxLevel="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
            <Policies />
        </RollingFile>
        <RollingFile name="FileError" fileName="${logPath}/error.log"
                     filePattern="${logPath}/$${date:yyyy-MM}/%d{dd}/error.%i.log">
            <PatternLayout pattern="${patternLayout}"/>
            <LevelRangeFilter minLevel="ERROR" maxLevel="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <Policies />
        </RollingFile>
    </Appenders>
    .......
</Configuration>

So now when the program grow to, number of functions increase business, if all records are still common log to a file, it will be very hard to control, that's when we need split log according to major Specifically.
But not every business needs its own separate log file, as in his previous post mentioned would split logs important business or handled more out for easy control. Here I add 3 class New simulation 3 major incurred, and will logging 1 class Business as an example.

public class BusinessA {
    Logger logger = LogManager.getLogger(getClass());
    public BusinessA(){
        logger.info("Info From BusinessA");
        logger.warn("Warn From BusinessA");
        logger.error("Error From BusinessA");
    }
}
public class BusinessB {
    Logger logger = LogManager.getLogger(getClass());
    public BusinessB(){
        logger.info("Info From BusinessB");
        logger.warn("Warn From BusinessB");
        logger.error("Error From BusinessB");
    }
}
public class BusinessC {
    Logger logger = LogManager.getLogger(getClass());
    public BusinessC(){
        logger.info("Info From BusinessC");
        logger.warn("Warn From BusinessC");
        logger.error("Error From BusinessC");
    }
}

Then add appender logging for class Business and declare the class using the newly created appender

.....
    <RollingFile name="LogBusinessA" fileName="${logPath}/business-a.log"
                 filePattern="${logPath}/$${date:yyyy-MM}/%d{dd}/business-a.%i.log">
        <PatternLayout pattern="${patternLayout}"/>
        <Policies />
    </RollingFile>
.....
<Loggers>
        <Logger name="net.tunghuynh.logging.BusinessA" level="debug" additivity="false">
            <AppenderRef ref="LogBusinessA"/>
            <AppenderRef ref="Console"/>
        </Logger>
        ........
    </Loggers>

IN Business This alone will just burn out 1 file own logs and records to the screen console with level logs from debug onwards
The results will still record a full log of all 3 major new add on screen console, but log Business only be written to the file business-a.log not appear in the log files in common info.log, warn.log, error.log

If your business handles in many class Then collection services under the package and can fully configure the package to log just to name Logging entire class in package there
Things are not so simple, the program has become 1 software or large applications, number of processors increases up to the log files of each professional must also do more for capacity growing, if deleted, it will take the log, if not removed, it is also difficult to read files or trace log, This time we again need cut log.
To cut the log files there 2 from common cut that is under Capacity or by the time, I usually combined all 2 this way. For example when cutting logs capacity exceeding 10Mb and cut logs by date, every day will cut log 1 times late in the day and then saved into folders, with those services that handle large quantities it can cut the log hourly. Example one appender cut the log by date configuration and capacity as follows

<RollingFile name="LogBusinessA" fileName="${logPath}/business-a.log"
             filePattern="${logPath}/$${date:yyyy-MM}/%d{dd}/business-a.%i.log">
    <PatternLayout pattern="${patternLayout}"/>
    <Policies>
        <TimeBasedTriggeringPolicy/>
        <SizeBasedTriggeringPolicy size="10 MB"/>
    </Policies>
    <DefaultRolloverStrategy max="10"/>
</RollingFile>

Also unavoidable circumstances spam system, or 1 Something constantly retry fault resulted despite cut log into multiple files butnumber of files stillgenerate too much leading to the full hard drive crashes server. So on her has added both configurationsDefaultRolloverStrategy to allow flow10 file in1 log chain, if it exceeds the number of files it automatically deletes old files.
The value of this configuration also need to reuse in other appender so you can makeproperty as above.

Thus your program has been log module integrates basic standards The most sufficient and look chuyên nghiệp hơn rất nhiều. 🙂

Đây là bài hướng dẫn tích hợp nên mình upload luôn source cho các bạn tiện tìm hiểu. Ai dùngIntelliJ IDEthì có thể open project là có luôn cấu hình lib trong project Các bạn có thể download source ở cuối bài nhé.

Nhưng đó mới chỉ đáp ứng được việc ghi log ra file, đôi khi việc đọc log trong file cũng gặp không ít bất tiện. Ví dụ khi cần kiểm soát các thao tác của user đăng nhập software, What used functions, What data manipulation software, general need data anonymous link strung follow login session or specific professional skills they will need to save the log to a database or a similar architecture to facilitate the query data.
This article was quite long and so I will guide asynchronous logging into the database in the sequel

Download code Log4JExample.zip