PHP
项目上了opentelemetry
的时候发现有部分片段时间不连续PHP8
)opentelemetry
扩展install-php-extensions opentelemetry
安装windows
官方没有, 可以到这里下载ddl
文件https://phpext.phptools.online/extension/php/opentelemetry-261composer
扩展composer install open-telemetry/opentelemetry-auto-laravel
TEL_PHP_AUTOLOAD_ENABLED=true
TEL_SERVICE_NAME=test
TEL_TRACES_EXPORTER=otlp
TEL_METRICS_EXPORTER=none
TEL_LOGS_EXPORTER=none
TEL_EXPORTER_OTLP_PROTOCOL=grpc
TEL_EXPORTER_OTLP_ENDPOINT=http://
TEL_EXPORTER_OTLP_HEADERS=Authentication=xxx
TEL_EXPORTER_OTLP_TIMEOUT=1000
TEL_EXPORTER_OTLP_TRACES_TIMEOUT=1000
open-telemetry/opentelemetry-auto-laravel
这个项目通过composer.json
的_register.php
让Laravel
自动加载https://github.com/open-telemetry/opentelemetry-php-contrib/blob/main/src/Instrumentation/Laravel/composer.json#L39{
"files": [
"_register.php"
]
}
request
, cache
,log
,http client
https://github.com/open-telemetry/opentelemetry-php-contrib/blob/main/src/Instrumentation/Laravel/src/LaravelInstrumentation.phpBatchProcessSpan
public static function autoload(): bool
{
if (!self::isEnabled() || self::isExcludedUrl()) {
return false;
}
Globals::registerInitializer(function (Configurator $configurator) {
$propagator = (new PropagatorFactory())->create();
if (Sdk::isDisabled()) {
//@see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md#general-sdk-configuration
return $configurator->withPropagator($propagator);
}
$emitMetrics = Configuration::getBoolean(Variables::OTEL_PHP_INTERNAL_METRICS_ENABLED);
$resource = ResourceInfoFactory::defaultResource();
$exporter = (new ExporterFactory())->create();
$meterProvider = (new MeterProviderFactory())->create($resource);
// 主要关注这一行, 这里我们会创建出一个 BatchSpanProcessor
$spanProcessor = (new SpanProcessorFactory())->create($exporter, $emitMetrics ? $meterProvider : null);
$tracerProvider = (new TracerProviderBuilder())
->addSpanProcessor($spanProcessor)
->setResource($resource)
->setSampler((new SamplerFactory())->create())
->build();
$loggerProvider = (new LoggerProviderFactory())->create($emitMetrics ? $meterProvider : null, $resource);
ShutdownHandler::register($tracerProvider->shutdown(...));
ShutdownHandler::register($meterProvider->shutdown(...));
ShutdownHandler::register($loggerProvider->shutdown(...));
return $configurator
->withTracerProvider($tracerProvider)
->withMeterProvider($meterProvider)
->withLoggerProvider($loggerProvider)
->withPropagator($propagator)
;
});
return true;
}
</details>
<?php
namespace App\Service\Tracing;
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\SpanInterface;
use OpenTelemetry\API\Trace\TracerInterface;
class Tracer
{
protected TracerInterface $tracer;
protected ?SpanInterface $lastSpan = null;
protected ?SpanInterface $rootSpan = null;
/**
* @var array Span
*/
protected array $spanMap = [];
public function __construct()
{
$this->tracer = Globals::tracerProvider()
->getTracer('io.opentelemetry.contrib.php.laravel');
}
/**
* 请查看 AppServiceProvider 注册为 scoped, 适用于 Octane
* @return Tracer
*/
public static function getInstance(): Tracer
{
return app(Tracer::class);
}
public function startRootSpan($name): void
{
$span = $this->startSpan($name);
$this->rootSpan = $span;
}
public function startAndEndLastSpan($name): SpanInterface
{
$this->endLastSpan();
return $this->startSpan($name);
}
public function startSpan($name): SpanInterface
{
$span = $this->tracer->spanBuilder($name)->startSpan();
$this->spanMap[$name] = $span;
$this->lastSpan = $span;
return $span;
}
public function endRootSpan(): void
{
$this->endSpan($this->rootSpan);
}
/**
* @return void 方便的 end 上一个 span
*/
public function endLastSpan(): void
{
$this->endSpan($this->lastSpan);
}
public function endSpan(?SpanInterface $span): void
{
if (is_null($span)) {
return;
}
$span->end();
}
}
Tracer
类注册到服务提供者app/Providers/AppServiceProvider.php
scoped
来注册<?php
namespace App\Providers;
use App\Utils\Tracing\Tracer;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// 整个生命周期只注册一次
$this->app->scoped(Tracer::class, function () {
return new Tracer();
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
<?php
namespace App\Http\Controllers;
use App\Service\Tracing;
class IndexAlbumsController extends Controller
{
public function index()
{
$tracer = Tracer::getInstance();
// 步骤 0
$tracer->startRootSpan('xxxx');
// 步骤 1
$tracer->startSpan('s1');
// 业务代码 xxx
// 结束步骤 1, 并开启步骤 2
$tracer->startAndEndLastSpan('s2');
// 业务代码 xxx
// 结束步骤 2
$tracer->endLastSpan();
// 结束 root
$tracer->endRootSpan();
}
}
$span->end()
的时候耗时几百毫秒, 百思不得其解end()
的实现BatchSpanProcessor
类的onEnd
方法class BatchSpanProcessor {
public function onEnd(ReadableSpanInterface $span): void
{
if ($this->closed) {
return;
}
if (!$span->getContext()->isSampled()) {
return;
}
if ($this->queueSize === $this->maxQueueSize) {
$this->dropped++;
return;
}
$this->queueSize++;
$this->batch[] = $span->toSpanData();
$this->nextScheduledRun ??= $this->clock->now() + $this->scheduledDelayNanos;
if (count($this->batch) === $this->maxExportBatchSize) {
$this->enqueueBatch();
}
if ($this->autoFlush) {
// flush
$this->flush();
}
}
}
flush
方法, 这里会根据配置到达一定数量, 一定时间把链路追踪上报PHP
常规运行没有多线程, flush
上报链路追踪的时候会阻塞当前进程flush
方法上多线程, 短期内不可能, 估计百分之九十九的项目都是没用多线程的Opentelemetry collector
代理这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.