覆盖 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));
}
}
在您的组件配置中设置日志调度程序以使用您的记录器和目标
...
'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__);
或者您可以为类似于那些别名的调用创建自己的助手。
现在我正在 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));
}
}
在您的组件配置中设置日志调度程序以使用您的记录器和目标
...
'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__);
或者您可以为类似于那些别名的调用创建自己的助手。