仪表化
Instrumention 是指您自己向应用程序添加可观察性代码的过程。
如果您正在 instrumenting 应用程序,您需要为您使用的语言使用 OpenTelemetry SDK。然后,您将使用 SDK 初始化 OpenTelemetry,并使用 API instrument 您的代码。这将从您的应用程序和任何也带有 instrumention 的已安装库中发出遥测数据。
如果您正在 instrumenting 库,请仅安装您语言的 OpenTelemetry API 包。您的库不会自行发出遥测数据。只有当它作为使用 OpenTelemetry SDK 的应用程序的一部分时,它才会发出遥测数据。有关 instrumenting 库的更多信息,请参阅 Libraries。
有关 OpenTelemetry API 和 SDK 的更多信息,请参阅 specification。
示例应用程序准备
These instructions use a modified version of the example app from Getting Started to help you learn how to instrument your PHP code.
If you want to instrument your own app or library, follow the instructions to adapt the process to your own code.
Dependencies
In an empty directory, initialize a minimal composer.json file with the following content
composer init \
--no-interaction \
--require slim/slim:"^4" \
--require slim/psr7:"^1" \
--require monolog/monolog:"^3"
composer update
创建并启动 HTTP 服务器
To highlight the difference between instrumenting a library and a standalone app, split out the dice rolling into a library file, which then will be imported as a dependency by the app file.
Create the library file named dice.php and add the following code to it
<?php
class Dice {
private $tracer;
function __construct() {
}
public function roll($rolls) {
$result = [];
for ($i = 0; $i < $rolls; $i++) {
$result[] = $this->rollOnce();
}
return $result;
}
private function rollOnce() {
$result = random_int(1, 6);
return $result;
}
}
Create the app file named index.php and add the following code to it
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;
use Monolog\Logger;
use Monolog\Level;
use Monolog\Handler\StreamHandler;
require __DIR__ . '/vendor/autoload.php';
require('dice.php');
$logger = new Logger('dice-server');
$logger->pushHandler(new StreamHandler('php://stdout', Level::INFO));
$app = AppFactory::create();
$dice = new Dice();
$app->get('/rolldice', function (Request $request, Response $response) use ($logger, $dice) {
$params = $request->getQueryParams();
if(isset($params['rolls'])) {
$result = $dice->roll($params['rolls']);
if (isset($params['player'])) {
$logger->info("A player is rolling the dice.", ['player' => $params['player'], 'result' => $result]);
} else {
$logger->info("Anonymous player is rolling the dice.", ['result' => $result]);
}
$response->getBody()->write(json_encode($result));
} else {
$response->withStatus(400)->getBody()->write("Please enter a number of rolls");
}
return $response;
});
$app->run();
To ensure that it is working, run the application with the following command and open https://:8080/rolldice?rolls=12 in your web browser.
php -S localhost:8080
Instrumentation setup
依赖项
Install OpenTelemetry API packages
composer require open-telemetry/api open-telemetry/sem-conv
初始化 SDK
If you’re instrumenting a library, skip this step.
To use the OpenTelemetry SDK for PHP you need packages that satisfy the dependencies for psr/http-client-implementation and psr/http-factory-implementation. Here we will use Guzzle, which provides both
composer require guzzlehttp/guzzle
Now you can install the OpenTelemetry SDK, and OTLP exporter
composer require \
open-telemetry/sdk \
open-telemetry/exporter-otlp
If you are an application developer, you need to configure an instance of the OpenTelemetry SDK as early as possible in your application. Here we will use the Sdk::builder() method, and we will globally register the providers.
You can build the providers by using the TracerProvider::builder(), LoggerProvider::builder(), and MeterProvider::builder() methods.
In the case of the example app, create a file named instrumentation.php with the following content
<?php
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Logs\EventLogger;
use OpenTelemetry\API\Logs\LogRecord;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\Contrib\Otlp\LogsExporter;
use OpenTelemetry\Contrib\Otlp\MetricExporter;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Logs\LoggerProvider;
use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;
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\TracerProvider;
use OpenTelemetry\SemConv\Attributes\ServiceAttributes;
use OpenTelemetry\SemConv\Incubating\Attributes\DeploymentIncubatingAttributes;
use OpenTelemetry\SemConv\Incubating\Attributes\ServiceIncubatingAttributes;
require 'vendor/autoload.php';
$resource = ResourceInfoFactory::emptyResource()->merge(ResourceInfo::create(Attributes::create([
ServiceIncubatingAttributes::SERVICE_NAMESPACE => 'demo',
ServiceAttributes::SERVICE_NAME => 'test-application',
ServiceAttributes::SERVICE_VERSION => '0.1',
DeploymentIncubatingAttributes::DEPLOYMENT_ENVIRONMENT_NAME => 'development',
])));
$spanExporter = new SpanExporter(
(new StreamTransportFactory())->create('php://stdout', 'application/json')
);
$logExporter = new LogsExporter(
(new StreamTransportFactory())->create('php://stdout', 'application/json')
);
$reader = new ExportingReader(
new MetricExporter(
(new StreamTransportFactory())->create('php://stdout', 'application/json')
)
);
$meterProvider = MeterProvider::builder()
->setResource($resource)
->addReader($reader)
->build();
$tracerProvider = TracerProvider::builder()
->addSpanProcessor(
new SimpleSpanProcessor($spanExporter)
)
->setResource($resource)
->setSampler(new ParentBased(new AlwaysOnSampler()))
->build();
$loggerProvider = LoggerProvider::builder()
->setResource($resource)
->addLogRecordProcessor(
new SimpleLogRecordProcessor($logExporter)
)
->build();
Sdk::builder()
->setTracerProvider($tracerProvider)
->setMeterProvider($meterProvider)
->setLoggerProvider($loggerProvider)
->setPropagator(TraceContextPropagator::getInstance())
->setAutoShutdown(true)
->buildAndRegisterGlobal();
Include this code at the top of your application file index.php
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
require __DIR__ . '/vendor/autoload.php';
require('dice.php');
require('instrumentation.php');
// ...
For debugging and local development purposes, the example exports telemetry to the console. After you have finished setting up manual instrumentation, you need to configure an appropriate exporter to export the app’s telemetry data to one or more telemetry backends.
The example also sets up the mandatory SDK default attribute service.name, which holds the logical name of the service, and the optional, but highly encouraged, attribute service.version, which holds the version of the service API or implementation.
Alternative methods exist for setting up resource attributes. For more information, see Resources.
Global Providers
Throughout the following examples we will usually obtain the globally registered providers via OpenTelemetry\API\Globals
$tracerProvider = \OpenTelemetry\API\Globals::tracerProvider();
$meterProvider = \OpenTelemetry\API\Globals::meterProvider();
$loggerProvider = \OpenTelemetry\API\Globals::loggerProvider();
Shutdown
It’s important that each provider’s shutdown() method is run when the PHP process ends, to enable flushing of any enqueued telemetry. In the above example, this has been taken care of with setAutoShutdown(true).
You can also use the ShutdownHandler to register each provider’s shutdown function as part of PHP’s shutdown process
\OpenTelemetry\SDK\Common\Util\ShutdownHandler::register([$tracerProvider, 'shutdown']);
\OpenTelemetry\SDK\Common\Util\ShutdownHandler::register([$meterProvider, 'shutdown']);
\OpenTelemetry\SDK\Common\Util\ShutdownHandler::register([$loggerProvider, 'shutdown']);
追踪
初始化追踪
If you’re instrumenting a library, skip this step.
要在应用程序中启用 追踪,您需要一个已初始化的 TracerProvider,它将允许您创建一个 Tracer。
If a TracerProvider is not created, the OpenTelemetry APIs for tracing will use a no-op implementation and fail to generate data.
If you followed the instructions to initialize the SDK above, you have a TracerProvider setup for you already. You can continue with acquiring a tracer.
获取 Tracer
Anywhere in your application where you write manual tracing code should call getTracer to acquire a tracer. For example
$tracerProvider = Globals::tracerProvider();
$tracer = $tracerProvider->getTracer(
'instrumentation-scope-name', //name (required)
'instrumentation-scope-version', //version
'http://example.com/my-schema', //schema url
['foo' => 'bar'] //attributes
);
The values of instrumentation-scope-name and instrumentation-scope-version should uniquely identify the Instrumentation Scope, such as the package, module or class name. While the name is required, the version is still recommended despite being optional.
It’s generally recommended to call getTracer in your app when you need it rather than exporting the tracer instance to the rest of your app. This helps avoid trickier application load issues when other required dependencies are involved.
In the case of the example app, there are two places where a tracer may be acquired with an appropriate Instrumentation Scope
First, in the application file index.php
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use OpenTelemetry\API\Globals;
require __DIR__ . '/vendor/autoload.php';
require('dice.php');
require('instrumentation.php');
$tracerProvider = Globals::tracerProvider();
$tracer = $tracerProvider->getTracer(
'dice-server',
'0.1.0',
'https://opentelemetry.org.cn/schemas/1.24.0'
);
$logger = new Logger('dice-server');
$logger->pushHandler(new StreamHandler('php://stdout', Logger::INFO));
$app = AppFactory::create();
$dice = new Dice();
$app->get('/rolldice', function (Request $request, Response $response) use ($logger, $dice, $tracer) {
// ...
}
And second, in the library file dice.php
<?php
use OpenTelemetry\API\Globals;
use OpenTelemetry\SemConv\TraceAttributes;
class Dice {
private $tracer;
function __construct() {
$tracerProvider = Globals::tracerProvider();
$this->tracer = $tracerProvider->getTracer(
'dice-lib',
'0.1.0',
'https://opentelemetry.org.cn/schemas/1.24.0'
);
}
public function roll($rolls) {
$result = [];
for ($i = 0; $i < $rolls; $i++) {
$result[] = $this->rollOnce();
}
return $result;
}
private function rollOnce() {
$result = random_int(1, 6);
return $result;
}
}
创建 Span
Now that you have tracers initialized, you can create spans.
The code below illustrates how to create a span.
<?php
public function roll($rolls) {
$span = $this->tracer->spanBuilder("rollTheDice")->startSpan();
$result = [];
for ($i = 0; $i < $rolls; $i++) {
$result[] = $this->rollOnce();
}
$span->end();
return $result;
}
Note, that it’s required to end() the span, otherwise it will not be exported.
If you followed the instructions using the example app up to this point, you can copy the code above in your library file dice.php. You should now be able to see spans emitted from your app.
Start your app as follows, and then send it requests by visiting https://:8080/rolldice?rolls=12 with your browser or curl.
php -S 8080 localhost
After a while, you should see the spans printed in the console by the SpanExporter, something like this
{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.namespace",
"value": {
"stringValue": "demo"
}
},
{
"key": "service.name",
"value": {
"stringValue": "test-application"
}
},
{
"key": "service.version",
"value": {
"stringValue": "0.1"
}
},
{
"key": "deployment.environment",
"value": {
"stringValue": "development"
}
}
]
},
"scopeSpans": [
{
"scope": {
"name": "dice-lib",
"version": "0.1.0"
},
"spans": [
{
"traceId": "007a1e7a89f21f98b600d288b7d65390",
"spanId": "c32797fc72c252d2",
"flags": 1,
"name": "rollTheDice",
"kind": 1,
"startTimeUnixNano": "1706111239077485365",
"endTimeUnixNano": "1706111239077735657",
"status": {}
}
],
"schemaUrl": "https://opentelemetry.org.cn/schemas/1.24.0"
}
]
}
]
}
Create nested Spans
嵌套 spans 让您可以追踪本质上是嵌套的工作。例如,下面的 rollOnce() 函数代表一个嵌套操作。下面的示例创建了一个追踪 rollOnce() 的嵌套 span
private function rollOnce() {
$parent = OpenTelemetry\API\Trace\Span::getCurrent();
$scope = $parent->activate();
try {
$span = $this->tracer->spanBuilder("rollTheDice")->startSpan();
$result = random_int(1, 6);
$span->end();
} finally {
$scope->detach();
}
return $result;
}
You must detach the active scope if you have activated it.
获取当前 Span
In the example above, we retrieved the current span, using the following method
$span = OpenTelemetry\API\Trace\Span::getCurrent();
Get a span from context
It can also be helpful to get the span from a given context that isn’t necessarily the active span.
$span = OpenTelemetry\API\Trace\Span::fromContext($context);
Span 属性
Attributes let you attach key/value pairs to a Span so it carries more information about the current operation that it’s tracking.
private function rollOnce() {
$parent = OpenTelemetry\API\Trace\Span::getCurrent();
$scope = $parent->activate();
try {
$span = $this->tracer->spanBuilder("rollTheDice")->startSpan();
$result = random_int(1, 6);
$span->setAttribute('dicelib.rolled', $result);
$span->end();
} finally {
$scope->detach();
}
return $result;
}
语义属性
There are semantic conventions for spans representing operations in well-known protocols like HTTP or database calls. Semantic conventions for these spans are defined in the specification at Trace Semantic Conventions. In the simple example of this guide the source code attributes can be used.
First add the semantic conventions as a dependency to your application
composer require open-telemetry/sem-conv
Add the following to the top of your file
use OpenTelemetry\SemConv\Attributes\CodeAttributes;
Finally, you can update your file to include semantic attributes
$span->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, 'rollOnce');
$span->setAttribute(CodeAttributes::CODE_FILE_PATH, __FILE__);
Create Spans with events
Spans can be annotated with named events (called Span Events) that can carry zero or more Span Attributes, each of which itself is a key:value map paired automatically with a timestamp.
$span->addEvent("Init");
...
$span->addEvent("End");
$eventAttributes = Attributes::create([
"operation" => "calculate-pi",
"result" => 3.14159,
]);
$span->addEvent("End Computation", $eventAttributes);
Create Spans with links
A Span may be linked to zero or more other Spans that are causally related via a Span Link. Links can be used to represent batched operations where a Span was initiated by multiple initiating Spans, each representing a single incoming item being processed in the batch.
$span = $tracer->spanBuilder("span-with-links")
->addLink($parentSpan1->getContext())
->addLink($parentSpan2->getContext())
->addLink($parentSpan3->getContext())
->addLink($remoteSpanContext)
->startSpan();
For more details how to read context from remote processes, see Context Propagation.
Set span status and record exceptions
可以在 Span 上设置 Status,通常用于指定 Span 未成功完成 - Error。默认情况下,所有 spans 的状态都是 Unset,这意味着 span 完成时没有错误。Ok 状态保留给您需要明确将 span 标记为成功而不是坚持默认的 Unset(即,“无错误”)的情况。
可以在 span 完成之前的任何时间设置状态。
It can be a good idea to record exceptions when they happen. It’s recommended to do this in conjunction with setting span status.
可以在 Span 结束前的任何时候设置状态
$span = $tracer->spanBuilder("my-span")->startSpan();
try {
// do something that could fail
throw new \Exception('uh-oh');
} catch (\Throwable $t) {
$span->setStatus(\OpenTelemetry\API\Trace\StatusCode::STATUS_ERROR, "Something bad happened!");
//This will capture things like the current stack trace in the span.
$span->recordException($t, ['exception.escaped' => true]);
throw $t;
} finally {
$span->end();
}
Span Processor
Different Span processors are offered by OpenTelemetry. The SimpleSpanProcessor immediately forwards ended spans to the exporter, while the BatchSpanProcessor batches them and sends them periodically.
$tracerProvider = TracerProvider::builder()
->addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporterFactory()->create()))
->build();
Transports
All exporters require a Transport, which is responsible for the sending of telemetry data
PsrTransport- uses a PSR-18 client to send data over HTTPStreamTransport- uses a stream to send data (eg to file orstdout)GrpcTransport- uses gRPC to send protobuf-encoded data
Exporter
See Exporters
指标
OpenTelemetry can be used to measure and record different types of metrics from an application, which can then be pushed to a metrics service such as the OpenTelemetry Collector
- counter
- async counter
- histogram
- async gauge
- up/down counter
- async up/down counter
Meter types and usage are explained in the metrics concepts documentation.
设置
First, create a MeterProvider
<?php
use OpenTelemetry\SDK\Metrics\MetricExporter\ConsoleMetricExporterFactory;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;
require 'vendor/autoload.php';
$reader = new ExportingReader((new ConsoleMetricExporterFactory())->create());
$meterProvider = MeterProvider::builder()
->addReader($reader)
->build();
Synchronous meters
A synchronous meter must be manually adjusted as data changes
$up_down = $meterProvider
->getMeter('demo_meter')
->createUpDownCounter('queued', 'jobs', 'The number of jobs enqueued');
//jobs come in
$up_down->add(5);
//job completed
$up_down->add(-1);
//more jobs come in
$up_down->add(2);
$meterProvider->forceFlush();
Synchronous metrics are exported when forceFlush() or shutdown() are called on the meter provider.
View output
{
"resourceMetrics": [
{
"resource": {},
"scopeMetrics": [
{
"scope": { "name": "demo_meter" },
"metrics": [
{
"name": "queued",
"description": "The number of jobs enqueued",
"unit": "jobs",
"sum": {
"dataPoints": [
{
"startTimeUnixNano": "1687332126443709851",
"timeUnixNano": "1687332126445544432",
"asInt": "6"
}
],
"aggregationTemporality": "AGGREGATION_TEMPORALITY_DELTA"
}
}
]
}
]
}
]
}
Asynchronous meters
Async meters are observable, eg ObservableGauge. When registering an observable/async meter, you provide one or more callback functions. The callback functions will be called by a periodic exporting metric reader whenever its collect() method is called, for example based on an event-loop timer. The callback(s) are responsible for returning the current data for the meter.
In this example, the callbacks are executed every time that $reader->collect() is executed
$queue = [
'job1',
'job2',
'job3',
];
$reader = $meterProvider
->getMeter('demo_meter')
->createObservableGauge('queued', 'jobs', 'The number of jobs enqueued')
->observe(static function (ObserverInterface $observer) use (&$queue): void {
$observer->observe(count($queue));
});
$reader->collect();
array_pop($queue);
$reader->collect();
View output
{"resourceMetrics":[{"resource":{},"scopeMetrics":[{"scope":{"name":"demo_meter"},"metrics":[{"name":"queued","description":"The number of jobs enqueued","unit":"jobs","gauge":{"dataPoints":[{"startTimeUnixNano":"1687331630161510994","timeUnixNano":"1687331630162989144","asInt":"3"}]}}]}]}]}
{"resourceMetrics":[{"resource":{},"scopeMetrics":[{"scope":{"name":"demo_meter"},"metrics":[{"name":"queued","description":"The number of jobs enqueued","unit":"jobs","gauge":{"dataPoints":[{"startTimeUnixNano":"1687331630161510994","timeUnixNano":"1687331631164759171","asInt":"2"}]}}]}]}]}
日志
As logging is a mature and well-established function, the OpenTelemetry approach is a little different for this signal.
The OpenTelemetry logger is not designed to be used directly, but rather to be integrated into existing logging libraries. In this way, you can choose to have some or all of your application logs sent to an OpenTelemetry-compatible service such as the collector.
Setup
First, we create a LoggerProvider
<?php
use OpenTelemetry\API\Logs\EventLogger;
use OpenTelemetry\API\Logs\LogRecord;
use OpenTelemetry\Contrib\Otlp\LogsExporter;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Logs\LoggerProvider;
use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
require 'vendor/autoload.php';
$exporter = new LogsExporter(
(new StreamTransportFactory())->create('php://stdout', 'application/json')
);
$loggerProvider = LoggerProvider::builder()
->addLogRecordProcessor(new SimpleLogRecordProcessor($exporter))
->setResource(ResourceInfoFactory::emptyResource())
->build();
Logging events
An EventLogger can use a Logger to emit log events
$logger = $loggerProvider->getLogger('demo', '1.0', 'http://schema.url', [/*attributes*/]);
$eventLogger = new EventLogger($logger, 'my-domain');
$record = (new LogRecord('hello world'))
->setSeverityText('INFO')
->setAttributes([/*attributes*/]);
$eventLogger->logEvent('foo', $record);
View sample output
{
"resourceLogs": [
{
"resource": {},
"scopeLogs": [
{
"scope": {
"name": "demo",
"version": "1.0"
},
"logRecords": [
{
"observedTimeUnixNano": "1687496730010009088",
"severityText": "INFO",
"body": {
"stringValue": "hello world"
},
"attributes": [
{
"key": "event.name",
"value": {
"stringValue": "foo"
}
},
{
"key": "event.domain",
"value": {
"stringValue": "my-domain"
}
}
]
}
]
}
]
}
]
}
Integrations for third-party logging libraries
Monolog
You can use the monolog handler to send monolog logs to an OpenTelemetry-capable receiver. First, install the monolog library and a handler
composer require \
monolog/monolog \
open-telemetry/opentelemetry-logger-monolog
Following on from the logging example above
$handler = new \OpenTelemetry\Contrib\Logs\Monolog\Handler(
$loggerProvider,
\Psr\Log\LogLevel::ERROR,
);
$monolog = new \Monolog\Logger('example', [$handler]);
$monolog->info('hello, world');
$monolog->error('oh no', [
'foo' => 'bar',
'exception' => new \Exception('something went wrong'),
]);
View sample output
{
"resourceLogs": [
{
"resource": {},
"scopeLogs": [
{
"scope": {
"name": "monolog"
},
"logRecords": [
{
"timeUnixNano": "1687496945597429000",
"observedTimeUnixNano": "1687496945598242048",
"severityNumber": "SEVERITY_NUMBER_ERROR",
"severityText": "ERROR",
"body": {
"stringValue": "oh no"
},
"attributes": [
{
"key": "channel",
"value": {
"stringValue": "example"
}
},
{
"key": "context",
"value": {
"arrayValue": {
"values": [
{
"stringValue": "bar"
},
{
"arrayValue": {
"values": [
{
"stringValue": "Exception"
},
{
"stringValue": "something went wrong"
},
{
"intValue": "0"
},
{
"stringValue": "/usr/src/myapp/logging.php:31"
}
]
}
}
]
}
}
}
]
}
]
}
]
}
]
}
Error Handling
By default, OpenTelemetry will log errors and warnings via PHP’s error_log function. The verbosity can be controlled or disabled via the OTEL_LOG_LEVEL setting.
The OTEL_PHP_LOG_DESTINATION variable can be used to control log destination or disable error logging completely. Valid values are default, error_log, stderr, stdout, psr3, or none. default (or if the variable is not set), will use error_log unless a PSR-3 logger is configured
$logger = new \Example\Psr3Logger(LogLevel::INFO);
\OpenTelemetry\API\LoggerHolder::set($logger);
For more fine-grained control and special case handling, custom handlers and filters can be applied to the PSR-3 logger (if the logger offers this ability).
下一步
You’ll also want to configure an appropriate exporter to export your telemetry data to one or more telemetry backends.