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 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 13

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

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

日志和告警应该区分开

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

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

建议使用sentry

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

用sentry + 1

1年前 评论

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