vinelab / tracing-laravel
简化Laravel分布式追踪
Requires
- php: >=7.1.3
- illuminate/bus: >=5.5.0
- illuminate/console: >=5.5.0
- illuminate/container: >=5.5.0
- illuminate/contracts: >=5.5.0
- illuminate/http: >=5.5.0
- illuminate/log: >=5.5.0
- illuminate/queue: >=5.5.0
- illuminate/routing: >=5.5.0
- illuminate/support: >=5.5.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 auto-updated.
Last update: 2024-09-09 09:48:07 UTC
README
简介
分布式追踪是跟踪对应用程序请求产生活动的过程。使用此功能,您可以:
- 追踪请求在复杂系统中的路径
- 发现该路径上组件(服务)的延迟
- 知道路径中的哪个组件正在创建瓶颈
- 检查组件之间发送的有效载荷
- 为每个单独的组件构建执行图等
简单来说,分布式追踪是一种知识工具。在项目中拥有它的一大优点是,开发者可以通过简单地跟踪它产生的痕迹来了解系统。
看看Uber如何使用分布式追踪来理解其产品中的大量微服务和交互。
分布式追踪由多个跨度组成,这些跨度表示在服务或这些服务的资源中花费的时间。
每个跨度具有以下内容:
- 操作名称
- 开始时间戳
- 结束时间戳
- 一组零个或多个键值对标签,以启用查找并记录附加信息
- 一组零个或多个与时间戳配对的日志
- 与相关跨度(例如父跨度)的引用
跨度通常以时间轴的形式显示,每个跨度都可以展开以查看更多详细信息
Tracer接口(通过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
Jaeger不支持,因为PHP缺乏稳定的工具。
然而,您仍然可以使用与Zipkin兼容的HTTP端点,通过Jaeger驱动程序将跨度发送到Jaeger收集器,具体请参阅Zipkin兼容性。事实上,这是推荐使用此库的方法,因为Jaeger的UI比Zipkin的UI要方便得多。
但也有一些缺点:
- 由于Zipkin只支持标签和时间注释,您将无法利用一些Jaeger特定的功能,如上下文化日志。
- HTTP是您唯一的传输选项(没有UDP选项)。
一旦Jaeger的自动化成熟,我们将考虑改进Jaeger支持。
无
该包还包括一个丢弃创建的跨度的null
驱动程序。
使用方法
您将通过此包提供的Trace
外观与跟踪一起工作。
创建跨度
启动新的跟踪就像调用具有代表跨度的逻辑操作名称的startSpan
方法一样简单。
$span = Trace::startSpan('Create Order');
通常,您需要继续现有的跟踪,这就是为什么startSpan
也接受额外的跨域上下文参数。 SpanContext可以通过各种渠道传播,包括HTTP请求、AMQP消息、数组,甚至另一个跨度。
$spanContext = Trace::extract($request, Formats::ILLUMINATE_HTTP); $rootSpan = Trace::startSpan('Create Order', $spanContext); $childSpan = Trace::startSpan('Validate Order', $rootSpan->getContext())
可能性是无限的。有关详细信息,请参阅上下文传播部分。
自定义跨度
重写跨度名称
$span->setName('Create Order');
添加标签,这些标签可以用作查找键(用于在UI上搜索跨度)或附加详细信息
$span->tag('shipping_method', $shipping_method);
检索跨度
您可以检索当前跨度,这通常是您最近创建的跨度
$span = Trace::getCurrentSpan()
在服务中处理请求时创建的第一个跨度称为根跨度(不要与跟踪的全局根跨度混淆)
在调用flush之后,根跨度将被重置。
$span = Trace::getRootSpan()
控制跨度
您可以通过调用该跨度的finish
来结束跨度。跨度持续时间通过从开始时间戳减去这个值来计算。
$span->finish()
您可以在跨度开始和结束之间记录其他数据。例如,annotate
创建一个时间戳事件来解释延迟。
$span->annotate('Order Validated')
刷新跨度
刷新是指将所有挂起的跨度发送到传输过程。它还会重置跟踪器的状态,包括活动跨度和UUID。
Trace::flush()
然而,您通常不需要显式调用flush
。由于PHP设计为在每个请求后死亡,我们已为您处理完成根跨度并在应用程序关闭时调用刷新。
只有当您在一个循环中连续处理请求时(例如,AMQP通道),您才必须手动调用flush
。
日志记录
每个根跨度都与一个唯一的标识符相关联,该标识符可以用于查找其跟踪。建议您将其包括在报告错误时的上下文中,以便在不同部分的监控堆栈之间架起桥梁。
// 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
属性中注册中间件类。
中间件在根跨度上添加以下标签
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
选项。
您可以在控制器中覆盖默认的跨度名称(默认为VERB /path/for/route
)。
Trace::getRootSpan()->setName('Create Order')
控制台命令
Lumen不支持此功能,但您仍然可以使用跟踪实例手动创建命令的跟踪。
通过将Vinelab\Tracing\Contracts\ShouldBeTraced
接口添加到您的类中,让控制台命令可追踪。
容器跨度将包含以下标签
type
(cli)argv
跨度将根据控制台命令命名。您可以在命令本身中覆盖跨度的默认名称
Trace::getRootSpan()->setName('Mark Orders Expired')
队列任务
Lumen不支持此功能,但您仍然可以使用tracer实例手动为作业创建跟踪。
通过将Vinelab\Tracing\Contracts\ShouldBeTraced
接口添加到您的作业类中,让队列作业可追踪。
容器跨度将包含以下标签
type
(queue)connection_name
(例如,sync、redis等)queue_name
job_input
如名称所示,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
中。
注意:此包不会自动处理队列闭包和队列事件监听器的跟踪。您仍然可以通过打开和关闭跨度来手动跟踪它们。未来版本可能考虑增强对这些功能的支持。
上下文传播
如我们之前讨论的,tracer了解如何在不同的应用程序(服务)之间注入和提取跟踪上下文。
我们已经看到了从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
的扩展方法来注册它。你应该从你的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项目中不使用外观(facades),你可以这样从容器中解析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(); } }