Monolog 发送你的日志到文件、到sockets、到邮箱、到数据库或(和)者其他网路存储服务(云)。Monolog可以做到同时保存到一个或多个存储介质(后面的栈冒泡处理)。

安装

 
$ composer require monolog/monolog

基本用法

php
use Monolog\Logger;
use Monolog\Handler\StreamHandler; // create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); // add records to the log
$log->warning('Foo');$log->error('Bar');

核心概念 ¶官方解释

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.

每一个Logger实例都有一个通道(也就是一个唯一的名称)和一个有由一个或多个处理程序组成的。当我们添加一个记录到Logger的时候,它会遍历这个处理程序栈。每一个处理程序决定是否去充分处理这个记录,如果是,则处理到此为止(停止冒泡)。这里的充分指的是我们想不想了,想的话就继续,不想就停止。

这就允许我们灵活的设置日志了。比如我们有一个StreamHandler,它在的最底部,它会把记录都保存到硬盘上,在它上面有一个MailHandler,它会在错误消息被记录的时候发送邮件。Handlers 都有一个$bubble属性,用来定义当某个处理程序在处理记录的时候是否阻塞处理(阻塞的话,就是这个记录到我这里就算处理完毕了,不要冒泡处理了,听话)。在这个例子中,我们设置MailHandler$bubblefalse,意思就是说记录都会被MailHandler处理,不会冒泡到StreamHandler了。

补充一下:这里提到了栈,也提到了冒泡,乍一看有点晕,因为我们理解冒泡是自下而上的过程,栈就是一个类似杯子的容器,然后上面又说底部是StreamHandler,上面是MailHandler,结果是MailHandler处理了,停止冒泡到StreamHandler了,给人的感觉是这个泡是从上往下冒的,666,这能叫冒泡么?英雄时刻:堆栈啥的,我也偶尔混淆,这里再次谨记,堆是先进先出(First-In/First-Out),想想[自来]水管;栈就是先进后出(First-In/Last-Out),想想一个有N层颜色的冰淇淋装在一个杯子里,下面是黄色的,…,最上面是粉红的,所以,你先吃得是粉红色的(MailHandler),后吃的是黄色的(StreamHandler),实际上,这个泡冒的没错,确切的说,这个泡冒在了一个倒立的杯子中,这样理解就形象了。

继续…

我们可以创建很多Logger,每个Logger定义一个通道(e.g.:db,request,router,…),每个通道可结合多个Handler,Handler可以被写成可通用的或者不可通用的。通道,同日志中日期时间一样,它是一个名称,在日志中就是一个字符串被记录下来,大概是这样 2016-04-25 12:33:00 通道名称 记录内容,具体格式看设置了,可以用来识别或者过滤。

每一个Handler都有一个Formatter,用来格式化日志了。不详细介绍了。

自定义日志等级在monolog中不可用,只有8种RFC 5424 等级,即 debug, info, notice, warning, error, critical, alert, emergency。但是如果我们真的有特殊需求的话,比如归类等,我们可以添加Processors到 Logger,当然是在日志消息被处理之前。我这估计这辈子都不会添加`Processors`。

日志等级

  • DEBUG (100): Detailed debug information.详细的Debug信息
  • INFO (200): Interesting events. Examples: User logs in, SQL logs.感兴趣的事件或信息,如用户登录信息,SQL日志信息
  • NOTICE (250): Normal but significant events.普通但重要的事件信息
  • WARNING (300): Exceptional occurrences that are not errors. Examples: Use of deprecated APIs, poor use of an API, undesirable things that are not necessarily wrong.
  • ERROR (400): Runtime errors that do not require immediate action but should typically be logged and monitored.
  • CRITICAL (500): Critical conditions. Example: Application component unavailable, unexpected exception.
  • ALERT (550): Action must be taken immediately. Example: Entire website down, database unavailable, etc. This should trigger the SMS alerts and wake you up.
  • EMERGENCY (600): Emergency: system is unusable.

配置一个Logger

Here is a basic setup to log to a file and to firephp on the DEBUG level:

php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler; // Create the logger
$logger = new Logger('my_logger'); // Now add some handlers
$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG));
$logger->pushHandler(new FirePHPHandler()); // You can now use your logger
$logger->addInfo('My logger is now ready');

我们来分析一下这个配置。 The first step is to create the logger instance which will be used in your code. The argument is a channel name, which is useful when you use several loggers (see below for more details about it). 第一步,创建Logger实例,参数即通道名字。

The logger itself does not know how to handle a record. It delegates it to some handlers. The code above registers two handlers in the stack to allow handling records in two different ways. Logger本身不知道如何处理记录,它将处理委托给Handler[s],上面的代码注册了两个Handlers,这样就可以用两种方法来处理记录。

Note that the FirePHPHandler is called first as it is added on top of the stack. This allows you to temporarily add a logger with bubbling disabled if you want to override other configured loggers. 提示:FirePHPHandler最先被调用,因为它被添加在栈的顶部。这就允许你临时添加一个阻塞的Logger,如果你想覆盖其他Logger[s]的话。

添加额外的数据到记录

Monolog 提供两种方法来添加额外的信息到简单的文本信息(along the simple textual message)。

使用日志上下文

第一种,即当前日志上下文,允许传递一个数组作为第二个参数,这个数组的数据是额外的信息:

$logger->addInfo('Adding a new user', array('username' => 'Seldaek'));

简单的Handler(SteamHandler)会简单的将数组格式化为字符串,功能丰富点的Handler(FirePHP)可以搞得更好看。

使用 processors

Processors 可以是任何可调用的方法(回调)。它们接受$record作为参数,然后返回它($record),返回之前,即是我们添加额外信息的操作,在这里,这个操作是改变$recordextrakey的值。像这样:

$logger->pushProcessor(function ($record) {
$record['extra']['dummy'] = 'Hello world!';
return $record;
});

Monolog 提供了一些内置的 processors。看dedicated chapter  收回我说的话,我可能很快就会用到 Processors的。

使用通道

通道是识别record记录的是程序哪部分的好方法(当然,关键词匹配啊),这在大型应用中很有用,如 MonologBundle in Symfony2。

想象一下,两个Logger共用一个Handler,通过这个Handler将记录写入一个文件。这时使用通道能够让我们识别出是哪一个Logger处理的。我们可简单的在这个文件中过滤这个或者那个通道。

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\FirePHPHandler; // Create some handlers
$stream = new StreamHandler(__DIR__ . '/my_app.log', Logger::DEBUG);
$firephp = new FirePHPHandler(); // Create the main logger of the app
$logger = new Logger('my_logger');
$logger->pushHandler($stream);
$logger->pushHandler($firephp); // Create a logger for the security-related stuff with a different channel
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
$securityLogger->pushHandler($firephp); // Or clone the first one to only change the channel
$securityLogger = $logger->withName('security');

自定义日志格式

在 Monolog 中个性化日志是很easy的。大部分 Handler 使用$record['formatted']的值。这个值依赖于 formatter 的设置。我们可以选择预定义的 formatter 类或者编写自己的。

配置一个预定义的 formatter 类,只需要将其设置成 Handler 的字段(属性)即可:

// the default format is "Y-m-d H:i:s"
$dateFormat = "Y n j, g:i a";
// the default output format is [%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$output = "%datetime% > %level_name% > %message% %context% %extra%\n";
$formatter = new LineFormatter($output, $dateFormat); // Create a handler
$stream = new StreamHandler(__DIR__ . 'my_app.log', Logger:DEBUG);
$stream->setFormatter($formatter);
// bind it to a logger object
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);

formatter 是可以在N个 Handler 之间复用的,并且可在N个 Logger 之间共享 Handler。

Handlers

记录日志到文件与系统日志(syslog)

  • StreamHandler:记录日志到任何 PHP stream,用它来记录到文件。
  • RotatingFileHandler: 每天一个文件,会自动删除比$maxFiles老的文件,这只是一个很随意的方案,You should use logrotate for high profile setups though。
  • SyslogHandler: 记录到系统日志
  • ErrorLogHandler:  Logs records to PHP’s error_log() function.

扩展一下…

发送提醒与邮件

看看就能理解

  • NativeMailerHandler: Sends emails using PHP’s mail() function.
  • SwiftMailerHandler: Sends emails using a Swift_Mailer instance.
  • PushoverHandler: Sends mobile notifications via the Pushover API.
  • HipChatHandler: Logs records to a HipChat chat room using its API.
  • FlowdockHandler: Logs records to a Flowdock account.
  • SlackHandler: Logs records to a Slack account.
  • MandrillHandler: Sends emails via the Mandrill API using a Swift_Message instance.
  • FleepHookHandler: Logs records to a Fleep conversation using Webhooks.
  • IFTTTHandler: Notifies an IFTTT trigger with the log channel, level name and message.

记录到指定server与网络日志

接着看看

SocketHandler: Logs records to sockets, use this for UNIX and TCP sockets. See an example. AmqpHandler: Logs records to an amqp compatible server. Requires the php-amqp extension (1.0+). GelfHandler: Logs records to a Graylog2 server. CubeHandler: Logs records to a Cube server. RavenHandler: Logs records to a Sentry server using raven. ZendMonitorHandler: Logs records to the Zend Monitor present in Zend Server. NewRelicHandler: Logs records to a NewRelic application. LogglyHandler: Logs records to a Loggly account. RollbarHandler: Logs records to a Rollbar account. SyslogUdpHandler: Logs records to a remote Syslogd server. LogEntriesHandler: Logs records to a LogEntries account.

开发环境中,利用浏览器扩展

装扩展

FirePHPHandler: Handler for FirePHP, providing inline console  messages within FireBug. ChromePHPHandler: Handler for ChromePHP, providing inline console  messages within Chrome. BrowserConsoleHandler: Handler to send logs to browser’s Javascript console  with no browser extension required. Most browsers supporting console  API are supported. PHPConsoleHandler: Handler for PHP Console, providing inline console  and notification popup messages within Chrome.

记录到数据库

顾名思义

RedisHandler: Logs records to a redis server. MongoDBHandler: Handler to write records in MongoDB via a Mongo extension connection. CouchDBHandler: Logs records to a CouchDB server. DoctrineCouchDBHandler: Logs records to a CouchDB server via the Doctrine CouchDB ODM. ElasticSearchHandler: Logs records to an Elastic Search server. DynamoDbHandler: Logs records to a DynamoDB table with the AWS SDK.

特殊的Handler

慢慢看

FingersCrossedHandler: A very interesting wrapper. It takes a logger as parameter and will accumulate log records of all levels until a record exceeds the defined severity level. At which point it delivers all records, including those of lower severity, to the handler it wraps. This means that until an error actually happens you will not see anything in your logs, but when it happens you will have the full information, including debug and info records. This provides you with all the information you need, but only when you need it.

DeduplicationHandler: Useful if you are sending notifications or emails when critical errors occur. It takes a logger as parameter and will accumulate log records of all levels until the end of the request (or flush()  is called). At that point it delivers all records to the handler it wraps, but only if the records are unique over a given time period (60seconds by default). If the records are duplicates they are simply discarded. The main use of this is in case of critical failure like if your database is unreachable for example all your requests will fail and that can result in a lot of notifications being sent. Adding this handler reduces the amount of notifications to a manageable level.

WhatFailureGroupHandler: This handler extends the GroupHandler ignoring exceptions raised by each child handler. This allows you to ignore issues where a remote tcp connection may have died but you do not want your entire application to crash and may wish to continue to log to other handlers.

BufferHandler: This handler will buffer all the log records it receives until close()  is called at which point it will callhandleBatch()  on the handler it wraps with all the log messages at once. This is very useful to send an email with all records at once for example instead of having one mail for every log record.

GroupHandler: This handler groups other handlers. Every record received is sent to all the handlers it is configured with.

FilterHandler: This handler only lets records of the given levels through to the wrapped handler.

SamplingHandler: Wraps around another handler and lets you sample records if you only want to store some of them.

NullHandler: Any record it can handle will be thrown away. This can be used to put on top of an existing handler stack to disable it temporarily.

PsrHandler: Can be used to forward log records to an existing PSR-3 logger

TestHandler: Used for testing, it records everything that is sent to it and has accessors to read out the information.

HandlerWrapper: A simple handler wrapper you can inherit from to create your own wrappers easily.

Formatters

✪ 为常用

  • LineFormatter: Formats a log record into a one-line string. ✪
  • HtmlFormatter: Used to format log records into a human readable html table, mainly suitable for emails.✪
  • NormalizerFormatter: Normalizes objects/resources down to strings so a record can easily be serialized/encoded.
  • ScalarFormatter: Used to format log records into an associative array of scalar values.
  • JsonFormatter: Encodes a log record into json.✪
  • WildfireFormatter: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler.
  • ChromePHPFormatter: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler.
  • GelfMessageFormatter: Used to format log records into Gelf message instances, only useful for the GelfHandler.
  • LogstashFormatter: Used to format log records into logstash event json, useful for any handler listed under inputs here.
  • ElasticaFormatter: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler.
  • LogglyFormatter: Used to format log records into Loggly messages, only useful for the LogglyHandler.
  • FlowdockFormatter: Used to format log records into Flowdock messages, only useful for the FlowdockHandler.
  • MongoDBFormatter: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler.

Processors

  • PsrLogMessageProcessor: Processes a log record’s message according to PSR-3 rules, replacing {foo}  with the value from $context[‘foo’].

  • IntrospectionProcessor: Adds the line/file/class/method from which the log call originated.

  • WebProcessor: Adds the current request URI, request method and client IP to a log record.

  • MemoryUsageProcessor: Adds the current memory usage to a log record.

  • MemoryPeakUsageProcessor: Adds the peak memory usage to a log record.

  • ProcessIdProcessor: Adds the process id to a log record.

  • UidProcessor: Adds a unique identifier to a log record.

  • GitProcessor: Adds the current git branch and commit to a log record.

  • TagProcessor: Adds an array of predefined tags to a log record.

Utilities

  • Registry: The Monolog\Registry  class lets you configure global loggers that you can then statically access from anywhere. It is not really a best practice but can help in some older codebases or for ease of use. Monolog\Registry允许我们配置全局的logger,并且我们可以全局静态访问,这虽然不是最佳实践,但可以在某些老的代码库中提供一些帮助或者仅仅只是简单使用。

  • ErrorHandler: The Monolog\ErrorHandler  class allows you to easily register a Logger instance as an exception handler, error handler or fatal error handler. Monolog\ErrorHandler 允许我们注册一个Logger实例作为一个异常处理句柄,错误处理句柄或者致命错误处理句柄。

  • ErrorLevelActivationStrategy: Activates a FingersCrossedHandler when a certain log level is reached. 当达到某个日志等级的时候激活 FingersCrossedHandler

  • ChannelLevelActivationStrategy: Activates a FingersCrossedHandler when a certain log level is reached, depending on which channel received the log record. 当达到某个日志等级的时候激活 FingersCrossedHandler,取决于哪个通道收到日志信息。

Extending Monolog

Monolog 是完全可以扩展的。可以轻松让logger适用于我们的需求。

编写自己的 Handler

虽然 Monolog 提供了很多内置的 Handler,但是我们依然可能没有找到我们想要的那个,这时我们就要来编写并使用自己的了。仅需 implement Monolog\Handler\HandlerInterface

来写个 PDOHandler,用来把日志存到数据库,我们继承 Monolog 提供的抽象类,以坚守 Don’t Rpeat Yourself 原则。

 use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler; class PDOHandler extends AbstractProcessingHandler
{
private $initialized = false;
private $pdo;
private $statement; public function __construct(PDO $pdo, $level = Logger::DEBUG, $bubble = false)
{
$this->pdo = $pdo;
parent::__construct($level, $bubble);
} protected function write(array $record)
{
if (!$this->initialized) {
$this->initialize();
} $this->statement->execute(array(
'channel' => $record['channel'],
'level' => $record['level'],
'message' => $record['formatted'],
'time' => $record['datetime']->format('U'),
));
} private function initialize()
{
$this->pdo->exec(
'CREATE TABLE IF NOT EXISTS monolog ' .'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)'
);
$this->statement = $this->pdo->prepare(
'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)'
); $this->initialized = true;
}
}

现在就可以在Logger中使用这个Handler了:

$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));

// You can now use your logger
$logger->addInfo('My logger is now ready');

Monolog\Handler\AbstractProcessingHandler提供了Handler需要的大部分逻辑,包括processors的使用以及record的格式化(which is why we use $record['formatted']  instead of $record['message'])。

 
知道 monolog 也是因为它在 packagist.org 中日志类中排名第一的下载量,抱着大家都用我用肯定没错的心态来在项目中使用了一把,目前使用的功能都很简单,但还是咬牙把文档都过了一遍,不完全整理了下,希望对需要的朋友有所帮助。 —— 由 小强中国

The PHP Package 之 monolog[转]的更多相关文章

  1. Excel导入导出工具——POI XSSF的使用

    工具简介 POI是Apache提供的一款用于处理Microsoft Office的插件,它可以读写Excel.Word.PowerPoint.Visio等格式的文件. 其中XSSF是poi对Excel ...

  2. 使用Callable或DeferredResult实现springmvc的异步请求

    使用Callable实现springmvc的异步请求 如果一个请求中的某些操作耗时很长,会一直占用线程.这样的请求多了,可能造成线程池被占满,新请求无法执行的情况.这时,可以考虑使用异步请求,即主线程 ...

  3. 基于注解的springmvc开发

    原理简析 1. 背景知识:org.springframework.web.ServletContainerInitializer接口 在基于注解的servlet开发中,ServletContainer ...

  4. 三种方式创建bean对象在springIOC容器中初始化、销毁阶段要调用的自定义方法

    1. 使用@Bean注解定义initMethod和destroyMethod 所谓initMethod和destroyMethod,是指在springIOC容器中,对于bean对象执行到初始化阶段和销 ...

  5. 对以内部 git 仓库为 composer 依赖的 package,加上版本号

    现实问题 之前同事做了一个 composer package,做为公司大量 laravel 项目的通用模块. 但是,在实际使用中,每个项目对改 package 的依赖版本是有所不同的.否则 compo ...

  6. NPM (node package manager) 入门 - 基础使用

    什么是npm ? npm 是 nodejs 的包管理和分发工具.它可以让 javascript 开发者能够更加轻松的共享代码和共用代码片段,并且通过 npm 管理你分享的代码也很方便快捷和简单. 截至 ...

  7. npm package.json属性详解

    概述 本文档是自己看官方文档的理解+翻译,内容是package.json配置里边的属性含义.package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象.其中很多属性可以通过np ...

  8. 关于Visual Studio 未能加载各种Package包的解决方案

    问题: 打开Visual Studio 的时候,总提示未能加载相应的Package包,有时候还无法打开项目,各种提示 解决方案: 进入用户目录 C:\Users\用户名\AppData\Local\M ...

  9. SSIS 包部署 Package Store 后,在 IS 中可以执行,AGENT 执行却报错

    可以执行 SSIS Package ,证明用 SSIS Package 的账户是可以执行成功的.SQL Server Agent 默认指定账号是 Network Service. 那么可以尝试一下将 ...

随机推荐

  1. Git 基础

    取得项目的 Git 仓库 有两种取得 Git 项目仓库的方法.第一种是在现存的目录下,通过导入所有文件来创建新的 Git 仓库.第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来. 在工作目录中初 ...

  2. 解决Type 'UnityEngine.Component' does not support slicing

    unity从4.x升级到5.x后部分脚本的编译错误 将animation改成GetComponent.<Animation>()

  3. ie7中ul不能嵌套div和li平级

    我要讲一个忧伤的故事,本以为清晰的层次结构,ul里不能嵌套div和li平级,不然会乱乱乱! 代码: <ul class="catshow">              ...

  4. [BZOJ 3191][JLOI 2013]卡牌游戏

    觉得这题很有必要讲一下! 现在发现在做概率题,基本是向 dp 和 马尔可夫链 靠齐 但是这一题真是把我坑了,因为状态太多,马式链什么的直接死了 我一开始的想法就是用 f[i][j] 表示剩余 i 个人 ...

  5. node.js环境安装,及连接mongodb测试

    1.node.js环境安装 npm config set python python2.7npm config set msvs_version 2013npm config set registry ...

  6. spring retry 使用

    1.  场景      系统方法调用时无状态的,同时因为网络原因,或者系统暂时故障,进行的重试 2. maven 依赖 <project xmlns="http://maven.apa ...

  7. 分析案例:应用服务器W3WP进程CPU持续超过百分之九十(Oracle客户端Bug)

    问题描述: 项目反馈应用负载的其中一台服务器业务操作的响应非常慢,登录该服务器发现W3WP进程CPU持续超过90%,哪怕在业务低峰期也是如此?远程查看后发现该应用服务器承载的请求确实很低,why??? ...

  8. table中bordercolor属性设置后最新ie浏览器或firefox中不显示边线,借助table的css来实现边线

    table中的bordercolor属性设置后在最新的ie或者firefox中均不显示边线,table的边线又是常用功能.只能使用css来实现了,更通用,更方便一些. css: ​.ctable{ b ...

  9. Gemfile分平台加载gem

    Gemfile分平台加载gem 区分平台以便加载不同的web server,象tzinfo-data只适用于windows # Windows does not include zoneinfo fi ...

  10. AMD模块介绍(翻译)

    http://dojotoolkit.org/documentation/tutorials/1.10/modules/index.html Dojo支持以异步模型定义(AMD)方式编写的模块,让会让 ...