通过OpenTelemetry为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTelemetry为PHP应用进行自动或手动埋点并上报数据。
前提条件
背景信息
OpenTelemetry PHP支持自动埋点和手动埋点,对PHP的版本要求如下:
- 自动埋点支持的版本:PHP 8.0 + 
- 手动埋点支持的版本:PHP 7.4 + 
支持自动埋点的框架列表如下,完整信息请参见OpenTelemetry官方文档。
示例Demo
示例代码仓库地址:php-opentelemetry-demo
基于OpenTelemery PHP Extension为应用自动埋点并上报链路数据
Auto-demo是基于PHP Slim Web框架实现一个模拟扔骰子游戏的应用,并使用OpenTelemetry为应用自动埋点(即自动创建Trace/Span等链路数据),实现无侵入的PHP应用链路追踪。
OpenTelemetry除了支持Slim框架的自动埋点,还支持多种框架,完整列表请参见官方文档。
前提条件
已安装PHP、Composer、PECL,且PHP版本≥8.0。
操作步骤
- 创建投骰子应用。 - 初始化。 
 - mkdir <project-name> && cd <project-name> composer init \ --no-interaction \ --stability beta \ --require slim/slim:"^4" \ --require slim/psr7:"^1" composer update- 编写应用代码。 - 在<project-name>目录下创建一个index.php文件,添加如下内容。 - 这段代码模拟扔骰子游戏,返回1-6之间的一个随机数。 - <?php use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Factory\AppFactory; require __DIR__ . '/vendor/autoload.php'; $app = AppFactory::create(); $app->get('/rolldice', function (Request $request, Response $response) { $result = random_int(1,6); $response->getBody()->write(strval($result)); return $response; }); $app->run();- 此时应用已经编写完成,执行 - php -S localhost:8080命令即可运行应用,访问地址为- http://localhost:8080/rolldice。
 
- 构建OpenTelemetry PHP扩展。 - 下载构建OpenTelemetry PHP extension所需要的工具。 - macOS - brew install gcc make autoconf
- Linux(apt) - sudo apt-get install gcc make autoconf
 
- 使用PECL构建OpenTelemetry PHP扩展。 - pecl install opentelemetry- 注意,构建成功时输出内容的最后几行如下(路径可能不完全一致): - Build process completed successfully Installing '/opt/homebrew/Cellar/php/8.2.8/pecl/20220829/opentelemetry.so' install ok: channel://pecl.php.net/opentelemetry-1.0.0beta6 Extension opentelemetry enabled in php.ini
- (可选)启用OpenTelemetry PHP扩展。 - 如果上一步输出了 - Extension opentelemetry enabled in php.ini,表明已经启用,请跳过当前步骤。- 在php.ini文件中添加如下内容: - [opentelemetry] extension=opentelemetry.so
- 再次验证是否构建并启用成功。 - 方法一: - php -m | grep opentelemetry- 预期输出: - opentelemetry
- 方法二 - php --ri opentelemetry- 预期输出: - opentelemetry opentelemetry support => enabled extension version => 1.0.0beta6
 
- 为投骰子应用添加OpenTelemetry PHP自动埋点需要的额外依赖。 - # 这一步构建时间较长,会在控制台打印很多内容 pecl install grpc composer config allow-plugins.php-http/discovery false composer require \ open-telemetry/sdk \ open-telemetry/opentelemetry-auto-slim \ open-telemetry/exporter-otlp \ php-http/guzzle7-adapter \ open-telemetry/transport-grpc- open-telemetry/sdk:OpenTelemetry PHP SDK。 
- open-telemetry/opentelemetry-auto-slim:OpenTelemetry PHP针对Slim框架实现的自动埋点插件。 
- open-telemetry/exporter-otlp:OpenTelemetry PHP OTLP协议数据上报所需的依赖。 
 
 
- 运行应用。 - 执行以下命令。 - env OTEL_PHP_AUTOLOAD_ENABLED=true \ OTEL_SERVICE_NAME=<your-service-name> \ OTEL_TRACES_EXPORTER=otlp \ OTEL_METRICS_EXPORTER=none \ OTEL_LOGS_EXPORTER=none \ OTEL_EXPORTER_OTLP_PROTOCOL=grpc \ OTEL_EXPORTER_OTLP_ENDPOINT=<endpoint> \ OTEL_EXPORTER_OTLP_HEADERS=Authentication=<token> \ OTEL_PROPAGATORS=baggage,tracecontext \ php -S localhost:8080- <your-service-name>:应用名,如: - php-demo。
- <endpoint>:前提条件中获取的gRPC接入点,如: - http://tracing-analysis-dc-hz.aliyuncs.com:8090。
- <token>:前提条件中获取的接入鉴权信息。 
 
- 在浏览器中访问以下链接。 - http://localhost:8080/rolldice- 每次进入该页面,OpenTelemetry都会自动创建Trace,并将链路数据上报至阿里云可观测链路 OpenTelemetry 版。 
- 查看链路数据。 - 登录可观测链路 OpenTelemetry 版控制台,在应用列表页面找到应用名为 - <your-service-name>的应用(例如php-demo),单击应用名称进入应用详情中查看调用链。
 
基于OpenTelemery PHP SDK为应用手动埋点并上报链路数据
Manual-demo是基于PHP Slim Web框架实现一个模拟扔骰子游戏的应用,并使用OpenTelemetry PHP SDK为应用手动埋点(即在代码中创建Span,并为Span设置属性、事件、状态等),实现自定义的PHP应用链路追踪。
当OpenTelemetry PHP Extension自动埋点不满足您的场景,或者需要增加一些自定义业务埋点时,可以使用手动埋点方式上报链路数据。
前提条件
已安装PHP、Composer、PECL,且PHP版本≥7.4。
操作步骤
- 创建投骰子应用。 - 初始化。 
 - mkdir <project-name> && cd <project-name> composer init \ --no-interaction \ --stability beta \ --require slim/slim:"^4" \ --require slim/psr7:"^1" composer update- 编写应用代码。 - 在<project-name>目录下创建一个index.php文件,添加如下内容。 - 这段代码模拟扔骰子游戏,返回1-6之间的一个随机数。 - <?php use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Factory\AppFactory; require __DIR__ . '/vendor/autoload.php'; $app = AppFactory::create(); $app->get('/rolldice', function (Request $request, Response $response) { $result = random_int(1,6); $response->getBody()->write(strval($result)); return $response; }); $app->run();- 此时应用已经编写完成,执行 - php -S localhost:8080命令即可运行应用,访问地址为- http://localhost:8080/rolldice。
 
- 导入OpenTelemetry PHP SDK所需依赖。 - gRPC上报- 下载PHP HTTP客户端库,用于链路数据上报。 - composer require guzzlehttp/guzzle
- 下载OpenTelemetry PHP SDK。 - composer require \ open-telemetry/sdk \ open-telemetry/exporter-otlp
- 下载使用gRPC上报数据时所需依赖。 - pecl install grpc # 如果之前已经下载过grpc,可以跳过这一步 composer require open-telemetry/transport-grpc
 - HTTP上报- 下载PHP HTTP客户端库,用于链路数据上报。 - composer require guzzlehttp/guzzle
- 下载OpenTelemetry PHP SDK。 - composer require \ open-telemetry/sdk \ open-telemetry/exporter-otlp
 
- 编写OpenTelemetry初始化工具类。 - 在index.php文件所在目录中创建opentelemetry_util.php文件。 
- 在文件中添加如下代码。 - gRPC上报- <?php use OpenTelemetry\API\Common\Instrumentation\Globals; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\Contrib\Otlp\SpanExporter; use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; use OpenTelemetry\SDK\Sdk; use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler; use OpenTelemetry\SDK\Trace\Sampler\ParentBased; use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessorBuilder; use OpenTelemetry\SDK\Trace\TracerProvider; use OpenTelemetry\SemConv\ResourceAttributes; use OpenTelemetry\Contrib\Grpc\GrpcTransportFactory; use OpenTelemetry\Contrib\Otlp\OtlpUtil; use OpenTelemetry\API\Signals; // OpenTelemetry 初始化配置(需要在PHP应用初始化时就进行OpenTelemetry初始化配置) function initOpenTelemetry() { // 1. 设置 OpenTelemetry 资源信息 $resource = ResourceInfoFactory::emptyResource()->merge(ResourceInfo::create(Attributes::create([ ResourceAttributes::SERVICE_NAME => '<your-service-name>', # 应用名,必填 ResourceAttributes::HOST_NAME => '<your-host-name>' # 主机名,选填 ]))); // 2. 创建将 Span 输出到控制台的 SpanExporter,可选 // $spanExporter = new SpanExporter( // (new StreamTransportFactory())->create('php://stdout', 'application/json') // ); // 2. 创建通过 gRPC 上报 Span 的 SpanExporter $headers = [ 'Authentication' => "<your-token>", ]; $transport = (new GrpcTransportFactory())->create('<grpc-endpoint>' . OtlpUtil::method(Signals::TRACE), 'application/x-protobuf', $headers); $spanExporter = new SpanExporter($transport); // 3. 创建全局的 TraceProvider,用于创建 tracer $tracerProvider = TracerProvider::builder() ->addSpanProcessor( (new BatchSpanProcessorBuilder($spanExporter))->build() ) ->setResource($resource) ->setSampler(new ParentBased(new AlwaysOnSampler())) ->build(); Sdk::builder() ->setTracerProvider($tracerProvider) ->setPropagator(TraceContextPropagator::getInstance()) ->setAutoShutdown(true) // PHP 程序退出后自动关闭 tracerProvider,保证链路数据都被上报 ->buildAndRegisterGlobal(); // 将 tracerProvider 添加到全局 } ?>- <your-service-name>:应用名。 
- <your-host-name>:主机名。 
- <your-token>:通过gRPC上报数据的鉴权Token。 
- <grpc-endpoint>:通过gRPC上报数据的接入点。 
 - HTTP上报- <?php use OpenTelemetry\API\Common\Instrumentation\Globals; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\Contrib\Otlp\SpanExporter; use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; use OpenTelemetry\SDK\Sdk; use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler; use OpenTelemetry\SDK\Trace\Sampler\ParentBased; use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\SpanProcessor\BatchSpanProcessorBuilder; use OpenTelemetry\SDK\Trace\TracerProvider; use OpenTelemetry\SemConv\ResourceAttributes; use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory; use OpenTelemetry\SDK\Common\Export\TransportFactoryInterface; // OpenTelemetry 初始化配置(需要在PHP应用初始化时就进行OpenTelemetry初始化配置) function initOpenTelemetry() { // 1. 设置 OpenTelemetry 资源信息 $resource = ResourceInfoFactory::emptyResource()->merge(ResourceInfo::create(Attributes::create([ ResourceAttributes::SERVICE_NAME => '<your-service-name>', # 应用名,必填 ResourceAttributes::HOST_NAME => '<your-host-name>' # 主机名,选填 ]))); // 2. 创建将 Span 输出到控制台的 SpanExporter,可选 // $spanExporter = new SpanExporter( // (new StreamTransportFactory())->create('php://stdout', 'application/json') // ); // 2. 创建通过 HTTP 上报 Span 的 SpanExporter $transport = (new OtlpHttpTransportFactory())->create('<http-endpoint>','application/x-protobuf'); $spanExporter = new SpanExporter($transport); // 3. 创建全局的 TraceProvider,用于创建 tracer $tracerProvider = TracerProvider::builder() ->addSpanProcessor( (new BatchSpanProcessorBuilder($spanExporter))->build() ) ->setResource($resource) ->setSampler(new ParentBased(new AlwaysOnSampler())) ->build(); Sdk::builder() ->setTracerProvider($tracerProvider) ->setPropagator(TraceContextPropagator::getInstance()) ->setAutoShutdown(true) // PHP 程序退出后自动关闭 tracerProvider,保证链路数据都被上报 ->buildAndRegisterGlobal(); // 将 tracerProvider 添加到全局 } ?>- <your-service-name>:应用名。 
- <your-host-name>:主机名。 
- <http-endpoint>:通过HTTP上报数据的接入点。 
 
 
- 修改应用代码,使用OpenTelemetry API创建Span。 - 在index.php文件中导入所需包。 - <?php use OpenTelemetry\API\Common\Instrumentation\Globals; use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\SDK\Trace\TracerProvider; require __DIR__ . '/opentelemetry_util.php';
- 调用initOpenTelemetry方法完成初始化,需要在PHP应用初始化时就进行OpenTelemetry初始化配置。 - // OpenTelemetry 初始化,包含设置应用名、Trace导出方式、Trace上报接入点,并创建全局TraceProvider initOpenTelemetry();
- 在rolldice接口中创建Span。 - /** * 1. 接口功能:模拟扔骰子,返回一个1-6之间的随机正整数 * 并演示如何创建Span、设置属性、事件、带有属性的事件 */ $app->get('/rolldice', function (Request $request, Response $response) { // 获取 tracer $tracer = \OpenTelemetry\API\Globals::tracerProvider()->getTracer('my-tracer'); // 创建 Span $span = $tracer->spanBuilder("/rolldice")->startSpan(); // 为 Span 设置属性 $span->setAttribute("http.method", "GET"); // 为 Span 设置事件 $span->addEvent("Init"); // 设置带有属性的事件 $eventAttributes = Attributes::create([ "key1" => "value", "key2" => 3.14159, ]); // 业务代码 $result = random_int(1,6); $response->getBody()->write(strval($result)); $span->addEvent("End"); // 销毁 Span $span->end(); return $response; });
- 创建嵌套Span。 - 新建一个rolltwodices接口,模拟扔两个骰子,返回两个1-6之间的随机正整数。 - 以下代码演示如何创建嵌套的Span。 - $app->get('/rolltwodices', function (Request $request, Response $response) { // 获取 tracer $tracer = \OpenTelemetry\API\Globals::tracerProvider()->getTracer('my-tracer'); // 创建 Span $parentSpan = $tracer->spanBuilder("/rolltwodices/parent")->startSpan(); $scope = $parentSpan->activate(); $value1 = random_int(1,6); $childSpan = $tracer->spanBuilder("/rolltwodices/parent/child")->startSpan(); // 业务代码 $value2 = random_int(1,6); $result = "dice1: " . $value1 . ", dice2: " . $value2; // 销毁 Span $childSpan->end(); $parentSpan->end(); $scope->detach(); $response->getBody()->write(strval($result)); return $response; });
- 使用Span记录代码中发生的异常。 - 新建error接口,模拟接口发生异常。 - 以下代码演示如何在代码发生异常时使用Span记录状态。 - $app->get('/error', function (Request $request, Response $response) { // 获取 tracer $tracer = \OpenTelemetry\API\Globals::tracerProvider()->getTracer('my-tracer'); // 创建 Span $span3 = $tracer->spanBuilder("/error")->startSpan(); try { // 模拟代码发生异常 throw new \Exception('exception!'); } catch (\Throwable $t) { // 设置Span状态为error $span3->setStatus(\OpenTelemetry\API\Trace\StatusCode::STATUS_ERROR, "exception in span3!"); // 记录异常栈轨迹 $span3->recordException($t, ['exception.escaped' => true]); } finally { $span3->end(); $response->getBody()->write("error"); return $response; } });
 
- 运行应用。 - 执行以下命令 - php -S localhost:8080
- 在浏览器中访问以下链接: - http://localhost:8080/rolldice http://localhost:8080/rolltwodices http://localhost:8080/error- 每次访问页面,OpenTelemetry会创建链路数据,并将链路数据上报至阿里云可观测链路 OpenTelemetry 版。 
- 查看链路数据。 - 登录可观测链路 OpenTelemetry 版控制台,在应用列表页面找到应用名为 - <your-service-name>的应用(例如php-manual-demo),单击应用名称进入应用详情中查看调用链。
 
