通过OpenTelemetry上报PHP应用

通过OpenTelemetry为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTelemetryPHP应用进行自动或手动埋点并上报数据。

前提条件

获取接入点信息

  1. 登录ARMS控制台,在左侧导航栏单击接入中心

  2. 服务端应用区域单击OpenTelemetry卡片。

  3. 在弹出的OpenTelemetry面板中选择数据需要上报的地域。

    说明

    初次接入的地域将会自动进行资源初始化。

  4. 选择连接方式上报方式,然后复制接入点信息。

    • 连接方式:若您的服务部署在阿里云上,且所属地域与选择的接入地域一致,推荐使用阿里云内网方式,否则选择公网方式。

    • 上报方式:根据客户端支持的协议类型选择HTTPgRPC协议上报数据。

    image.png

背景信息

OpenTelemetry PHP支持自动埋点和手动埋点,对PHP的版本要求如下:

  • 自动埋点支持的版本:PHP 8.0 +

  • 手动埋点支持的版本:PHP 7.4 +

支持自动埋点的框架列表如下,完整信息请参见OpenTelemetry官方文档

展开查看支持监控的PHP框架

  • CodeIgniter Framework

  • PHP HTTP Async Client

  • PHP IO

  • Laravel

  • MongoDB

  • PDO

  • PSR-15 middleware

  • PSR-18 HTTP clients

  • PSR-3

  • Slim

  • Symfony

  • WordPress

  • Yii

示例Demo

示例代码仓库地址:php-opentelemetry-demo

基于OpenTelemery PHP Extension为应用自动埋点并上报链路数据

Auto-demo是基于PHP Slim Web框架实现一个模拟扔骰子游戏的应用,并使用OpenTelemetry为应用自动埋点(即自动创建Trace/Span等链路数据),实现无侵入的PHP应用链路追踪。

OpenTelemetry除了支持Slim框架的自动埋点,还支持多种框架,完整列表请参见官方文档

前提条件

已安装PHP、Composer、PECL,且PHP版本≥8.0。

操作步骤

  1. 创建投骰子应用。

    1. 初始化。

    mkdir <project-name> && cd <project-name>
    
    
    composer init \
     --no-interaction \
     --stability beta \
     --require slim/slim:"^4" \
     --require slim/psr7:"^1"
    composer update
    1. 编写应用代码。

      在<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

  2. 构建OpenTelemetry PHP扩展。

    1. 下载构建OpenTelemetry PHP extension所需要的工具。

      • macOS

        brew install gcc make autoconf
      • Linux(apt)

        sudo apt-get install gcc make autoconf
    2. 使用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
    3. (可选)启用OpenTelemetry PHP扩展。

      如果上一步输出了Extension opentelemetry enabled in php.ini,表明已经启用,请跳过当前步骤。

      php.ini文件中添加如下内容:

      [opentelemetry]
      extension=opentelemetry.so
    4. 再次验证是否构建并启用成功。

      • 方法一:

        php -m | grep opentelemetry

        预期输出:

        opentelemetry
      • 方法二

        php --ri opentelemetry

        预期输出:

        opentelemetry
        opentelemetry support => enabled
        extension version => 1.0.0beta6
    5. 为投骰子应用添加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协议数据上报所需的依赖。

  3. 运行应用。

    1. 执行以下命令。

      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>:前提条件中获取的接入鉴权信息。

    2. 在浏览器中访问以下链接。

      http://localhost:8080/rolldice

      每次进入该页面,OpenTelemetry都会自动创建Trace,并将链路数据上报至阿里云可观测链路 OpenTelemetry 版

    3. 查看链路数据。

      登录可观测链路 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。

操作步骤

  1. 创建投骰子应用。

    1. 初始化。

    mkdir <project-name> && cd <project-name>
    
    
    composer init \
     --no-interaction \
     --stability beta \
     --require slim/slim:"^4" \
     --require slim/psr7:"^1"
    composer update
    1. 编写应用代码。

      在<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

  2. 导入OpenTelemetry PHP SDK以及OpenTelemetry gRPC Explorer所需依赖。

    1. 下载PHP HTTP客户端库,用于链路数据上报。

      composer require guzzlehttp/guzzle
    2. 下载OpenTelemetry PHP SDK。

      composer require \
       open-telemetry/sdk \
       open-telemetry/exporter-otlp
    3. 下载使用gRPC上报数据时所需依赖。

      pecl install grpc # 如果之前已经下载过grpc,可以跳过这一步
      composer require open-telemetry/transport-grpc
  3. 编写OpenTelemetry初始化工具类。

    1. index.php文件所在目录中创建opentelemetry_util.php文件。

    2. 在文件中添加如下代码。

      <?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\Common\Signal\Signals;
      
      // OpenTelemetry 初始化配置(包含设置应用名、Trace导出方式、Trace上报接入点,并创建全局TraceProvide。需要在PHP应用初始化时就进行OpenTelemetry初始化配置)
      function initOpenTelemetry()
      { 
       // 1. 设置 OpenTelemetry 资源信息
       $resource = ResourceInfoFactory::emptyResource()->merge(ResourceInfo::create(Attributes::create([
       ResourceAttributes::SERVICE_NAME => '<your-service-name>', # 应用名,必填,如php-manual-demo
       ResourceAttributes::HOST_NAME => '<your-host-name>' # 主机名,选填
       ])));
      
      
       // 2. 创建将 Span 输出到控制台的 SpanExplorer
       // $spanExporter = new SpanExporter(
       // (new StreamTransportFactory())->create('php://stdout', 'application/json')
       // );
      
       // 2. 创建通过 gRPC 上报 Span 的 SpanExplorer
       $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上报数据的接入点。

  4. 修改应用代码,使用OpenTelemetry API创建Span。

  5. index.php文件中导入所需包。

    1. <?php
      
      use OpenTelemetry\API\Common\Instrumentation\Globals;
      use OpenTelemetry\SDK\Common\Attribute\Attributes;
      use OpenTelemetry\SDK\Trace\TracerProvider;
      
      require __DIR__ . '/opentelemetry_util.php';
    2. 调用initOpenTelemetry方法完成初始化,需要在PHP应用初始化时就进行OpenTelemetry初始化配置。

      // OpenTelemetry 初始化,包含设置应用名、Trace导出方式、Trace上报接入点,并创建全局TraceProvider
      initOpenTelemetry();
    3. 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;
      });
    4. 创建嵌套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;
      });
    5. 使用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;
       }
      });
  6. 运行应用。

    1. 执行以下命令

      php -S localhost:8080
    2. 在浏览器中访问以下链接:

      http://localhost:8080/rolldice
      http://localhost:8080/rolltwodices
      http://localhost:8080/error

      每次访问页面,OpenTelemetry会创建链路数据,并将链路数据上报至阿里云可观测链路 OpenTelemetry 版

    3. 查看链路数据。

      登录可观测链路 OpenTelemetry 版控制台,在应用列表页面找到应用名为<your-service-name>的应用(例如php-manual-demo),单击应用名称进入应用详情中查看调用链。