Telemetry for Laravel
alpha
API reference
API reference
Everything hangs off the Cbox\Telemetry\Facades\Telemetry facade.
Metrics
// Counter — monotonic, shared across processes
Telemetry::counter('orders.created', 'Orders created')->inc();
Telemetry::counter('orders.created')->inc(5, ['tenant' => 'acme']);
// Push gauge — set, or adjust up/down (active connections, in-flight jobs)
Telemetry::gauge('cache.warmed_keys')->set(1240);
Telemetry::gauge('jobs.in_flight')->increment(labels: ['queue' => 'default']);
Telemetry::gauge('jobs.in_flight')->decrement(labels: ['queue' => 'default']);
// Observable gauge — callback at scrape time, single or multi-series
Telemetry::gauge('queue.depth', fn () => Queue::size(), unit: '{jobs}');
Telemetry::gauge('queue.depth', fn () => [
[12, ['queue' => 'default']],
[3, ['queue' => 'mail']],
]);
// Histogram — distributions, with closure timing
Telemetry::histogram('checkout.duration', unit: 'ms')->record($ms);
Telemetry::histogram('import.duration', buckets: [100, 500, 1000])
->time(fn () => $importer->run());
Traces
// Closure form — ends, records exceptions, rethrows
$result = Telemetry::span('billing.recalculate', function ($span) {
$span->setAttribute('tenant.id', $id);
return $service->run();
});
// Manual form
$span = Telemetry::span('phase.one', attributes: ['shard' => 3]);
$span->addEvent('checkpoint', ['rows' => 5000]);
$span->recordException($e);
$span->setStatus(SpanStatus::Ok);
$span->updateName('phase.one retried');
$span->end();
// Context
Telemetry::currentSpan(); // ?Span
Telemetry::traceId(); // ?string
Telemetry::traceparent(); // ?string — W3C header value
Telemetry::continueTrace($header); // continue a remote trace
Telemetry::resetContext(); // forget the active trace
// Outbound propagation
Http::withTraceparent()->post($url, $payload);
Events & logs
Telemetry::event('autoscale.decision', ['workers' => 7]);
// config/logging.php — ship logs as trace-correlated OTLP log records
'telemetry' => ['driver' => 'telemetry', 'level' => 'info'],
Providers & exporters
Telemetry::provider(new QueueMetricsProvider); // contract-based
Telemetry::contributes('my-app', fn (Registry $r) => ...); // inline
Telemetry::addExporter(new ClickhouseExporter(...)); // runtime exporter
Export & introspection
Telemetry::collect(); // list<MetricFamily> — what a scrape sees
Telemetry::flush(); // export buffered spans + events now
Telemetry::flushMetrics(); // push metrics to exporters now
Telemetry::enabled(); // bool
Telemetry::handleExceptionsUsing(fn (Throwable $e) => ...);
Testing
$fake = Telemetry::fake();
$fake->assertCounterIncremented('orders.created', ['tenant' => 'acme']);
$fake->assertCounterNotIncremented('orders.cancelled');
$fake->assertGaugeSet('cache.warmed_keys');
$fake->assertHistogramRecorded('checkout.duration');
$fake->assertSpanRecorded('billing.recalculate', fn ($span) => ...);
$fake->assertSpanNotRecorded('other.work');
$fake->assertEventEmitted('autoscale.decision', fn ($event) => ...);
$fake->assertEventNotEmitted('never.happened');
$fake->counterValue('orders.created', ['tenant' => 'acme']);
$fake->gaugeValue('queue.depth');
$fake->histogramCount('checkout.duration');
$fake->recordedSpans('billing.recalculate');
$fake->recordedEvents();
Artisan
php artisan telemetry:doctor # verify store, exporters, config
php artisan telemetry:flush [--wipe] # push metrics to exporters
php artisan telemetry:dashboards [--export=] # install the Grafana dashboards
php artisan about # shows the Telemetry section