seaslog的文档非常详细: [中文文档](https://github.com/SeasX/SeasLog/blob/master/README_zh.md) 这里只提几个注意点: ## 千分比采样性能追踪 ~~~ ;是否开启性能追踪 1开启 0关闭(默认) seaslog.trace_performance = 0 ;性能追踪时的千分比采样率 ;默认10,即百分之一 **测试的时候可以设置为1000** seaslog.trace_performance_sample_rate = 10 ;性能追踪时的开始层级 默认从第1层开始 seaslog.trace_performance_start_depth = 1 ;性能追踪时深度层级 默认5层, **如果使用框架类可以调高一点,我设的是20** seaslog.trace_performance_max_depth = 5 ;性能追踪时每层的函数最大数 按wall_time降序排列top 默认top5 seaslog.trace_performance_max_functions_per_depth = 5 ;性能追踪时当前请求执行时间的记录阈值 只有当请求执行时间大于该值时,才记录性能日志 默认1000ms seaslog.trace_performance_min_wall_time = 1000 ;性能追踪时每个方法执行时间的记录阈值 只有当方法执行时间大于该值时,才参与计算 默认10ms seaslog.trace_performance_min_function_wall_time = 10 ~~~ 1. 在某一个**请求超过了1秒**,就在每100次里面就会记录1次这1秒的性能追踪。在测试时可以把 **seaslog.trace_performance_sample_rate**设为1000,让触发率达百分之百; 2. 如果使用框架开发的话,由于加载的层级过多,所以建议设置 **seaslog.trace_performance_max_depth = 20** **追踪内容如下:** ``` { ... "16":[ { "cm":"GuzzleHttp\Ring\Client\CurlHandler::_invokeAsArray", "ct":1, "wt":1767, "mu":87912 }, { // 类名::方法名 "cm":"Symfony\Component\VarDumper\Cloner\Data::dumpChildren", // call_times 方法调用计数 "ct":105, // wall_time, 见下文 "wt":149, // 使用内存:memory_usage byte "mu":45064 } ], "main()":{ // 总的wall_time和memory_usage "wt":1844, "mu":4519032 } } ``` **理解wall_time** wall time也可以称为real-world time或wall-block time,指的是用计时器(如手表或挂钟)度量的实际时间。实际时间和用计算微处理器的脉冲时钟或时钟周期不同,实际时间的一秒钟包含的微处理器时钟周期数决定于微处理器的时钟速度。微处理器时钟不是计时器,而只是一个以高的、精确的并且不变的频率输出脉冲的发生器。一个2 GHz计算机的微处理器时钟要比1 GHz的快一倍。所谓根据日期、小时、分钟和秒来按序显示时间的计算机显示的是实际时间,而不是微处理器周期时间。??在处理问题时,一个程序运行或执行任务所花的时间通常是以秒度量的实际时间。当计算机支持多任务时,每个程序的实际执行时间决定于处理器是如何在各个程序之间分配资源的。例如,如果在一个连续的60秒时间段内,计算机要运行三个程序,这时就可能一个程序占用10秒,第二个占用20秒,而第三个占用30秒,但各个程序占用的时间并不是连续的时间块,而是以循环机制分配的,就像是在通信中的时分复用一样。对于一个计算机用户来说,计算机处理一个具体的任务花的总时间是以实际时间来度量来度量的,这个时间不一定是连续的,因为计算机在一个时间段内可能要完成多个任务。 > 由于在一个生命周期里面,PHP是单线程的。轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。我的理解就是在循环调用中,执行时间加上等待时间就是wall_time ## 回溯层级的配置 一般我们都要封装一下再使用,比如下面的二次封装 ``` Log::err($arg1,$arg2,$arg3,....); ``` 但这样就会让日志模板中的某些变量失去意义 <br /> ![](https://box.kancloud.cn/7af8e70b251f1f29f9ddd0b1e628bff2_1206x170.png) ![](https://box.kancloud.cn/401d3267467f250f3df9f590a385a78a_1169x206.png) <br /> 这种情况下,seasLog给我们提供了一个回溯层级的配置 `seaslog.recall_depth` 日志函数所在的层级。这将影响预置变量中的行号取值 `%F`。 默认值为 0。 比如我把日志封装到`libs/Log`的类里面,然后在业务代码中调用`Log::$level()`。如果不设置回溯层级的话,`%F`和`%C`只能记录到`libs/Log`的文件。但设置以后 <br /> ![](https://box.kancloud.cn/19e8e58960556ed16a45d66acaf7b181_1149x229.png) ## 日志报警(TP5举例) 我们一般都是基于框架开发,而框架都有类似“行为,标签,钩子,事件”这类AOP切面编程机制。在TP中的处理方案就是: 1. 增加钩子 2. 监听钩子 3. 特定type的日志扔进队列 4. 异步发邮件(短信,钉钉)报警 **二次封装** ~~~ <?php namespace libs; use think\facade\Log as TPlog; /** * 自定义日志适配器,多参数记录 * Class Log * @method void emergency(mixed $message, array $context = []) static 记录emergency信息 * @method void alert(mixed $message, array $context = []) static 记录alert信息 * @method void critical(mixed $message, array $context = []) static 记录critical信息 * @method void error(mixed $message, array $context = []) static 记录error信息,会造成程序运行,业务逻辑错误,脏数据等,需要及时处理和解决; * @method void warning(mixed $message, array $context = []) static 记录warning信息,程序能正常运行,业务逻辑没错误,但需要及时解决; * @method void notice(mixed $message, array $context = []) static 记录notice信息,程序能正常运行,业务逻辑没错误但不美; * @method void info(mixed $message, array $context = []) static 需要关注的。像用户登录,交易等入参; * @method void debug(mixed $message, array $context = []) static 记录debug信息 * @method void sql(mixed $message, array $context = []) static 记录sql信息 * @package libs * Log::info('日志title',__METHOD__,__LINE__,'haha',''''); */ class Log { /** * 静态调用 * @param $method * @param $args * @return mixed */ public static function __callStatic($method, $args = []) { // 以高性能日志收集组件seaslog为最优先; if(extension_loaded('SeasLog')){ return self::seasLog($method, $args); } return TPlog::$method($args); } private static function seasLog($method, $args){ $config = config('log.'); if (empty($config['path'])) { $config['path'] = app()->getRuntimePath() . 'log' . DIRECTORY_SEPARATOR; } \SeasLog::setBasePath($config['path']); \SeasLog::setLogger('seasLog'); if($config['append_info'] || $method == 'error'){ $args = [ '_sys' => self::getSysLog(), '_msg' => $args ]; } $args = json_encode($args, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if($method == 'error'){ // todo: 写入队列,异步发邮件报警 } \SeasLog::log($method,$args); // 如果为true的话,马上写入; config('app.app_debug') && \SeasLog::flushBuffer(); } /** * 追加调试日志 * @return array */ private static function getSysLog() { $request = app('request'); $runtime = round(microtime(true) - app()->getBeginTime(), 10); $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞'; $memory_use = number_format((memory_get_usage() - app()->getBeginMem()) / 1024, 2); $info = [ 'method' => $request->method(), 'uri' => $request->url(), 'ip' => $request->ip(), 'c/a' => $request->controller() . '/' . $request->action(), 'runtime' => number_format($runtime, 6) . 's', 'reqs' => $reqs . 'req/s', 'memory' => $memory_use . 'kb', 'file' => count(get_included_files()), ]; return $info; } } ~~~ **钩子文件** ~~~php <?php namespace app\common\behavior; class LogAlarm { #todo: 后续要写进rabbitMq public function run($params) { // 发送邮件通知 list($type , $message) = $params; if ('error' == $type) { #todo: 这个地方要异步发送 // mail('admin@mail.com' , '日志报警' , implode(' ' , $message)); } } } ~~~