Tp & Laravel 複寫 錯誤日誌服務 實現 釘釘機器人通知[保姆級教程]

liaosp發表於2022-09-27

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 協議》,轉載必須註明作者和本文連結

相關文章