Java logging system - log4j

 

1.1 Introduction

Log4j is written in a reliable and flexible logging framework consists of Java, Apache's is an open source project;

Use Log4j, we are more convenient to record log information, it can not only control the log output destination, but also to control the content format log output; generated by defining different log levels can be more precise control over the log

Cheng, so as to achieve the needs of our applications; All this thanks to a flexible configuration file, does not require us to change the code.

 

1.2 log4j structure

In Log4j mainly consists of three major components:

  • Logger: log object, responsible for capturing log information;

  Logger object is used to replace or System.out System.err log output, which is responsible output log information; wherein, log4j logging framework provides info, error, debug, etc. Developer API for use; 
  
  and Commons - same as logging, log4j also log level concept; each object is assigned a level logger, not assigned logger class inherit the logger root level, output log; requesting each object has a method of logging levels, if the request is equal to greater than the current methods logger object level, the output request is processed, otherwise the request is ignored; 

  log4j defines seven levels in the level class, the following relationship: 
      of Level.ALL <Level.DEBUG <Level.INFO <Level.WARN <level .error <Level.FATAL < Level.OFF 
  
  each level, specific meanings are as follows:     
      ALL: open all logs; 
      dEBUG: debug code that applies to the period; 
      INFO: Code applicable during the run; 
      WARN: the code will apply to potential error events ; 
      eRROR: error code that applies to the presence of event; 
      FATAL: for severe error events; 
      OFF: closed all day ;
  • Appender: Log output destination, is responsible for the good format log output information to the designated place, it can be the console, disk files;

Each log object has a corresponding appender, each appender represents a log output destination; 

where there are several log4j appender to choose from: 
      ConsoleAppender: Console; 
      FileAppender: disk files; 
      DailyRollingFileAppender: produce a day log disk files; 
      RollingFileAppender: create a new log file when the disk file reaches a specified size;
  • Layout: log formatter, log in charge of information dissemination of different styles;
Each appender Layout and a corresponding, appende responsible for the output log information to the specified location, and is responsible for the Layout Display out log information formatted according to the requirements; 

  wherein, Layout log4j there are several to choose from: 
      HTMLLayout: In html layout display tabular form; 
      PatternLayout: custom specified format display; 
      SimpleLayout: contains the level of information string log information; 
      TTCCLayout: log contains time generated, thread, category and so forth;

 

1.3 Use

First, we need to add a dependency in pom.xml applications in:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

Secondly, the statement test code:

public class log4jDemo {

    Logger log= Logger.getLogger(log4jDemo.class);
    
    @Test
    public void test(){
        log.trace("Trace Message!");
        log.debug("Debug Message!");
        log.info("Info Message!");
        log.warn("Warn Message!");
        log.error("Error nihao 你好!");
        log.fatal("Fatal Message!");
    }
}

Finally, the statement in the profile classpath: log4j.properties or log4j.xml;

例1:log4j.properties:

log4j.rootLogger = INFO, FILE, CONSOLE

log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=e:/log.out
log4j.appender.FILE.ImmediateFlush=true
log4j.appender.FILE.Threshold = DEBUG
log4j.appender.FILE.Append=true
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.ImmediateFlush=true
log4j.appender.CONSOLE.Threshold = DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.encoding=UTF-8
log4j.appender.CONSOLE.layout.conversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

Example 2: log4j.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>

    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="target" value="System.out"/>
        <param name="immediateFlush" value="true"/>
        <param name="threshold" value="DEBUG"/>
        <param name="append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d - %c -%-4r [%t] %-5p %x - %m%n" />
        </layout>
    </appender>

    <appender name="FILE" class="org.apache.log4j.FileAppender">
        <param name="File" value="e:/log.out" />
        <param name="ImmediateFlush" value="true"/>
        <param name="Threshold" value="DEBUG"/>
        <param name="Append" value="true"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
        </layout>
    </appender>

    <category name="com.jiaboyan" additivity="false">
        <level value="error"></level>
        <appender-ref ref="CONSOLE" />
    </category>

    <root>
        <priority value="info" />
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>

</log4j:configuration>

Through the above steps, log4j can normally run.

 

1.4 log4j configuration file Detailed

Next, a specific explanation respective attributes log4j profile :( log4j.properties to explain an example);

  • Logger

Logger root configuration, the syntax is: 
      Log4j.rootLogger = [Level], AppenderName, AppenderName, ... 

  wherein, level refers to the level of the root logger logs the object, Log4j recommended to use only four levels, from high to low respectively ERROR > WARN> the iNFO> the DEBUG; 
  AppenderName refers to the log information output destination root logger object, here a plurality of output destinations can be specified; 

  for example: 
      Log4j.rootLogger = the iNFO, the FILE, the CONSOLE
  • Appender
The Appender configuration information log output destination, the syntax is: 
      log4j.appender.appenderName = className 

  wherein, AppenderName refers to the name of the output destination of the log information, customizable, and consistent need in the root Logger AppenderName; 
  className refers log output destination processing class, must be fully qualified class name; 
  
  example. 1: 
      log4j.appender.FILE = org.apache.log4j.FileAppender (log information output to the corresponding disk file); 
      on the FileAppender remaining options: 
          log4j .appender.FILE.Threshold = the DEBUG (the lowest level of output from the log specified, the default is the DEBUG; log request if the level is lower than this level, this request is not output log information) 
          log4j.appender.FILE.File = E: / mylog.log (output to a log file mylog.log e disc) 
          log4j.appender.FILE.Encoding = UTF-. 8 (log output encoding settings) 
          log4j.appender.FILE.Append = to false(Add appended to the log file, the default is true, false cover) 
          log4j.appender.FILE.ImmediateFlush = to true (log message output request is immediately defaults to true) 
          log4j.appender.FILE.BufferedIO = to true ( log message request does not immediately output, which is stored in the cache, a file to disk when the cache is full before, the default is false, ImmediateFlush this time should be set to false) 
          log4j.appender.FILE.BufferSize = 8192 (cache size default is 8k) 
  
  example 2: 
      log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender tells that (the log information output to the console) 
      on ConsoleAppender remaining options: 
          log4j.appender.CONSOLE.Target = the System.out (log using the information to the console System.out.println) 
          log4j.appender.CONSOLE.ImmediateFlush = to true (supra)
          log4j.appender.CONSOLE.Threshold = the DEBUG (supra) 
          log4j.appender.CONSOLE.encoding = UTF-. 8 (log output settings coding) 
  
  Example. 3: 
      log4j.appender.DRFILE = org.apache.log4j.DailyRollingFileAppender (the output logs, a log file is generated every day, FileAppender different from the above)     
      on the remaining options DailyRollingFileAppender: 
          log4j.appender.DRFILE.File = E: / log.out (supra); 
          log4j.appender.DRFILE.ImmediateFlush = to true (supra) ; 
          log4j.appender.DRFILE.Threshold = the DEBUG (supra); 
          log4j.appender.DRFILE.Append = to true  (supra);
          log4j.appender.DRFILE.DatePattern = YYYY-MM-'.' dd (identification generates a new log file per day, of course, can be specified by month, week, hour, minute); the following format:
               '.'. 1) YYYY - : the MM month 
               '.' 2) YYYY- : WW week  
               '.'. 3) YYYY-MM- : dd day 
               '.'. 4) the MM-YYYY-DD- : A twice daily 
               '.' 5) yyyy DD---MM HH: hour 
               . 6) the mM-dd-YYYY-HH- '.' mm: min 

  example. 4: 
      log4j.appender.RFILE = org.apache.log4j.RollingFileAppender (in a log file reaches a specified size after, and then generate a new file to continue logging) 
      on the remaining options RollingFileAppender: 
          log4j.appender.RFILE.Threshold = DEBUG (ibid) 
          log4j.appender.RFILE.File = E: / mylog.log (ibid) 
          log4j.appender.RFILE.encoding = UTF-. 8 (supra) 
          log4j.appender.RFILE.Append = to false (supra) 
          log4j.appender.RFILE.ImmediateFlush = to true (supra) 
          log4j .appender.RFILE.MaxFileSize = 100KB (designated cutting the log file size, default 10MB, units KB / MB / GB; when the log reaches the specified size, the current log file contents to cut a new log file, a new file By default, the "original file name +.2 to" original file name +.1 " form" name) 
          log4j.appender.RFILE.MaxBackupIndex = 2 (the maximum number of files generated by the cutting, if the second file exceeds the specified size, then the first a file will be deleted)
  • Layout
Log information format Layout, the syntax is: 
      log4j.appender.appenderName.layout = className 

  wherein, AppenderName above stated is the name Appender, Appender must be bound to each other and Layout; 
  and className is handling log format, must be fully qualified class name; 
  
  example. 1: 
      log4j.appender.FILE.layout = Org.apache.log4j.HTMLLayout (html layout in tabular form) 
      log4j.appender.FILE.layout.LocationInfo = to true (output file name and java line number, the default value false) 
  
  example 2: 
      log4j.appender.FILE.layout = Org.apache.log4j.SimpleLayout (simple layout style includes only the log information and level) 
  
  example. 3: 
      log4j.appender.FILE.layout =  ORG. apache.log4j.PatternLayout (custom layout style, may comprise the time, the log level and log type)
      log4j. appender.FILE.layout.ConversionPattern = {D% the mM-dd-YYYY HH: mm: SS 5P% C%} {}. 1:% L -% m% n-(specifies how to format messages) 
      Specific Formatting:
           % P: Priority output log information, i.e. DEBUG, INFO, WARN, ERROR,
           FATAL. % d: output log date or time point in time, the default format is ISO8601, you can specify the format followed in, such as: D% {YYYY / the MM / dd HH: mm: SS, the SSS}.
          % R & lt: output from the application to the number of milliseconds spent output the log information.
          % T: output generated thread name of the log event.
          % l: log event occurrence location output corresponds% c% M.: composition (% F% L), including the full name of the class, method, file name and line number in the code. For example: test.TestLog4j.main (TestLog4j.java:10 ).
          % C: log object output log information belongs, i.e. getLogger content () was added.
                          % Category output log information belongs;: C
                           % Logger: No log4j this format;
           % M: output log information generating method name.
          % F: output file name where the log messages generated.
          %L :: outputs row number code.
          % M :: specified output code specific log information.
          % n: a carriage return line feed output, Windows platform "rn", Unix platform as "the n-" .
          % The X-: output associated with the current thread and NDC (nested diagnostic context), especially in applications like java servlets used in this multi-client multi-threaded.
          %%: output a "%" character.

 

1.5 Performance Optimization

In our application, the log operation almost every method necessary behavior, whether it is a record request information, or positioning aids problem, log information plays an important role, greatly facilitates program development.

But has brought with, because of frequent IO performance and disk reading and writing, the application is also reduced. And, java IO is the blocking, the lock also cause performance degradation. So for tuning log, it becomes necessary homework.

First, set aside frequent disk read and write IO and talk, discuss it in terms of pure performance of log4j, in the case of high concurrency, log4j lock can lead to decreased performance applications, reason, or look at the following code :

Method class callAppenders Category:

// log object evokes log output destination Appender: printing a log 
public  void callAppenders (the LoggingEvent Event) {
     int Writes = 0 ;
     // through log object set 
    for (the Category C = the this ; C =! Null ; C = c.parent ) {
         // the category Logger is parent, where c is the log object request itself: 
        the synchronized (c) {
             // here the lock: 
            IF ! (c.aai = null ) { 
              Writes + = c. aai.appendLoopOnAppenders (Event); 
            } 
            IF (! c.additive) {
               BREAK ; 
            }
        }
    }
    if(writes == 0) {
      repository.emitNoAppenderWarning(this);
    }
}

Through the above code, we can find in order to obtain the corresponding log object Appender, will all add synchronized synchronization lock before each acquisition. No matter how many threads requests need to be here to get a lock operation, in order to print the log. This means that more threads, the greater concurrency, the more intense the competition here lock, leading to reduced system performance.

Secondly, we go back and look at the problem and IO disk reads and writes. In the actual production environment, log information generated by the system needs to be saved in a file on disk for later analysis system, or the system to find the problem.

Before, we said that Java's IO is blocking, the following will look at the actual code below:

The JDK1.7 sun.nio.cs.StreamEncoder categories:

public void write(char cbuf[], int off, int len) throws IOException {
     synchronized (lock) {
         ensureOpen();
         if ((off < 0) || (off > cbuf.length) || (len < 0) ||
             ((off + len) > cbuf.length) || ((off + len) < 0)) {
             throw new IndexOutOfBoundsException();
         } else if (len == 0) {
             return;
         }
         implWrite(cbuf, off, len);
     }
 }

Can be seen, the final output stage stream java-IO, also added a synchronized synchronization lock. This is what we call the java blocking IO.

 

1.5.1 log4j performance test

 

In 2.3, the author mentions FileAppender, the main function of such a letter is to log output to a disk file. Of these, ImmediateFlush, BufferedIO, BufferSize these three attributes is particularly noteworthy;

When ImmediateFlush = true when a print representing each log request will be immediately output, which is immediately synchronized to disk to go. In high concurrency, system performance is greatly affected, IO and greatly enhance the number of disk read and write.

When ImmediateFlush = false time, with the opposite top, each represents a request the printing log is not output immediately, use java.io.OutputStreamWriter cache, cache size is 1024 bytes.

When ImmediateFlush = false, BufferedIO = true, BufferSize = 8192 time indication java.io.BufferedWriter cache, cache size is 8192 bytes default, each of the print request is not output immediately when the cache

After reaching 8192 bytes will fall plate operation. As a result, greatly reducing the IO and disk read and write operations to enhance the performance of the system.

 

Test code:

public class log4jDemo {
    Logger log = Logger.getLogger(log4jDemo.class);

    @Test
    public void test() throws InterruptedException {
        for(int x=0;x<20;x++) {
            long start = System.currentTimeMillis();
            for (int y = 0; y < 50; y++) {
                log.info("Info Message!");
            }
            long time = System.currentTimeMillis() - start;
            System.out.println(time);
        }
    }
}

Example 1: When ImmediateFlush = true, the test result (in milliseconds):

931 631 372 371 374 371 371 383 376 439 376 383 372 416 393 368 368 366 376 376 394 384 373 396 371 380 368 382 373 369 373 379 374 370 381 367 371 379 372 385 381 379 375 398 409 415 392 371 403 406

Example 2: When ImmediateFlush = false, the test result (in milliseconds):

845 693 344 338 353 340 372 373 345 337 332 341 345 352 346 332 336 333 379 359 333 330 356 338 333 341 346 331 341 337 339 329 341 339 339 334 341 328 331 329 328 330 329 336 334 332 332 331 333 330

Example 3: When ImmediateFlush = false, BufferedIO = true, BufferSize = 8192, the test result (in milliseconds):

731 853 356 332 336 334 334 334 334 331 332 344 331 330 332 332 330 331 340 334 329 333 331 335 334 334 332 331 336 335 331 354 334 333 334 354 331 333 334 332 333 331 347 332 333 330 332 330 333 331

Example 4: Use AsyncAppender asynchronous processing, the test result (in milliseconds):

292 178 146 177 216 278 215 147 136 102 92 96 97 93 93 95 93 94 92 93 97 93 94 93 95 94 96 107 94 91 93 94 99 98 96 95 95 98 102 95 93 92 91 107 155 137 110 98 93 93

Through the above four examples, we can see that the worst performance is ImmediateFlush = true time, while the performance is best when the open log AsyncAppender asynchronous process;

 

1.5.2 log4j hooks

The previous section, we mentioned the log4j cache by the test results, in the case of caching enabled, log4j's performance has been improved significantly. Since the advantage of the cache so obvious, why is not enabled by default log4j cache it?

There is cached, both advantages and disadvantages. Lee, improve system response performance; disadvantages, because when the system abnormality collapse, or are forced to close jvm, causing loss of data in the cache, the log is not present, not been able to determine the cause of the abnormality. I think this is the reason log4j open the cache and no default!

There is the log, on the one hand a request for information systems of records; on the other hand, help develpoer to detect, eliminate the error. If even the integrity of the log can not be retained, then the meaning of logs exist, then what is? So, log4j does not set the cache turned on by default, but provides an option;

So how do we make can have both fish and bear it? In api log4j provided temporarily unable to achieve this requirement, but jvm provided us with a way to help us realize this is jvm shutdown hook procedures;

Registered in the jvm in a hook procedure when jvm closed, all by method executes the system has been set addShutdownHook hooks added, when the system has finished performing these hooks, jvm will be closed.

So, in our log, how to hook procedures? Consider the following realization:

Test code:

public class log4jDemo {
    Logger log = Logger.getLogger(log4jDemo.class);
    @Test
    public void test() throws InterruptedException {
        log.info("Info Message!");
    }
}

:( cache configuration file does not open, immediately brush into the disk)

<appender name="FILE" class="org.apache.log4j.FileAppender">
    <param name="File" value="e:/log.out" />
    <param name="ImmediateFlush" value="true"/> 关闭缓存
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
    </layout>
</appender>

The results :( After the run, generate a log file information)

 

Once the brush, logs off the disc;

Next, modify the configuration file information, open the cache, do not immediately brush into the disk;

<appender name="FILE" class="org.apache.log4j.FileAppender">
    <param name="File" value="e:/log.out" />
    <param name="ImmediateFlush" value="false"/> 开启缓存
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
    </layout>
</appender>

The results :( After the run, generate a log file information)

 

jvm end of the run, the log information is not saved to disk, the log has been lost;

Finally, we add hooks and see what happens?

创建新的Appender,继承FileAppender,在构造中添加钩子程序代码:
public class HookFileAppender extends FileAppender {
    public HookFileAppender(){
        super();
        //添加钩子程序:
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
    public HookFileAppender(Layout layout, String filename) throws IOException {
        super(layout,filename);
        //添加钩子程序:
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
    public HookFileAppender(Layout layout, String filename, boolean append) throws IOException {
        super(layout,filename,append);
        //添加钩子程序:
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }
    public HookFileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
                 int bufferSize) throws IOException {
        super(layout,filename,append,bufferedIO,bufferSize);
        Runtime.getRuntime().addShutdownHook(new Log4jHockThread());
    }

    class Log4jHockThread extends Thread{
        @Override
        public void run() {
            //jvm结束之前,运行flush操作,将日志落盘;
            if(qw != null){
                qw.flush();
            }
        }
    }

}

配置文件修改:(新的appender,开启缓存)

<appender name="HOOKFILE" class="com.jiaboyan.logDemo.HookFileAppender">
    <param name="File" value="e:/log.out" />
    <param name="ImmediateFlush" value="false"/>
    <!--<param name="bufferedIO" value="true"/>-->
    <!--<param name="bufferSize" value="8192"/>-->
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n" />
    </layout>
</appender>
<root>
    <priority value="info" />
    <appender-ref ref="HOOKFILE" />
</root>

The results are:

 

Before the end of jvm, opened Log4jHockThread thread, the cache log off the disk operation, to avoid the loss of the log.

 
 

Reference: https: //www.jianshu.com/p/f2d4a54f9c41

 

Guess you like

Origin www.cnblogs.com/caoweixiong/p/11273091.html