Quick tip: how to test meter provider in PHP OpenTelemetry

I’m working on a library that uses the OpenTelemetry PHP SDK, and I ran into some issues while trying to test metrics. The way the OTel SDK is set up—using global state, _register files, Composer, and autoload—made it tricky to see the metrics output. I needed the metrics to show up in the console for debugging instead of being sent off to some remote system.

Figuring out how to tweak things to get what I needed was a bit of a challenge, but I managed to find a way to make it work for local testing and debugging. If you’re in a similar situation, you might need to get creative with how you configure the SDK.

Here is the code:

<?php

use OpenTelemetry\API\Common\Time\Clock;
use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Logs\NoopEventLoggerProvider;
use OpenTelemetry\API\Logs\NoopLoggerProvider;
use OpenTelemetry\API\Trace\NoopTracerProvider;
use OpenTelemetry\Context\Propagation\NoopTextMapPropagator;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory;
use OpenTelemetry\SDK\Common\InstrumentationScope\Configurator;
use OpenTelemetry\SDK\Metrics\Exemplar\ExemplarFilter\AllExemplarFilter;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricExporter\ConsoleMetricExporter;
use OpenTelemetry\SDK\Metrics\MetricFactory\StreamFactory;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;
use OpenTelemetry\SDK\Metrics\StalenessHandler\NoopStalenessHandlerFactory;
use OpenTelemetry\SDK\Metrics\View\CriteriaViewRegistry;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;

$consoleExporter = new ConsoleMetricExporter();
$metricReader = new ExportingReader($consoleExporter);

// create console exporting metter provider
$meterProvider = new MeterProvider(
    null,
    ResourceInfoFactory::emptyResource(),
    Clock::getDefault(),
    Attributes::factory(),
    new InstrumentationScopeFactory(Attributes::factory()),
    [$metricReader],
    new CriteriaViewRegistry(),
    new AllExemplarFilter(),
    new NoopStalenessHandlerFactory(),
    new StreamFactory(),
    Configurator::meter(),
);

$tracerProvider = new NoopTracerProvider();
$loggerProvider = new NoopLoggerProvider();
$eventLoggerProvider = new NoopEventLoggerProvider();
$textMapPropagator = new NoopTextMapPropagator();
$g = new Globals(
    $tracerProvider,
    $meterProvider,
    $loggerProvider,
    $eventLoggerProvider,
    $textMapPropagator
);

$ref = new \ReflectionClass(Globals::class);
$ref->setStaticPropertyValue('globals', $g);

// then this will return prepared meter provider that is in this case outputting everything to the console. 
Globals::meterProvider()

ob_start();
// test code

$content = ob_get_clean();

You can use similar approach to replace other parts like TraceProvider or LoggerProvider.

I hope this will save you some time 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *