hypef 九、日志

教程: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\LogService2
 public 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这种只不过实现的具体方法不一样。

你可能感兴趣的:(php,php)