Laravel分布式全链路追踪实战
我以larevel框架做分布式全链路追踪为实例
安装必要的包
composer require jaeger/jaeger-client-php
composer require opentracing/opentracing
创建 Service Provider
php artisan make:provider TracingServiceProvider
<?php
// app/Providers/TracingServiceProvider.phpnamespace App\Providers;use Jaeger\Config;
use OpenTracing\GlobalTracer;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;class TracingServiceProvider extends ServiceProvider
{public function register(){$this->app->singleton('jaeger.tracer', function () {$config = new Config(['sampler' => ['type' => 'const','param' => true,],'logging' => true,'local_agent' => ['reporting_host' => env('JAEGER_AGENT_HOST', 'localhost'),'reporting_port' => env('JAEGER_AGENT_PORT', 6831),],],env('APP_NAME', 'laravel-app'));return $config->initializeTracer();});// 设置全局 tracerGlobalTracer::set($this->app->make('jaeger.tracer'));}public function boot(){//}
}
注册 Service Provider
// config/app.php
'providers' => [// ...App\Providers\TracingServiceProvider::class,
],
创建中间件
php artisan make:middleware TracingMiddleware
<?php
// app/Http/Middleware/TracingMiddleware.phpnamespace App\Http\Middleware;use Closure;
use OpenTracing\GlobalTracer;
use OpenTracing\Tags;
use Illuminate\Http\Request;class TracingMiddleware
{public function handle(Request $request, Closure $next){$tracer = GlobalTracer::get();// 从请求头中提取追踪上下文$spanContext = $tracer->extract(\OpenTracing\Formats\HTTP_HEADERS,$request->headers->all());$scope = $tracer->startActiveSpan('http.request',['child_of' => $spanContext,'tags' => [Tags\HTTP_METHOD => $request->method(),Tags\HTTP_URL => $request->fullUrl(),'http.host' => $request->getHost(),'http.path' => $request->path(),'http.query' => $request->getQueryString() ?: '','http.user_agent' => $request->userAgent(),'http.client_ip' => $request->ip(),]]);// 将 span 存储到请求中,供后续使用$request->attributes->set('tracing_scope', $scope);try {$response = $next($request);$scope->getSpan()->setTag(Tags\HTTP_STATUS_CODE, $response->getStatusCode());return $response;} catch (\Exception $e) {$scope->getSpan()->setTag(Tags\ERROR, true);$scope->getSpan()->log(['event' => 'error','error.kind' => get_class($e),'message' => $e->getMessage(),'stack' => $e->getTraceAsString(),]);throw $e;}}public function terminate($request, $response){if ($request->attributes->has('tracing_scope')) {$scope = $request->attributes->get('tracing_scope');$scope->close();}}
}
注册中间件
// app/Http/Kernel.php
protected $middleware = [// ...\App\Http\Middleware\TracingMiddleware::class,
];
数据库查询追踪
// app/Providers/AppServiceProvider.phpnamespace App\Providers;use Illuminate\Support\ServiceProvider;
use OpenTracing\GlobalTracer;
use Illuminate\Support\Facades\DB;class AppServiceProvider extends ServiceProvider
{public function boot(){if (config('tracing.enabled', false)) {DB::listen(function ($query) {$tracer = GlobalTracer::get();$scope = $tracer->startActiveSpan('db.query', ['tags' => ['db.system' => 'mysql','db.statement' => $query->sql,'db.query.bindings' => json_encode($query->bindings),'db.query.time' => $query->time . 'ms',]]);$scope->close();});}}
}
HTTP 客户端追踪(Guzzle)
// app/Services/TracingHttpClient.phpnamespace App\Services;use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use OpenTracing\GlobalTracer;
use OpenTracing\Formats;
use OpenTracing\Tags;class TracingHttpClient
{public static function create(): Client{$stack = HandlerStack::create();$stack->push(self::createTracingMiddleware());return new Client(['handler' => $stack,'timeout' => 30,]);}private static function createTracingMiddleware(): callable{return function (callable $handler) {return function ($request, array $options) use ($handler) {$tracer = GlobalTracer::get();$scope = $tracer->startActiveSpan('http.client.request', ['tags' => [Tags\HTTP_METHOD => $request->getMethod(),Tags\HTTP_URL => (string) $request->getUri(),'http.target' => $request->getRequestTarget(),]]);// 注入追踪头$tracer->inject($scope->getSpan()->getContext(),Formats\HTTP_HEADERS,$request->getHeaders());return $handler($request, $options)->then(function ($response) use ($scope) {$scope->getSpan()->setTag(Tags\HTTP_STATUS_CODE, $response->getStatusCode());$scope->close();return $response;},function ($reason) use ($scope) {$scope->getSpan()->setTag(Tags\ERROR, true);$scope->getSpan()->log(['error' => $reason]);$scope->close();throw $reason;});};};}
}
队列任务追踪
// app/Jobs/TracedJob.phpnamespace App\Jobs;use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use OpenTracing\GlobalTracer;
use OpenTracing\Tags;class TracedJob implements ShouldQueue
{use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;protected $spanContext;public function __construct(array $spanContext = null){$this->spanContext = $spanContext;}public function handle(){$tracer = GlobalTracer::get();$scope = $tracer->startActiveSpan('job.process', ['child_of' => $this->spanContext,'tags' => ['job.name' => static::class,'job.queue' => $this->queue,'job.attempts' => $this->attempts(),]]);try {$this->processJob();$scope->close();} catch (\Exception $e) {$scope->getSpan()->setTag(Tags\ERROR, true);$scope->getSpan()->log(['event' => 'error','error.kind' => get_class($e),'message' => $e->getMessage(),]);$scope->close();throw $e;}}protected function processJob(){// 实际的作业处理逻辑}// 在分发作业时保存追踪上下文public static function dispatchWithTracing(){$tracer = GlobalTracer::get();$spanContext = $tracer->getActiveSpan()?->getContext();return new static($spanContext);}
}
配置文件
php artisan vendor:publish --provider="App\Providers\TracingServiceProvider"
// config/tracing.phpreturn ['enabled' => env('TRACING_ENABLED', true),'service_name' => env('TRACING_SERVICE_NAME', env('APP_NAME', 'laravel-app')),'jaeger' => ['agent_host' => env('JAEGER_AGENT_HOST', 'localhost'),'agent_port' => env('JAEGER_AGENT_PORT', 6831),],'sampler' => ['type' => env('TRACING_SAMPLER_TYPE', 'const'),'param' => env('TRACING_SAMPLER_PARAM', true),],'tags' => ['environment' => env('APP_ENV', 'production'),'version' => env('APP_VERSION', '1.0.0'),],
];
.env 配置
TRACING_ENABLED=true
TRACING_SERVICE_NAME=my-laravel-app
JAEGER_AGENT_HOST=localhost
JAEGER_AGENT_PORT=6831
TRACING_SAMPLER_TYPE=const
TRACING_SAMPLER_PARAM=true
http请求的使用示例
// 在控制器中使用
namespace App\Http\Controllers;use App\Services\TracingHttpClient;
use OpenTracing\GlobalTracer;
use OpenTracing\Tags;class UserController extends Controller
{public function show($id){$tracer = GlobalTracer::get();$scope = $tracer->startActiveSpan('user.controller.show', ['tags' => ['user.id' => $id,]]);try {// 调用外部服务$client = TracingHttpClient::create();$response = $client->get('https://api.example.com/users/' . $id);$user = json_decode($response->getBody(), true);$scope->getSpan()->log(['message' => 'User fetched successfully']);return response()->json($user);} catch (\Exception $e) {$scope->getSpan()->setTag(Tags\ERROR, true);$scope->getSpan()->log(['error' => $e->getMessage()]);throw $e;} finally {$scope->close();}}
}