聊聊自己从开始工作到如今,对日志的认识。
在离校实习前,也多多少少做过一些自个玩的项目,后台管理系统、公众号都有整过,对所谓的日志没有什么概念,需要做什么记录都是操作数据库的自定义log表。
开始实习后,慢慢有接触到一些所谓日志的概念,最先接触的是ThinkPHP3.1的框架,不过有使用到的都是自定义打印日志,对想要记录的数组写入到自定义日志文件里。比如每次想要打印数据,调用一个公共函数,依赖系统函数fopen、fwrite、fclose,日志文件名以日期加调用方法名定义。
随着一天天的使用,到了某一日,也许是觉得这样的日志都集中在一个目录下,数量太过庞大;也许是觉得一个日志文件写入内容太多,如果是通过浏览器访问日志文件要等待特别久时间,甚至会卡死;也许是发现业务日志即调用即写入的方法,在并发量不低的情况下,导致不同用户的日志内容混杂,不利于排错;也许是发现了TP3.1项目Runtime内的代码执行LOG随时间、又随日志大小切割很好用;也许是看了TP3.1源码打印日志的部分,发现日志还能这么用。
也忘了具体是什么原因了,也许是一个个原因的积累,总之决定换个日志记录方式。
看了下TP3.1源码的日志实现部分,两个部分让我感觉像是发现了新大陆,一个是register_shutdown_function函数(细节不说明了,另外的博客里有详细解释),一个是每次调用日志,都将内容存在一个数组增量内,即保存在内存,在所有业务逻辑执行完成后,register_shutdown_function函数注册的方法将被执行,在此处将日志刷新到日志文件内,同时具体输出到哪个日志文件呢?这个也有讲究,首先日志文件名也是按照年月日来命令,但是在输出前,会优先判断当前日志文件的大小,若超过预定义最大值,则将当前文件重命名,加上时间戳的前缀来标识,新生成日志文件来存储日志。这种实现方式的好处在于,日志大容量被切割成多个文件,避免一个文件过大。个人认为也有不好的地方,比如所有日志文件都存储一个目录下,随着时间增长这个目录的文件数会越来越多。
日志暂存储在内存,封装后一次调用error_log函数打印。现在说来可能只是个基础的概念,不过对当时的自己来说,确实觉得神奇了好久。
想自己重新实现一种打印日志的方式,首先要满足几点要求:
1、日志能满足大小超过设置最大值后进行切割;
2、单个用户的请求日志,不会被并发影响,保证每次的排错日志相邻内容都是一个用户的;
3、可以根据不同的业务模块打印日志;
4、日志文件存储目录与日期相关,保证一个目录文件数不会太多,同时想定位到指定日期日志方便查找。
日志工具类实现源码:
<?php
class LogModel {
static $logArr = array();
static $logRootPath = '';
static $class = '';
static $method = '';
/**
* 日志工具初始化
* */
public static function init() {
//获取日志文件目录
if (empty(self::$logRootPath)) self::$logRootPath = realpath(APP_PATH.'Log') . DIRECTORY_SEPARATOR;
}
/**
* 日志内容保存
* @static
* @access public
* @param $logName 日志模块名称
* @param $content 日志内容
* @param $logTitle 日志内容头部
* @return void
* */
public static function writeLog($content, $logName = 'Common', $logFile = 'common', $logTitle = '') {
//获取日志文件目录
if (empty(self::$logRootPath)) self::$logRootPath = realpath(APP_PATH.'Log') . DIRECTORY_SEPARATOR;
if (empty(self::$logArr[$logName][$logFile])) {
self::$logArr[$logName][$logFile] = '----------------start---------------------'.PHP_EOL;
}
if (is_array($content)) {
if(version_compare(PHP_VERSION,'5.4.0','<')){
$tempStr = json_encode($content);
$content = preg_replace_callback("#\\\u([0-9a-f]{4})#i",function($matchs) {
return iconv('UCS-2BE', 'UTF-8', pack('H4', $matchs[1]));
},$tempStr);
} else $content = json_encode($content, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
if (!empty($logTitle)) $logTitle .= ':';
self::$logArr[$logName][$logFil