什么是SeasLog
SeasLog是一个C语言编写的PHP扩展,提供一组规范标准的功能函数,在PHP项目中方便、规范、高效地写日志,以及快速地读取和查询日志。
为什么使用SeasLog
无论在什么应用中,log日志都是架构中不可缺少的一个重要组成部分,它通常是系统或软件、应用的运行记录。通过log的分析,可以方便用户了解系统或软件、应用的运行情况;如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。
我们知道,php内置了很多log函数,如error_log、syslog、file_put_content,这些函数功能强大且性能极好,但由于各种缺陷(error_log、file_put_content无错误级别、无固定格式像是信马由缰随处乱画;syslog不区分模块、而且与系统日志混合,读syslog记录会让人抓狂的。),灵活度降低了很多,很不能满足应用需求。基本上所有的开发者,都会自行设计封装log库,当然也会有很多开发者选择已有的开源日志库。
也有很多开源log类库弥补了上述缺陷,如log4php、plog、Analog等(当然也有很多应用在项目中自己开发的log类)。其中以log4php最为著名,它的设计精良、格式***、文档完善、功能强大。但是经过测试,log4php的性能非常差。
这是log4php与SeasLog的性能对比图:
设计思路
那么有没有一种log类库同时满足以下需求呢:
●分模块、分级别
●配置简单(***是勿须配置)
●日志格式清晰易读
●应用简单、性能很棒
我们知道,PSR-3标准是一个国际化的日志标准,它要求了“模块、级别、清晰、易用”等日志工作应具备的特点。因此,只要我们遵循了PSR-3标准,则我们可以完成“分模块、分级别”以及“日志格式清晰易读”的要求。
“配置简单”这个需求也很好完成。如果严格按照既定规则,其实只需要设置默认目录就可以了。
OK,现在我们只剩下“性能”这一个要求。
既然是日志,免不了会写文件,或者通过pipe通过网络传送到某个存储中心(我们暂不考虑存储中心的设计)。可以想见,假设一个请求中需要写出1000处log,那么势必会有1000次IO,这对性能将是一个很大的拖延点。一般对于处理这种多次相同的请求场景,我们要解决的其实也很简单,使用cache或buffer,把多次请求作归并,从而降低对磁盘或网络的IO,这是一个基本的思想。
SeasLog也是这么做的。设定一个buffer_size(默认100条log),使用PHP请求内存,每写一次log,塞入内存,同时buffer_size加;当buffer_size等于设置值时,则进行一次IO,同时清除buffer; 当然,如果请求结束了、或执行了die、exit或其他异常退出时,不管buffer_size有没有攒够设置值,立刻进行一次IO,同时清除buffer。
到目前为止,SeasLog的正式版本为1.1.6,采用Apache 2.0开源协议,同时可以在php.net官方,和Github库上获得它的完整代码。
Php.net http://pecl.php.net/package/seaslog
Github https://github.com/Neeke/SeasLog
目前提供了什么
●在PHP项目中便捷、规范地记录log
●可配置的默认log目录与模块
●指定log目录与获取当前配置
●初步的分析预警框架
●高效的日志缓冲、便捷的缓冲debug
●遵循 PSR-3 日志接口规范
怎么安装
获得源码后,可自行编译。
$ /path/to/phpize
$ ./configure --with-php-config=/path/to/php-config
$ make && make install
当然,使用PECL管理工具会更方便:
$ pecl install seaslog
seaslog.ini的配置
复制
; configuration for php SeasLog module extension = seaslog.so seaslog.default_basepath = /log/seaslog-test ;默认log根目录 seaslog.default_logger = default ;默认logger目录 seaslog.disting_type = 1 ;是否以type分文件 1是 0否(默认) seaslog.disting_by_hour = 1 ;是否每小时划分一个文件 1是 0否(默认) seaslog.use_buffer = 1 ;是否启用buffer 1是 0否(默认) seaslog.buffer_size = 100 ;buffer中缓冲数量 默认0(不使用buffer_size) seaslog.level = 0 ;记录日志级别 默认0(所有日志)
1.
2.
3.
4.
5.
6.
7.
8.
9.
seaslog.disting_type = 1 开启以type分文件,即log文件区分info\warn\erro
seaslog.disting_by_hour = 1 开启每小时划分一个文件
seaslog.use_buffer = 1 开启buffer。默认关闭。当开启此项时,日志预存于内存,当请求结束时(或异常退出时)一次写入文件。
seaslog.buffer_size = 100 设置缓冲数量为100. 默认为0,即无缓冲数量限制.当buffer_size大于0时,缓冲量达到该值则写一次文件.
seaslog.level = 3 记录的日志级别.默认为0,即所有日志均记录。当level为1时,关注debug以上级别(包括debug),以此类推。level大于8时,所有日志均不记录。#p#
默认常量有哪些
遵循PSR-3标准,SeasLog 共将日志分成8个级别
●SEASLOG_DEBUG "debug"
●SEASLOG_INFO "info"
●SEASLOG_NOTICE "notice"
●SEASLOG_WARNING "warning"
●SEASLOG_ERROR "error"
●SEASLOG_CRITICAL "critical"
●SEASLOG_ALERT "alert"
●SEASLOG_EMERGENCY "emergency"
都提供哪些方法
SeasLog 提供了这样一组函数,可以方便地获取与设置根目录、模块目录、快速写入与统计log。 相信从下述伪代码的注释中,您可以快速获取函数信息(有经验的coder,也可将下述代码保存成SeasLog.php导入你的IDE,code tips,你懂的),具体使用将紧接其后:
复制
<?php /** * @author neeke@php.net 云智慧 */ class SeasLog { public function __construct() { #SeasLog init } public function __destruct() { #SeasLog distroy } /** * 设置basePath * @param $basePath * @return bool */ static public function setBasePath($basePath) { return TRUE; } /** * 获取basePath * @return string */ static public function getBasePath() { return 'the base_path'; } /** * 设置模块目录 * @param $module * @return bool */ static public function setLogger($module) { return TRUE; } /** * 获取***一次设置的模块目录 * @return string */ static public function getLastLogger() { return 'the lastLogger'; } /** * 统计所有类型(或单个类型)行数 * @param string $level * @param string $log_path * @param null $key_word * @return array | long */ static public function analyzerCount($level = 'all',$log_path = '*',$key_word = NULL) { return array(); } /** * 以数组形式,快速取出某类型log的各行详情 * @param $level * @param string $log_path * @param null $key_word * @param int $start * @param int $limit * @return array */ static public function analyzerDetail($level = SEASLOG_INFO,$log_path = '*',$key_word = NULL, $start = 1,$limit = 20) { return array(); } /** * 获得当前日志buffer中的内容 * @return array */ static public function getBuffer() { return array(); } /** * 记录debug日志 * @param $message * @param array $content * @param string $module */ static public function debug($message,array $content = array(),$module = '') { #$level = SEASLOG_DEBUG } /** * 记录info日志 * @param $message * @param array $content * @param string $module */ static public function info($message,array $content = array(),$module = '') { #$level = SEASLOG_INFO } /** * 记录notice日志 * @param $message * @param array $content * @param string $module */ static public function notice($message,array $content = array(),$module = '') { #$level = SEASLOG_NOTICE } /** * 记录warning日志 * @param $message * @param array $content * @param string $module */ static public function warning($message,array $content = array(),$module = '') { #$level = SEASLOG_WARNING } /** * 记录error日志 * @param $message * @param array $content * @param string $module */ static public function error($message,array $content = array(),$module = '') { #$level = SEASLOG_ERROR } /** * 记录critical日志 * @param $message * @param array $content * @param string $module */ static public function critical($message,array $content = array(),$module = '') { #$level = SEASLOG_CRITICAL } /** * 记录alert日志 * @param $message * @param array $content * @param string $module */ static public function alert($message,array $content = array(),$module = '') { #$level = SEASLOG_ALERT } /** * 记录emergency日志 * @param $message * @param array $content * @param string $module */ static public function emergency($message,array $content = array(),$module = '') { #$level = SEASLOG_EMERGENCY } /** * 通用日志方法 * @param $level * @param $message * @param array $content * @param string $module */ static public function log($level,$message,array $content = array(),$module = '') { } }
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.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
#p#
在项目中如何使用
获取与设置basePath
复制
$basePath_1 = SeasLog::getBasePath(); SeasLog::setBasePath('/log/base_test'); $basePath_2 = SeasLog::getBasePath(); var_dump($basePath_1,$basePath_2); /* string(19) "/log/seaslog-ciogao" string(14) "/log/base_test" */
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
直接使用 SeasLog::getBasePath(),将获取php.ini(seaslog.ini)中设置的seaslog.default_basepath 的值。
使用 SeasLog::getBasePath() 函数,将改变 seaslog_get_basepath() 的取值。
设置logger与获取lastLogger
复制
$lastLogger_1 = SeasLog::getLastLogger(); SeasLog::setLogger('testModule/app1'); $lastLogger_2 = SeasLog::getLastLogger(); var_dump($lastLogger_1,$lastLogger_2); /* string(7) "default" string(15) "testModule/app1" */
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
与basePath相类似的,
直接使用 SeasLog::getLastLogger(),将获取php.ini(seaslog.ini)中设置的seaslog.default_logger 的值。
使用 SeasLog::setLogger() 函数,将改变 SeasLog::getLastLogger() 的取值。
快速写入log
上面已经设置过了basePath与logger,于是log记录的目录已经产生了,
log记录目录 = basePath / logger / {fileName}.log log文件名,以 年月日 分文件,如今天是2014年02月18日期,那么 {fileName} = 20140218;
还记得 php.ini 中设置的 seaslog.disting_type 吗?
默认的 seaslog.disting_type = 0,如果今天我使用了 SeasLog ,那么将产生最终的log文件:
复制
●LogFile = basePath / logger / 20140218.log
1.
如果 seaslog.disting_type = 1,则最终的log文件将是这样的三个文件
复制
●infoLogFile = basePath / logger / INFO.20140218.log ●warnLogFile = basePath / logger / WARN.20140218.log ●erroLogFile = basePath / logger / ERRO.20140218.log
1.
2.
3.
于是可以这么快速地记录log:
复制
SeasLog::debug('this is a {userName} debug',array('{userName}' => 'neeke')); SeasLog::info('this is a info log'); SeasLog::notice('this is a notice log'); SeasLog::warning('your {website} was down,please {action} it ASAP!',array('{website}' => 'github.com','{action}' => 'rboot')); SeasLog::error('a error log'); SeasLog::critical('some thing was critical'); SeasLog::alert('yes this is a {messageName}',array('{messageName}' => 'alertMSG')); SeasLog::emergency('Just now, the house next door was completely burnt out! {note}',array('{note}' => 'it`s a joke'));
1.
2.
3.
4.
5.
6.
7.
8.
既然已经作为PHP的扩展存在,那么,此时可以在任何使用PHP的地方,方便地使用SeasLog来记录你的log了。
这里有一个小技巧,在一个框架controller构造方法中,或在一个Service的构造方法中,使用SeasLog::setLogger() 方法,则可以将下文中所有的log记录在同一个模块中。同时,由于SeasLog记录的log中,默认记录了进程pid,所以可以有效地通过pid,将上下文相关的日志非常快速地联系起来。
如下:
复制
debug | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | this is a neeke debug info | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | this is a info log notice | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | this is a notice log warning | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | your github.com was down,please rboot it ASAP! error | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | a error log critical | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | some thing was critical alert | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | yes this is a alertMSG emergency | 6886 | 1422894818.129 | 2015:02:03 00:33:38 | Just now, the house next door was completely burnt out! it`s a joke debug | 6888 | 1422894820.234 | 2015:02:03 00:33:40 | this is a neeke debug info | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | this is a info log notice | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | this is a notice log warning | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | your github.com was down,please rboot it ASAP! error | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | a error log critical | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | some thing was critical alert | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | yes this is a alertMSG emergency | 6888 | 1422894820.235 | 2015:02:03 00:33:40 | Just now, the house next door was completely burnt out! it`s a joke
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
使用SeasLog进行健康预警
SeasLog同时也提供了一个简单的预警demo,可以通过简单的配置,完成及时发现问题并实时发送预警邮件的功能。
预警的配置
复制
[base] wait_analyz_log_path = /log/base_test [fork] ;是否开启多线程 1开启 0关闭 fork_open = 1 ;线程个数 fork_count = 3 [warning] email[smtp_host] = smtp.163.com email[smtp_port] = 25 email[subject_pre] = 预警邮件 - email[smtp_user] = seaslogdemo@163.com email[smtp_pwd] = seaslog#demo email[mail_from] = seaslogdemo@163.com email[mail_to] = gaochitao@weiboyi.com email[mail_cc] = ciogao@gmail.com email[mail_bcc] = [analyz] ; enum ; SEASLOG_DEBUG "debug" ; SEASLOG_INFO "info" ; SEASLOG_NOTICE "notice" ; SEASLOG_WARNING "warning" ; SEASLOG_ERROR "error" ; SEASLOG_CRITICAL "critical" ; SEASLOG_ALERT "alert" ; SEASLOG_EMERGENCY "emergency" test1[module] = test/bb test1[level] = SEASLOG_ERROR test1[bar] = 1 test1[mail_to] = gaochitao@weiboyi.com test2[module] = 222 test2[level] = SEASLOG_WARNING test3[module] = 333 test3[level] = SEASLOG_CRITICAL test4[module] = 444 test4[level] = SEASLOG_EMERGENCY test5[module] = 555 test5[level] = SEASLOG_DEBUG
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.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
crontab配置
复制
;每天凌晨3点执行 0 3 * * * /path/to/php /path/to/SeasLog/Analyzer/SeasLogAnalyzer.php
1.
2.