PHP日记接口规范

关于日记系统,PHP规范给出了相当好的说明和定义,请见: Logger Interface

apic

1.17.1 简化版

但在用过这么多开源框架进行各种项目开发,以及内部框架的开发后,发现实际上日记的分类并没有使用得这么丰富,而通常只是频繁地几类。因此,为了减少不必要的复杂性,这里特地精简为三种,只有:

  • error 系统异常类
  • info 业务纪录类
  • debug 开发调试类
    现分说如下。

(1)error 系统异常类

此类是后台开发同学认为 不应该发生却发生的事情 ,即我们通常所说的系统异常。如:调用app store支付的接口失败了,我们需要纪录一下当时的场景,以便复查和给用户补尝其损失。又如:写入一条纪录到数据纪录,但失败了,我们需要纪录一下,以便排查。

用法很简单,如:

  1. //只有描述
  2. DI()->logger->error('fail to insert DB');
  3. //描述 + 简单的信息
  4. DI()->logger->error('fail to insert DB', 'try to register user dogstar');
  5. //描述 + 当时的上下文数据
  6. $data = array('name' => 'dogstar', 'password' => '123456');
  7. DI()->logger->error('fail to insert DB', $data);

上面三条纪录,效果类如:

  1. dogstar@ubuntu:dev.phalapi.com$ tailf ./Runtime/log/201502/20150207.log
  2. 2015-02-07 20:37:55|ERROR|fail to insert DB
  3. 2015-02-07 20:37:55|ERROR|fail to insert DB|try to register user dogstar
  4. 2015-02-07 20:37:55|ERROR|fail to insert DB|{"name":"dogstar","password":"123456"}

截图效果:apic

(2)info 业务纪录类

此类通常为业务需要进行必要的操作纪录,以便发生系统问题后的回滚处理、问题排查以及数据统计。如在有缓存的情况下,可能数据没及时写入数据库而导致数据丢失或者回档,这里可以通过日记简单查看是否可以恢复。以及说明一下操作发生的背景或原由,如通常游戏中用户的经验值添加:

  1. //假设:10 + 2 = 12
  2. DI()->logger->info('add user exp', array('name' => 'dogstar', 'before' => 10, 'addExp' => 2, 'after' => 12, 'reason' => 'help one more phper'));
  3. //对应的LOG
  4. 2015-02-07 20:48:51|INFO|add user exp|{"name":"dogstar","before":10,"addExp":2,"after":12,"reason":"help one more phper"}

但当哪天我们看到以下的LOG是就会发现系统存在隐藏的BUG:

  1. //WTF? 10 + 2 = 11 ???
  2. 2015-02-07 20:48:51|INFO|add user exp|{"name":"dogstar","before":10,"addExp":2,"after":11,"reason":"help one more phper"}

而当用户玩家来投诉客服时,客服妹子来找到我们后台开发时,我们可以证明得了是系统原因造成了用户丢失1点经验值。特别地,若我们看到以下的LOG时,不难看出有人在用非法的渠道刷经验:

  1. 2015-02-07 20:52:35|INFO|add user exp|{"name":"dogstar","before":10,"addExp":2,"after":12,"reason":"help one more phper"}
  2. 2015-02-07 20:52:35|INFO|add user exp|{"name":"dogstar","before":12,"addExp":2,"after":14,"reason":"help one more phper"}
  3. ....
  4. 2015-02-07 20:52:35|INFO|add user exp|{"name":"dogstar","before":998,"addExp":2,"after":1000,"reason":"help one more phper"}

所幸我们有日记并及时发现了,随后如何处理就视具体的项目而定了。但当产品来追问时,我们可以及时给出反馈和做出处理。

还有更为重要的是 数据统计 。这块就App数据分析和统计这块已经有了很好的第三方服务支持,一如:友盟。但我们仍然可以轻松实现自己的数据统计,以便二次确认和定制化。毕竟,总是依赖第三方不是那么轻便,而且存在敏感数据安全问题。这里简单提供一个上报接口,如:

  1. public function getRules() {
  2. return array(
  3. 'report' => array(
  4. 'username' => array('name' => 'username', 'require' => true),
  5. 'msg' => array('name' => 'msg', 'require' => true),
  6. ),
  7. );
  8. }
  9. public function report() {
  10. DI()->logger->info($this->username, $this->msg);
  11. }

客户端在需要的场景,如用户打开应用,请求:

  1. http://dev.phalapi.com/demo/?service=DogstarTest.Report&username=dogstar&msg=enter%20app

即可看到:

  1. 2015-02-07 21:01:13|INFO|dogstar|enter app

到后期,若我们需要统计用户的登录情况时,可以这样统计:

  1. $ cat ./Runtime/log/201502/20150207.log | grep "enter app" | awk -F '|' '{print $3}' | sort | uniq -c
  2. 11 dogstar
  3. 5 King
  4. 2 Tom

(3)debug 开发调试类

此类为开发调试用,用法如上,这里不再赘述。以下是一些简单的示例:

  1. DI()->logger->debug('just for test');
  2. DI()->logger->debug('just for test', '一些其他的描述 ...');
  3. DI()->logger->debug('just for test', array('name' => 'dogstar', 'password' => '******'));

1.17.2 使用DI()->logger->log()进行更灵活的分类

若上面的error/info/debug都不能满足项目的需求时,可以这样更灵活地进行日记纪录:

  1. DI()->logger->log('demo', 'add user exp', array('name' => 'dogstar', 'after' => 12));
  2. DI()->logger->log('test', 'add user exp', array('name' => 'dogstar', 'after' => 12));
  3. //对应的日记
  4. 2015-02-07 21:13:27|DEMO|add user exp|{"name":"dogstar","before":10,"addExp":2,"after":12,"reason":"help one more phper"}
  5. 2015-02-07 21:15:39|TEST|add user exp|{"name":"dogstar","after":12}

注意到,DI()->logger->log()第一个参数为日记分类的名称,在写入日记时会自动转换为大写。其接口函数签名为:

  1. /**
  2. * 日记纪录
  3. *
  4. * 可根据不同需要,将日记写入不同的媒介
  5. *
  6. * @param string $type 日记类型,如:info/debug/error, etc
  7. * @param string $msg 日记关键描述
  8. * @param string/array $data 场景上下文信息
  9. * @return NULL
  10. */
  11. abstract public function log($type, $msg, $data);

1.17.3 别忘记了日记的级别设置

上面的三类日记分别对应的标识为:

  • error 系统异常类:PhalApi_Logger::LOG_LEVEL_ERROR
  • info 业务纪录类:PhalApi_Logger::LOG_LEVEL_INFO
  • debug 开发调试类:PhalApi_Logger::LOG_LEVEL_DEBUG
    根据不同的项目在不同环境下的需要,我们可以这样有选择性地将需要的日记分类写入保存:
  1. //日记纪录
  2. DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime',
  3. PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);

多个日记级别需要使用或运算进行组合。

1.17.4 扩展你的日记存储

普遍情况下,我们认为将日记存放在文件是比较合理的,因为便于查看、管理和统计。当然,如果你的项目需要将日记纪录在数据库,也可以快速扩展实现。如实现数据库的存储思路:

  1. //$ vim ./Apps/Common/Logger/DB.php
  2. class Common_Logger_DB extends PhalApi_Logger {
  3. public function log($type, $msg, $data) {
  4. //TODO 数据库的日记写入 ...
  5. }

随后,利用DI注册一下即可:

  1. //日记纪录 - DB
  2. DI()->logger = new Common_Logger_DB($dbConfig,
  3. PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);

原文: https://www.phalapi.net/wikis/1-17.html