Java Logging Techniques Summary(Log4j Example)
1. Three Main Components:
1) Logger
2) Appender
3) Layout
These three types of components work together to enable developers to log message according to message type and level, and to control at runtime how these messages are formatted and where they are reported.
Relationship between these important components
2. Logger may be assigned levels.
1) TRACE
2) DEBUG
3) INFO
4) WARN
5) ERROR
6) FATAL
If a given logger is not assigned a level, then it inherits one from its closest ancestor with an assigned level.
Inside it is managed by LoggerManager. Which has a map set the name as key, and the Logger as value.
3. Appender
1) Log4j allows logging requests to print to multiple destinations.
2) Main sub class of Appender
1) ConsoleAppender
2) FileAppender/RollingFileAppender
3) JMSAppender
4) SMTPAppender
5) JDBCAppender
6) SocketAppender
3) A Logger may have multiple Appender.
Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy. <Observer Design Pattern>
In other words, appenders are inherited additively from the logger hierarchy.
For example, if a console appender is added to the root logger, then all enabled logging requests will at least print on the console.
4. Layout
1) More often than not, users wish to customize not only the destination but also the output format.
This is accomplished by associating a layout with an appender.
2) The hierarchy of Layout is shown as below.
5. A simple example of using Log4j
1) pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>edu.xmu.logging</groupId> <artifactId>Logging-Log4J</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> </dependencies> </project>
2) log4j.properties in the class path
# Define the root logger with appender X log = C:/YangKunLun/logging log4j.rootLogger = ERROR, FILE # Define the file appender log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender # Set the name of the file log4j.appender.FILE.File=${log}/log.out # Set the immediate flush to true(default) log4j.appender.FILE.ImmediateFlush=true # Set the threshold to debug mode log4j.appender.FILE.Threshold=debug # Set the append to true, override log4j.appender.FILE.Append=true # Set the maxmium file size before rollover # log4j.appender.FILE.MaxFileSize=5KB # Set the date pattern log4j.appender.FILE.DatePattern='.' yyyy-MM-dd-HH-mm # Set the backup index # log4j.appender.FILE.MaxBackupIndex=2; # Define the layout for X appender log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.conversionPattern=%m%n
3) Log4jExampleTest.java
package edu.xmu.log4j; import java.util.Date; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.Test; public class Log4jExampleTest { private static Logger logger = Logger.getLogger("edu.xmu"); static { logger.setLevel(Level.INFO); } @Test public void test() { // Debug < Info < Warn < Error < Fatal logger.debug((new Date()) + " [DEBUG] Hello this is an debug message"); logger.info((new Date()) + " [INFO] Hello this is an info message"); logger.warn((new Date()) + " [WARN] Hello this is an warn message"); logger.error((new Date()) + " [ERROR] Hello this is an error message"); logger.fatal((new Date()) + " [FATAL] Hello this is an fatal message"); } }
4) Log4jExampleTest2.java
package edu.xmu.log4j; import java.util.Date; import org.apache.log4j.Appender; import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.Logger; import org.apache.log4j.SimpleLayout; import org.junit.Test; public class Log4jExampleTest2 { private static Logger logger = Logger.getLogger("edu.xmu.log4j"); @Test public void test() { // Log4jExampleTest test = new Log4jExampleTest(); Layout layout = new SimpleLayout(); Appender appender = new FileAppender(); appender.setLayout(layout); logger.addAppender(appender); logger.debug((new Date()) + " [DEBUG] Hello this is an debug message"); logger.info((new Date()) + " [INFO] Hello this is an info message"); logger.warn((new Date()) + " [WARN] Hello this is an warn message"); logger.error((new Date()) + " [ERROR] Hello this is an error message"); logger.fatal((new Date()) + " [FATAL] Hello this is an fatal message"); } }
Comments:
1) When we de-comment the comments in Log4jExampleTest2.java, we can find that the level of this logger in this class is no longer that inherits from RootLogger that is ERROR.
But inherits from "edu.xmu" which is INFO.
6. Configuration file in depth
1) You may wonder there are so many differents choice for us to choose from. How can we remember all the properties in order to config the configuration file properly?
Let take a look at a simple configuration file.
# Here we define the root logger for log4j. # The syntax of defining root logger is : # log4j.rootLogger = "RootLevel", "RootAppender1", "RootAppender02" # Here we using "STDOUT" as micro variables. log4j.rootLogger = INFO, STDOUT # Here we specify what the STDOUT is. # As STDOUT must be an Appender. # Here we specify that STOUT is the instance of ConsoleAppender # So every time we encounter log4j.appender.STDOUT, we can use "org.apache.log4j.ConsoleAppender" as replacement. log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender # Then we dig into the source code of "org.apache.log4j.ConsoleAppender" # We can find a method named public void setTarget(String value) # So we can set as below: log4j.appender.STDOUT.Target = System.out # It equals : org.apache.log4j.ConsoleAppender.setTarget("System.out");
2) Extends
Let's take a look at the more complex configuration file
<log4j.logger.edu.xmu is a custom logger that defined in configuration file>
<When we want to get this custom logger, we just use Logger.getLogger("edu.xmu");>
Please pay attention that we ignore the prefix of "log4j.logger" when getting the logger.
# Here we define macro named "log" whose value is "c:/..." log = C:/YangKunLun/logging # Here we define the default level for root loggger # And added two Appenders for this root logger log4j.rootLogger = INFO, FILE, STDOUT log4j.logger.edu.xmu = WARN, FILE, STDOUT # This has been explained in example above log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender log4j.appender.STDOUT.Target = System.out # Here we define the log4j.appender.FILE is an instance of DailyRollingFileAppender log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender # When we look up the class org.apache.log4j.DailyRollingFileAppender # We can find a method named setFile(String file) # This means we want to set the output file as "c:/yangkunlun/logging/log.out" log4j.appender.FILE.File=${log}/log.out # We have to define the Layout for each Appender log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.conversionPattern=%m%n log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout # Regard configuration file as java code.
7. Code configuration in depth
package edu.xmu.log4j; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Date; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.FileAppender; import org.apache.log4j.Layout; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.SimpleLayout; import org.junit.Test; public class Log4jExampleTest2 { private static Logger logger = Logger.getLogger("edu.xmu.log4j"); @Test public void test() throws IOException { // Log4jExampleTest test = new Log4jExampleTest(); Layout layout = new SimpleLayout(); ConsoleAppender consoleAppender = new ConsoleAppender(); consoleAppender.setName("SYSOUT"); consoleAppender.setLayout(layout); consoleAppender.setWriter(new OutputStreamWriter(System.out)); consoleAppender.setThreshold(Level.ERROR); FileAppender fileAppender = new FileAppender(); fileAppender.setName("FILE"); fileAppender.setLayout(layout); fileAppender.setAppend(true); fileAppender.setImmediateFlush(true); fileAppender.setWriter(new FileWriter(new File( "C:/YangKunLun/logging/log2.out"))); fileAppender.setThreshold(Level.DEBUG); logger.addAppender(fileAppender); logger.addAppender(consoleAppender); logger.setLevel(Level.WARN); logger.debug((new Date()) + " [DEBUG] Hello this is an debug message"); logger.info((new Date()) + " [INFO] Hello this is an info message"); logger.warn((new Date()) + " [WARN] Hello this is an warn message"); logger.error((new Date()) + " [ERROR] Hello this is an error message"); logger.fatal((new Date()) + " [FATAL] Hello this is an fatal message"); } }
Comments:
1) The code above is equivalent to the configuration file in 6-2
2) Pay attention to that fileAppender.setWriter(new FileWriter(...)) is not fileAppender.setFile("...");
Pay attention to that console.setWriter(new OutputStreamWriter(...)) is not consoleAppender.setTarget("...");
3) The relationship of levels of Logger and Appender is like that in JUL. Please refer to previous chapter.
Reference Links: