0
点赞
收藏
分享

微信扫一扫

虾皮的员工都说好,PHP 错误日志监控钉钉通知


虾皮的员工都说好,PHP 错误日志监控钉钉通知_json

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

前言

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

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

虾皮的员工都说好,PHP 错误日志监控钉钉通知_php_02

思路

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

开发

  • 继承方式 修改 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++, 24 * 3600);

}

$info[$type] = $message;
}

if ($info) {


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


return true;
}
}

触发

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

效果:

虾皮的员工都说好,PHP 错误日志监控钉钉通知_php_03

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

虾皮的员工都说好,PHP 错误日志监控钉钉通知_日志记录_04



举报

相关推荐

0 条评论