日志的重要程度不言而喻, 不管是在开发过程中, 还是部署到生产环境后, 都是经常使用的.
随着 psr-3
的出现, 终于统一了 php
中日志的风格.
但是, 好用的记录日志系统, 也很重要. monolog
是我遇到的最好的日志系统.
而且, laravel
中也是用的 monolog
.
phpfudao 这可能是php世界中最好的日志库——monolog
通过 composer
或者 github
.
composer require monolog/monolog
Every Logger instance has a channel (name) and a stack of handlers. Whenever you add a record to the logger, it traverses the handler stack. Each handler decides whether it fully handled the record, and if so, the propagation of the record ends there.This allows for flexible logging setups, for example having a StreamHandler at the bottom of the stack that will log anything to disk, and on top of that add a MailHandler that will send emails only when an error message is logged. Handlers also have a $bubble property which defines whether they block the record or not if they handled it. In this example, setting the MailHandler's $bubble argument to false means that records handled by the MailHandler will not propagate to the StreamHandler anymore.
You can create many Loggers, each defining a channel (e.g.: db, request, router, ..) and each of them combining various handlers, which can be shared or not. The channel is reflected in the logs and allows you to easily see or filter records.
Each Handler also has a Formatter, a default one with settings that make sense will be created if you don't set one. The formatters normalize and format incoming records so that they can be used by the handlers to output useful information.
Custom severity levels are not available. Only the eight RFC 5424 levels (debug, info, notice, warning, error, critical, alert, emergency) are present for basic filtering purposes, but for sorting and other use cases that would require flexibility, you should add Processors to the Logger that can add extra information (tags, user ip, ..) to the records before they are handled.
每一个日志服务实例 (Logger) 都有一个通道(名称),并有一个处理器 (Handler)栈. 无论何时你添加一条 记录 到对应的日志服务实例,这个处理器栈将被遍历一遍:每个处理器都将依次决定是否要处理这条记录,而如果要处理,则遍历结束(译注:类似DOM事件冒泡)。
这样子可以创建非常灵活的日志配置。比如一个 StreamHandler 可以把所有日志都写入磁盘,而上面加个MailHandler 可以把错误日志作为邮件发送出去。处理器还有一个 $bubble 属性定义了是否屏蔽某条记录或者处理了某条记录。在这个示例中,配置 MailHandler 的 $bubble 参数为 false 则意味着 MailHandler 将不会把自己已处理过的记录继续冒泡给 StreamHandler.
你可以创建许多日志服务实例(Logger),每一个则定义一个通道(比如数据库、请求、路由...)。而每一个日志服务实例都可以组合各种各样的处理器,可以共享处理器也可以不共享。这个通道将会在日志中反映出来,从而允许你可以很容易地查看或者筛选记录。
每一个处理还会有一个格式化器(Formatter)。如果你没有配置一个,则一个有意义的默认的格式化器将被创建。格式化器用来规范化并格式化输入的记录,以便处理器能输出一些有用的信息。
不支持自定义的严重性级别。只支持使用RFC 5424中定义的八个级别(调试/Debug、信息/Info、提示/Notice、警告/Warning、错误/Error、严重/Critical、警报/Alert、紧急/Emergency)来作为基本的筛选目的。不过,如果为了排序或者其他需要灵活性的使用场景,你可以添加加工程序(Processor)从而可以在(处理器)处理前添加额外的信息(标签、用户IP...)。
using the logging context
The first way is the context, allowing to pass an array of data along the record:
<?php $logger->info('Adding a new user', array('username' => 'Seldaek'));
Simple handlers (like the StreamHandler for instance) will simply format the array to a string but richer handlers can take advantage of the context (FirePHP is able to display arrays in pretty way for instance).
using processors
The second way is to add extra data for all records by using a processor. Processors can be any callable. They will get the record as parameter and must return it after having eventually changed the extra part of it. Let's write a processor adding some dummy data in the record:
<?php $logger->pushProcessor(function ($record) { $record['extra']['dummy'] = 'Hello world!'; return $record; });
Monolog provides some built-in processors that can be used in your project. Look at the dedicated chapter for the list.
Tip: processors can also be registered on a specific handler instead of the logger to apply only for this handler.
第一种方式是使用上下文(context),这允许你在传递记录的时候传递一个数组格式的数据:
简单的处理器(比如StreamHandler)将只是把数组转换成字符串。而复杂的处理器则可以利用上下文的优点(如 FirePHP 则将以一种优美的方式显示数组)。
第二种方式是使用加工程序来为所有的记录添加额外数据。加工程序可以是任何可以调用的函数。
加工程序接收日志记录作为参数,并且需要在修改了extra字段后再返回日志记录。
Monolog提供了一些内置的加工程序,你可以在你的项目中使用它们.
小技巧:加工程序可以被注册到一个特定的处理器上而不是直接在日志服务实例上,从而可以只在对应的处理器上生效.
存放 handler
的数据结构是一个“栈”,一个日志实例可以有多个 handler
,通过 Logger
实例的 pushHandler
方法压入一个 handler
,该方法接受一个 HandlerInterface
类型的参数.
如果你设置了多个 handler
,当你新增一条日志的时候,他会从栈顶开始往下传播,关心这个级别日志的 handler
将会处理这条日志.
所有的 handler
都会继承 AbstractProcessingHandler
这个抽象类,并且只需要实现里面的抽象方法 write
就可以了.
同时这个抽象类会继承 AbstractHandler
这个抽象类,这个抽象类的构造函数有两个参数:level
和 bubble
.
level
:表示该 handler
关心的最低日志级别,是个整型.
bubble
:表示日志被当前 handler
处理后是否接着向下传递.
每个 handler
可以单独设置记录的日志格式,AbstractHandler
抽象类中有一个 setFormatter
方法,该参数接受一个 FormatterInterface
类型的参数.
可以看到 monolog
自带的 formatter
都继承自 ormalizerFormatter
,该类实现了 format和formatBatch
方法。
存放 processor
的结构也是一个“栈”,意味着你也可以通过 pushProcessor
方法给一个 Logger
实例配置多个 processor
。
我们注意到,这里 pushProcesso
接受一个 callable
,也就是需要一个函数或者类方法,但是官方自带的这些 processor
都是类,
随便点进去一个源码就会发现,其实这些类都用到了 __invoke
魔术方法,所以在被当做 callable
调用的时候会自动调用 __invoke
.
代码在我的test
仓库中可以下载.
<?php
/**
* monolog package usage
* User: qiuyu
* Date: 2018/2/14
* Time: 下午1:44
*/
require dirname(__FILE__)."/../vendor/autoload.php";
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Processor\UidProcessor;
use Monolog\Processor\ProcessIdProcessor;
use Monolog\Formatter\JsonFormatter;
// 实例化一个日志实例, 参数是 channel name
$logger = new Logger('qiuyuhome');
// StreamHandler_1
$streamHander1 = new StreamHandler(__DIR__.'/testLog1.log', Logger::INFO);
// 设置日志格式为json
$streamHander1->setFormatter(new JsonFormatter());
// 入栈, 往 handler stack 里压入 StreamHandler 的实例
$logger->pushHandler($streamHander1);
// StreamHandler_2
// 如果第三个参数为false, 则只会执行这个一个Handler. 默认是true
$streamHander2 = new StreamHandler(__DIR__.'/testLog2.log', Logger::INFO);
// 入栈, 往 handler stack 里压入 StreamHandler 的实例
$logger->pushHandler($streamHander2);
/**
* processor 日志加工程序,用来给日志添加额外信息.
*
* 这里调用了内置的 UidProcessor 类和 ProcessIdProcessor 类.
* 在生成的日志文件中, 会在最后面显示这些额外信息.
*/
$logger->pushProcessor(new UidProcessor());
$logger->pushProcessor(new ProcessIdProcessor());
$logger->pushProcessor(function ($record) {
$record['message'] = 'Hello ' . $record['message'];
return $record;
});
/**
* 设置记录到日志的信息.
*
* 开始遍历 handler stack.
* 先入后出, 后压入的最先执行. 所以先执行 FirePHPHandler, 再执行 StreamHandler
* 如果设置了 ErrorLogHandler 的 $bubble = false, 会停止冒泡, StreamHandler 不会执行.
* 第二个参数为数组格式, 通过使用使用上下文(context)添加了额外的数据.
* 简单的处理器(比如StreamHandler)将只是把数组转换成字符串。而复杂的处理器则可以利用上下文的优点(如 FirePHP 则将以一种优美的方式显示数组).
*/
$logger->info('Welcome to QiuYu Blog.', ['username' => 'QiuYu']);
运行文件的同目录下, 会生成 testLog1.log
和 testLog2.log
日志文件. 内容为:
testLog1.log
{"message":"Hello Welcome to QiuYu Blog.","context":{"username":"QiuYu"},"level":200,"level_name":"INFO","channel":"qiuyuhome","datetime":{"date":"2018-02-14 23:33:28.928112","timezone_type":3,"timezone":"Asia/Shanghai"},"extra":{"process_id":21520,"uid":"54a8a7d"}}
testLog2.log
[2018-02-14 23:33:28] qiuyuhome.INFO: Hello Welcome to QiuYu Blog. {"username":"QiuYu"} {"process_id":21520,"uid":"54a8a7d"}
当才华还支持不起理想时,就应该静下心来好好学习了。
Original url: Access
Created at: 2018-12-17 17:29:22
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论