日志在WEB运用中的利用非常广泛,记录访问者IP,访问者操作的数据,接口要求的信息,非常提示信息等,尤其在产品环境中,我们每每须要通过日志来剖析当前运用的运营情形,是否在存在一些不可见的未知缺点。
在程序实现过程中,我们设计的输出日志每每都是在程序运行当中,如:
连接数据库失落败了,捕捉到非常后,往本地磁盘写入一条日志;

图片上传韶光超时,往本地磁盘写一条日志;
渲染页面中涌现XSS攻击,往网络日志做事器写一条日志;
要求对方做事器Api接口,涌现超时,往网络日志做事器写一条日志;
……
这种实时输出日志的办法每每存在两个紧张的问题:
对本地磁盘的IO提高或者对网络日志做事器要求涌现超时;
若日志写失落败,极有可能对后面程序片段的实行产生影响;
既然这天记,那么解释它并不是非常主要的数据,却不能由于记录日志的失落败影响主要的业务,那么我们是否可以把日志输入放在所有程序都实行完成的时候处理呢?因此有必要深入理解要求实行的PHP生命周期过程。
二.Web要求与PHP生命周期
PHP有两种运行模式:WEB模式,CLI模式
无论是哪种模式,PHP的事情事理都是同等的,都须要SAPI的运行
在Web要求时,Nginx就一定要和FastCGI通信,通过php_fpm调用php-cgi,须要通过SAPI接口,进入PHP的脚本实行。
在CLI模式下直接操作php-cgi也须要通过SAPI实行,以下展示了全体处理过程:
SAPI也叫Service API,在全体要求过程中承担了很主要的角色,外部运用通过SAPI进行对上层调用,所谓的外部运用可以理解为Apache,FastCgi等,上层也便是我们写的PHP程序。
以下是SAPI的大略示意图:
当要求调用SAPI时,将会按顺序匆匆发以下几个阶段:
MINIT(Module init)
该阶段将调用PHP_MINIT_FUNCTION(extension)
该函数浸染为遍历须要加载的扩展,并初始化扩展,注册模块常量,类等。
RINIT(Request init)
该阶段将调用 PHP_RINT_FUNCTION(extension)
该函数的浸染为遍历加载的扩展,并针对要求信息进行一些初始化事情,比如记录要求开始韶光,初始化要求$_POST, $_GET全局遍历,若开启session模块下,注册全局Session变量等。
Execute php code
这部分便是自己编写的PHP代码,也便是真正实行的我们代码实行的部分。
RSHUTDOWN
该阶段将调用PHP_RSHUTDOWN_FUNCTION(extension)
该函数的浸染遍历加载的扩展,并针对要求信息进行一些析构事情,比如记录要求结束韶光,把相应的输入写入日志,开启session模块下,全局Session变量写到tmp目录下文件等。
MSHUTDOWN
该阶段将调用PHP_MSHUTDOWN_FUNCTION(extension)
该函数的浸染为当Web要求结束或命令行实行脚本结束后会实行,完成开释资源操作。
可以通过下图直不雅观的看出php实行test.php生命周期的过程:
实在我们编写的程序在繁芜PHP生命周期中每每只运行在第3步,如果我们的日志能在第4步处理,那是不是可以避免文章开头提出的问题?那么除了利用编写扩展注册PHP_RSHUTDOWN_FUNCTION方法,我们还可以通过register_shutdown_function()
注册的自定义函数,这些自定义函数在我们编写的PHP代码结束的时候将会被调用。
register_shutdown_function()
是php系统函数,注册shutdown的函数将会在PHP生命周期中第4阶段进行实行,如程序结束,或涌现exit
/die
等命令后都会触发注册函数。
三.程序实现
为此利用register_shutdown_function()
,我们可以设计一个处理事宜类,该类能够注册我们须要的事宜,在PHP Shutdown的时候针对已经注册的事宜进行处理,这些事宜中可能就包括我们须要的日志输出。
该类实现的功能很大略:
须要一个增加事宜的方法,把我们须要的处理事宜过程放入;
须要一个调用事宜的方法,把我们增加的所有事宜在SHUTDOWN时调用;
末了须要注册方法,注册调用方法至SHUTDOWN中;
以下是该类(Service_Core_ShutdownEvent)的实当代码:
/ Manage php shutdown events. @author Lancer He <lancer.he@gmail.com> @since 2014-08-21/class Service_Core_ShutdownEvent { / array to store user events. @var array / private static $_events = array(); / register shutdown / public static function register() { register_shutdown_function(array('Service_Core_ShutdownEvent', 'call')); } / Register event. @return boolean / public static function add() { $event = func_get_args(); if ( empty($event) ) { trigger_error(\"大众Register event need method.\"大众); return false; } if ( ! is_callable($event[0]) ) { trigger_error(\公众Register event can not be call.\"大众); return false; } self::$_events[] = $event; return true; } / call event when you need. / public function call() { foreach (self::$_events as $event) { $callback = array_shift($event); call_user_func_array($callback, $event); } }}
对付编写好的处理事宜类,我们利用一个大略的Log类来验证这个注册事宜类是否可行,这个Log类(Service_Core_NetLog)详细的程序就不展示了,紧张演示以下两个方法:
Service_Core_NetLog::trace(string $string);Service_Core_NetLog::notice(string $string);
两个方法都比较大略,浸染往磁盘中按照一定规则输出一行Log,因此我在一个掌握器中来测试它,通过Web访问或者CLI的办法要求:
通过Sleep的办法来验证,上图红框处可以更加直不雅观的验证有两条日志是在程序结束往后写入磁盘。
四.小结
通过深入理解PHP生命周期,更直不雅观的理解PHP运行的机制,通过注册shutdown办法register_shutdown_function
在PHP程序结束后处理我们须要的逻辑过程,如日志输出,邮件关照等,降落了程序中的处理额外业务逻辑的风险。
PHP生命周期日志输出异步日志