在Yii中覆盖Logger以扩展日志记录级别

时间:2019-08-26 10:13:32

标签: php logging yii2 overriding helper

现在,我正在yii2上开发一个项目,除了记录器中可用的级别之外,还出现了添加更多致命和关键任务的任务。

实际上,我重新定义了logger.php并开始重新定义Yii.php,但是它拉扯了供应商内部的许多依赖关系,并且错误开始出现:

  

致命错误:未捕获的错误:在/app/vendor/yiisoft/yii2/base/Module.php:183中找不到类'Yii'堆栈跟踪:#0 /app/components/Application.php(14):yii \ base \ Module :: setInstance(Object(app \ components \ Application))#1 /app/web/index.php(16):app \ components \ Application-> __ construct(Array)#2 {main}放在/ 183行上的app / vendor / yiisoft / yii2 / base / Module.php

记录器

class Logger extends \yii\log\Logger
{
/**
 * Critical message level. An tracing message is one that reveals the code execution flow.
 */
const LEVEL_CRITICAL = 0x12;

/**
 * Fatal message level. An tracing message is one that reveals the code execution flow.
 */
const LEVEL_FATAL = 0x16;

/**
 * Returns the text display of the specified level.
 * @param int $level the message level, e.g. [[LEVEL_ERROR]], [[LEVEL_WARNING]].
 * @return string the text display of the level
 */
public static function getLevelName($level)
{
    static $levels = [
        self::LEVEL_ERROR => 'error',
        self::LEVEL_WARNING => 'warning',
        self::LEVEL_INFO => 'info',
        self::LEVEL_TRACE => 'trace',
        self::LEVEL_CRITICAL => 'critical',
        self::LEVEL_FATAL => 'fatal',
        self::LEVEL_PROFILE_BEGIN => 'profile begin',
        self::LEVEL_PROFILE_END => 'profile end',
        self::LEVEL_PROFILE => 'profile',
    ];

    return isset($levels[$level]) ? $levels[$level] : 'unknown';
}
}

yii.php

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');

require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/components/Yii.php';

$config = require __DIR__ . '/config/console.php';

$application = new yii\console\Application($config);
$exitCode = $application->run();
exit($exitCode);

Yii.php

<?php

namespace app\components;

use app\components\Logger;
use yii\di\Container;

class Yii extends \yii\BaseYii
{
    private static $_logger;

    /**
     * @return Logger message logger
     */
    public static function getLogger()
    {
        if (self::$_logger !== null) {
            return self::$_logger;
        }

        return self::$_logger = static::createObject('app\components\Logger');
    }

    /**
     * Sets the logger object.
     * @param Logger $logger the logger object.
     */
    public static function setLogger($logger)
    {
        self::$_logger = $logger;
    }

    public static function critical($message, $category = 'application')
    {
        static::getLogger()->log($message, Logger::LEVEL_CRITICAL, $category);
    }
    public static function fatal($message, $category = 'application')
    {
        static::getLogger()->log($message, Logger::LEVEL_FATAL, $category);
    }
}

spl_autoload_register(['app\components\Yii', 'autoload'], true, true);
Yii::$classMap = require __DIR__ . '/../vendor/yiisoft/yii2/classes.php';
Yii::$container = new Container();

是否可以使其变得更简单,以免使用它重新定义每个组件的路径?

1 个答案:

答案 0 :(得分:3)

使用您的实现覆盖\ yii \ log \ logger,以添加更多级别。您已经完成了此操作,因此我将跳过记录器代码。

namespace app\components;

class MyLogger extends \yii\log\Logger
{
    ...
}

覆盖日志目标,以便它可以处理您的额外级别

namespace app\components;

use \yii\log\Logger;
use \yii\helpers\VarDumper;

class MyFileTarget extends \yii\log\FileTarget
{
    private $_levels = 0;

    public function getLevels()
    {
        return $this->_levels;
    }

    public function setLevels($levels)
    {
        static $levelMap = [
            'error' => Logger::LEVEL_ERROR,
            'warning' => Logger::LEVEL_WARNING,
            'info' => Logger::LEVEL_INFO,
            'trace' => Logger::LEVEL_TRACE,
            'critical' => MyLogger::LEVEL_CRITICAL,
            'fatal' => MyLogger::LEVEL_FATAL,
            'profile' => Logger::LEVEL_PROFILE,
        ];

        if (is_array($levels)) {
            $this->_levels = 0;
            foreach ($levels as $level) {
                if (isset($levelMap[$level])) {
                    $this->_levels |= $levelMap[$level];
                } else {
                    throw new InvalidConfigException("Unrecognized level: $level");
                }
            }
        } else {
            $bitmapValues = array_reduce($levelMap, function ($carry, $item) {
                return $carry | $item;
            });
            if (!($bitmapValues & $levels) && $levels !== 0) {
                throw new InvalidConfigException("Incorrect $levels value");
            }
            $this->_levels = $levels;
        }
    }

    public function formatMessage($message)
    {
        list($text, $level, $category, $timestamp) = $message;
        $level = MyLogger::getLevelName($level);
        if (!is_string($text)) {
            // exceptions may not be serializable if in the call stack somewhere is a Closure
            if ($text instanceof \Throwable || $text instanceof \Exception) {
                $text = (string) $text;
            } else {
                $text = VarDumper::export($text);
            }
        }
        $traces = [];
        if (isset($message[4])) {
            foreach ($message[4] as $trace) {
                $traces[] = "in {$trace['file']}:{$trace['line']}";
            }
        }
        $prefix = $this->getMessagePrefix($message);
        return $this->getTime($timestamp) . " {$prefix}[$level][$category] $text"
            . (empty($traces) ? '' : "\n    " . implode("\n    ", $traces));
    }
}

More info about log targets

在组件配置中设置日志分派器以使用记录器和目标

   ...
   'components' => [
        ...
        'log' => [
            'logger' => \app\components\MyLogger::class,
            'targets' => [
                [
                    'class' => \app\components\MyFileTarget::class,
                    'levels' => ['fatal', 'critical'],
                    'categories' => ['app\*'],
                    'file' => '@runtime/logs/critical.log',
                ],
            ],
        ],
        ...
    ],
    ...

More info about log configuration

然后通过日志组件而不是别名Yii::error()来调用记录器

Yii::$app->log->getLogger()->log('msg', MyLogger::LEVEL_CRITICAL, __METHOD__);

或者您可以为类似于这些别名的呼叫创建自己的助手。