Tp & Laravel 复写 错误日志服务 实现 钉钉机器人通知[保姆级教程]

Tp & Laravel 错误日志 钉钉机器人通知

这边以Tp为例子,Laravel 差不多也是一样的,其实学会了这个框架复写可以做很多有趣的事情

前言

在我以往的开发的过程中,会写一些错误的日志,但是一般运营那边没有反馈,我们也不太会在意日志,所以一般小概率的报错也不会有人察觉,这样不好,不好。

在当下互联网寒冬,容不得我们半点马虎,所以还是要认真对待自己的工作,报错的时候第一时间知道,偷偷的改掉。

Tp & Laravel 复写 错误日志服务 实现 钉钉机器人通知[保姆级教程]

思路

借助免费的钉钉消息通知,可以很方便实现我们客户端的通知,但是也不能让程序一直 发送这个错误信息吧,所以还是要做限制的,一天同一个错误不超过我们自己指定的数量。

开发

  • 继承方式 修改 Log 源码加入钉钉通知逻辑
  • 站在在巨人的肩膀上触发具体的钉钉通知

查看 config/log.php文件代码,看到框架已经兼容了多渠道配置的方式,比如这边可以用file的方式,当然也可以扩展es或者kafka的驱动方式

├─log
│  │  Channel.php
│  │  ChannelSet.php
│  │
│  └─driver
│          File.php
│          Socket.php

其实如何写扩展,官方文档也没有写,这个就需要结合大家的经验去做了。这就需要查阅源码才可以实现了。

当我配置一个渠道为fileNotice 执行一下程序报:Driver [FileNotice] not supported 我们就可以根据 这个not supported 关键词找到具体报错的位置了。

    /**
     * 获取驱动类
     * @param string $type
     * @return string
     */
    protected function resolveClass(string $type): string
    {
        if ($this->namespace || false !== strpos($type, '\\')) {
            $class = false !== strpos($type, '\\') ? $type : $this->namespace . Str::studly($type);

            if (class_exists($class)) {
                return $class;
            }
        }

        throw new InvalidArgumentException("Driver [$type] not supported.");
    }

以上就是具体报错的位置,从这边的判断,如果包含反斜杠,也就是命名空间+类 就直接引用了,最终我的配置文件这样写

<?php

// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
    // 默认日志记录通道
    'default'      => env('log.channel', 'fileNotice'),
    // 日志记录级别
    'level'        => [],
    // 日志类型记录的通道 ['error'=>'email',...]
    'type_channel' => [],
    // 关闭全局日志写入
    'close'        => false,
    // 全局日志处理 支持闭包
    'processor'    => null,

    // 日志通道列表
    '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,
        ],

        'fileNotice' => [
            // 日志记录方式
            'type'           => 'app\log\FileNotice',
            // 日志保存目录
            'path'           => '',
            // 单文件日志写入
            'single'         => false,
            // 独立日志级别
            'apart_level'    => [],
            // 最大日志文件数量
            'max_files'      => 0,
            // 使用JSON格式记录
            'json'           => false,
            // 日志处理
            'processor'      => null,
            // 关闭通道日志写入
            'close'          => false,
            // 日志输出格式化
            'format'         => '[%s][%s] %s',
            // 是否实时写入
            'realtime_write' => false,

            //钉钉token
            'token' => 'xxx',

            //钉钉secret
            'secret' => 'xxx',
        ],
        // 其它日志通道配置
    ],

];

这边添加了钉钉token的配置项和secret 的配置项,这边可以灵活的使用 env 不同环境之间的切换

接下去创建具体的日志文件 app/log/FileNotice,我们继承文件 File 类型就好了

<?php

namespace app\log;

use think\log\driver\File;

class FileNotice extends File
{

}

现在可以复写具体的类和方法了,差一个钉钉发送的功能,这个之前论坛老哥已经推荐过消息发送的包,直接引用即可。

composer require guanguans/notify -vvv

save 是复制过来的方法 具体实现代码:

<?php

namespace app\log;

use Guanguans\Notify\Factory;
use think\log\driver\File;

class FileNotice extends File
{

    /**
     * 日志写入接口
     * @access public
     * @param array $log 日志信息
     * @return bool
     */
    public function save(array $log): bool
    {
        $destination = $this->getMasterLogFile();

        $path = dirname($destination);
        !is_dir($path) && mkdir($path, 0755, true);

        $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);
            }

            if (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level'])) {
                // 独立记录的日志级别
                $filename = $this->getApartLevelFile($path, $type);
                $this->write($message, $filename);
                continue;
            }

            $send_num = cache($msg) ?? 0;

            //这边写入钉钉发送的逻辑
            if ($type == 'error' && $send_num < 1) {
                Factory::dingTalk()
                    ->setToken($this->config['token'])
                    ->setSecret($this->config['secret'])
                    ->setMessage((new \Guanguans\Notify\Messages\DingTalk\LinkMessage([
                        'title' => '错误详情',
                        'text' => $msg,
                        'messageUrl' => $this->config['messageUrl'],
                        'picUrl' => 'https://avatars.githubusercontent.com/u/22309277?v=4',
                    ])))
                    ->send();

                cache($msg, $send_num+1, 24 * 3600);

            }

            $info[$type] = $message;
        }

        if ($info) {


            return $this->write($info, $destination);
        }


        return true;
    }
}

触发

 Log::error('测试报错');

效果:
Tp & Laravel 错误日志 钉钉机器人通知

寒冬来了,有义务 分享给瑟瑟发抖的各位

Tp & Laravel 复写 错误日志服务 实现 钉钉机器人通知[保姆级教程]

原文链接

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 13

这样是同步的吧 如果同时要多个log(文件写入 各种通知等等) 会影响程序吧?

3年前 评论
Image liaosp (楼主) 3年前
Image yzbfeng (作者) 3年前
Image minororange 3年前
Image liaosp (楼主) 3年前
Image yzbfeng (作者) 3年前
Image liaosp (楼主) 3年前
朕略显ぼうっと萌

日志和告警应该区分开

日志应该偏向于准确性,并且保证写入速度

告警应该考虑规则,比如 钉钉一分钟只能超过多少条就不会给你记录了, 相同的错误应该只记录一条,根据不同的规则,区分告警等级 ,

建议使用sentry

3年前 评论
Image liaosp (楼主) 3年前
Image 朕略显ぼうっと萌 (作者) 3年前
AloneUtopia

用sentry + 1

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
Image
未填写
文章
39
粉丝
16
喜欢
146
收藏
135
排名:315
访问:3.0 万
私信
所有博文
社区赞助商