失业两个月找存在感系列——几行代码轻松实现laravel链式追踪,一分钟快速排查系统问题

YY理解

所谓的链式追踪,并没有多高大尚,简单的可以理解为在一次请求中,生成一个链路请求trace_id,在整个请求过程中记录的日志都包含这个trace_id,对于拆分系统的微服务而言,可以在调用其他服务的时候把这个trace_id传入调用系统,这样整个请求生命周期中一直都有这个标识,可以顺藤摸瓜,问题有迹可循

优点

能快速的通过trace_id 查找完整的请求链路日志,通过命令或者elk方式只需要这个ID就能搜索出所有线索日志,进而快速排查问题

laravel代码实现

思路

laravel通常是运行在php-fpm模式下,请求都是在一个进程中完成的,所以本身就是一条从开始到结束的直线。只需要我们在请求最初时候产生这个trace_id ,然后在后续的代码处理记录日志的时候只要把这个ID加上即可。

laravel实现思路

对于laravel而言,可以使用前置中间件和后置中间件以及重新log记录日志方式便可以实现

laravel代码实现

  • 首先kernel.php 注册中间件

    /** * The application's route middleware groups. * * @var array */protected $middlewareGroups = [ 'gptapp' => [ \App\Http\Middleware\RequestBefore::class, \App\Http\Middleware\ResponseAfter::class, ], ];
  • 编写请求之前前置中间件 RequestBefore,在请求到业务逻辑之前进行生成trace_id 保存到request 对象上

    class RequestBefore { public function handle(Request $request, Closure $next) { //没有则生成唯一请求ID if (! $request->header('Request-Id')) { $requestId = (string)md5( uniqid() . time()); $request->headers->set('Request-Id', $requestId); } return $next($request); } }
  • 封装带有trace_id的日志记录方法 Log, laravel8以后有自带上下文的日志记录方法(此步骤可以省略)

    class extends Log { static function info ($arr) { //加上唯一ID  $arr['request_id'] = request()->headers->get('Request-Id', $requestId); //调用laravel log门面写日志 SysLog::info(json_encode($arr)); } }
  • 编写请求响应之后的后置中间件 ResponseAfter,使用带有trace_id 的日志记录方法进行日志保存

    class ResponseAfter { public function handle(Request $request, Closure $next) { $response = $next($request); // 执行操作 Log::info([ 'request' => [ 'client_ip' => $request->getClientIp(), 'method' => $request->getMethod(), 'route' => $request->getRequestUri(), 'content_type' => $request->getContentType(), 'content' => $request->all(), 'headers' => $request->headers->all(), ], 'response' => [ 'headers' => $response->headers->all(), 'contents' => $response->getContent(), ], ]); return $response; } }

总结

php是世界上最好的编程语言,其他编程语言能做的,php也能!!!

本作品采用《CC 协议》,转载必须注明作者和本文链接
PHP是世界上最好的编程语言,它能快速的进行技术变现,让代码多一份价值。
本帖由系统于 2年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 19

有点麻烦,直接withContext或者shareContext

class RequestBefore { public function handle(Request $request, Closure $next) { $requestId = (string) Str::uuid(); //withContext,未指定channel的每条日志都会记录request-id \Illuminate\Support\Facades\Log::withContext([ 'request-id' => $requestId ]); //shareContext,每条日志都会记录request-id \Illuminate\Support\Facades\Log::shareContext([ 'request-id' => $requestId ]); return $next($request)->header('Request-Id', $requestId); } }
2年前 评论
PHP之父一只码 (楼主) 2年前
DonnyLiu

赞,不错的方案 :+1:

2年前 评论
PHP之父一只码 (楼主) 2年前

欢迎大佬们给出更好的方案

2年前 评论

如果是fpm的话一个静态变量就可以搞定。

class TraceIdMaker { protected static $inc; public static function getInstance(): TraceIdMaker { if (self::$inc === null) { self::$inc = new self(); } return self::$inc; } public static function make(): string { return self::getInstance()->getNowCompleteTraceId(); } protected $commonTraceId; protected $stop = 0; protected function __construct() { $this->commonTraceId = $this->createTraceId(); } protected function createTraceId(): string { return md5(uniqid(mt_rand(), true)); } public function getNowCompleteTraceId(): string { $this->stop += 1; return $this->commonTraceId . ':traceNumber:' . str_pad($this->stop, 3, '0', STR_PAD_LEFT); } }

laravel 的 这个 AppServiceProvider 里面加上这个就行。

 /* @var $monolog Logger */ $monolog = \Log::getMonolog(); $monolog->pushProcessor(function ($item) { if (array_key_exists('message', $item)) { $item['message'] = TraceIdMaker::make() . ':' . $item['message']; } return $item; });

其实最早的时候用其他的也是类似。fpm不通请求都是隔离的。这个方案就可以快速实现。

2年前 评论
PHP之父一只码 (楼主) 2年前

有点麻烦,直接withContext或者shareContext

class RequestBefore { public function handle(Request $request, Closure $next) { $requestId = (string) Str::uuid(); //withContext,未指定channel的每条日志都会记录request-id \Illuminate\Support\Facades\Log::withContext([ 'request-id' => $requestId ]); //shareContext,每条日志都会记录request-id \Illuminate\Support\Facades\Log::shareContext([ 'request-id' => $requestId ]); return $next($request)->header('Request-Id', $requestId); } }
2年前 评论
PHP之父一只码 (楼主) 2年前

大佬目前找到工作了吗

2年前 评论
PHP之父一只码 (楼主) 2年前

请问这种追踪是只能用于微服务架构对吗?

我这边想处理的链追踪是分布式架构的,就是后端有多个服务器

用户第一个请求在 IP1,第二个请求在 IP2 .....

期望在查询日志的时候,可以把某一个用户的请求全部查询出来,用这个方式是不是不适用?

2年前 评论
罐装仙人掌CuratorC 2年前

我之前也写过一个 github.com/kphcdr/laravel-trace-id 公司的项目一直在用

2年前 评论

队列能用吗?

2年前 评论
July993 2年前
caonima888 2年前

这种只是追日志吧 真链路追踪还得是这种

file

2年前 评论
rooy 2年前
kolin (作者) 2年前

UFO @ 一只码科技
文章
9
粉丝
42
喜欢
83
收藏
185
排名:458
访问:2.0 万
私信
所有博文
社区赞助商