LOG4J's personal analysis

Source: Internet
Author: User

Recently a little roughly read the source of log4j, after all, this is the Java Open Source log framework is almost the most common implementation. In most practical applications, the log4j is also required to be encapsulated by facade-mode log frameworks such as Common-logging, SLF4J, and the actual logging, output level, formatting, etc. are done by log4j. This article focuses on log4j and explores the internals of this open source log framework.

Characteristics

specific configuration and examples of log4j here is not much to say, mainly focus on its configuration features. As an open-source log framework, the LOG4J configuration is flexible and easy to understand to meet most log requirements, and is not complex to configure.

There are mainly the following configurable features:

    • Log level: DEBUG, INFO, WARN, error, etc.;
    • output Mode (Appender): Log4j comes with Asyncappender, Consoleappender, Rollingfileappender, Dailyrollingfileappender, etc., can also be customized to achieve the corresponding interface.
    • Output format (Layout): Log4j also comes with patternlayout, datelayout, etc.
    • You can follow the package's "parent-child" relationship to have the child package inherit the parent package configuration.
Architecture roughlyThe main is to provide the interface through Logmanager, and then hierachy management logger inheritance hierarchy, LogEvent encapsulation to log information, in addition to layout and Appender layer package implementation functions.
Some implementation details
log4j to improve the performance impact of logging, a number of implementation details have been optimized.
    • Log judgments for date and size limits
If you set the logging time and the file size limit so that each limit is rollover, the current file is closed, and the new file is opened for re-logging. Because this requires each log to be judged, in order to improve performance, for the date size, will be pre-calculated for the next time rollover need to nextcheck, and then each time you log logs only need to take the current time to judge. The code is as follows:
dailyrollingfileappenderprotected void Subappend (Loggingevent event) {    Long n = system.currenttimemillis ();    if (n >= nextcheck) {      now.settime (n);      Nextcheck = Rc.getnextcheckmillis (now);      try {rollOver ();      }      catch (IOException IoE) {          if (IoE instanceof interruptedioexception) {              thread.currentthread (). interrupt ();      Loglog.error ("RollOver () failed.", IoE);      }    }    Super.subappend (event);   }}
for file size restrictions, because each time the file size is more cumbersome, there is a way to add each log size to the count variable, and then decide whether to exceed the size of the rollover operation. The code is as follows:
Rollingfileappender  protected  void Subappend (Loggingevent event) {    super.subappend (event);    if (fileName! = null && QW = null) {        Long size = ((Countingquietwriter) QW). GetCount ();        if (size >= maxfilesize && size >= nextrollover) {            rollOver ();}}}}   Countingquietwriter public void Write (string string) {try {out.write (string);    Count + = String.Length ();    } catch (IOException e) {errorhandler.error ("Write failure.", E, errorcode.write_failure);  }} public long GetCount () {return count; }
    • Fill blank area of pattern
When filling pattern, if the content does not exceed the minimum size, a blank character fill is required. LOG4J uses a fast space padding method, mainly the need to fill the length into two parts, more than 32 of the part, then the loop to add 32 spaces, less than 32, then the length of bit bit to judge to add the corresponding 1,2,4, 8, 16 space characters. This way, if you want to fill less than 32 white space characters, you need 4 loops to resolve. The code is as follows:
  Static string[] SPACES = {"", "" "" "" "" "        //1,2,4,8 SPACES    "                ",//SPACES    " "                                };//32 Spaces  /**     Fast space padding method.  */public  void Spacepad (stringbuffer sbuf, int length) {While    (length >=) {      sbuf.append ( SPACES[5]);      length = +;    }        for (int i = 4; I >= 0; i--) {      if (length & (1<<i) = = 0) {sbuf.append (spaces[i]);}}}}  
    • Inheritance of Logger
    For log4j, the package x.y.z of Java is the child package of the package x.y, and if the properties of the parent package x.y are defined, the child packages are automatically inherited. To achieve this, log4j takes advantage of a parent reference inside the logger so that when the property is obtained, it is only necessary to get its properties through the parent pointer. In GetLogger, this parent reference is dynamically computed, because the order of the parent package and the child package call GetLogger is not deterministic, so ensure that the parent can be properly set if the performance is ideal, Log4j uses a provisionnode as a placeholder to aid judgment. The code is as follows:
Public Logger GetLogger (String name, Loggerfactory Factory) {//system.out.println ("getinstance (" +name+ ") called.");    Categorykey key = new Categorykey (name); Synchronize to prevent write conflicts.    Read conflicts (In//Getchainedlevel method) is possible only if variable//assignments is non-atomic.    Logger Logger;      Synchronized (HT) {Object o = ht.get (key); if (o = = null) {logger = factory.makenewloggerinstance (name); Logger.sethierarchy (this); Ht.put (key, logger);      Updateparents (logger); return logger;      } else if (o instanceof Logger) {return (Logger) O; } else if (o instanceof provisionnode) {//system.out.println ("(" +name+ ") Ht.get (This) returned Provisionnode"); logger = f Actory.makenewloggerinstance (name); Logger.sethierarchy (this); Ht.put (key, logger); Updatechildren ((Provisionnode)      o, logger); updateparents (logger); return logger;  } else {//It should is impossible to arrive herereturn null;      But let ' s keep the compiler happy. }    }  } 
If the class of the child package calls GetLogger first, then the logger obtained at this time is null, so that in Updateparents, the parent package is constantly created in the order of x.y, X, and so on.Provisionnode as a placeholder, if the parent package is still not found, the root logger will be the default father, so there must be a default property. If the class of the parent package calls GetLogger first, it will get Provisionnode, which will require the parent reference to be updated with the Provisionnode record's child logger. This ensures that the order is normal at the time of GetLogger, and also that it is faster to get the inheritance property through the parent. The Updatechildren and Updateparents methods are as follows:
  Final private void updateparents (Logger cat) {String name = Cat.name;    int length = Name.length ();    Boolean parentfound = false;    System.out.println ("updateparents called for" + name); If name = "w.x.y.z", Loop Thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z" for (int i = Name.lastindexof ('. ', length -1);                                 I >= 0;      i = Name.lastindexof ('. ', i-1)) {String substr = name.substring (0, I);      System.out.println ("Updating Parent:" + substr); Categorykey key = new Categorykey (SUBSTR);      Simple constructor Object o = Ht.get (key);      Create A provision node for a future parent. if (o = = null) {//SYSTEM.OUT.PRINTLN ("No parent" +substr+ "found. Creating provisionnode. ");      Provisionnode pn = new Provisionnode (cat); Ht.put (key, PN); } else if (o instanceof Category) {parentfound = True;cat.parent = (category) O;//system.out.println ("Linking" + Cat.name + "+" + (Category) o). No need to Update the ancestors of the closest ancestor} else if (o instanceof provisionnode) {((Provisionnode) O). AddElement (c      at); } else {Exception e = new IllegalStateException ("Unexpected Object Type" +o.getclass () + "in Ht.");      E.printstacktrace ();    }}/If we could not find any existing parents and then link with root.  if (!parentfound) cat.parent = root; } Final private void Updatechildren (Provisionnode pn, Logger Logger) {//system.out.println ("Updatechildren called F    or "+ logger.name);    final int last = Pn.size ();      for (int i = 0; i < last; i++) {Logger L = (Logger) pn.elementat (i);      System.out.println ("Updating Child" +p.name);  Unless this child already points to a correct (lower) the parent,//Make-cat.parent point-to-l.parent and l.parent to      Cat.      if (!l.parent.name.startswith (Logger.name)) {logger.parent = L.parent;l.parent = logger; }    }  }
    • NDC and MDC
NDC and MDC can easily provide context for different threads. NDC uses Hashtable to record the thread's stack information, and the configuration log4j uses%x to print out the information for easy tracking. The MDC is saved with threadlocal. The concept is pretty good, but the code implementation is rather ugly, it is not taken out.
    • Where the implementation is not good enough
1, because log4j is in the early implementation of an open-source log framework, the code also has support for JAVA1, so in some collection container selection is more "backward". For example, most thread synchronization directly chooses Hashtable, stack and other implementations on the implementation of directly using the Synchronized keyword to synchronize the container, the concurrency of these containers is far worse than 1.5 beginning to exist in the collection of concurrent, such as Concurrenthashmap , Copyonwritearraylist and other containers. So the fact that the Hashtable, stack and other containers are actually not recommended to use. 2, for the MDC to use Threadlocal to save the different thread context, perhaps did not notice that threadlocal entry is weakreference, so it is easy to be collected by GC, so that the context of the content will be mysteriously disappeared. 3, for Asyncappender, the implementation of this asynchronous Appender, using a consumption-production mode of thread synchronization. The wait, Notifyall, and ArrayList of the object monitor are also used as queues for synchronization. It's natural to use Blockingqueue and other sub-class synchronization queues to be much better. 4, it is well known that when writing a file if using buffer will greatly improve performance, similarly, log4j also provides the setting buffer property, but unfortunately, if you do not pay attention to flush, when the program exits will lose some log records, Although log4j provides the Logmanager.shutdown method, it can be cumbersome if the program exits unexpectedly without calling shutdown. Therefore, there is a lack of effective means to solve the problem.
    • Compare a log implementation before the individual

I once wrote a log implementation for Android. Because Android itself is extremely demanding for smooth operation, a buffer+ asynchronous logging function is implemented to achieve the best log performance.

The main use of OutputStream is the transfer of. Roughly as follows:

OutputStream writer = new Bufferedoutputstream (new Asyncoutputstream (new FileOutputStream));
This way, when buffer is full, it is automatically converted to an asynchronous write file, which should be able to minimize the IO burden of logging and avoid the overhead of excessive asynchrony.





    

LOG4J's personal analysis

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.