如何及时定位到非必现问题?

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 自定义channel;区分哪个环境中使用自定义的channel;使用优雅的 monolog 驱动

修改日志配置


  1. 自定义channel
  2. 区分哪个环境中使用自定义的channel
  3. 使用优雅的 monolog 驱动


<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
return [
    'default' => env('LOG_CHANNEL', 'stack'),
    'channels' => [
        'stack' => [
            'driver' => 'stack',
            //测试环境除了使用daily保存每天日志到logs/laravel.log,还使用’dingding‘channel
            'channels' => env("APP_ENV") == 'test' ? ['daily', 'dingding'] : ['daily'],
            'ignore_exceptions' => false,
        ],
        //配置钉钉 驱动选择 monolog 
        'dingding' => [
            'driver' => 'monolog',
            'level' => 'error',
            'handler' => \App\Handler\DingdingLogHandler::class,  //自定义handler
        ],
        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'days' => 14,
        ],
        .
        .
        .
    ],
];


上面不重要的代码使用3个竖向排列的.省略显示,下面的代码段非重要的也用3个竖向排列的.省略。


自定义钉钉Handler


  1. 我们可以很方便的通过 Illuminate\Support\Facades\Request 获得请求链接,请求参数,请求头
  2. 错误信息通过外部传入,默认的Error报错都会运行到send()方法中


<?php
namespace App\Handler;
use App\Library\CurlRequest;
use App\Library\Utility;
use Monolog\Logger;
use Monolog\Handler;
class DingdingLogHandler extends Handler\AbstractProcessingHandler
{
    private $apiKey;
    private $channel;
    public function __construct(
        $level = Logger::ERROR,
        bool $bubble = true
    ) {
        parent::__construct($level, $bubble);
    }
    protected function write(array $record): void
    {
        $this->send($record['formatted']);
    }
    protected function send(string $message): void
    {
        $microSecond = Utility::getMicroSecond();
        $key = "xxxx";
        $hashString = hash_hmac("sha256", $microSecond ."\n" . $key, $key, true);
        $sign = urlencode(base64_encode($hashString));
        CurlRequest::post("https://oapi.dingtalk.com/robot/send?access_token=xxxxx&timestamp=".$microSecond."&sign=".$sign,
            [
                "msgtype" => "text",
                "at" => [
                    "atMobiles" => [
                        "xxxx",
                        "xxxx"
                    ]
                ],
                "text" => [
                       "content" => "请求链接:\n" . Request::path() . "\n请求参数:\n" . \GuzzleHttp\json_encode(Request::toArray()) . "\n请求头:\n" . \GuzzleHttp\json_encode(Request::header()) . "\n错误信息:\n" . $message
                ]
            ]);
    }
}


效果图:


微信图片_20221111215100.jpg

上述两段代码是实现错误信息同步到钉钉群的核心代码,下面介绍各种场景的实现思路:


实现慢查询的核心思路:


response中间件:



  1. 这是网络请求和响应的中间件,左右的网络请求都会到经过这个中间件的处理
  2. 耗时操作的计算只在测试环境中部署,上线时去掉。(或者通过生产环境或者开发环境的动态变量做判断,这种实现应该更好)
  3. 有些请求三方的接口不再我们优化的范围内,为了避免频繁接口提醒,可以设置白名单
  4. 慢查询之所以有Log::info()和Log::error()的区别是:前者只存储到日志;后者不仅存储到日志,还同步信息到钉钉(我们定义钉钉channel的日志级别为error)


<?php
namespace App\Http\Middleware;
use App\Library\Utility;
use App\Model\ErrorCode;
use Closure;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
class ApiResponse
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $requestMicroSecond = Utility::getMicroSecond();
        $response = $next($request);
        if (!is_array($response->original) && (int)($response->original) != 0) {
            $errCode = (int)$response->original;
            //TODO 上线删除 避免资源浪费
            self::calcTime($requestMicroSecond);
            return response()->json(['ret' => $errCode, 'msg' => ErrorCode::getErrorMsg($errCode)])
                ->header("Timestamp", Utility::getMicroSecond());
        } else {
            $ret = ['ret' => 0, 'msg' => ErrorCode::getErrorMsg(0)];
            if (!empty($response->original) && is_array($response->original)) {
                $ret['data'] = $response->original;
            } else if ($response->original != Null) {
                echo $response->original;
                exit;
            }
            //TODO 上线删除
            self::calcTime($requestMicroSecond);
            return response()->json($ret)->header("Timestamp", Utility::getMicroSecond());
        }
    }
    function calcTime($requestTime)
    {
        //白名单
        $exceptApis = [
            'api/pay/order/status',
            'api/pay/order/repair',
            'api/log/token/get',
        ];
        $responseTime = Utility::getMicroSecond();
        $consumeTime = $responseTime - $requestTime;
        if ($consumeTime >= 1000) {
            if (in_array(Request::path(), $exceptApis)) {
                //只记录 不发到钉钉报警
                Log::info("耗时操作(调用三方):" . $consumeTime . "ms");
            } else {
                Log::error("耗时操作:" . $consumeTime . "ms");
            }
        }
    }
}


效果图


微信图片_20221111215105.jpg


排查非必现的问题


原理:我们可以在出问题的代码段中,通过Log::error()打印错误日志,当被触发是就会同步到钉钉群消息中。

比如我们的错误场景是: 安卓客户端发短信验证码登录的时候偶尔会出现验证码验证失败的问题,客户端确信给服务端传了正确的验证码,服务端反击说:你如果传了正常的验证码不可能验证不通过。


这种扯皮是没有用的,加上这种是非必现问题,打印Log定位问题吧,要爬好久log,且不能及时知道什么时候出错的,所以同步错误信息到钉钉群是一个非常典型的应用场景。


上代码


public function validateMsg(Request $request)
    {
        $authCode = new AuthCode($request->phone);
        $code = $authCode->get();
        if (empty($code) || $code != $request->code) {
            Log::error("短信验证码校验问题: AuthCode:" . $code . " 传入code:" . $request->code);
            return ErrorCode::TYPE_CODE_INCORRECT;
        }
        $authCode->delCode();
        .
        .
        .
    }


效果图


微信图片_20221111215110.jpg

最终发现是服务端的问题,本地的authCode为空,被清理了,导致校验失败。


相关实践学习
日志服务之数据清洗与入湖
本教程介绍如何使用日志服务接入NGINX模拟数据,通过数据加工对数据进行清洗并归档至OSS中进行存储。
相关文章
|
2月前
|
监控 搜索推荐 数据挖掘
精准定位目标客户群
精准定位目标客户群
111 2
|
8月前
定位有几种,他们的特点分别是什么? 是相对于什么进行定位的?
定位有几种,他们的特点分别是什么? 是相对于什么进行定位的?
56 0
|
2月前
定位有几种,他们的特点分别是什么?是相对于什么进行定位的?
定位有几种,他们的特点分别是什么?是相对于什么进行定位的?
23 2
|
2月前
|
监控 定位技术 Android开发
如何获得你的准确位置及iphon手机应用定位不准确原因分析
如何获得你的准确位置及iphon手机应用定位不准确原因分析
29 0
|
9月前
|
IDE 前端开发 安全
使用xpath定位遇到的坑
使用xpath定位遇到的坑
141 0
|
10月前
测试定位打卡
测试定位打卡
46 0
|
12月前
|
XML 数据格式 开发者
|
前端开发
定位
定位
78 0
|
安全 开发者
定位 | 学习笔记
快速学习定位。
75 0
定位 | 学习笔记
文件定位
文件定位
96 0

热门文章

最新文章