本文目录导读:

我来详细介绍Laravel链路追踪的接入方案。
常见链路追踪工具
Zipkin(推荐)
Jaeger
SkyWalking
OpenTelemetry
使用 Zipkin 接入示例
安装依赖
composer require openzipkin/zipkin-laravel composer require guzzlehttp/guzzle
配置文件
创建 config/zipkin.php:
<?php
return [
'enabled' => env('ZIPKIN_ENABLED', true),
'endpoint_url' => env('ZIPKIN_ENDPOINT', 'http://localhost:9411/api/v2/spans'),
'app_name' => env('ZIPKIN_APP_NAME', 'laravel-app'),
'sample_rate' => env('ZIPKIN_SAMPLE_RATE', 1.0), // 采样率 0-1
'timeout' => env('ZIPKIN_TIMEOUT', 1),
'tags' => [
'env' => env('APP_ENV', 'production'),
'version' => env('APP_VERSION', '1.0.0'),
],
];
创建 Zipkin 服务提供者
// app/Providers/ZipkinServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Zipkin\Annotation;
use Zipkin\Endpoint;
use Zipkin\Samplers\BinarySampler;
use Zipkin\TracingBuilder;
use Zipkin\Reporters\Http;
use Illuminate\Support\Facades\Log;
use Zipkin\Instrumentation\Http\Client\HttpClient;
use Zipkin\Instrumentation\Http\Server\PSR15Middleware;
class ZipkinServiceProvider extends ServiceProvider
{
protected $tracer;
protected $tracing;
public function register()
{
$this->app->singleton('zipkin.tracer', function ($app) {
return $this->createTracer();
});
}
public function boot()
{
if (!config('zipkin.enabled')) {
return;
}
$this->app['events']->listen('kernel.handled', function ($request, $response) {
$this->addTraceToResponse($request, $response);
});
}
protected function createTracer()
{
$endpoint = Endpoint::create(
config('zipkin.app_name'),
gethostbyname(gethostname()),
null,
config('zipkin.port', 80)
);
$reporter = new Http(
['endpoint_url' => config('zipkin.endpoint_url')],
function () {
return ['timeout' => config('zipkin.timeout')];
}
);
$sampler = BinarySampler::createAsAlwaysSample();
// 或者使用概率采样
// $sampler = new PercentageSampler(config('zipkin.sample_rate'));
$this->tracing = TracingBuilder::create()
->havingLocalEndpoint($endpoint)
->havingSampler($sampler)
->havingReporter($reporter)
->build();
return $this->tracing->getTracer();
}
protected function addTraceToResponse($request, $response)
{
try {
$tracer = $this->app['zipkin.tracer'];
if ($tracer && $request && $response) {
// 为当前请求创建 span
$span = $tracer->nextSpan();
$span->start();
// 添加请求信息
$span->setName($request->method() . ' ' . $request->path());
$span->tag('http.method', $request->method());
$span->tag('http.path', $request->path());
$span->tag('http.status_code', $response->getStatusCode());
// 添加性能指标
if (defined('LARAVEL_START')) {
$duration = (microtime(true) - LARAVEL_START) * 1000;
$span->tag('duration_ms', round($duration, 2));
}
$span->finish();
$tracer->flush();
}
} catch (\Exception $e) {
Log::error('Zipkin trace error: ' . $e->getMessage());
}
}
}
创建中间件
// app/Http/Middleware/ZipkinTracing.php
<?php
namespace App\Http\Middleware;
use Closure;
use Zipkin\Tracer;
use Illuminate\Support\Facades\Log;
class ZipkinTracing
{
protected $tracer;
public function __construct()
{
$this->tracer = app('zipkin.tracer');
}
public function handle($request, Closure $next)
{
if (!config('zipkin.enabled') || !$this->tracer) {
return $next($request);
}
// 创建 span
$span = $this->tracer->nextSpan();
$span->start();
try {
// 注入 trace 上下文到请求头
$span->inject($request->headers->all(), function ($carrier, $key, $value) {
$carrier[$key] = $value;
});
$response = $next($request);
// 记录响应信息
$span->tag('http.status_code', $response->status());
return $response;
} catch (\Exception $e) {
$span->tag('error', $e->getMessage());
throw $e;
} finally {
$span->finish();
$this->tracer->flush();
}
}
}
注册中间件和服务提供者
// config/app.php
'providers' => [
// ...
App\Providers\ZipkinServiceProvider::class,
],
// app/Http/Kernel.php
protected $middleware = [
// ...
\App\Http\Middleware\ZipkinTracing::class,
];
使用 OpenTelemetry(更现代的方案)
安装依赖
composer require open-telemetry/opentelemetry-auto-laravel composer require open-telemetry/exporter-otlp composer require open-telemetry/sdk
配置文件
// config/opentelemetry.php
<?php
return [
'enabled' => env('OTEL_ENABLED', true),
'service_name' => env('OTEL_SERVICE_NAME', 'laravel-app'),
'endpoint' => env('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://localhost:4318'),
'sampler' => [
'type' => env('OTEL_TRACES_SAMPLER', 'always_on'),
'ratio' => env('OTEL_TRACES_SAMPLER_ARG', 1.0),
],
'exporter' => [
'type' => env('OTEL_EXPORTER', 'otlp'),
'timeout' => env('OTEL_EXPORTER_TIMEOUT', 10),
],
'instrumentation' => [
'http_client' => env('OTEL_PHP_INSTRUMENTATION_HTTP_CLIENT', true),
'guzzle' => env('OTEL_PHP_INSTRUMENTATION_GUZZLE', true),
'redis' => env('OTEL_PHP_INSTRUMENTATION_REDIS', true),
'eloquent' => env('OTEL_PHP_INSTRUMENTATION_ELOQUENT', true),
],
];
创建 OpenTelemetry 服务提供者
// app/Providers/OpenTelemetryServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\Exporter\Otlp\OtlpHttpExporter;
use OpenTelemetry\API\Globals;
use OpenTelemetry\SDK\Trace\Sampler\ParentBased;
use OpenTelemetry\SDK\Trace\Sampler\TraceIdRatioBasedSampler;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
class OpenTelemetryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('opentelemetry.tracer', function ($app) {
return $this->createTracerProvider();
});
}
public function boot()
{
if (!config('opentelemetry.enabled')) {
return;
}
// 自动检测框架
if (class_exists(Globals::class)) {
Globals::registerInitializer(function ($builder) {
return $builder;
});
}
// 记录启动时间
$this->app['events']->listen('kernel.handled', function ($request, $response) {
$this->addTraceHeaders($response);
});
}
protected function createTracerProvider()
{
$resource = ResourceInfo::create(Attributes::create([
'service.name' => config('opentelemetry.service_name'),
'telemetry.sdk.name' => 'opentelemetry',
'telemetry.sdk.language' => 'php',
]));
$exporter = new OtlpHttpExporter(
config('opentelemetry.endpoint')
);
$sampler = new ParentBased(
new AlwaysOnSampler()
);
$tracerProvider = TracerProvider::builder()
->setResource($resource)
->addSpanProcessor(new SimpleSpanProcessor($exporter))
->setSampler($sampler)
->build();
return $tracerProvider->getTracer('laravel-app');
}
protected function addTraceHeaders($response)
{
try {
$tracer = $this->app['opentelemetry.tracer'];
$span = $tracer->getCurrentSpan();
if ($span && $response) {
$span->end();
}
} catch (\Exception $e) {
\Log::error('OpenTelemetry error: ' . $e->getMessage());
}
}
}
自定义 Span 示例
在控制器中使用
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index()
{
// 使用 OpenTelemetry
if (config('opentelemetry.enabled')) {
$tracer = app('opentelemetry.tracer');
$span = $tracer->spanBuilder('users.list')
->setAttribute('user.type', 'admin')
->startSpan();
}
try {
// 业务逻辑
$users = \App\Models\User::all();
// 添加数据库查询 span
if (isset($span)) {
$span->setAttribute('db.statement', 'SELECT * FROM users');
$span->setAttribute('db.system', 'mysql');
$span->setAttribute('db.count', $users->count());
}
return response()->json($users);
} catch (\Exception $e) {
if (isset($span)) {
$span->setAttribute('error', true);
$span->setAttribute('error.message', $e->getMessage());
}
throw $e;
} finally {
if (isset($span)) {
$span->end();
$tracer->forceFlush();
}
}
}
}
在队列任务中使用
// app/Jobs/ProcessPodcast.php
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
if (config('opentelemetry.enabled')) {
$tracer = app('opentelemetry.tracer');
$span = $tracer->spanBuilder('job.process')
->setAttribute('job.name', static::class)
->setAttribute('job.queue', $this->queue)
->startSpan();
}
try {
// 任务处理逻辑
sleep(2);
} finally {
if (isset($span)) {
$span->end();
}
}
}
}
Docker Compose 部署 Zipkin
version: '3.8'
services:
zipkin:
image: openzipkin/zipkin:latest
container_name: zipkin
ports:
- "9411:9411"
environment:
- STORAGE_TYPE=elasticsearch
- ES_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
elasticsearch:
image: elasticsearch:7.9.3
container_name: elasticsearch
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- "9200:9200"
# 或者使用内存存储(简单部署)
# zipkin-inmemory:
# image: openzipkin/zipkin:latest
# container_name: zipkin-inmemory
# ports:
# - "9411:9411"
常用配置建议
.env 配置
# Zipkin 配置 ZIPKIN_ENABLED=true ZIPKIN_ENDPOINT=http://localhost:9411/api/v2/spans ZIPKIN_APP_NAME=laravel-app ZIPKIN_SAMPLE_RATE=0.5 # OpenTelemetry 配置 OTEL_ENABLED=true OTEL_SERVICE_NAME=laravel-app OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_TRACES_SAMPLER=always_on
采样策略
// 动态采样
public function shouldSample($request)
{
// 只采样 10% 的请求
if (mt_rand(1, 100) > 10) {
return false;
}
// 始终采样错误请求
if ($request->is('api/*')) {
return true;
}
return true;
}
常见问题解决
- 性能影响:设置合适的采样率,避免全量采集
- 内存泄漏:确保 Span 正确结束和释放
- 网络超时:设置合理的超时时间
- 数据丢失:使用缓冲策略,异步发送
选择哪种方案取决于你的具体需求、团队熟悉度和基础设施,Zipkin 更适合传统架构,OpenTelemetry 面向云原生场景。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播