由于一些历史原因,php中并没有内建的日志接口,故长期以来也没一个功能完备并且应用广泛的日志库。在我的工作生涯中,如果系统需要记录一些应用日志的话,基本上就是封装一个日志类,然后把一些要记录的字段写入到磁盘文件。

这样就难免要一遍一遍的造轮子,并且在没有一个规范的情况下,记录下来的日志也是不方便分析的。但是希望读完本文后希望你们可以放弃自己造这种日志类的轮子了,因为几乎你不可能造得比我们今天要介绍的主角:monolog更圆。

monolog是一个为5.3以上版本php开发的日志库,但是需要注意的是现在主干版本只支持php 7以上版本,如果你的服务器环境还是php 5的话,可以使用monolog的1.x版本。

值得一提的是monolog是一个符合psr-3规范的日志类库,并且符合psr-4加载规范。如果有对psr规范不太了解的同学可以参看如下链接:http://www.php-fig.org/psr/,我们在这里就不具体介绍这些规范了,反正知道monolog是复合当前最新行业规范的日志库就够了。

如果想要在你的代码中引入monolog的话只需要执行:

composer require monolog/monolog

mongolog中有几个很重要的概念:

第一个:handler 日志管理器

存放handler的数据结构是一个“栈”,一个日志实例可以有多个handler,通过Logger实例的pushHandler方法压入一个handler,该方法接受一个HandlerInterface类型的参数。如果你设置了多个handler,当你新增一条日志的时候,他会从栈顶开始往下传播,关心这个级别日志的handler将会处理这条日志。所有的handler都会继承AbstractProcessingHandler这个抽象类,并且只需要实现里面的抽象方法write就可以了;同时这个抽象类会继承AbstractHandler这个抽象类,这个抽象类的构造函数有两个参数:level和bubble,前者表示该handler关心的最低日志级别,是个整型,后者表示日志被当前handler处理后是否接着向下传递。参照如下代码:

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Handler\ErrorLogHandler;

$logger = new Logger('my_logger');

$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::INFO));

$logger->pushHandler(new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, Logger::ERROR, false));

$logger->info('码王教育——可能是最具含金量的IT培训');

如上这段代码,这条日志被ErrorLogHandler处理了,并且ErrorLogHandler的bubble参数设置为false,则日志不会被写入my_app.log中了。

第二个:formatter 设置日志格式

每个handler可以单独设置记录的日志格式,AbstractHandler抽象类中有一个setFormatter方法,该参数接受一个FormatterInterface类型的参数。可以看到monolog自带的formatter都继承自NormalizerFormatter,该类实现了format和formatBatch方法。我们修改上面的示例代码,让两个handler记录不同格式的日志:

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Handler\ErrorLogHandler;

use Monolog\Formatter\JsonFormatter;

$logger = new Logger('my_logger');

$stream_handler = new StreamHandler(__DIR__.'/my_app.log', Logger::INFO);

$stream_handler->setFormatter(new JsonFormatter());

$logger->pushHandler($stream_handler);

$logger->pushHandler(new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, Logger::INFO));

$logger->info('码王教育——可能是最具含金量的IT培训');

此时可以看到my_app.log中记录的日志就变为了更方便解析的json格式了。

第三个:processor 日志处理器,用来给日志添加额外信息

存放processor的结构也是一个“栈”,意味着你也可以通过pushProcessor方法给一个Logger实例配置多个processor。我们注意到,这里pushProcessor接受一个callable,也就是需要一个函数或者类方法,但是官方自带的这些processor都是类,随便点进去一个源码就会发现,其实这些类都用到了__invoke魔术方法,所以在被当做callable调用的时候会自动调用__invoke。我们接着修改上面示例,给我们的日志加上更多信息:

use Monolog\Logger;

use Monolog\Handler\StreamHandler;

use Monolog\Handler\ErrorLogHandler;

use Monolog\Formatter\JsonFormatter;

use Monolog\Processor\UidProcessor;

use Monolog\Processor\ProcessIdProcessor;

$logger = new Logger('my_logger');

$stream_handler = new StreamHandler(__DIR__.'/my_app.log', Logger::INFO);

$stream_handler->setFormatter(new JsonFormatter());

$logger->pushHandler($stream_handler);

$logger->pushHandler(new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, Logger::INFO));

$logger->pushProcessor(new UidProcessor);

$logger->pushProcessor(new ProcessIdProcessor);

$logger->info('码王教育——可能是最具含金量的IT培训');

再次执行这段代码就能看到,我们在日的后面加上了uid和process_id。

通过以上的介绍相信你对monolog的使用和库的整体架构都有一个比较系统的认识了,这个时候你应该能发现,自己开发一个handler、formatter、processor是如此简单,只用实现两个接口,再写一个匿名函数,就能完全控制自己的日志处理方式和日志格式了。

程序开发中很重要的一点就是像monolog这样易于扩展,要做到这点其实只要遵守以下几点:

1、依赖接口而不是实现(依赖反转、设计模式)

2、在有标准的时候遵守行业标准(psr)

在学习完了monolog的用法和设计架构之后,何不现在在mongolog之上实现一套记录日志到关系型数据的代码:)

更多的PHP进阶学习资料尽在码王信息,欢迎加群632109190进行讨论和学习

这可能是php世界中最好的日志库——monolog的更多相关文章

  1. 在Go语言项目中使用Zap日志库

    在Go语言项目中使用Zap日志库 本文先介绍了Go语言原生的日志库的使用,然后详细介绍了非常流行的Uber开源的zap日志库,同时介绍了如何搭配Lumberjack实现日志的切割和归档. 在Go语言项 ...

  2. Go语言项目中使用zap日志库(翻译)

    本文先介绍了Go语言原生的日志库的使用,然后详细介绍了非常流行的Uber开源的zap日志库,同时介绍了如何搭配Lumberjack实现日志的切割和归档. 在Go语言项目中使用Uber-go的Zap L ...

  3. golang中使用zap日志库

    1. 快速使用 package main import ( "go.uber.org/zap" "time" ) func main() { // 1. sug ...

  4. 自行实现的jar包中,日志库的适配实现

    ​ 日常情况下,我们自己都会自行实现一些基础的jar包,如dao包.service包或一些其他完成特定功能的jar包.如果没有一套调试日志信息,出现问题时想查找问题非常不方便.可能大多数小伙伴都会有自 ...

  5. java 世界中Annotation

    java 世界中Annotation 在github上开始汇总一些自己学习,收集,总结,经验的一些信息,有利于跟踪,修改,提升.如果你感兴趣 可以关注一下,也可以提供自己的内容进来. https:// ...

  6. 行走于Swift的世界中(转)

    从Swift正式公布到现在,我基本一直在关注和摸索Swift.对于一门新语言来说,开荒阶段的探索自然是激动人心的,但是很多时候,资料的缺失和细节的隐藏也让人着实苦恼.最近几天的感受是,Swift 并不 ...

  7. 编写高质量代码改善C#程序的157个建议——建议112:将现实世界中的对象抽象为类,将可复用对象圈起来就是命名空间

    建议112:将现实世界中的对象抽象为类,将可复用对象圈起来就是命名空间 在我们身边的世界中,对象是什么?对象就是事物,俗称“东西”.那么,什么东西算得上是一个对象呢?对象有属性.有行为.以动物为例,比 ...

  8. CSS世界中那些说起来很冷的知识

    CSS世界中那些说起来很冷的知识 最近读了张鑫旭的新书<CSS世界>收获了不少对CSS的深度理解 也正值个人在公司内部进行部分章节的内容分享,于是顺带着直接把我即将分享的内容先给大家过过目 ...

  9. 总结:js世界中的特殊符号

    常用符号:+ ++ - -- || / /' && 等 这些基本上每天都能用到,但是 js 世界中有些特殊符号是不常用的,我也是偶然在阅读大神代码的时候发现的,一番查找之后得出了以下结 ...

随机推荐

  1. Http远程调用服务

    GET public static string GetJsonStr(string webApi)        {            string serviceAddress = webAp ...

  2. Web 页面测试总结—控件类

    web端页面测试,最常见的是基本控件的测试,只有了解常见的控件和其测试方法,才能掌握测试要点,避免漏测情况发生.根据日常工作总结,将控件和常见逻辑集合在一起,总结了几个控件类测试查场景如下. 导航条 ...

  3. Android7.0 Phone应用源码分析(四) phone挂断流程分析

    电话挂断分为本地挂断和远程挂断,下面我们就针对这两种情况各做分析 先来看下本地挂断电话的时序图: 步骤1:点击通话界面的挂断按钮,会调用到CallCardPresenter的endCallClicke ...

  4. 使用Nginx+CppCMS构建高效Web应用服务器(之二)

    使用Nginx+CppCMS构建高效Web应用服务器(之二) 上一篇 使用Nginx+CppCMS构建高效Web应用服务器(之一) 大致介绍了网站的整体架构,实际上通过调用REST获取数据并没有实现. ...

  5. 【CNMP系列】VIM编辑器详解

    缘起 大学的时候做过Linux内核驱动程序研发,之前写C语言就是用的Vim编辑器,当年的Vim还不如今天之强大,当时的插件也没有现在这么多,只是觉得这个编辑器能满足我想要的所有,查看Linux内核代码 ...

  6. Layoutinlater 转

    http://blog.csdn.net/guolin_blog/article/details/12921889

  7. Windows搭建以太坊的私有链环境

    1.下载Geth.exe 运行文件,并安装 https://github.com/ethereum/go-ethereum/releases/ 下载后,只有一个Geth.exe的文件 2.cmd进入按 ...

  8. MyBatis框架原理(三)

    一.框架结构原理图 二.MyBatis框架执行流程 三.总结 原理详解: MyBatis应用程序根据XML配置文件创建SqlSessionFactory,SqlSessionFactory在根据配置, ...

  9. OpenStack及其构成简介

    新的一年新的开始,突然想学习下Openstack,之前了解过很多,但是想系统的学习一下,第一次写博客,只想把学到的东西记录下来加深印象,如有写的不好的地方请多多见谅.下面开门见山. 1.What is ...

  10. 使用register_shutdown_function触发写日志,使用fastcgi_finish_request提高响应速度

    公司内部的市场管理系统,一直是我一个人维护,最近老是有开发埋怨,内网的账号被人改了密码,账号被解绑了...哈的,错在这还不是一个完整的系统,既没有严格的权限也没有做操作日志呀... 权限现在是准备做在 ...