fix(config): 修复Config类循环依赖

This commit is contained in:
zimoyin
2026-03-11 03:58:21 +08:00
parent 3471deb3f1
commit 5adf3930e2
3 changed files with 192 additions and 45 deletions
+136 -1
View File
@@ -2,6 +2,7 @@
namespace app\utils;
use app\config\Config;
use support\Log;
use Throwable;
@@ -145,6 +146,7 @@ class LoggerIns
public function info(string $message, array|Throwable $context = []): void
{
$processedMessage = $this->buildMessage($message, $context);
if (empty($processedMessage)) return;
Log::info($processedMessage);
}
@@ -156,6 +158,7 @@ class LoggerIns
public function debug(string $message, array|Throwable $context = []): void
{
$processedMessage = $this->buildMessage($message, $context);
if (empty($processedMessage)) return;
Log::debug($processedMessage);
}
@@ -167,6 +170,7 @@ class LoggerIns
public function warning(string $message, array|Throwable $context = []): void
{
$processedMessage = $this->buildMessage($message, $context);
if (empty($processedMessage)) return;
Log::warning($processedMessage);
}
@@ -188,6 +192,7 @@ class LoggerIns
public function error(string $message, array|Throwable $context = []): void
{
$processedMessage = $this->buildMessage($message, $context);
if (empty($processedMessage)) return;
Log::error($processedMessage);
}
@@ -213,6 +218,7 @@ class LoggerIns
}
$processedMessage = $this->buildMessage($message, $context);
if (empty($processedMessage)) return;
Log::{$level}($processedMessage);
}
@@ -237,7 +243,136 @@ class LoggerIns
// 3. 添加名称前缀 [name]
$namePrefix = "<RP_NAME:{$this->name}>";
return $namePrefix . $processedMessage;
$result = $namePrefix . $processedMessage;
if ($this->isFilter()) return "";
return $result;
}
/**
* 判断当前日志是否需要过滤(核心过滤逻辑)
* @return bool true=需要过滤,false=保留日志
*/
private function isFilter(): bool
{
$logFilter = Config::getInstance()->logFilter;
// 无过滤规则时直接返回false(不过滤)
if (empty($logFilter)) {
return false;
}
// 1. 解析调用堆栈,获取业务代码的类名/方法名
$stackInfo = $this->parseBusinessStackInfo();
$className = $stackInfo['class'] ?? $this->name ?? '';
$methodName = $stackInfo['method'] ?? '';
// 2. 通配符匹配函数
$matchWildcard = function (string $pattern, string $value): bool {
// 空规则特殊处理:如:debug 拆分后类规则为空,代表匹配所有类
if ($pattern === '') {
return true;
}
// 将通配符*转换为正则的.*,并转义其他特殊字符
$regexPattern = preg_quote($pattern, '/');
$regexPattern = str_replace('\*', '.*', $regexPattern);
// 正则全程匹配
return preg_match('/^' . $regexPattern . '$/', $value) === 1;
};
// 3. 遍历过滤规则,匹配则返回true(需要过滤)
foreach ($logFilter as $filter) {
// 跳过非法过滤规则
if (!is_string($filter)) {
continue;
}
// 拆分规则为类规则和方法规则(最多拆2部分,避免方法名含:)
[$filterClassRule, $filterMethodRule] = array_pad(explode(':', $filter, 2), 2, '*');
// 匹配类名规则(支持通配符*
$isClassMatch = $matchWildcard($filterClassRule, $className);
// 匹配方法名规则(支持通配符*
$isMethodMatch = $matchWildcard($filterMethodRule, $methodName);
// 类和方法都匹配时,返回true(过滤该日志)
if ($isClassMatch && $isMethodMatch) {
return true;
}
}
// 无匹配规则,返回false(保留日志)
return false;
}
/**
* 解析调用堆栈,获取实际业务代码的类名和方法名
* @return array ['class' => 业务类名, 'method' => 业务方法名]
*/
private function parseBusinessStackInfo(): array
{
$stackInfo = ['class' => '', 'method' => ''];
// 获取调用栈(忽略参数,避免性能损耗,取前10层足够)
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
// 跳过的日志相关类/方法(根据实际项目调整)
$skipPatterns = [
'class' => [
'Monolog\\', // 跳过Monolog核心类
__CLASS__, // 跳过当前类自身
'app\\logger\\', // 跳过大日志封装类(根据你的项目调整)
],
'method' => [
'buildMessage', // 跳过当前方法
'isFilter', // 跳过过滤方法
'parseBusinessStackInfo', // 跳过堆栈解析方法
'replacePlaceholders',// 跳過占位符替换方法
'formatExceptionStackTrace', // 跳过异常格式化方法
'log', 'error', 'info', 'warning', 'debug' // 跳过日志级别方法
]
];
// 遍历调用栈,找到第一个业务代码层
foreach ($trace as $step) {
// 跳过无文件/无方法的栈帧
if (!isset($step['file']) || !isset($step['function'])) {
continue;
}
// 跳过日志相关类
$currentClass = $step['class'] ?? '';
$isSkipClass = false;
foreach ($skipPatterns['class'] as $pattern) {
if ($pattern && str_contains($currentClass, $pattern)) {
$isSkipClass = true;
break;
}
}
if ($isSkipClass) {
continue;
}
// 跳过日志相关方法
$currentMethod = $step['function'];
if (in_array($currentMethod, $skipPatterns['method'])) {
continue;
}
// 解析业务类名(简化为项目内相对路径,和原逻辑一致)
$projectRoot = defined('BASE_PATH') ? BASE_PATH : (function_exists('base_path') ? base_path() : '');
$file = $step['file'];
if ($projectRoot && str_starts_with($file, $projectRoot)) {
$file = substr($file, strlen($projectRoot) + 1);
// 转换为类名格式(app/controller/Index.php → app.controller.Index
$class = str_replace(['.php', '\\', '/'], ['', '.', '.'], $file);
} else {
$class = $currentClass ?: basename($file, '.php');
}
// 赋值并终止遍历(找到第一个业务层即可)
$stackInfo['class'] = trim($class);
$stackInfo['method'] = $currentMethod;
break;
}
return $stackInfo;
}
/**