log4j - Logger VS Appender

 

回到顶部(go to top)

loggers 和 appenders 是两个不同的概念

一定要搞清楚两者的作用,不要搞反了。

Log4J makes a distinction between

  • loggers, which are responsible for generating log messages. logger负责产生log信息
  • appenders, which are responsible for sending those messages somewhere (a file, the console, a database, etc.) appender负责把log写到哪里

 

回到顶部(go to top)

loggers 的继承结构

Loggers有继承结构。如果一个logger命名为“admin”, 那么它是名为“admin.component1” logger的父亲。

Loggers form a hierarchy, the root logger is the parent of the logger named admin, which is the parent of admin.component1, etc., and you can attach appenders to any logger in the hierarchy.

 

对于xml格式的配置文件,默认的父logger就是<root>,其他自定义的<logger>是子logger。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<appender class="org.apache.log4j.ConsoleAppender" name="stdout">
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="[%d] [%X{hostname}] [%X{status}] [%t] %p %c{2} %m%n"/>
    </layout>
</appender>
 
.......
 
    <logger name="MonikerLogger" additivity="false">
        <level value="INFO"/>
        <appender-ref ref="rollingMonikerFile" />
    </logger>
 
    <root>
        <level value="INFO"/>
        <appender-ref ref="stdout" />
        <appender-ref ref="rollingFile"/>
    </root>

  

  

结构图如下:

 

回到顶部(go to top)

如何调用父logger,子logger

1.通过logger的名字<logger name="XXXXX">来决定调用哪个的logger

2.如果找不到指定名字的logger,那就默认调用root logger.

 

假设项目中有如下的logger,共有三个logger:

  1. MonikerLogger
  2. com.XXX.unity.risk.engineutils.HeartBeat
  3. root (by default, when not found specific name)
1
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<logger name="MonikerLogger" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="rollingMonikerFile" />
</logger>
 
<logger name="com.XXX.unity.risk.engineutils.HeartBeat" additivity="false">
    <appender-ref ref="stdout" />
</logger>
 
<root>
    <level value="INFO"/>
    <appender-ref ref="stdout" />
    <appender-ref ref="rollingFile"/>
</root>

  

Case 1

private final static Logger loggerMoniker = Logger.getLogger("MonikerLogger");

直接点名调用logger的名称,“MonikerLogger”

 

Case 2

private final static Logger logger = Logger.getLogger(Test.class);

Test.class 是指Test类的类全名,这里假设是com.XXX.unity.risk.engineutils.Test

可以看到没有匹配一个特定的logger,所以会选择root

 

Case 3

Logger log = Logger.getLogger(HeartBeat.class);

HeartBeat.class 是指Test类的类全名,这里假设是com.XXX.unity.risk.engineutils.HeartBeat

可以看到匹配到特定的 com.XXX.unity.risk.engineutils.HeartBeat  logger,所以会选择 com.XXX.unity.risk.engineutils.HeartBeat

 

 

回到顶部(go to top)

loggers 能把消息发给哪些appenders

  • 默认情况下,logger会把消息发送给所有和它直接相连的appenders
  • 再者,当未在logger中设置additivity="false"时,还会发送给它的父亲logger的appenders.

这也是为什么,在命名logger时,会采用类似java类的结构。例如 “com.example” logger是 “com.example.Class1” 和 “com.example.subpkg.AnotherClass” 的父亲logger。

By default a logger will send messages to all appenders that are attached directly to it, or to any of its ancestors in the hierarchy (this is why loggers are conventionally named like Java classes, e.g. you can control logging for com.example.Class1 and com.example.subpkg.AnotherClass by configuring the com.example logger).

 

 

回到顶部(go to top)

案例分析

问题描述

This might be a very easy question for some, but personally I find Log4j config to be nightmarishly difficult and that learning to perform brain surgery might be less challenging.

I am trying to lave multiple loggers logging into different files. Here is what I have in my log4j.properties file:

# Root logger option
log4j.rootLogger=INFO, file, admin

# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=/home/nick/logging/file.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n

log4j.appender.admin=org.apache.log4j.RollingFileAppender
log4j.appender.admin.File=/home/nick/logging/admin.log
log4j.appender.admin.MaxFileSize=1MB
log4j.appender.admin.MaxBackupIndex=1
log4j.appender.admin.layout=org.apache.log4j.PatternLayout
log4j.appender.admin.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n

And here is my (very simple) Java app used to test the config:

public static void main(String[] args) throws Exception {

    Properties resource = new Properties();
    InputStream in = new FileInputStream("/home/nick/logging/log4j.properties");
    resource.load(in);
    PropertyConfigurator.configure(resource);

    Logger admin = Logger.getLogger("admin");
    Logger file = Logger.getLogger("file");

    admin.info("hello admin");
    file.info("hello file");
}

The 2nd problem is that both messages are written to both logs. Here is the actual result:

File admin:log:

2014-04-27 11:55:30 INFO  admin - hello admin
2014-04-27 11:55:30 INFO  file - hello file

File file.log:

2014-04-27 11:55:30 INFO  admin - hello admin
2014-04-27 11:55:30 INFO  file - hello file

Here is the required result:

File admin:log:

2014-04-27 11:55:30 INFO  admin - hello admin

File file.log:

2014-04-27 11:55:30 INFO  file - hello file

错误原因

The configuration you have given in the question defines one logger (the root logger) which sends all the messages it generates to two separate appenders, one for each of the two files. Your code then requests two different loggers and generates one log message with each logger. Both these loggers inherit the appender configuration from the root logger, so they both send their messages to both of the configured appenders.

enter image description here

解决方案

Instead of attaching the two appenders to the root logger, you should attach the file appender to the file logger and the admin appender to the admin logger:

log4j.rootLogger=INFO
log4j.logger.file=INFO, file
log4j.logger.admin=INFO, admin

This way the file logger will send messages only to file.log, the admin logger only to admin.log, and all messages from other loggers will be silently discarded, as there are no appenders attached to the root.

enter image description here


The additivity flag is the exception to this rule - setting a logger's additivity to false essentially disconnects the arrow from a logger up to its parent, so messages generated by that logger (or flowing into it from one of its children) will not go any further up the tree, they will only go to appenders attached directly to the logger in question.

 

 

回到顶部(go to top)

英文原版答案

Ref: https://stackoverflow.com/questions/23322602/log4j-config-different-logs-to-different-files

Log4J makes a distinction between loggers, which are responsible for generating log messages, and appenders, which are responsible for sending those messages somewhere (a file, the console, a database, etc.). Loggers form a hierarchy, the root logger is the parent of the logger named admin, which is the parent of admin.component1, etc., and you can attach appenders to any logger in the hierarchy. By default a logger will send messages to all appenders that are attached directly to it, or to any of its ancestors in the hierarchy (this is why loggers are conventionally named like Java classes, e.g. you can control logging for com.example.Class1 and com.example.subpkg.AnotherClass by configuring the com.example logger).

Loggers and appenders form separate namespaces and this is the source of your confusion - the logger named admin and the appender named admin are two separate entities.

The configuration you have given in the question defines one logger (the root logger) which sends all the messages it generates to two separate appenders, one for each of the two files. Your code then requests two different loggers and generates one log message with each logger. Both these loggers inherit the appender configuration from the root logger, so they both send their messages to both of the configured appenders.

enter image description here

Instead of attaching the two appenders to the root logger, you should attach the file appender to the file logger and the admin appender to the admin logger:

log4j.rootLogger=INFO
log4j.logger.file=INFO, file
log4j.logger.admin=INFO, admin

This way the file logger will send messages only to file.log, the admin logger only to admin.log, and all messages from other loggers will be silently discarded, as there are no appenders attached to the root.

enter image description here


The additivity flag is the exception to this rule - setting a logger's additivity to false essentially disconnects the arrow from a logger up to its parent, so messages generated by that logger (or flowing into it from one of its children) will not go any further up the tree, they will only go to appenders attached directly to the logger in question.

 

posted on   frank_cui  阅读(996)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

levels of contents
点击右上角即可分享
微信分享提示