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

Overriding Logger in Yii to extend logging levels

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

实际上,我重新定义了 logger.php 本身并开始重新定义 Yii.php,但是它在 vendor 内部拉了很多依赖项,并且开始出现如下错误:

Fatal error: Uncaught Error: Class 'Yii' not found in /app/vendor/yiisoft/yii2/base/Module.php:183 Stack trace: #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} thrown in /app/vendor/yiisoft/yii2/base/Module.php on line 183

记录器

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();

是否可以以某种方式使其更简单,以免为使用它的每个组件重新定义路径?

使用添加更多级别的实现覆盖 \yii\log\logger。你已经完成了,所以我将跳过 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__);

或者您可以为类似于那些别名的调用创建自己的助手。