mirror of
https://github.com/zhamao-robot/zhamao-logger.git
synced 2026-03-17 12:34:52 +08:00
initial commit
This commit is contained in:
parent
ab34f6f1f3
commit
06b76e4c96
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
### Composer 相关文件 ###
|
||||
composer.phar
|
||||
/vendor/
|
||||
composer.lock
|
||||
|
||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
||||
# composer.lock
|
||||
### cghooks.lock ###
|
||||
cghooks.lock
|
||||
70
.php-cs-fixer.php
Normal file
70
.php-cs-fixer.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'@Symfony' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'list_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one',
|
||||
],
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare',
|
||||
],
|
||||
],
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'single_line_comment_style' => [
|
||||
'comment_types' => [
|
||||
],
|
||||
],
|
||||
'yoda_style' => [
|
||||
'always_move_variable' => false,
|
||||
'equal' => false,
|
||||
'identical' => false,
|
||||
],
|
||||
'multiline_whitespace_before_semicolons' => [
|
||||
'strategy' => 'no_multi_line',
|
||||
],
|
||||
'constant_case' => [
|
||||
'case' => 'lower',
|
||||
],
|
||||
'class_attributes_separation' => true,
|
||||
'combine_consecutive_unsets' => true,
|
||||
'declare_strict_types' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_unused_imports' => true,
|
||||
'not_operator_with_successor_space' => false,
|
||||
'not_operator_with_space' => false,
|
||||
'ordered_class_elements' => true,
|
||||
'php_unit_strict' => false,
|
||||
'phpdoc_separation' => false,
|
||||
'single_quote' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'multiline_comment_opening_closing' => true,
|
||||
'phpdoc_summary' => false,
|
||||
'php_unit_test_class_requires_covers' => false,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/src')
|
||||
)
|
||||
->setUsingCache(false);
|
||||
62
composer.json
Normal file
62
composer.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "zhamao/logger",
|
||||
"description": "Another Console Logger for CLI Applications",
|
||||
"type": "library",
|
||||
"license": "Apache-2.0",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ZM\\Logger\\": "src/ZM/Logger"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "jerry",
|
||||
"email": "admin@zhamao.me"
|
||||
},
|
||||
{
|
||||
"name": "sunxyw",
|
||||
"email": "dev@sunxyw.xyz"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"require": {
|
||||
"php": "^7.2 || ^7.3 || ^7.4 || ^8.0 || ^8.1",
|
||||
"psr/log": "^1 || ^2 || ^3"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"brainmaestro/composer-git-hooks": "^2.8"
|
||||
},
|
||||
"extra": {
|
||||
"hooks": {
|
||||
"post-merge": "composer install",
|
||||
"pre-commit": [
|
||||
"echo committing as $(git config user.name)",
|
||||
"composer cs-fix -- --diff"
|
||||
],
|
||||
"pre-push": [
|
||||
"composer cs-fix -- --dry-run --diff",
|
||||
"composer analyse"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"[ $COMPOSER_DEV_MODE -eq 0 ] || vendor/bin/cghooks add"
|
||||
],
|
||||
"analyse": "phpstan analyse --memory-limit 300M",
|
||||
"cs-fix": "php-cs-fixer fix"
|
||||
}
|
||||
}
|
||||
8
phpstan.neon
Normal file
8
phpstan.neon
Normal file
@ -0,0 +1,8 @@
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
level: 4
|
||||
paths:
|
||||
- ./src/
|
||||
ignoreErrors:
|
||||
- '#Used constant OS_TYPE_(LINUX|WINDOWS) not found#'
|
||||
- '#Unsafe usage of new static#'
|
||||
199
src/ZM/Logger/ConsoleColor.php
Normal file
199
src/ZM/Logger/ConsoleColor.php
Normal file
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Logger;
|
||||
|
||||
/**
|
||||
* @method static none($text = null)
|
||||
* @method static bold($text = null)
|
||||
* @method static dark($text = null)
|
||||
* @method static italic($text = null)
|
||||
* @method static underline($text = null)
|
||||
* @method static blink($text = null)
|
||||
* @method static rapid_blink($text = null)
|
||||
* @method static reverse($text = null)
|
||||
* @method static concealed($text = null)
|
||||
* @method static strike($text = null)
|
||||
* @method static default($text = null)
|
||||
* @method static black($text = null)
|
||||
* @method static red($text = null)
|
||||
* @method static green($text = null)
|
||||
* @method static yellow($text = null)
|
||||
* @method static blue($text = null)
|
||||
* @method static magenta($text = null)
|
||||
* @method static cyan($text = null)
|
||||
* @method static gray($text = null)
|
||||
* @method static white($text = null)
|
||||
* @method static bright_black($text = null)
|
||||
* @method static bright_red($text = null)
|
||||
* @method static bright_green($text = null)
|
||||
* @method static bright_yellow($text = null)
|
||||
* @method static bright_blue($text = null)
|
||||
* @method static bright_magenta($text = null)
|
||||
* @method static bright_cyan($text = null)
|
||||
* @method static bright_white($text = null)
|
||||
* @method static bg_default($text = null)
|
||||
* @method static bg_black($text = null)
|
||||
* @method static bg_red($text = null)
|
||||
* @method static bg_green($text = null)
|
||||
* @method static bg_yellow($text = null)
|
||||
* @method static bg_blue($text = null)
|
||||
* @method static bg_magenta($text = null)
|
||||
* @method static bg_cyan($text = null)
|
||||
* @method static bg_gray($text = null)
|
||||
* @method static bg_white($text = null)
|
||||
* @method static bg_bright_black($text = null)
|
||||
* @method static bg_bright_red($text = null)
|
||||
* @method static bg_bright_green($text = null)
|
||||
* @method static bg_bright_yellow($text = null)
|
||||
* @method static bg_bright_blue($text = null)
|
||||
* @method static bg_bright_magenta($text = null)
|
||||
* @method static bg_bright_cyan($text = null)
|
||||
* @method static bg_bright_white($text = null)
|
||||
*/
|
||||
class ConsoleColor
|
||||
{
|
||||
public const RESET = 0;
|
||||
|
||||
public const STYLES = [
|
||||
'none' => null,
|
||||
'bold' => '1', // 加粗
|
||||
'dark' => '2', // 昏暗
|
||||
'italic' => '3', // 倾斜
|
||||
'underline' => '4', // 下划线
|
||||
'blink' => '5', // 闪烁
|
||||
'rapid_blink' => '6', // 快速闪烁,兼容性不佳
|
||||
'reverse' => '7', // 反转
|
||||
'concealed' => '8', // 遮盖,兼容性不佳
|
||||
'strike' => '9', // 删除线
|
||||
|
||||
'default' => '39', // 默认颜色
|
||||
'black' => '30', // 黑色
|
||||
'red' => '31', // 红色
|
||||
'green' => '32', // 绿色
|
||||
'yellow' => '33', // 黄色,各终端表现不一,建议使用 bright_yellow 替代
|
||||
'blue' => '34', // 蓝色
|
||||
'magenta' => '35', // 紫色
|
||||
'cyan' => '36', // 青色
|
||||
// 'white' => '37', // 白色,实际表现为灰色
|
||||
'gray' => '37',
|
||||
'white' => '97', // 此处为亮白色
|
||||
|
||||
'bright_black' => '90', // 亮黑色(暗灰色)
|
||||
'bright_red' => '91', // 亮红色
|
||||
'bright_green' => '92', // 亮绿色
|
||||
'bright_yellow' => '93', // 亮黄色
|
||||
'bright_blue' => '94', // 亮蓝色
|
||||
'bright_magenta' => '95', // 亮紫色
|
||||
'bright_cyan' => '96', // 亮青色
|
||||
'bright_white' => '97', // 亮白色
|
||||
|
||||
'bg_default' => '49',
|
||||
'bg_black' => '40',
|
||||
'bg_red' => '41',
|
||||
'bg_green' => '42',
|
||||
'bg_yellow' => '43',
|
||||
'bg_blue' => '44',
|
||||
'bg_magenta' => '45',
|
||||
'bg_cyan' => '46',
|
||||
'bg_gray' => '47',
|
||||
'bg_white' => '107',
|
||||
|
||||
'bg_bright_black' => '100',
|
||||
'bg_bright_red' => '101',
|
||||
'bg_bright_green' => '102',
|
||||
'bg_bright_yellow' => '103',
|
||||
'bg_bright_blue' => '104',
|
||||
'bg_bright_magenta' => '105',
|
||||
'bg_bright_cyan' => '106',
|
||||
'bg_bright_white' => '107',
|
||||
];
|
||||
|
||||
protected $styles = [];
|
||||
|
||||
protected $text = '';
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
$this->addStyle($name);
|
||||
if (isset($arguments[0])) {
|
||||
$this->setText($arguments[0]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $arguments)
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->addStyle($name);
|
||||
if (isset($arguments[0])) {
|
||||
$instance->setText($arguments[0]);
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$style_code = $this->getStylesCode();
|
||||
|
||||
return sprintf("\033[%sm%s\033[%dm", $style_code, $this->text, self::RESET);
|
||||
}
|
||||
|
||||
public static function apply(array $styles, string $text): ConsoleColor
|
||||
{
|
||||
$instance = new self();
|
||||
$instance->setText($text);
|
||||
$instance->applyStyles($styles);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function applyStyles(array $styles): ConsoleColor
|
||||
{
|
||||
$this->styles = array_merge($this->styles, $styles);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setText(string $text): ConsoleColor
|
||||
{
|
||||
$this->text = $text;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addStyle(string $style): ConsoleColor
|
||||
{
|
||||
$style = TextUtil::separatorToCamel($style);
|
||||
if (array_key_exists($style, self::STYLES)) {
|
||||
$this->styles[] = $style;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getStylesCode(): string
|
||||
{
|
||||
array_walk($this->styles, static function (&$style) {
|
||||
// 4bit (classic)
|
||||
if (array_key_exists($style, self::STYLES)) {
|
||||
$style = self::STYLES[$style];
|
||||
return;
|
||||
}
|
||||
// 8bit (256color)
|
||||
if (str_contains($style, 'color')) {
|
||||
preg_match('~^(bg_)?color_(\d{1,3})$~', $style, $matches);
|
||||
$type = $matches[1] === 'bg_' ? '48' : '38';
|
||||
$value = $matches[2];
|
||||
$style = "{$type};5;{$value}";
|
||||
return;
|
||||
}
|
||||
// 24bit (rgb)
|
||||
if (str_contains($style, 'rgb')) {
|
||||
preg_match('~^(bg_)?rgb_(\d{1,3})_(\d{1,3})_(\d{1,3})$~', $style, $matches);
|
||||
$type = $matches[1] === 'bg_' ? '48' : '38';
|
||||
[, , $r, $g, $b] = $matches;
|
||||
$style = "{$type};2;{$r};{$g};{$b}";
|
||||
}
|
||||
});
|
||||
|
||||
return implode(';', $this->styles);
|
||||
}
|
||||
}
|
||||
170
src/ZM/Logger/ConsoleLogger.php
Normal file
170
src/ZM/Logger/ConsoleLogger.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Logger;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Psr\Log\InvalidArgumentException;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
class ConsoleLogger extends AbstractLogger
|
||||
{
|
||||
public const VERSION = '1.0.0-alpha';
|
||||
|
||||
public static $format = '[%date%] [%level%] %process%%body%';
|
||||
|
||||
public static $date_format = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* @var string[][] 颜色表
|
||||
*/
|
||||
protected static $styles = [
|
||||
LogLevel::EMERGENCY => ['blink', 'white', 'bg_bright_red'],
|
||||
LogLevel::ALERT => ['white', 'bg_bright_red'],
|
||||
LogLevel::CRITICAL => ['underline', 'red'],
|
||||
LogLevel::ERROR => ['red'],
|
||||
LogLevel::WARNING => ['bright_yellow'],
|
||||
LogLevel::NOTICE => ['cyan'],
|
||||
LogLevel::INFO => ['green'],
|
||||
LogLevel::DEBUG => ['gray'],
|
||||
];
|
||||
|
||||
protected static $levels = [
|
||||
LogLevel::EMERGENCY, // 0
|
||||
LogLevel::ALERT, // 1
|
||||
LogLevel::CRITICAL, // 2
|
||||
LogLevel::ERROR, // 3
|
||||
LogLevel::WARNING, // 4
|
||||
LogLevel::NOTICE, // 5
|
||||
LogLevel::INFO, // 6
|
||||
LogLevel::DEBUG, // 7
|
||||
];
|
||||
|
||||
protected static $logLevel;
|
||||
|
||||
protected $exceptionHandler;
|
||||
|
||||
public function __construct($logLevel = LogLevel::INFO)
|
||||
{
|
||||
self::$logLevel = array_flip(self::$levels)[$logLevel];
|
||||
//ExceptionHandler::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
*/
|
||||
public static function getStyles(): array
|
||||
{
|
||||
return self::$styles;
|
||||
}
|
||||
|
||||
public function colorize($string, $level): string
|
||||
{
|
||||
$string = $this->stringify($string);
|
||||
$styles = self::$styles[$level] ?? [];
|
||||
return ConsoleColor::apply($styles, $string)->__toString();
|
||||
}
|
||||
|
||||
public function trace(): void
|
||||
{
|
||||
$log = "Stack trace:\n";
|
||||
$trace = debug_backtrace();
|
||||
//array_shift($trace);
|
||||
foreach ($trace as $i => $t) {
|
||||
if (!isset($t['file'])) {
|
||||
$t['file'] = 'unknown';
|
||||
}
|
||||
if (!isset($t['line'])) {
|
||||
$t['line'] = 0;
|
||||
}
|
||||
$log .= "#{$i} {$t['file']}({$t['line']}): ";
|
||||
if (isset($t['object']) && is_object($t['object'])) {
|
||||
$log .= get_class($t['object']) . '->';
|
||||
}
|
||||
$log .= "{$t['function']}()\n";
|
||||
}
|
||||
$log = $this->colorize($log, LogLevel::DEBUG);
|
||||
echo $log;
|
||||
}
|
||||
|
||||
public function getTrace(): string
|
||||
{
|
||||
// if (self::$info_level !== null && self::$info_level == 4) {
|
||||
// $trace = debug_backtrace()[2] ?? ['file' => '', 'function' => ''];
|
||||
// $trace = '[' . ($trace['class'] ?? '') . ':' . ($trace['function'] ?? '') . '] ';
|
||||
// }
|
||||
// return $trace ?? '';
|
||||
return '';
|
||||
}
|
||||
|
||||
// $trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
|
||||
// $trace = '[' . ($trace['class'] ?? '') . ':' . ($trace['function'] ?? '') . '] ';
|
||||
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
if (!in_array($level, self::$levels, true)) {
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
if (array_flip(self::$levels)[$level] > self::$logLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$output = str_replace(
|
||||
['%date%', '%level%', '%body%'],
|
||||
[date(self::$date_format), strtoupper(substr($level, 0, 4)), $message],
|
||||
self::$format
|
||||
);
|
||||
$output = $this->interpolate($output, $context);
|
||||
echo $this->colorize($output, $level) . "\n";
|
||||
}
|
||||
|
||||
public static function getVersion(): string
|
||||
{
|
||||
return self::VERSION;
|
||||
}
|
||||
|
||||
private function stringify($item)
|
||||
{
|
||||
if (is_object($item) && method_exists($item, '__toString')) {
|
||||
return $item;
|
||||
}
|
||||
if (is_string($item) || is_numeric($item)) {
|
||||
return $item;
|
||||
}
|
||||
if (is_callable($item)) {
|
||||
return '{Closure}';
|
||||
}
|
||||
if (is_bool($item)) {
|
||||
return $item ? '*True*' : '*False*';
|
||||
}
|
||||
if (is_array($item)) {
|
||||
return json_encode(
|
||||
$item,
|
||||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_LINE_TERMINATORS
|
||||
);
|
||||
}
|
||||
if (is_resource($item)) {
|
||||
return '{Resource}';
|
||||
}
|
||||
if (is_null($item)) {
|
||||
return 'NULL';
|
||||
}
|
||||
return '{Not Stringable Object:' . get_class($item) . '}';
|
||||
}
|
||||
|
||||
private function interpolate($message, array $context = [])
|
||||
{
|
||||
if (is_array($message)) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
$replace = [];
|
||||
foreach ($context as $key => $value) {
|
||||
$replace['{' . $key . '}'] = $this->stringify($value);
|
||||
}
|
||||
|
||||
return strtr($message, $replace);
|
||||
}
|
||||
}
|
||||
53
src/ZM/Logger/ConsolePrettyPrinter.php
Normal file
53
src/ZM/Logger/ConsolePrettyPrinter.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Logger;
|
||||
|
||||
/**
|
||||
* 输出漂亮的表格参数形式,支持自适应终端大小
|
||||
*
|
||||
* Class ConsolePrettyPrinter
|
||||
*/
|
||||
class ConsolePrettyPrinter
|
||||
{
|
||||
protected $params;
|
||||
|
||||
protected $head;
|
||||
|
||||
protected $foot;
|
||||
|
||||
public function __construct(array $params, $head = '', $foot = '')
|
||||
{
|
||||
$this->params = $params;
|
||||
$this->head = $head === '' ? str_pad('', 65, '=') : $head;
|
||||
$this->foot = $foot === '' ? str_pad('', 65, '=') : $foot;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $params): ConsolePrettyPrinter
|
||||
{
|
||||
return new static($params);
|
||||
}
|
||||
|
||||
public function printAll(): void
|
||||
{
|
||||
$this->printHead();
|
||||
$this->printBody();
|
||||
$this->printFoot();
|
||||
}
|
||||
|
||||
public function printHead()
|
||||
{
|
||||
// TODO: 写头
|
||||
}
|
||||
|
||||
public function printBody()
|
||||
{
|
||||
// TODO: 写主体
|
||||
}
|
||||
|
||||
public function printFoot()
|
||||
{
|
||||
// TODO: 写尾
|
||||
}
|
||||
}
|
||||
14
src/ZM/Logger/TextUtil.php
Normal file
14
src/ZM/Logger/TextUtil.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ZM\Logger;
|
||||
|
||||
class TextUtil
|
||||
{
|
||||
public static function separatorToCamel(string $string, string $separator = '_'): string
|
||||
{
|
||||
$string = $separator . str_replace($separator, ' ', strtolower($string));
|
||||
return ltrim(str_replace(' ', '', ucwords($string)), $separator);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user