使用 Monolog 记录全栈跟踪


Logging full stack trace with Monolog

我在应用程序中使用Monolog作为独立库,最近我遇到了一个问题。假设,在我的应用程序中的某个时刻,我捕获了一个异常,我想记录它:

$mylogger->error('Exception caught', array('exception' => $exception));

除了一件小事外,这非常有效 - 它不会记录整个堆栈跟踪。是否可以使用单日志内置格式化程序记录异常的完整堆栈跟踪?

实际上,

从版本1.12.0开始,可以在日志文件中包含堆栈跟踪:有一种新的LineFormatter方法称为includeStacktraces

要使用它,您需要覆盖 monolog 格式化程序的默认行为:

配置.yml

monolog:
    handlers:
        main:
            formatter: your.monolog.service.id
            (rest of config is as usual)

服务.yml

services:
    your.monolog.service.id:
        class: Monolog'Formatter'LineFormatter
        calls:
            - [includeStacktraces]

查看 github 了解更多信息:拉取请求

我有一个非常简单的解决方案!!

$mylogger->error((string) $exception);

你可以简单地使用带有"include_stacktraces"参数的配置文件(Symfony Bundle):

monolog:
    handlers:
        main:
            type: stream
            path: "%kernel.logs_dir%/%kernel.environment%.log"
            level: info
            channels: ["!event"]
            max_files: 10
            include_stacktraces: true <--- SET TO TRUE

@see此提交

@see完整架构(配置)

除了Tomasz Madeyski的回答之外,您还可以仅通过代码使用它:

use Monolog'Logger;
use Monolog'Handler'StreamHandler;
use Monolog'ErrorHandler;
use Monolog'Formatter'LineFormatter;
$formatter = new LineFormatter(LineFormatter::SIMPLE_FORMAT, LineFormatter::SIMPLE_DATE);
$formatter->includeStacktraces(true); // <--
$stream = new StreamHandler('error.log');
$stream->setFormatter($formatter);
$logger = new Logger('logger');
$logger->pushHandler($stream);

否,您无法使用内置格式化程序记录堆栈跟踪。 在这里看到我的问题。

如果您查看LineFormatter.php您会发现 normalizeException 方法负责抓取异常数据。因此,我必须创建一个扩展LineFormatter的新格式化程序。 代码如下:

<?php
namespace Monolog'Formatter;
use Exception;
class ExceptionLineFormatter extends LineFormatter
{
    protected function normalizeException(Exception $e)
    {
        return 'Message: ' . $e->getMessage() . 
                'Stack Trace: '. $e->getTraceAsString();
    }
}

我像这样初始化我的记录器:

$logFile = 'MyLogFile.txt';
$handler = new StreamHandler($logFile);
$handler->setFormatter(new ExceptionLineFormatter);
$log = new Logger('MyLogger');
$handler = self::getStreamHander();
$log->pushHandler($handler);

这将打印出您的堆栈跟踪。

这就是我的做法,是的,多年后...

$mylogger->error('Exception caught', $exception->getTrace());

因为getTrace()返回一个数组,这正是Monolog想要的。

赞成的答案有效,但您不会被迫为此创建自定义服务。

monolog.formatter.line已经存在于Symfony 3.4全栈中。您可以简单地在其上添加一个方法调用,这要归功于CompilerPassInterface

use Symfony'Component'DependencyInjection'Compiler'CompilerPassInterface;
use Symfony'Component'DependencyInjection'ContainerBuilder;
use use Symfony'Component'HttpKernel'Kernel;
class AppKernel extends Kernel implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $container->getDefinition('monolog.formatter.line')->addMethodCall('includeStacktraces');
    }
}

默认情况下,monolog 处理程序似乎不会接收服务,因此您仍然必须指定它。例:

monolog:
    handlers:
        main:
            type: rotating_file
            max_files: 12
            date_format: 'Y-m'
            path: '%kernel.logs_dir%/%kernel.environment%.log'
            level: debug
            formatter: monolog.formatter.line

如果您只想在抛出异常时记录堆栈跟踪,则可以在AppServiceProvider中执行此操作:

public function register()
{
     $logger = Log::getMonolog();
     $logger->pushProcessor(function ($record) {
        if (isset($record['context']['exception']))
        {
            $record['extra']['stacktrace'] = $record['context']['exception']->getTraceAsString();
        }
        return $record;
    });
}

这会将堆栈跟踪添加到extra列,然后可以按行格式化程序使用它

getTraceAsString将为您提供Stack Trace数组作为"行尾"分隔字符串。 在PHP_EOL上爆炸,然后通过记录每个元素的数组进行查找。 希望这有帮助。

<?php
function test() {
    throw new Exception;
}
try {
    test();
} catch(Exception $e) {
    $array = explode(PHP_EOL, $e->getTraceAsString());
    foreach($array as $line){
        $mylogger->error($line);
}

应该产生这样的东西:

#0 index.php(14): test()
#1 {main}