tp6提供了默认的file方式输出日志,而有些时候我们希望以其他形式输出。
比如在k8s中,日志需要输出到控制台才可以被promtail抓取。基于这种考量,下面我们来自己制作一个日志输出的channel。
首先,我们在log.php中新增自己的log channel = console
// 默认日志记录通道
'default' => 'console',
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
// 其它日志通道配置
'console' => [
// 日志记录方式
'type' => 'app\\service\\Console',
],
然后在 app\service\Console.php 中编写自己的日志
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <[email protected]>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\service;
use think\App;
use think\contract\LogHandlerInterface;
/**
* 本地化调试输出到文件
*/
class Console implements LogHandlerInterface
{
/**
* 配置参数
* @var array
*/
protected $config = [
'time_format' => 'c',
'single' => false,
'file_size' => 2097152,
'path' => '',
'apart_level' => [],
'max_files' => 0,
'json' => true,
'json_options' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
'format' => '[%s][%s] %s',
];
private $stdout = null;
// 实例化并传入参数
public function __construct(App $app, $config = [])
{
if (is_array($config)) {
$this->config = array_merge($this->config, $config);
}
if (empty($this->config['format'])) {
$this->config['format'] = '[%s][%s] %s';
}
$this->stdout = $this->openOutputStream();
}
/**
* 日志写入接口
* @access public
* @param array $log 日志信息
* @return bool
*/
public function save(array $log): bool
{
$info = [];
// 日志信息封装
$time = \DateTime::createFromFormat('0.u00 U', microtime())->setTimezone(new \DateTimeZone(date_default_timezone_get()))->format($this->config['time_format']);
foreach ($log as $type => $val) {
$message = [];
foreach ($val as $msg) {
if (!is_string($msg)) {
$msg = var_export($msg, true);
}
$message[] = $this->config['json'] ?
json_encode(['time' => $time, 'type' => $type, 'msg' => $msg], $this->config['json_options']) :
sprintf($this->config['format'], $time, $type, $msg);
}
$info[$type] = $message;
}
if ($info) {
return $this->write($info);
}
return true;
}
/**
* 日志写入
* @access protected
* @param array $message 日志信息
* @param string $destination 日志文件
* @return bool
*/
protected function write(array $message): bool
{
$info = [];
foreach ($message as $type => $msg) {
$info[$type] = is_array($msg) ? implode(PHP_EOL, $msg) : $msg;
}
$message = implode(PHP_EOL, $info) . PHP_EOL;
$stream = $this->stdout;
if (false === @fwrite($stream, $message)) {
throw new \RuntimeException('Unable to write output.');
}
return fflush($stream);
}
private function openOutputStream()
{
if (!$this->hasStdoutSupport()) {
return fopen('php://output', 'w');
}
return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
}
protected function hasStdoutSupport(): bool
{
return false === $this->isRunningOS400();
}
private function isRunningOS400(): bool
{
$checks = [
function_exists('php_uname') ? php_uname('s') : '',
getenv('OSTYPE'),
PHP_OS,
];
return false !== stripos(implode(';', $checks), 'OS400');
}
}
这样日志就输出到控制台,可以被promtail抓取了