zharp / logs
Zharp Application Monitoring for PHP, Laravel and Symfony: traces, logs and errors on OpenTelemetry.
Requires
- php: ^8.2
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- guzzlehttp/psr7: ^2.6
- open-telemetry/api: ^1.4
- open-telemetry/context: ^1.1
- open-telemetry/exporter-otlp: ^1.3
- open-telemetry/sdk: ^1.6
Requires (Dev)
- illuminate/http: ^10.0 || ^11.0 || ^12.0
- monolog/monolog: ^3.0
- phpunit/phpunit: ^10.5 || ^11.0
- symfony/http-kernel: ^6.4 || ^7.0
Suggests
- ext-opentelemetry: With open-telemetry/opentelemetry-auto-pdo, -auto-guzzle, -auto-laravel or -auto-symfony: database and outgoing HTTP spans too.
- ext-protobuf: Faster OTLP encoding than the pure-PHP google/protobuf.
- monolog/monolog: For Zharp\Monolog\ZharpHandler (Laravel and Symfony use Monolog).
Provides
None
Conflicts
None
Replaces
None
README
Zharp Application Monitoring for PHP, Laravel and Symfony: every request traced, logs sent with their trace, exceptions grouped into issues. A thin layer over the official OpenTelemetry PHP SDK — Zharp configures the OTLP exporter, resource, W3C tracecontext + baggage propagation and a log bridge, and creates the request span when nothing else does.
Full documentation: zharp.io/docs/sdks/php
Install
composer require zharp/zharp
PHP 8.2+ (what the OpenTelemetry SDK needs). Composer may ask to allow the php-http/discovery
and tbachert/spi plugins that the OpenTelemetry SDK uses — allow them.
Laravel
Nothing to register — the service provider is auto-discovered. In .env:
ZHARP_KEY=zpk_… ZHARP_SERVICE=checkout-api ZHARP_ENVIRONMENT=production # ZHARP_ENDPOINT=… optional
Every HTTP request gets a SERVER span named by its route (GET /orders/{id}), continuing the
caller's trace. Exceptions Laravel reports become unhandled Zharp errors with the request's trace;
everything Laravel logs (info and up, ZHARP_LOG_LEVEL to change) is sent with the trace, user
and session. Telemetry goes once the response is out, and after every queue job.
php artisan vendor:publish --tag=zharp-config publishes config/zharp.php.
Symfony
Same environment variables, then enable the bundle in config/bundles.php:
Zharp\Symfony\ZharpBundle::class => ['all' => true],
Every main request gets a SERVER span (named by the route name), an exception that escapes a
controller is an unhandled error (4xx HttpExceptions aren't), and with MonologBundle every
channel's logs are sent.
Plain PHP
Zharp\Zharp::init(['key' => 'zpk_…', 'service' => 'checkout-api', 'environment' => 'production']);
An uncaught exception or fatal error is reported; Zharp\RequestSpan::start(...) makes a request
span for frameworks without an integration. Monolog: $logger->pushHandler(new Zharp\Monolog\ZharpHandler('info')).
API
use Zharp\Zharp; Zharp::info('Order placed', ['order_id' => 42]); // debug, info, warn, error, fatal Zharp::captureError($e, ['order_id' => 42]); // a caught exception, as a handled issue Zharp::setUser(id: $user->id, email: $user->email); // this request's user, when the server knows better Zharp::setTag('region', 'eu-west'); // on every span and log after Zharp::startSpan('render invoice', fn () => render($invoice)); Zharp::flush(); // send now
A browser or mobile app using a Zharp SDK sends traceparent and baggage (enduser.id,
session.id) with its API calls; the request's span and logs carry them with no code.
Logs only
Logs, errors, user/session and tags without any tracing:
Zharp\Zharp::init(['key' => 'zpk_…', 'service' => 'checkout-api', 'tracing' => false]);
or ZHARP_TRACING=false in .env (Laravel, Symfony, plain PHP). No spans are made or sent,
Zharp::startSpan() just runs its callback, and OpenTelemetry instrumentation gets no tracer or
propagator, so nothing adds traceparent/baggage to outgoing requests. An incoming
traceparent and baggage are still read by the Laravel middleware / Symfony subscriber, so a
request's logs and errors keep the caller's trace id, user and session.
Database and HTTP client spans
PHP needs the opentelemetry extension for
automatic instrumentation. With it, add e.g. open-telemetry/opentelemetry-auto-pdo and
open-telemetry/opentelemetry-auto-guzzle: they use the providers Zharp::init() registers, so
their spans join the request's trace. Without the extension, Laravel and Symfony requests are still
traced by Zharp's middleware / subscriber.
Options
init() option |
Environment | Default |
|---|---|---|
key |
ZHARP_KEY |
— (nothing is sent without one) |
service |
ZHARP_SERVICE, OTEL_SERVICE_NAME |
APP_NAME in Laravel, else unknown-service |
environment |
ZHARP_ENVIRONMENT |
APP_ENV in Laravel |
release |
ZHARP_RELEASE |
none |
endpoint |
ZHARP_ENDPOINT |
https://api.zharp.io/api/v1/otlp |
sample_rate |
ZHARP_SAMPLE_RATE |
1.0 (share of traces kept; logs and errors always sent) |
| — | ZHARP_LOG_LEVEL |
info (off sends no framework logs) |
tracing |
ZHARP_TRACING |
true (false = logs only) |
capture_uncaught |
— | true (the framework integrations use the framework's handler instead) |
ZHARP_DISABLED=true turns it off. Zharp never throws into your app.
Development
composer install
composer test
License
MIT © Zharp