logback由三个jar包组成:
logback-core, logback-classic and logback-access。
logback-core是其他两个部分的基础,logback-classic 继续了core,并且实现了SLF4J 的接口,在一般应用中使用这两个就足够了。 logback-access提供远程日志的功能,一般用不到。
logback 的实现逻辑由三个部分组成:
Logger
,Appender
,Layout
。logger来源于 logback-classic包,而Appender
和Layout
则来自于core包。
Logger :
可以将整个系统的日志管理输出看成一个树,如果对这颗树不做任何控制,那么将输出系统中的所有日志。而每个logger就相当于这个树的每个分支,如上一张的例子中
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
这里的logger是HelloWorld.class 的这个分支。并且这种树状结构构成了子父级的关系。如以“com.foo”命名的logger就是‘com.foo.bar’的父亲。真个logger的根级节点可以通过 如下代码获取:
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
在这种继承树的结构下,如果一个logger没有被赋予级别,那么他就会继承其父类的级别。
Example 1
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | none | DEBUG |
X.Y | none | DEBUG |
X.Y.Z | none | DEBUG |
Example 2
Logger name | Assigned level | Effective level |
---|---|---|
root | ERROR | ERROR |
X | INFO | INFO |
X.Y | DEBUG | DEBUG |
X.Y.Z | WARN | WARN |
Example 4
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | none | INFO |
X.Y.Z | none | INFO |
一条logger一般通过logger.info("msg"),logger.error("msg")等方式在代码中进行输出,info表示这条日志的级别就是info级别的,以此类推。需要注意的是,这条日志输出语句最终能否得到执行输出取决于两个方面:
一,logger 的输出时设置的级别(通过,logger.info("msg"),logger.error("msg")等方法得出)
二,logback对于该logger的输出控制级别。
只有当控制级别高于设置级别时,该条日志输出语句才能运行。logback的日志级别的关系关系如下:
TRACE < DEBUG < INFO < WARN < ERROR
.
通过一个例子说明,相关源码位于chapt2中(LogLeverTest)。
Logger logger = (Logger) LoggerFactory.getLogger("com.foo");
//将日志级别设置为info级别的
logger.setLevel(Level. INFO);
Logger barlogger = (Logger) LoggerFactory.getLogger("com.foo.Bar");
//WARN >= INFO 可以输出
logger.warn("Low fuel level.");
// DEBUG < INFO. 无法输出
logger.debug("Starting search for nearest gas station.");
//barlogger 继承 com.foo的级别,采用info级别
// INFO >= INFO 可以输出
barlogger.info("Located nearest gas station.");
// DEBUG < INFO.无法输出
barlogger.debug("Exiting gas station search");
需要注意的是,通过相同名字获取的logger是同一个对象,这对于提高系统的效率很有好处。一般情况下,我们需要对特定的类进行日志输出,为了避免重复问题,建议当采用名称的方式创建时,采用类的完整路径的方式进行命名。
Logger x = LoggerFactory.getLogger("wombat"); Logger y = LoggerFactory.getLogger("wombat");
这里的x,y指向同一个对象。
还有,虽然logger存在这种类似树的结构,但是并不要求必须先创建父类logger然后采用创建子类logger,完全可以按照任意顺序尽心创建。
此外在LogLeverTest的例子中,采用logger.setLevel(Level. INFO);的方式进行了logger的级别设置,在实际的使用中一般将通过配置文件的方式进行设置。
Appenders and Layouts
printf
方法)来实现个性化的日志输出。
For example, the PatternLayout with the conversion pattern "%-4relative [%thread] %-5level %logger{32} - %msg%n" will output something akin to:
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
The first field is the number of milliseconds elapsed since the start of the program. The second field is the thread making the log request. The third field is the level of the log request. The fourth field is the name of the logger associated with the log request. The text after the '-' is the message of the request.
提高日志效率及参数化的输出支持
很多情况下,我们会采用如下方式进行日志的输出
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
但是当日志级别升级为info或者更高级别时,这条语句是得不到运行的。但是String.valueof却需要每次都运行,这样会浪费系统资源,因此建议采用如下方式进行日志的输出编写方式。
if(logger.isDebugEnabled()) { logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); }
logger.debug("The new entry is {}.", entry);当然采用这种参数的方式也可以降低系统的消耗,如果这条语句不执行,那么entty的tostring方法将不会执行。
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
Object[] paramArray = {newVal, below, above}; logger.debug("Value {} was inserted between {} and {}.", paramArray);