教程:Hyperf
hyperf结合monolog是属于低耦合,通过配置文件和调用工厂类设置参数设置实例。
monolog详见Monolog 修改_lsswear的博客-CSDN博客,理解monolog原理之后若有对应的报错比较好改。
一、使用LoggerFactory
#/config/autoload/logger.php
return ['default' => ['handler' => ['class' => Monolog\Handler\StreamHandler::class,'constructor' => ['stream' => BASE_PATH . '/runtime/logs/hyperf.log','level' => Monolog\Logger::DEBUG,],],'formatter' => ['class' => Monolog\Formatter\LineFormatter::class,'constructor' => ['format' => null,'dateFormat' => 'Y-m-d H:i:s','allowInlineLineBreaks' => true,],],],'test1' => ['handler' => ['class' => Monolog\Handler\RotatingFileHandler::class,'constructor' => ['stream' => BASE_PATH . '/runtime/logs/test1.log','level' => Monolog\Logger::DEBUG,'filename' => BASE_PATH . '/runtime/logs/test1.log',],],'formatter' => ['class' => Monolog\Formatter\JsonFormatter::class,'constructor' => ['format' => null,'dateFormat' => 'Y-m-d H:i:s','allowInlineLineBreaks' => true,],],],
];
#/config/autoload/dependencies.php
return [Psr\Log\LoggerInterface::class => Hyperf\Logger\LoggerFactory::class,
];#App\Service\LogService
class LogService
{/*** @Inject* @var LoggerInterface*/protected $logger;public function __construct(LoggerFactory $loggerFactory){// 第一个参数对应日志的 name, 第二个参数对应 config/autoload/logger.php 内的 key//$this->logger = $loggerFactory->get('log', 'default');$this->logger = $loggerFactory->get('log1', 'test1');}public function info($info, $content = []){$this->logger->info($info, $content);}
}#App\Controller\TestController
/*** @AutoController()*/
class TestController extends AbstractController
{/*** @Inject* @var LogService*/private $logserver;
public function testlog1(){$this->logserver->info('test1', ['qwe' => '123', 'asd' => '6778']);}
}#日志输出 /runtime/logs/test1-2023-07-19.log
{"message":"test1","context":{"qwe":"123","asd":"6778"},"level":200,"level_name":"INFO","channel":"log1","datetime":"2023-07-19T09:57:04.018739+00:00","extra":{}}
若dependencies.php没设置,就是官网的写法,不能用Inject注入。
/*** @var \Psr\Log\LoggerInterface*/protected $logger;public function __construct(LoggerFactory $loggerFactory){// 第一个参数对应日志的 name, 第二个参数对应 config/autoload/logger.php 内的 key$this->logger = $loggerFactory->get('log', 'default');}
二、静态调用
因为是静态调用,会不走构造,所以日志实例对象不能放到构造。
#App\Service\LogService2
use Hyperf\Utils\ApplicationContext;
use \Hyperf\Logger\LoggerFactory;class LogService2
{static $obj;static $logger;public static function get($name = "app"){self::$logger = self::$obj->get($name, 'test1');return self::$logger;}public static function __callStatic($funcname, $arguments){$container = ApplicationContext::getContainer();self::$obj = $container->get(LoggerFactory::class);$name = isset($arguments[2]) ? $arguments[2] : 'test1';$msg = $arguments[0];$data = isset($arguments[1]) ? $arguments[1] : [];self::get($name);self::$logger->$funcname($msg, $data);}
}#App\Controller\TestController
public function testlog1(){//$this->logserver->info('test1', ['qwe' => '123', 'asd' => '6778']);LogService2::info('test2', ['qqq' => '555']);}#日志输出
{"message":"test2","context":{"qqq":"555"},"level":200,"level_name":"INFO","channel":"test1","datetime":"2023-07-20T09:49:11.443349+00:00","extra":{}}
三、改造
#App\Service\MlogService
class MlogService extends RotatingFileHandler
{protected function streamWrite($stream, array $record): void{$str = (string) $record['formatted'] . PHP_EOL . var_export($record['context'], true) . PHP_EOL;fwrite($stream, $str);}
}#/config/autoload/logger.php
'test2' => ['handler' => ['class' => App\Service\MlogService::class,'constructor' => ['stream' => BASE_PATH . '/runtime/logs/test2.log','level' => Monolog\Logger::DEBUG,'filename' => BASE_PATH . '/runtime/logs/test2.log',],],'formatter' => ['class' => Monolog\Formatter\LineFormatter::class,'constructor' => ['format' => "[%datetime%] %channel%.%level_name%: %message%\n%extra%\n",'dateFormat' => 'Y-m-d H:i:s','allowInlineLineBreaks' => true,],],],#App\Service\LogService2
public static function get($name = "app"){//self::$logger = self::$obj->get($name, 'test1');self::$logger = self::$obj->get($name, 'test2');return self::$logger;}#App\Controller\TestController
public function testlog2(){LogService2::info('test3', ['qqq1' => '555-0']);}#日志输出
[2023-07-21 09:30:38] test1.INFO: test3
[]array ('qqq1' => '555-0',
)
可以看到数组是有格式输出,这样再看起来就非常舒服。
四、stdout日志
根据文档,大概意思就是框架默认是使用Hyperf\Framework\Logger\StdoutLogger类输入日志到命令行,通过设置dependencies.php改变处理类。
使用举例
#Hyperf\DbConnection\Connection
public function __construct(ContainerInterface $container, DbPool $pool, array $config){parent::__construct($container, $pool);$this->factory = $container->get(ConnectionFactory::class);$this->config = $config;$this->logger = $container->get(StdoutLoggerInterface::class);$this->reconnect();}public function reconnect(): bool{……$this->connection->setReconnector(function ($connection) {$this->logger->warning('Database connection refreshing.');if ($connection instanceof \Hyperf\Database\Connection) {$this->refresh($connection);}});……}
#Hyperf\Framework\Logger\StdoutLogger
public function warning($message, array $context = []): void{$this->log(LogLevel::WARNING, $message, $context);}
/*** {@inheritdoc}*/public function log($level, $message, array $context = []): void{$config = $this->config->get(StdoutLoggerInterface::class, ['log_level' => []]);if (! in_array($level, $config['log_level'], true)) {return;}$keys = array_keys($context);$tags = [];foreach ($keys as $k => $key) {if (in_array($key, $this->tags, true)) {$tags[$key] = $context[$key];unset($keys[$k]);}}$search = array_map(function ($key) {return sprintf('{%s}', $key);}, $keys);$message = str_replace($search, $context, $this->getMessage((string) $message, $level, $tags));$this->output->writeln($message);}#Hyperf\Framework\ConfigProvider
use Hyperf\Contract\ApplicationInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Framework\Logger\StdoutLogger;public function __invoke(): array{return ['dependencies' => [ApplicationInterface::class => ApplicationFactory::class,StdoutLoggerInterface::class => StdoutLogger::class,],'annotations' => ['scan' => ['paths' => [__DIR__,],],],];}
使用之前设置的可静态调用Service类做处理,调用monolog直接将输出屏幕的内容输出到日志中。
#App\Service\StdoutLoggerService
class StdoutLoggerService
{public function __invoke(ContainerInterface $container){return LogService2::get('sys');}
}
#App\Service\LogService2public static function get($name = "app"){//self::$logger = self::$obj->get($name, 'test1');if (empty(self::$obj)) {$container = ApplicationContext::getContainer();self::$obj = $container->get(LoggerFactory::class);}self::$logger = self::$obj->get($name, 'test2');return self::$logger;}#
根据两段代码,调用都是info、warning这种只不过实现的具体方法不一样。