<!--
JCL(Jakarta Commons Logging)
commons-logging.jar, log4j.jar + log4j.properties or log4j.xml
commons-logging.properties //Is optional, but is recommended, if absent, will guess(discover) your preferred logging system dynamically in some order.
Theory:
LogFactory guess (discover) your preferred logging system dynamically.
1) LogFactory.getAttribute("org.apache.commons.logging.Log"), this attribute can be set by code LogFactory.setAttribute() or set it in commons-logging.properties(preferred)
2) System property of org.apache.commons.logging.Log
3) Log4J.jar
4) If the application is executing on a JDK 1.4 system, use the corresponding wrapper class (Jdk14Logger).
5) Fall back to the default simple logging wrapper (SimpleLog).
Best practice:
commons-logging.properties: If you have a particular preference then providing a simple commons-logging.properties file which specifies the concrete logging library to be used is recommended
Log Level:
1.trace (the least serious)
2.debug
3.info
4.warn
5.error
6.fatal (the most serious)
Fault:
1) Use class loader to discover logging system dynamically, so it conflicts with OSGi.
2) log.isInfoEnabled() deduces the code readability.
SLF4J(Simple Logging Facade for Java)
A abstract layer of logging, like commons-logging, need a concrete log framework: JUL(java.util.logging), Log4j, Loback
Features
1) Binding log implementation statically, so it can be used with OSGi.
2) No need "if log.isInfoEnabled()"
3) Use placeholder : log.info("Hello {0}, {1}", "Alice", "Bob");
Usage
Log4j: slf4j-api.jar, slf4j-log4j.jar, log4j.jar
Logback: slf4j-api.jar, logback.jar
JUL: slf4j-api.jar, slf4j-jdk14.jar
Simple: slf4j-api.jar, slf4j-simple.jar
None: slf4j-api.jar, slf4j-nop.jar
Redirect to Slf4J
log4j-over-slf4j.jar replace log4j.jar
jcl-over-slf4j.jar replace commons-logging.jar
Add jul-to-slf4j.jar
Error
Exception in thread "main" java.lang.StackOverflowError
at java.util.Hashtable.containsKey(Hashtable.java:306)
at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:36)
at org.apache.log4j.LogManager.getLogger(LogManager.java:39)
Reason
dependency cycle: Slf4j redirect to Log4j, and Log4j redirect to Slf4j
classpath: slf4j-log4j.jar, log4j-over-slf4j.jar
Logback(https://logback.qos.ch, successor of Log4j, native implementation of slf4j, no need slf4j adapter)
logback-classic will import logback-core and slf4j.
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL -->
<configuration scan="false" debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level %caller %msg%n</pattern>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level %replace(%caller{1}){'\bCaller\+0\s\sat',''} - %msg%n</pattern>
-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level %replace(%caller{1}){'\bCaller\+0\s\sat',''} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
JUL(java.util.logging)
JUL log level: FINEST,FINER,FINE (TRACE), CONFIG (DEBUG), INFO, WARNING, SEVERE(ERROR), ALL, OFF
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
public class Test {
public static void main(String[] args) {
Logger log = Logger.getLogger(ODNTest.class.getName());
log.setLevel(Level.ALL); //Useless, solution: \jdk8\jre\lib\logging.properties\java.util.logging.ConsoleHandler.level = ALL
/*
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.ALL);
log.addHandler(consoleHandler);
*/
/*
FileHandler fileHandler = new FileHandler("C:/testlog%g.log");
fileHandler.setLevel(Level.ALL);
fileHandler.setFormatter(new MyLogFormater());
log.addHandler(fileHandler);
*/
log.severe("Severe 111");
log.warning("Warning 222");
log.info("Info 333");
log.config("Config 444");
log.fine("Fine 555");
log.finer("Finer 666");
log.finest("Finest 777");
}
}
class MyLogFormater extends Formatter {
public String format(LogRecord record) {
return record.getLevel() + ":" + record.getMessage()+"\n";
}
}
logback.xml
<!-- OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="log_dir" value="c:/tmp" />
<property name="maxHistory" value="7" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-5level [%thread] %replace(%caller{1}){'\bCaller\+0\s\sat',''} %m%n</pattern>
<!-- <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [%thread] %logger %m %n</pattern> -->
</encoder>
</appender>
<appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/pim_%d{yyyyMMdd}.log</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %-5level [%thread] %logger [%file:%line] - %msg%n</pattern>
</encoder>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10MB</maxFileSize>
</triggeringPolicy>
</appender>
<logger name="org.apache.ibatis" level="INFO"/>
<root level="info">
<appender-ref ref="STDOUT"/>
<appender-ref ref="fileAppender"/>
</root>
</configuration>
-->