[经验] 关于 Laravel 的错误&日志服务的运用

laravel-log-viewer

当我们捕获到异常后除了在页面上显示之外,还会将异常记录到日志中,尤其是在线上,这一点很重要,为方便我们排查问题提供了有力的依据,因为线上出现问题是不可能在浏览器中显示错误信息的(这样会暴露服务器上的文件位置和代码信息)。当然,日志的作用远远不限于此,还可以用来记录线上用户行为,站点访问信息,用户操作记录等等。

正因如此,对于大型系统而言,日志是不可或缺的模块,Laravel自然也对日志提供了完善的支持。目前Laravel集成了强大的Monolog库进行日志记录。下面我们就来大致看一下Laravel中如何对日志进行配置以及日志的简单使用。

1、日志处理器配置

首先我们来看日志处理器的配置。Laravel目前支持四种日志处理器:

  • single —— 将日志记录到单个文件中。该日志处理器对应MonologStreamHandler
  • daily —— 以日期为单位将日志进行归档,每天创建一个新的日志文件记录日志。该日志处理器 对应Monolog的RotatingFileHandler
  • syslog —— 将日志记录到syslog中。该日志处理器 对应Monolog的SyslogHandler
  • errorlog —— 将日志记录到PHP的error_log中。该日志处理器 对应Monolog的ErrorLogHandler

项目实际日志处理器通过config/app.php中的log配置项决定,默认配置值为single。这里我们使用默认值,不做修改:

'log' => 'single',

当然如果这四种方式满足不了你的需求,还可以使用configureMonologUsing方法完全控制Monolog的日志处理器:

$app->configureMonologUsing(function($monolog) {
    $monolog->pushHandler(...);
});

注:必须将上述这段代码置于bootstrap/app.php文件返回$app之前处才能生效。

2、使用Log记录日志

配置完成后,就可以在代码中使用Log门面来记录日志,Log门面背后实际上是Illuminate\Log\Writer,而在Writer的构造函数中注入了Monolog\Logger。生成的日志文件存放在storage/logs目录下。

目前,Log门面支持八种日志级别(使用RFC 5424标准):

Log::emergency($error);     //紧急状况,比如系统挂掉
Log::alert($error);     //需要立即采取行动的问题,比如整站宕掉,数据库异常等,这种状况应该通过短信提醒
Log::critical($error);     //严重问题,比如:应用组件无效,意料之外的异常
Log::error($error);     //运行时错误,不需要立即处理但需要被记录和监控
Log::warning($error);    //警告但不是错误,比如使用了被废弃的API
Log::notice($error);     //普通但值得注意的事件
Log::info($error);     //感兴趣的事件,比如登录、退出
Log::debug($error);     //详细的调试信息

下面我们就来分别演示下这几种日志级别的日志记录,我们将在TestControllerlog方法中进行测试:

public function log(){
    Log::emergency("系统挂掉了");
    Log::alert("数据库访问异常");
    Log::critical("系统出现未知错误");
    Log::error("指定变量不存在");
    Log::warning("该方法已经被废弃");
    Log::notice("用户在异地登录");
    Log::info("用户xxx登录成功");
    Log::debug("调试信息");
}

在浏览器中访问http://laravel.app:8000/test/log,对应在storage/logs/laravel.log记录的日志信息如下:

[2015-11-09 14:24:05] local.EMERGENCY: 系统挂掉了
[2015-11-09 14:24:05] local.ALERT: 数据库访问异常
[2015-11-09 14:24:05] local.CRITICAL: 系统出现未知错误
[2015-11-09 14:24:05] local.ERROR: 指定变量不存在
[2015-11-09 14:24:05] local.WARNING: 该方法已经被废弃
[2015-11-09 14:24:05] local.NOTICE: 用户在异地登录
[2015-11-09 14:24:05] local.INFO: 用户xxx登录成功
[2015-11-09 14:24:05] local.DEBUG: 调试信息

可见对应的日志记录包含了日志记录时间、日志级别和日志消息等信息。当然我们也可以在记录日志时传递上下文信息:

Log::info("用户xxx登录成功",['user_id'=>1]);

对应的日志记录为:

[2015-11-09 14:25:47] local.INFO: 用户xxx登录成功 {"user_id":1}

如果要访问底层Monolog的实例可以使用如下方法:

$monolog = Log::getMonolog();
dd($monolog);

我们可以在浏览器页面中查看当前Monolog对象实例的属性信息:

Laravel Monolog 对象实例

我们可以从中看出当前使用的日志处理器,日志记录的位置,日志记录的格式等信息。

以上 转载 自:http://laravelacademy.org/post/1878.html

3、个人的开发实战分享

力求让每个没接触过的Log服务的初学者,可以明白看懂。
(1).如果/config/app.php文件中,'log' => 'single'这样你的日志文件会越来越大,使用编辑器打开会变卡。好吧,这个坑我反正要卡三四秒,简易修改该为'daily',也利于管理。
(2).Log::info()等方法,可以填一个参数,也可以填两个参数,如:

Log::info('微信订单进入回调');   

Log::info('订单详情',$order_array);

(3).在项目开发中,一些调试函数(dd(),var_dump(),print_r())如果不能发挥作用,这时候用Log::info()来排错就可以解燃眉之急。
(4).laravel.log是laravel自带的,其他的一些开源组件比如微信开发组件 easywechat 在使用它的时候,会生成一个同目录下的wechat.log,用来记录日志信息。
(5).知道了Log服务的简单配置和使用方法,我们还需要知道在什么场景下使用。

/**
* 生成微信支付信息
*
* @param $data
* @return $this|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function wechatPay($data)
{
   // 获得支付实例
   $payment = \Wechat::payment();
   // 生成订单
       $attributes = [
           'trade_type'   => 'NATIVE', // JSAPI,NATIVE,APP...
           'body'         => 'macbook pro',
           'detail'       => '15.4寸 i7四核心 16G',
           'out_trade_no' => $data->order_guid,
           'total_fee'    => '1429900'
       ];
       // 创建订单
       $order = new Order($attributes);
       $result = $payment->prepare($order);
       if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
           \Log::info('生成订单号..'.$data->order_guid);
           //创建订单成功的逻辑
       }else{
          \Log::info('订单创建失败..'.$data->order_guid);
           return back()->withErrors('生成订单错误!');
       }
   }
   return back()->withErrors($result->ResultData);

}

本案例中,创建订单立即记录日志,如果在这里出错了,不管是生成订单还是没有生成,Log服务可以帮助我们查找bug。此外,记录日志可以对用户数据留存,经常记录日志,即使以后删除数据库相关数据,我们仍可以根据日志进行数据挖掘和用户行为分析。

(6). 重点 :8ball: 在微信的开发中,开始我不知道是否有类似于chrome的postman的专用于微信支付测试的小程序,这样我只能去阿里云服务器上调试我的代码,此时此刻,dd(),print_r(),var_dumo()等函数已经不能打印信息输出到浏览器上,因此选择Log服务进行打印调试是最合适的选择。

    \Log::info('进入回调函数' -- 进入时间 -- '.date('Y-m-d H:i:s'));   
    \Log::info('订单号--'.$order_guid.' -- 生成时间 -- '.date('Y-m-d H:i:s'));   

通过运用Log服务进行排错,记录回调路由的订单号。我归纳出自己对于微信支付开发的经验,配合略读官方文档,发现了在回调路由指向的控制器方法内没有正确返回‘true’或者‘false’这个BUG。
如果没有返回信息,微信端会每隔一定时长,发送付费用户支付成功或失败的信息给商家的服务器回调路由,这个失误可能会导致购买成功的客户买一件变几件等等严重损害公司利益的重大事故。

后记

在项目实战中,composer拉取组件也会出很多不正常现象,影响进度,影响心情。近期有时间总结一下怎么跳出composer这些坑。

再见。

是非之外有一座花园,我们会在那里相遇
本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
Martist
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 12

可否默认请请求参数打印在日志中?

7年前 评论
Martist

@lanrion 当然

7年前 评论

怎么修改日志存放的位置了? 这个似乎好多文档里面没有提到

6年前 评论

有人实现过用Log打印对象吗?

5年前 评论
bestcyt

写的很好,谢谢分享~

5年前 评论

@MarksGui 请教下,是否找到了修改日志存放路径的方法。同样需要改laravel的默认路径

5年前 评论

@keer 目前是需要改目录的日志,是直接用monolog这个包里面的方法来记录日志。你也可以使用 \Log::getMonolog() 返回 monolog 实例,然后调用它原生的方法

5年前 评论

@MarksGui try $app->useStoragePath('/pwd');

5年前 评论

多服务器分布式架购的情况下,日志怎么统一管理?有没有日志相关的经验,能否分享一下?

4年前 评论

@陈伯乐 自问自答一下,目前我用是 Laravel 官方推荐的 PaperTrail 服务

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!