背景

为了方便工作,我业余时间开发了一个线上接收日志的系统,提供日志推送接口。于是基于TP6 自定义日志驱动,每次打日志时,推送到线上查看。

接口文档

https://www.yuque.com/docs/share/20a266c3-61bd-472c-a971-b9cc2f957f96?# 《沙屿沫服务接口规范》

密码:去《关于我》私聊我吧

步骤

1、修改日志配置文件 config/log.php
2、创建日志驱动类(放置的位置没有要求,能加载到即可。)
3、对接线上日志系统。

1、修改日志配置文件
在 channels 增加一个通道

<?php

// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
...// 其他代码

// 日志通道列表
'channels' => [
...// 其他代码

'shayvmo' => [
'type' => \app\OnlineLog::class,// 第二步创建的日志驱动类名
'realtime_write' => false,// 推送日志需要时间,不建议同步写入,否则请求链路变长
'url' => '',// 线上日志系统url参数
'appid' => '',// 线上日志系统appid参数
'secret_key' => '',// 线上日志系统secret_key参数
],
],

];

2、创建日志驱动类

<?php

declare (strict_types=1);


namespace app;


use app\util\Signature;
use think\App;
use think\contract\LogHandlerInterface;

/**
*
* @ClassName OnlineLog
* @Version 1.0
* @Description
* @package app
*/
class OnlineLog implements LogHandlerInterface
{
protected $config = [
'url' => '',
'appid' => '',
'secret_key' => '',
];

protected $app;

public function __construct(App $app, $config = [])
{
if (is_array($config)) {
$this->config = array_merge($this->config, $config);
}
}

public function save(array $log): bool
{
$function = function (string $url, array $data = []) {
$header = [
'Content-Type: application/x-www-form-urlencoded'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$result = curl_exec($ch);
curl_close($ch);
return $result;
};

// 线上日志系统签名类
$signature = new Signature($this->config['appid'], $this->config['secret_key']);
foreach ($log as $key => $item) {
foreach ($item as $i) {
$i = json_decode($i, true);
$i['log_type'] = $key;
$signData = $signature->getSignData($i);
$function($this->config['url'], $signData);
}
}
return true;
}
}

3、对接线上日志系统
1)日志辅助类

<?php

declare (strict_types=1);


namespace app\service;

/**
*
* @ClassName LogService
* @Version 1.0
* @Description
* @package app\service
*/
class LogService
{
public static function info(string $title, array $content)
{
self::push($title, $content);
}

public static function debug(string $title, array $content)
{
self::push($title, $content, 'debug');
}

public static function error(string $title, array $content)
{
self::push($title, $content, 'error');
}

public static function warning(string $title, array $content)
{
self::push($title, $content, 'warning');
}

public static function push(string $title, array $content, string $log_type = 'info')
{
\think\facade\Log::channel('shayvmo')->$log_type(json_encode([
'request_ip' => request()->ip(),
'title' => $title,
'content' => json_encode($content, JSON_UNESCAPED_UNICODE),
]));
}
}

2)签名类

<?php

declare (strict_types=1);


namespace app\util;


/**
*
* @ClassName Signature
* @Version 1.0
* @Description 加密类
* @package App\util
*/
class Signature
{
private $appId;

private $secretKey;

public function __construct(string $appId, string $secretKey)
{
$this->appId = $appId;
$this->secretKey = $secretKey;
}

public function getSignData(array $data): array
{
$data['appid'] = $this->appId;
$data['nonce_str'] = strtoupper(bin2hex(random_bytes(16)));
$content = $this->getContent($data);
$data['sign'] = $this->getSign($content);
return $data;
}

public function checkSign(array $data, string $sign): bool
{
return $this->getSign($this->getContent($data)) === $sign;
}

private function getContent(array $data): string
{
ksort($data);

$temp = [];

foreach ($data as $key => $item) {
if ($key === 'sign' || $item === '' || is_null($item)) {
continue;
}
$temp[] = $key .'='.$item;
}

return implode('&', $temp);
}

private function getSign(string $content): string
{
return strtoupper(md5(md5($content . $this->secretKey) . $this->secretKey));
}
}