mirror of
https://github.com/zhamao-robot/zhamao-logger.git
synced 2026-07-27 18:45:36 +08:00
Compare commits
2 Commits
1.1.6
...
add-caller
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ee6e43f06 | ||
|
|
9da98fde5a |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "zhamao/logger",
|
"name": "zhamao/logger",
|
||||||
"description": "Another Colorful Console Logger for CLI Applications",
|
"description": "Another Console Logger for CLI Applications",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "jerry",
|
"name": "jerry",
|
||||||
"email": "github@cwcc.me"
|
"email": "admin@zhamao.me"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sunxyw",
|
"name": "sunxyw",
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ use Psr\Log\LogLevel;
|
|||||||
|
|
||||||
class ConsoleLogger extends AbstractLogger
|
class ConsoleLogger extends AbstractLogger
|
||||||
{
|
{
|
||||||
public const VERSION = '1.1.6';
|
public const VERSION = '1.1.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志输出格式
|
* 日志输出格式
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public static $format = '[%date%] [%level%] %body%';
|
public static $format = '[%date%] [%level%] %caller% %body%';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志输出日期格式
|
* 日志输出日期格式
|
||||||
@@ -93,15 +93,13 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
*/
|
*/
|
||||||
protected $decorated;
|
protected $decorated;
|
||||||
|
|
||||||
protected $use_stderr = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 ConsoleLogger 实例
|
* 创建一个 ConsoleLogger 实例
|
||||||
*
|
*
|
||||||
* @param string $level 日志等级
|
* @param string $level 日志等级
|
||||||
* @param null|resource $stream
|
* @param null|resource $stream
|
||||||
*/
|
*/
|
||||||
public function __construct(string $level = LogLevel::INFO, $stream = null, bool $decorated = true, bool $use_stderr = false)
|
public function __construct(string $level = LogLevel::INFO, $stream = null, bool $decorated = true)
|
||||||
{
|
{
|
||||||
$this->decorated = $decorated;
|
$this->decorated = $decorated;
|
||||||
self::$log_level = $this->castLogLevel($level);
|
self::$log_level = $this->castLogLevel($level);
|
||||||
@@ -115,18 +113,12 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
if (($stat['mode'] & 0170000) === 0100000) { // whether is regular file
|
if (($stat['mode'] & 0170000) === 0100000) { // whether is regular file
|
||||||
$this->decorated = false;
|
$this->decorated = false;
|
||||||
} else {
|
} else {
|
||||||
$this->decorated
|
$this->decorated =
|
||||||
= PHP_OS_FAMILY !== 'Windows' // linux or unix
|
PHP_OS_FAMILY !== 'Windows' // linux or unix
|
||||||
&& function_exists('posix_isatty')
|
&& function_exists('posix_isatty')
|
||||||
&& posix_isatty($stream); // whether is interactive terminal
|
&& posix_isatty($stream); // whether is interactive terminal
|
||||||
}
|
}
|
||||||
$this->stream = $stream;
|
$this->stream = $stream;
|
||||||
$this->use_stderr = $use_stderr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLevel(string $level): void
|
|
||||||
{
|
|
||||||
self::$log_level = $this->castLogLevel($level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,6 +139,27 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
return self::VERSION;
|
return self::VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入字段到指定字段之前,或之后
|
||||||
|
* @param string $field 字段名
|
||||||
|
* @param string $alter 要插入的字段
|
||||||
|
* @param string $position 插入位置,before 或 after
|
||||||
|
*/
|
||||||
|
public static function alterFormat(string $field, string $alter, string $position = 'before'): void
|
||||||
|
{
|
||||||
|
$format = self::$format;
|
||||||
|
$pos = strpos($format, $field);
|
||||||
|
if ($pos === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($position === 'before') {
|
||||||
|
$format = substr_replace($format, $alter, $pos, 0);
|
||||||
|
} else {
|
||||||
|
$format = substr_replace($format, $alter, $pos + strlen($field), 0);
|
||||||
|
}
|
||||||
|
self::$format = $format;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加静态上下文
|
* 添加静态上下文
|
||||||
*/
|
*/
|
||||||
@@ -170,7 +183,7 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
{
|
{
|
||||||
$log = 'Stack trace:' . PHP_EOL;
|
$log = 'Stack trace:' . PHP_EOL;
|
||||||
$trace = debug_backtrace();
|
$trace = debug_backtrace();
|
||||||
// array_shift($trace);
|
//array_shift($trace);
|
||||||
foreach ($trace as $i => $t) {
|
foreach ($trace as $i => $t) {
|
||||||
if (!isset($t['file'])) {
|
if (!isset($t['file'])) {
|
||||||
$t['file'] = 'unknown';
|
$t['file'] = 'unknown';
|
||||||
@@ -179,7 +192,6 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
$t['line'] = 0;
|
$t['line'] = 0;
|
||||||
}
|
}
|
||||||
$log .= "#{$i} {$t['file']}({$t['line']}): ";
|
$log .= "#{$i} {$t['file']}({$t['line']}): ";
|
||||||
/* @phpstan-ignore-next-line */
|
|
||||||
if (isset($t['object']) && is_object($t['object'])) {
|
if (isset($t['object']) && is_object($t['object'])) {
|
||||||
$log .= get_class($t['object']) . '->';
|
$log .= get_class($t['object']) . '->';
|
||||||
}
|
}
|
||||||
@@ -212,65 +224,60 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
return ConsoleColor::apply($styles, $string)->__toString();
|
return ConsoleColor::apply($styles, $string)->__toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public function log($level, $message, array $context = []): void
|
public function log($level, $message, array $context = []): void
|
||||||
{
|
{
|
||||||
$level = $this->castLogLevel($level);
|
$level = $this->castLogLevel($level);
|
||||||
|
|
||||||
$log_replace = [
|
|
||||||
'%date%' => date(self::$date_format),
|
|
||||||
'%level_long%' => strtoupper(self::$levels[$level]),
|
|
||||||
'%level%' => strtoupper(substr(self::$levels[$level], 0, 4)),
|
|
||||||
'%body%' => $message,
|
|
||||||
'%level_short%' => strtoupper(substr(self::$levels[$level], 0, 1)),
|
|
||||||
];
|
|
||||||
|
|
||||||
$output = str_replace(array_keys($log_replace), array_values($log_replace), self::$format);
|
|
||||||
$output = $this->interpolate($output, array_merge($this->static_context, $context));
|
|
||||||
|
|
||||||
foreach ($this->log_callbacks as $callback) {
|
|
||||||
if ($callback($level, $output, $message, $context, $this->shouldLog($level)) === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->shouldLog($level)) {
|
if (!$this->shouldLog($level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$output = str_replace(
|
||||||
|
['%date%', '%level%', '%body%', '%caller%'],
|
||||||
|
[
|
||||||
|
date(self::$date_format),
|
||||||
|
strtoupper(substr(self::$levels[$level], 0, 4)),
|
||||||
|
$message,
|
||||||
|
$this->getCaller(),
|
||||||
|
],
|
||||||
|
self::$format
|
||||||
|
);
|
||||||
|
$output = $this->interpolate($output, array_merge($this->static_context, $context));
|
||||||
|
|
||||||
|
foreach ($this->log_callbacks as $callback) {
|
||||||
|
if ($callback($level, $output, $message, $context) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->decorated) {
|
if ($this->decorated) {
|
||||||
$output = $this->colorize($output, $level) . PHP_EOL;
|
$output = $this->colorize($output, $level) . PHP_EOL;
|
||||||
} else {
|
} else {
|
||||||
$output = $output . PHP_EOL;
|
$output .= PHP_EOL;
|
||||||
}
|
}
|
||||||
// use stream
|
// use stream
|
||||||
if ($this->stream) {
|
if ($this->stream) {
|
||||||
fwrite($this->stream, $output);
|
fwrite($this->stream, $output);
|
||||||
fflush($this->stream);
|
fflush($this->stream);
|
||||||
} else {
|
|
||||||
if ($level <= 4 && $this->use_stderr) {
|
|
||||||
fwrite(STDERR, $output);
|
|
||||||
} else {
|
} else {
|
||||||
// use plain text output
|
// use plain text output
|
||||||
echo $output;
|
echo $output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function setDecorated(bool $decorated): void
|
|
||||||
{
|
|
||||||
$this->decorated = $decorated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换日志等级
|
* 转换日志等级
|
||||||
*/
|
*/
|
||||||
protected function castLogLevel(string $level): int
|
private function castLogLevel(string $level): int
|
||||||
{
|
{
|
||||||
if (in_array($level, self::$levels, true)) {
|
if (in_array($level, self::$levels, true)) {
|
||||||
return array_flip(self::$levels)[$level];
|
return array_flip(self::$levels)[$level];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidArgumentException('Invalid log level: ' . $level);
|
throw new InvalidArgumentException('无效的日志等级');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,7 +285,7 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
*
|
*
|
||||||
* @param mixed $item 日志内容
|
* @param mixed $item 日志内容
|
||||||
*/
|
*/
|
||||||
protected function stringify($item): string
|
private function stringify($item): string
|
||||||
{
|
{
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case is_callable($item):
|
case is_callable($item):
|
||||||
@@ -312,7 +319,7 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
/**
|
/**
|
||||||
* 判断是否应该记录该等级日志
|
* 判断是否应该记录该等级日志
|
||||||
*/
|
*/
|
||||||
protected function shouldLog(int $level): bool
|
private function shouldLog(int $level): bool
|
||||||
{
|
{
|
||||||
return $level <= self::$log_level;
|
return $level <= self::$log_level;
|
||||||
}
|
}
|
||||||
@@ -323,7 +330,7 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
* @param string $message 日志内容
|
* @param string $message 日志内容
|
||||||
* @param array $context 变量列表
|
* @param array $context 变量列表
|
||||||
*/
|
*/
|
||||||
protected function interpolate(string $message, array $context = []): string
|
private function interpolate(string $message, array $context = []): string
|
||||||
{
|
{
|
||||||
$replace = [];
|
$replace = [];
|
||||||
foreach ($context as $key => $value) {
|
foreach ($context as $key => $value) {
|
||||||
@@ -332,4 +339,23 @@ class ConsoleLogger extends AbstractLogger
|
|||||||
|
|
||||||
return strtr($message, $replace);
|
return strtr($message, $replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取调用者信息
|
||||||
|
*/
|
||||||
|
private function getCaller(): string
|
||||||
|
{
|
||||||
|
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||||
|
$caller = $trace[1] ?? [];
|
||||||
|
$file = $caller['file'] ?? '';
|
||||||
|
if ($file) {
|
||||||
|
// 截取路径,获取 src 之后至多两级目录
|
||||||
|
$path = substr($file, strpos($file, 'src') + 4);
|
||||||
|
$path = explode(DIRECTORY_SEPARATOR, $path);
|
||||||
|
$path = array_slice($path, 0, 2);
|
||||||
|
} else {
|
||||||
|
$path = ['unknown'];
|
||||||
|
}
|
||||||
|
return implode(' > ', $path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ class TablePrinter
|
|||||||
$line_data[$current_line] = [
|
$line_data[$current_line] = [
|
||||||
'used' => $valid_width - $k_len - 2 - $partial_v_len,
|
'used' => $valid_width - $k_len - 2 - $partial_v_len,
|
||||||
'can_put_second' => false,
|
'can_put_second' => false,
|
||||||
'lines' => $k . ': '
|
'lines' => $k . ': ' .
|
||||||
. ConsoleColor::apply([$this->value_color], $partial_v . str_pad('', $valid_width - $k_len - 2 - $partial_v_len, '.')),
|
ConsoleColor::apply([$this->value_color], $partial_v . str_pad('', $valid_width - $k_len - 2 - $partial_v_len, '.')),
|
||||||
];
|
];
|
||||||
++$current_line;
|
++$current_line;
|
||||||
// 下一个参数
|
// 下一个参数
|
||||||
@@ -237,13 +237,8 @@ class TablePrinter
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$size = exec('stty size 2>/dev/null');
|
$size = exec('stty size 2>/dev/null');
|
||||||
// in case stty is not available
|
|
||||||
if (empty($size)) {
|
|
||||||
$size = 0;
|
|
||||||
} else {
|
|
||||||
$size = (int) explode(' ', trim($size))[1];
|
$size = (int) explode(' ', trim($size))[1];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (empty($size)) {
|
if (empty($size)) {
|
||||||
return $this->terminal_size = 79;
|
return $this->terminal_size = 79;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user