Laravel 8 - 监听特定请求期间发生的 eloquent 事件

Laravel 8 - Listen to eloquent events made during a specific request

目前,我正在尝试将遥测库 (OpenTelemetry php lib) 连接到 Laravel 事件模型。这个想法是将跟踪转发给第三方服务。单个跟踪与请求和在请求期间进行的所有数据库调用一致。我创建了三个中间件组件:

我的 OnRequest 中间件组件如下所示:

<?php

namespace App\Http\Middleware;

use App\Helpers\Otel\TraceProviderFactory;
use Closure;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class OnRequest
{
     public function handle(Request $request, Closure $next)
     {
         $provider = TraceProviderFactory::getTraceProvider($request->get('context_uuid'));
         DB::listen(function (QueryExecuted $query) use ($provider){
              $provider->startDbSpan($query);
         });
         $provider->endSpans();

         return $next($request);
    }
}

在这种情况下,数据库侦听调用被跳过,因为它是异步的。如何确保每个数据库查询都已注册?执行顺序很重要。一切都需要以正确的顺序进行,否则跟踪记录不会被正确记录。

我会在可终止的中间件中完成此操作(在将响应发送到客户端之后),以允许您记录从开始收听到发送响应之间的所有查询。这也不会阻止将响应返回给客户端。

...

class OnRequest
{
     protected $provider;

     public function handle(Request $request, Closure $next)
     {
         $this->provider = TraceProviderFactory::getTraceProvider($request->input('context_uuid'));

         DB::listen(function (QueryExecuted $query) {
              $this->provider->startDbSpan($query);
         });

         return $next($request);
    }

    public function terminate($request, $response)
    {
         $this->provider->endSpans();
    }
}

您应该将此中间件设为单例,这样它就不会为可终止部分使用新实例。 (我们希望分配相同的 $provider 实例)。在服务提供商中注册单例:

public function register()
{
    $this->app->singleton(OnRequest::class);
}

你只需要一个中间件就可以完成所有事情。

Laravel 8.x Docs - Middleware - Terminable Middleware