本文目录导读:

- OpenTelemetry(推荐)
- Jaeger + Jaeger PHP Client
- SkyWalking PHP Agent
- Zipkin PHP
- Laravel专用方案
- 日志集成
- 环境变量配置
- 最佳实践
- 注意事项
我来详细介绍PHP接入链路追踪的几种主流方案:
OpenTelemetry(推荐)
安装扩展
# 安装PHP扩展 pecl install opentelemetry
基础配置
<?php
require_once 'vendor/autoload.php';
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\Exporter\JaegerExporter;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
// 创建TracerProvider
$resource = ResourceInfoFactory::emptyResource()->merge(
ResourceInfo::create([
'service.name' => 'your-service-name',
])
);
$exporter = new JaegerExporter(
'jaeger',
'http://jaeger:9411/api/v2/spans'
);
$tracerProvider = new TracerProvider(
new SimpleSpanProcessor($exporter),
$resource
);
$tracer = $tracerProvider->getTracer('your-app');
自动埋点
<?php
// 使用OpenTelemetry自动埋点
$tracer = \OpenTelemetry\API\Globals::tracer();
// 创建span
$span = $tracer->spanBuilder('database-query')
->setAttribute('db.system', 'mysql')
->setAttribute('db.statement', 'SELECT * FROM users')
->startSpan();
// 执行操作
try {
// 业务逻辑
$span->end();
} catch (\Exception $e) {
$span->recordException($e);
$span->setStatus(StatusCode::STATUS_ERROR);
$span->end();
throw $e;
}
中间件方式(Laravel示例)
<?php
namespace App\Http\Middleware;
use Closure;
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\StatusCode;
class TracingMiddleware
{
public function handle($request, Closure $next)
{
$tracer = Globals::tracer();
$span = $tracer->spanBuilder($request->getMethod() . ' ' . $request->getPathInfo())
->setSpanKind(SpanKind::KIND_SERVER)
->setAttribute('http.method', $request->getMethod())
->setAttribute('http.url', $request->fullUrl())
->startSpan();
$response = $next($request);
$span->setAttribute('http.status_code', $response->getStatusCode());
$span->end();
return $response;
}
}
Jaeger + Jaeger PHP Client
<?php
require_once 'vendor/autoload.php';
use Jaeger\Config;
use OpenTracing\GlobalTracer;
use OpenTracing\Reference;
// 初始化Jaeger
$config = new Config(
[
'sampler' => [
'type' => 'const',
'param' => true,
],
'logging' => true,
],
'your-service-name'
);
$config->initializeTracer();
$tracer = GlobalTracer::get();
// 创建span
$span = $tracer->startActiveSpan('business-operation', [
'tags' => [
'db.type' => 'mysql',
'customer.id' => '123',
]
]);
try {
// 子操作
$childSpan = $tracer->startActiveSpan('database-query');
// 执行操作
$childSpan->close();
$span->close();
} catch (\Exception $e) {
$span->setTag('error', true);
$span->log(['error' => $e->getMessage()]);
$span->close();
} finally {
$tracer->flush();
}
SkyWalking PHP Agent
<?php
// 安装SkyWalking Agent
// composer require skywalking/agent
use Skywalking\Agent;
use Skywalking\Util\Config;
// 初始化
$config = new Config([
'grpc' => '127.0.0.1:11800',
'service_name' => 'your-service-name',
'instance_name' => 'instance-1'
]);
Agent::init($config);
// 创建Span
$span = Agent::startSpan('business-operation', [
'peer' => 'localhost:8080',
'tags' => [
'db.type' => 'mysql',
'customer.id' => '123',
]
]);
try {
// 业务逻辑
$span->stop();
} catch (\Exception $e) {
$span->setError($e);
$span->stop();
}
Zipkin PHP
<?php
use Zipkin\Annotation;
use Zipkin\Endpoint;
use Zipkin\Samplers\Percentage;
use Zipkin\TracingBuilder;
use Zipkin\Reporters\Http;
// 创建Reporter
$endpoint = Endpoint::create('your-service-name');
$reporter = new Http([
'base_url' => 'http://zipkin:9411/api/v2/spans'
]);
// 采样器
$sampler = Percentage::create(0.1);
// 创建Tracing
$tracing = TracingBuilder::create()
->havingLocalEndpoint($endpoint)
->havingReporter($reporter)
->havingSampler($sampler)
->build();
$tracer = $tracing->getTracer();
// 创建Span
$span = $tracer->newTrace()
->setName('business-operation')
->start();
$span->tag('db.type', 'mysql');
$span->annotate('db.statement', 'SELECT * FROM users');
// 子Span
$child = $tracer->newChild($span->getContext())
->setName('database-query')
->start();
// 完成操作
$child->finish();
$span->finish();
// 发送到Zipkin
$tracing->flush();
Laravel专用方案
使用 Laravel OpenTelemetry
<?php
// config/opentelemetry.php
return [
'service_name' => 'laravel-app',
'exporter' => 'zipkin',
'zipkin' => [
'endpoint' => 'http://localhost:9411/api/v2/spans',
],
'sampler' => [
'type' => 'always_on',
],
];
// ServiceProvider
class TelemetryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(Tracer::class, function ($app) {
return TracerProvider::create()
->setResource(ResourceInfo::create([
'service.name' => config('opentelemetry.service_name')
]))
->setExporter(ExporterFactory::create(config('opentelemetry.exporter')))
->getTracer();
});
}
}
日志集成
<?php
// 在日志中包含Trace ID
class TraceIdProcessor
{
public function __invoke(array $record): array
{
$tracer = \OpenTelemetry\API\Globals::tracer();
$span = $tracer->getCurrentSpan();
if ($span) {
$context = $span->getContext();
$record['extra']['trace_id'] = $context->getTraceId();
$record['extra']['span_id'] = $context->getSpanId();
}
return $record;
}
}
// 配置到Monolog
$logger->pushProcessor(new TraceIdProcessor());
环境变量配置
# .env 配置 OTEL_SERVICE_NAME=your-service-name OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_TRACES_EXPORTER=otlp OTEL_PROPAGATORS=jaeger,b3,baggage,tracecontext
最佳实践
统一封装
<?php
class TracingHelper
{
public static function trace(string $operation, callable $callback, array $attributes = [])
{
$tracer = \OpenTelemetry\API\Globals::tracer();
$span = $tracer->spanBuilder($operation)
->setAttributes($attributes)
->startSpan();
try {
$result = $callback();
$span->setStatus(StatusCode::STATUS_OK);
return $result;
} catch (\Exception $e) {
$span->recordException($e);
$span->setStatus(StatusCode::STATUS_ERROR);
throw $e;
} finally {
$span->end();
}
}
}
// 使用
$result = TracingHelper::trace('user-registration', function() {
// 业务逻辑
}, ['user.type' => 'vip']);
性能监控
<?php
// 监控数据库查询
DB::listen(function ($query) {
$tracer = \OpenTelemetry\API\Globals::tracer();
$span = $tracer->spanBuilder('query')
->setAttribute('db.system', 'mysql')
->setAttribute('db.statement', $query->sql)
->setAttribute('db.bindings', json_encode($query->bindings))
->startSpan();
$span->end();
});
// 监控Redis操作
Redis::macro('trace', function ($command, $params) {
$tracer = \OpenTelemetry\API\Globals::tracer();
$span = $tracer->spanBuilder('redis-' . $command)
->setAttribute('redis.command', $command)
->setAttribute('redis.params', json_encode($params))
->startSpan();
try {
$result = $this->command($command, $params);
$span->end();
return $result;
} catch (\Exception $e) {
$span->recordException($e);
$span->end();
throw $e;
}
});
注意事项
- 性能影响:合理设置采样率
- 安全:确保追踪数据不包含敏感信息
- 异步场景:处理协程、消息队列的链路传递
- 错误处理:确保发生异常时也能正确记录
- 资源清理:及时关闭Span释放资源
选择合适的方案,建议优先使用OpenTelemetry,它是目前最通用的标准。