为什么log4j的概念模型是错的--zlog的模型简介

原创
2012/04/11 20:18
阅读数 5.8K

    假设你的log4j系统中有这样的配置

log4j.logger.aa=ERROR, A1
log4j.logger.aa.bb=
log4j.logger.aa.cc=INFO

    aa是一个父logger。aa.bb和aa.cc是aa的子logger。

    aa.bb继承了aa's等级和输出(appender),等级是ERROR,输出是A1。

    aa.cc继承并覆盖了aa的级别。所以aa.cc的等级是INFO输出还是A1。

    需求1:如果我现在想把所有的aa的日志,所有的级别,输出到一个特别的文件里面去,并保持原来的aa.bb、aa.cc的输出不变,如何实现?

    需求2:假设我动了aa.bb的代码,想在生产上把aa.bb的所有日志,从DEBUG级别开始输出到某个aa.bb.debug.log,而保持aa的ERROR以上级别的日志不动(方便运维人员不改变他们的习惯,直接看原来的日志)

   log4j将会如何实现这些需求?很困难,也许要在appender上引入阈值(threshold),或者引入子logger不继承父logger的选项。子logger可以自己指定一个等级,或者从父logger那里继承一个。但说到底,log4j的logger必须有且只能有一个等级,等级和logger是绑定的。

   zlog将会如何实现这些需求?首先,zlog继承了syslog配置文件的思想,一个分类的不同等级可以同时存在不同的规则内。这就让过滤同一分类的不同等级的日志到不同日志文件成为可能。

aa.debug         “/var/log/aa.debug.log”
aa.=notice       “/var/log/aa.notice.log”

是不是觉得自由了很多?

    其次,在zlog里面,所有的规则之间都是独立的,没有父子关系。纲目分类的关系表现在分类字符串中间的下划线。举例:

#rule 1
aa_bb.DEBUG         “/var/log/aa_bb.log”

#rule 2
aa_cc.INFO          “/var/log/aa_cc.log”

#rule 3
aa_.ERROR           “/var/log/aa_error.log”

#rule 4
aa.*                “/var/log/aa.log”

没有继承,只有4条独立的规则。如果代码里面的分类名是“aa_bb”。代码就像这样:

category_t ab;

ab = zlog_get_category(“aa_bb”);

ZLOG_DEBUG(ab, “ab's debug”);
ZLOG_ERROR(ab, “ab's error”);

    配置中,rule 1的分类字符串”aa_bb”和rule 3的分类字符串”aa_”,与代码中的名字为”aa_bb”的分类变量是匹配的。于是代码中属于aa_bb分类变量、>=DEBUG日志输出到aa_bb.log,属于aa_bb分类变量、>=ERROR会被输出到 aa_error.log。ERROR等级的日志会被同时写在两个文件里面。不过rule 4的变量字符串是”aa”,它不匹配分类变量”aa_bb”,它精确匹配拥有“aa”名字的分类变量。

    这就是纲目分类模型。规则和规则之间是分开的。一个代码分类变量可以匹配多个规则分类字符串,一个规则可以属于多个代码分类变量。规则中的纲分类字符串(以下划线结尾的)匹配代码中的目分类变量,纲分类字符串的范围包括了了目分类字符串。这样,用户可以选择任意范围的纲目分类字符串来输出,而不影响其他规则的行为。

    事实上,在zlog_get_category()被调用的时候,并不保证代码分类变量一定有相匹配的规则分类字符串。分类变量可以有很多与之匹配的规则,也有可能一条都没有,这取决于配置文件是怎么写的。当配置文件改变并调用zlog_reload()后,代码分类变量和规则分类字符串的匹配会被重新计算。根据上面所说的匹配方式,每个分类变量都会从新的配置文件里面找到自己的新规则。

    也就是说,在zlog里面,多输出是由多条规则来实现的,而不是log4j的多个appender。一条规则代表程序员对于某种分类和等级的日志的输出需求。没有必要指定某个分类必须为什么等级。分类、等级、输出这3者可以自由搭配,完全解耦,这样就带来了极大的灵活性。

   必须感谢unix系统syslog的设计者,从思想上来说,zlog只是在他们的基础上增加了一点点的改动来匹配纲目分类,但灵活性远超java系列的绑定思想。也许log4j的设计者被继承这两个字晃花了眼,以为继承就是解决一切问题的灵丹妙药……

展开阅读全文
打赏
0
19 收藏
分享
加载中
zlog无疑提供了一种灵活而强大的模型,不同于log4j。
单就设计方面,没有对错之分。任何设计师都会在设计的“灵活性”和“适用性”上取得一个平衡。

作者一开始举的两个例子,我想为log4j说两句话,这两个例子正好是没有按照log4j的设计哲学来使用的反例。
使用log4j还是要事先设计好父子logger的职责、层次,级别等等。如果事先没有预留,就会有楼主的说的问题。
开发之前靠有经验的人设计“日志规范”是最好的。

log4j的模型提示我们:父log总是子log的超集(子log当然可以有意的屏蔽自己),因此本文中log4j的例子中父log只关心Error级别,那么后面要在产品环境中动态添加临时“分流”的特殊logger时,就无法利用log4j的子logger模式了。

当然太灵活的设计也不是没有缺点,如果不给开发人员一些“镣铐”,人们总是在开发中倾向于“事后设计”或者“事后补救”,而忽视事前设计,这在大型的长周期的项目中总是有害的。
2017/08/10 10:53
回复
举报
难易博主

引用来自“lxbzmy”的评论

log4j当初是从程序层面解决程序调整的困难。
syslog的设计是从维护层面解决维护难题。
角度不同设计不同。
你没看懂我的设计。
2014/08/28 11:42
回复
举报
log4j当初是从程序层面解决程序调整的困难。
syslog的设计是从维护层面解决维护难题。
角度不同设计不同。
2014/08/28 10:44
回复
举报
楼主是个勤思考的人
2012/10/19 20:54
回复
举报
难易博主

引用来自“一千年前的人”的评论

既然已经到这里, 我就多问一个:zlog实现这个怎么样,可以动态加载规则吗:
在程序运行中动态打开一部分(特定类的)debug日志以跟踪生产环境的问题..

可以在配置文件内修改日志级别,
自动(每写多少条日志) 或者手动 (zlog_reload)重载配置文件
详见
http://hardysimpson.github.com/zlog/UsersGuide-CN.html#htoc15
http://hardysimpson.github.com/zlog/UsersGuide-CN.html#htoc29
2012/10/19 20:49
回复
举报
难易博主

引用来自“一千年前的人”的评论

引用来自“宏哥”的评论

引用来自“一千年前的人”的评论

引用来自“宏哥”的评论

引用来自“一千年前的人”的评论

非常感谢楼主的思路。 我想请教LZ的是:
1、出于什么需求, 需要划分出多个appender: aa.bb, aa.cc, aa.dd .. 通常都是一个appender就好了。
2、在log4j中,同一句日志可以被输入到多个appender中。 在zlog中也是这样的吗,还是一句日志只能单选一个appender?

楼主说的很正确, 其实日志根本就是直接打印到err的文件描述字即可
如果需要,可以通过外部重定向, 直接进行分割, 如果需要分离, 每行加前缀即可
参考宏哥的一个打印宏, 总共只有几行代码
哪里需要搞如此复杂, 吃饱的

恩, 我想想借鉴一下...
现在我们要在程序运行中打开一部分(特定类的)debug日志以跟踪生产环境的问题..

#define D_PRINTF(log_level,arg...){\
if((g_debug_level|LOG_FATAL )& log_level){\
fprintf(stderr,arg); \
}\
}
一行代码, show太多次,我都不好意思了, 在软件当中可以通过定向到另外一个软件来处理, 可以分割...
这个早在几十年前, DJB时代就是这样, multilog reference
http://cr.yp.to/daemontools.html
不得不说, java那套东西太落后了.

恩 我看到宏哥说过几次了。 这次又耐心的给我说了一次。。
哎。 贼船好上不好下... 回头我用java试验一下宏哥这个log思路..

他这条语句,在简单小的命令行程序下是好的。
但如果涉及到多进程的服务端程序,需要日志动态的多输出、改变格式、重定向到别的机器,就需要库。例如syslog就是一个例子。
2012/10/19 20:46
回复
举报
既然已经到这里, 我就多问一个:zlog实现这个怎么样,可以动态加载规则吗:
在程序运行中动态打开一部分(特定类的)debug日志以跟踪生产环境的问题..
2012/10/19 20:46
回复
举报

引用来自“一千年前的人”的评论

引用来自“宏哥”的评论

引用来自“一千年前的人”的评论

引用来自“宏哥”的评论

引用来自“一千年前的人”的评论

非常感谢楼主的思路。 我想请教LZ的是:
1、出于什么需求, 需要划分出多个appender: aa.bb, aa.cc, aa.dd .. 通常都是一个appender就好了。
2、在log4j中,同一句日志可以被输入到多个appender中。 在zlog中也是这样的吗,还是一句日志只能单选一个appender?

楼主说的很正确, 其实日志根本就是直接打印到err的文件描述字即可
如果需要,可以通过外部重定向, 直接进行分割, 如果需要分离, 每行加前缀即可
参考宏哥的一个打印宏, 总共只有几行代码
哪里需要搞如此复杂, 吃饱的

恩, 我想想借鉴一下...
现在我们要在程序运行中打开一部分(特定类的)debug日志以跟踪生产环境的问题..

#define D_PRINTF(log_level,arg...){\
if((g_debug_level|LOG_FATAL )& log_level){\
fprintf(stderr,arg); \
}\
}
一行代码, show太多次,我都不好意思了, 在软件当中可以通过定向到另外一个软件来处理, 可以分割...
这个早在几十年前, DJB时代就是这样, multilog reference
http://cr.yp.to/daemontools.html
不得不说, java那套东西太落后了.

恩 我看到宏哥说过几次了。 这次又耐心的给我说了一次。。
哎。 贼船好上不好下... 回头我用java试验一下宏哥这个log思路..

http://www.oschina.net/question/96003_75441

还是以案例, 直接给出答案
2012/10/19 20:45
回复
举报

引用来自“宏哥”的评论

引用来自“一千年前的人”的评论

引用来自“宏哥”的评论

引用来自“一千年前的人”的评论

非常感谢楼主的思路。 我想请教LZ的是:
1、出于什么需求, 需要划分出多个appender: aa.bb, aa.cc, aa.dd .. 通常都是一个appender就好了。
2、在log4j中,同一句日志可以被输入到多个appender中。 在zlog中也是这样的吗,还是一句日志只能单选一个appender?

楼主说的很正确, 其实日志根本就是直接打印到err的文件描述字即可
如果需要,可以通过外部重定向, 直接进行分割, 如果需要分离, 每行加前缀即可
参考宏哥的一个打印宏, 总共只有几行代码
哪里需要搞如此复杂, 吃饱的

恩, 我想想借鉴一下...
现在我们要在程序运行中打开一部分(特定类的)debug日志以跟踪生产环境的问题..

#define D_PRINTF(log_level,arg...){\
if((g_debug_level|LOG_FATAL )& log_level){\
fprintf(stderr,arg); \
}\
}
一行代码, show太多次,我都不好意思了, 在软件当中可以通过定向到另外一个软件来处理, 可以分割...
这个早在几十年前, DJB时代就是这样, multilog reference
http://cr.yp.to/daemontools.html
不得不说, java那套东西太落后了.

恩 我看到宏哥说过几次了。 这次又耐心的给我说了一次。。
哎。 贼船好上不好下... 回头我用java试验一下宏哥这个log思路..
2012/10/19 20:41
回复
举报
难易博主

引用来自“一千年前的人”的评论

引用来自“难易”的评论

引用来自“一千年前的人”的评论

非常感谢楼主的思路。 我想请教LZ的是:
1、出于什么需求, 需要划分出多个appender: aa.bb, aa.cc, aa.dd .. 通常都是一个appender就好了。
2、在log4j中,同一句日志可以被输入到多个appender中。 在zlog中也是这样的吗,还是一句日志只能单选一个appender?

不是分appender,而是分类,层级的分类。
假设你的程序有5个模块,分别有自己的详细日志,但同时你希望把这五个模块的错误日志放到一个文件里面,便于查错,看到错误,在去相应的详细日志去找原因。这样一个模块的不同级别的日志就需要放到不同的日志文件内。
zlog是多输出的,可以配多条规则来匹配一个分类,达到多输出的效果。

哈哈, 谢谢。 我明白了。 zlog和log4j相比最大的特点就是: 二者都有层级,但是zlog没有继承。

没错,继承是复杂的。。事实上面向对象越少用继承越好。。。
2012/10/19 20:40
回复
举报
更多评论
打赏
21 评论
19 收藏
0
分享
返回顶部
顶部