Thinkphp如何自定义日志输出

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 <liu21st@gmail.com>
// +----------------------------------------------------------------------
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抓取了

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
thinkphp6中,可以通过以下方式自定义抛出异常: 1. 创建自定义异常类 首先,我们需要创建一个自定义的异常类,继承自think\Exception类,例如: ```php namespace app\exception; use think\Exception; class MyException extends Exception { protected $message = '自定义异常信息'; protected $code = 10001; } ``` 在上面的例子中,我们创建了一个名为MyException的自定义异常类,设置了异常信息和异常代码。 2. 抛出自定义异常 在需要抛出异常的地方,可以通过以下方式抛出自定义异常: ```php throw new MyException(); ``` 或者: ```php throw new MyException('自定义异常信息', 10001); ``` 在上面的例子中,我们使用了自定义异常类MyException,并传入了异常信息和异常代码。 3. 异常处理 最后,我们需要在异常处理的地方捕获并处理自定义异常,例如: ```php namespace app\exception; use think\exception\Handle; class ExceptionHandle extends Handle { public function render(\Exception $e) { if ($e instanceof MyException) { // 处理自定义异常 return json(['code' => $e->getCode(), 'msg' => $e->getMessage()]); } // 其他异常交给系统处理 return parent::render($e); } } ``` 在上面的例子中,我们创建了一个名为ExceptionHandle的异常处理类,继承自think\exception\Handle类,重写了render方法,在该方法中捕获并处理自定义异常MyException。 当系统抛出自定义异常时,会调用ExceptionHandle的render方法进行处理,返回一个相应的响应结果。如果抛出的异常不是自定义异常,则交给系统默认的异常处理方式处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值