orgenus / tracing-laravel
让Laravel的分布式追踪变得简单
Requires
- php: ^7.1.3|^8.0.0|^8.1.0|^8.2.0|^8.2.2
- ext-json: *
- illuminate/bus: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/console: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/container: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/contracts: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/http: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/log: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/queue: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/routing: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- illuminate/support: ~5.5.0|~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0
- openzipkin/zipkin: ~2.0.2|~3.0
- psr/http-message: ~1.0
- ramsey/uuid: ~3.0|~4.0
Requires (Dev)
- google/cloud-pubsub: ^1.18
- guzzlehttp/psr7: ~1.0|~2.0
- lucid-arch/laravel-foundation: ^7.0|^8.0
- lucidarch/lucid: ^1.0
- mockery/mockery: ~1.0
- php-amqplib/php-amqplib: ~2.8|~3.0
- phpunit/phpunit: ~7.0|~8.0|~9.0
- vinelab/http: ~1.5
Suggests
- lucid-arch/laravel-foundation: Base package for the legacy Lucid Architecture that we also support tracing for
- lucidarch/lucid: Latest distribution package for the Lucid Architecture that we also support tracing for
- php-amqplib/php-amqplib: A pure PHP implementation of the AMQP protocol
- vinelab/http: Fault-tolerant HTTP client for sending and receiving JSON and XML that we also support tracing for
This package is not auto-updated.
Last update: 2024-09-28 00:28:00 UTC
README
简介
分布式追踪是追踪应用程序请求所产生活动的过程。使用此功能,您可以
- 追踪请求在复杂系统中的路径
- 发现路径上组件(服务)的延迟
- 知道路径上的哪个组件正在创建瓶颈
- 检查组件之间发送的有效负载
- 为每个单独的组件构建执行图等
简单来说,分布式追踪是一种知识工具。拥有它的一个最重要的好处是,开发者可以通过简单地跟踪其创建的轨迹来了解系统。
看看Uber是如何使用分布式追踪来理解他们产品中大量微服务和交互的
分布式追踪由多个跨度组成,这些跨度表示在服务或其资源中花费的时间。
每个 跨度 具有以下内容
- 操作名称
- 开始时间戳
- 完成时间戳
- 一组零个或多个键值标签,用于查找和记录其他信息
- 一组零个或多个与时间戳配对的日志
- 相关跨度的引用(例如,父跨度)
跨度通常显示为时间轴,其中每个跨度都可以展开以查看更多详细信息
追踪器接口(通过Trace
外观提供)创建跨度并了解如何在进程边界处注入(序列化)和提取(反序列化)它们。
有关分布式追踪语义的更多详细信息,请参阅OpenTracing规范
要求
此软件包需要 PHP >= 7.1 和 Laravel 5.5或更高版本。我们还提供有限的Lumen支持(基本追踪和HTTP中间件)。
安装
首先,使用Composer安装软件包
composer require vinelab/tracing-laravel
安装后,您可以使用vendor:publish
命令发布软件包配置。此命令将tracing.php
配置文件发布到您的配置目录
php artisan vendor:publish --provider="Vinelab\Tracing\TracingServiceProvider"
您可以在.env
文件中配置驱动程序和服务名称
TRACING_DRIVER=zipkin TRACING_SERVICE_NAME=orders
您还应在下面的章节中添加相应的驱动程序凭据。
有关Lumen,请参阅专用部分中的安装说明。
驱动程序先决条件
Zipkin
使用以下环境变量来配置Zipkin
ZIPKIN_HOST=localhost ZIPKIN_PORT=9411
如果收集器无法通过给定的主机名访问,您可能会在每次请求中看到有关该内容的调试消息。如果您想在生产环境中忽略这些消息,只需编辑您的日志配置以排除debug
级别的消息即可。
Jaeger
由于PHP缺乏稳定的仪器支持,Jaeger“非官方”支持。
然而,您仍然可以使用 Zipkin 驱动程序通过 与 Zipkin 兼容的 HTTP 端点 将 spans 发送到 Jaeger 收集器。实际上,这是推荐使用此库的方式,因为 Jaeger 的 UI 比Zipkin 的 UI 要方便得多。
但是也有一些缺点
- 您将无法使用一些 Jaeger 特定的功能,例如上下文化日志,因为 Zipkin 只支持标签和时间注解
- HTTP 是您唯一的传输选项(没有 UDP 选项)
一旦 Jaeger 的工具成熟,我们将考虑提高 Jaeger 的支持。
空
该包还包括一个丢弃创建的 spans 的 null
驱动程序。
使用方法
您将通过此包提供的 Trace
门面进行跟踪。
创建跨度
开始新的跟踪就像调用带有表示 span 所代表逻辑操作名称的 startSpan
方法一样简单
$span = Trace::startSpan('Create Order');
通常,您需要继续现有的跟踪,这就是为什么 startSpan
还接受 span 上下文的附加参数。 SpanContext 可以通过各种渠道传播,包括 HTTP 请求、AMQP 消息、数组,甚至是另一个 span
$spanContext = Trace::extract($request, Formats::ILLUMINATE_HTTP); $rootSpan = Trace::startSpan('Create Order', $spanContext); $childSpan = Trace::startSpan('Validate Order', $rootSpan->getContext())
可能性是无限的。有关更多详细信息,请参阅 上下文传播 部分。
自定义跨度
覆盖 span 名称
$span->setName('Create Order');
添加标签,这些标签可以用作查找键(在 UI 上搜索 span)或附加详细信息
$span->tag('shipping_method', $shipping_method);
检索跨度
您可以检索当前 span,这同时也是您最近创建的 span
$span = Trace::getCurrentSpan()
在服务中处理请求时创建的第一个 span 被称为根 span(不要与跟踪的全局根 span 混淆)
在调用 flush 之后,根 span 会重置。
$span = Trace::getRootSpan()
控制跨度
您可以通过调用该 span 的 finish
来完成 span。通过从开始时间戳减去这个时间来推导 span 持续时间
$span->finish()
您可以在 span 开始和完成之间记录额外的数据。例如,annotate
创建一个带时间戳的事件来解释延迟
$span->annotate('Order Validated')
刷新跨度
Flush 指的是将所有挂起的 spans 发送到传输的过程。它还会重置跟踪器的状态,包括活动 spans 和 UUID
Trace::flush()
大多数时候,您不需要明确调用 flush
。由于 PHP 被设计为在每次请求后死亡,我们已经在应用程序关闭时为您处理完成根 span 和调用 flush。
只有在连续处理请求的循环中(例如 AMQP 通道)时,您才必须手动调用 flush
。
日志记录
每个根 span 都关联着一个唯一的标识符,可以用来查找其跟踪。建议您在报告错误时将其包括在 上下文 中,以便在您的监控堆栈的不同部分之间建立桥梁
// Illuminate\Foundation\Exceptions\Handler /** * Get the default context variables for logging. * * @return array */ protected function context() { return array_filter([ 'userId' => Auth::id(), 'uuid' => Trace::getUUID(), ]); }
自定义驱动程序 也可能支持记录结构化数据(在 Zipkin 中不可用),这可以用于将跟踪与日志门面集成
use Illuminate\Support\Facades\Event; /** * Bootstrap any application services. * * @return void */ public function boot() { Event::listen(MessageLogged::class, function (MessageLogged $e) { Tracer::getCurrentSpan()->log((array) $e); }); }
中间件
此包包括一个 \Vinelab\Tracing\Middleware\TraceRequests
中间件,用于处理从传入的 HTTP 请求中继续跟踪。
您应该在您的 app/Http/Kernel.php
类的 $middleware
属性中注册中间件类。
该中间件在根 span 上添加以下 标签
type
(http)request_method
request_path
request_uri
request_headers
request_ip
request_input
response_status
response_headers
response_content
仅包括在白名单内容类型中时,请求和响应体才被包括。请参阅您的
config/tracing.php
中的logging.content_types
选项。
您可以在控制器中覆盖默认的 span 名称(即 VERB /path/for/route
)
Trace::getRootSpan()->setName('Create Order')
控制台命令
Lumen 不支持此功能,但您仍然可以使用跟踪器实例手动创建命令的跟踪。
通过将 Vinelab\Tracing\Contracts\ShouldBeTraced
接口添加到您的类中,让您的控制台命令被跟踪。
容器跨度将包含以下标签
type
(命令行)argv
跨度将以控制台命令命名。您可以在命令本身中覆盖跨度的默认名称
Trace::getRootSpan()->setName('Mark Orders Expired')
队列作业
Lumen 不支持此功能,但您仍然可以使用跟踪实例手动为作业创建跟踪
将 Vinelab\Tracing\Contracts\ShouldBeTraced
接口添加到您的作业类中,以便让您的队列作业可被跟踪。
容器跨度将包含以下标签
type
(队列)connection_name
(例如 sync, redis 等)队列名称
作业输入
正如其名,job_input
允许您将作业的构造函数参数视为 JSON。对象的序列化到该 JSON 字符串可以通过实现以下接口进行控制:Arrayable
、Jsonable
、JsonSerializable
或 __toString
方法。默认的回退行为是打印对象的所有公共属性。
构造函数参数必须以与类属性相同的名称保存(请参见下面的 ProcessPodcast 示例)。
跨度将以队列作业类的名称命名。您可以在作业本身中覆盖跨度的默认名称
app('tracing.queue.span')->setName('Process Podcast')
请注意,队列跨度不一定是跟踪的根跨度。您通常希望队列从作业被分派时的位置继续跟踪。您可以通过简单地将 SpanContext 传递给作业的构造函数来实现这一点
class ProcessPodcast implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $podcast; protected $spanContext; public function __construct(Podcast $podcast, SpanContext $spanContext) { $this->podcast = $podcast; $this->spanContext = $spanContext; } public function handle(AudioProcessor $processor) { // Process uploaded podcast... } }
上面的作业可以这样分发
ProcessPodcast::dispatch($podcast, Trace::getRootSpan()->getContext());
其余的将自动处理。请注意,SpanContext 将从记录的 job_input
中排除。
重要:此包不会自动处理队列闭包和队列事件监听器的跟踪。您仍然可以通过打开和关闭跨度手动跟踪它们。可能在包的将来版本中考虑对这些功能的改进。
上下文传播
正如我们之前讨论的,跟踪器理解如何在不同的应用程序(服务)之间注入和提取跟踪上下文。
我们已经看到了从 HTTP 请求提取跟踪的示例
$spanContext = Trace::extract($request, Formats::ILLUMINATE_HTTP);
当然,您不需要手动执行此操作,因为此包已包含一个 中间件 来为您处理此操作,但跟踪可能不一定来自 HTTP 请求。
第二个参数是格式描述符,它告诉我们如何从给定的载体中反序列化跟踪头。默认情况下,以下格式受支持
use Vinelab\Tracing\Propagation\Formats; $spanContext = Trace::extract($carrier, Formats::TEXT_MAP); $spanContext = Trace::extract($carrier, Formats::PSR_REQUEST); $spanContext = Trace::extract($carrier, Formats::ILLUMINATE_HTTP); $spanContext = Trace::extract($carrier, Formats::AMQP); $spanContext = Trace::extract($carrier, Formats::GOOGLE_PUBSUB);
您也可以使用 registerExtractionFormat
方法添加您自己的格式。
Trace::registerExtractionFormat("pubsub", new PubSubExtractor());
注入格式必须实现 Vinelab\Tracing\Contracts\Extractor
。请参阅默认的 Zipkin 实现示例。
interface Extractor { public function extract($carrier): ?SpanContext; }
当然,您也可以将现有跟踪上下文从 当前跨度 注入到给定的载体中,以便另一个服务可以继续跟踪
$message = Trace::inject($message, Formats::AMQP); $channel->basic_publish($message, $this->exchangeName, $routingKey);
默认情况下,以下格式受支持
use Vinelab\Tracing\Propagation\Formats; $carrier = Trace::inject($carrier, Formats::TEXT_MAP); $carrier = Trace::inject($carrier, Formats::PSR_REQUEST); $carrier = Trace::inject($carrier, Formats::ILLUMINATE_HTTP); $carrier = Trace::inject($carrier, Formats::AMQP); $carrier = Trace::inject($carrier, Formats::GOOGLE_PUBSUB); $carrier = Trace::inject($carrier, Formats::VINELAB_HTTP);
您也可以使用 registerInjectionFormat
方法添加您自己的格式。
注入格式必须实现 Vinelab\Tracing\Contracts\Injector
。请参阅默认的 Zipkin 实现示例。
interface Injector { public function inject(SpanContext $spanContext, &$carrier): void; }
如果您需要显式传递跨度上下文,也可以使用 injectContext
方法。
$carrier = Trace::injectContext($carrier, Formats::TEXT_MAP, $span->getContext());
重要:如果您需要快速完成任务,不需要创建自定义传播格式。您始终可以使用默认的 TEXT_MAP
格式来从关联数组中注入或提取跟踪头。
自定义驱动程序
编写新驱动程序
新驱动程序必须遵守 Vinelab\Tracing\Contracts\Tracer
合同。请参阅默认的 ZipkinTracer 实现示例。
use Vinelab\Tracing\Contracts\Extractor; use Vinelab\Tracing\Contracts\Injector; use Vinelab\Tracing\Contracts\Span; use Vinelab\Tracing\Contracts\SpanContext; public function startSpan(string $name, SpanContext $spanContext = null, ?int $timestamp = null): Span; public function getRootSpan(): ?Span; public function getCurrentSpan(): ?Span; public function getUUID(): ?string; public function extract($carrier, string $format): ?SpanContext; public function inject($carrier, string $format); public function injectContext($carrier, string $format, SpanContext $spanContext); public function registerExtractionFormat(string $format, Extractor $extractor): array; public function registerInjectionFormat(string $format, Injector $injector): array; public function flush(): void;
注册新驱动程序
一旦您编写了自定义驱动程序,您可以使用 TracingDriverManager
的 extend 方法注册它。您应从您的 AppServiceProvider
或任何其他应用程序使用的服务提供者的 boot
方法中调用 extend
方法。例如,如果您编写了 JaegerTracer
,您可以这样注册它
use Vinelab\Tracing\TracingDriverManager; /** * Bootstrap any application services. * * @return void */ public function boot() { resolve(TracingDriverManager::class)->extend('jaeger', function () { return new JaegerTracer; }); }
一旦您的驱动程序已注册,您可以在环境变量中将它指定为您的跟踪驱动程序。
TRACING_DRIVER=jaeger
与Lumen一起使用
您需要手动在bootstrap/app.php
文件中注册服务提供商。
$app->register(Vinelab\Tracing\TracingServiceProvider::class);
您还应该在该文件中注册中间件。
$app->middleware([ Vinelab\Tracing\Middleware\TraceRequests::class, ]);
将以下行添加到public/index.php
文件的末尾。
$tracer = app(Vinelab\Tracing\Contracts\Tracer::class); optional($tracer->getRootSpan())->finish(); $tracer->flush();
最后,如果您需要自定义默认设置,也可以从该仓库复制config/tracing.php
。
如果您在Lumen项目中不使用外观,您可以通过以下方式从容器中解析tracer实例:
use Vinelab\Tracing\Contracts\Tracer; app(Tracer::class)->startSpan('Create Order')
请注意,Lumen目前不支持自动跟踪控制台命令和作业,因为它不派发一些事件和终止回调。然而,您仍然可以在需要的地方手动创建跟踪。
集成
Lucid架构
该软件包包含可选的Vinelab\Tracing\Integration\Concerns\TracesLucidArchitecture
特质,用于启用Lucid项目的跟踪。
class TracingServiceProvider extends ServiceProvider { use TracesLucidArchitecture; /** * Bootstrap any application services. * * @return void */ public function boot() { $this->traceLucidArchitecture(); } }