tiny-owl-kit / observability
TinyOwl PHP SDK — lightweight observability and event logging with enterprise-grade HMAC-SHA256 security
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.5
- psr/http-server-middleware: ^1.0
Suggests
- laravel/framework: Required for the Laravel middleware and service provider
- psr/http-server-middleware: Required for the PSR-15 middleware (TinyOwl\Observability\Integration\Psr15Middleware)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-02 20:28:56 UTC
README
Official PHP SDK for TinyOwl — lightweight observability and event logging with enterprise-grade HMAC-SHA256 security.
Installation
composer require tiny-owl-kit/observability
Quick start
use TinyOwl\Observability\TinyOwl; $client = new TinyOwl([ 'apiKey' => getenv('TINYOWL_API_KEY'), 'projectSecret' => getenv('TINYOWL_PROJECT_SECRET'), ]); $client->info('App started', ['version' => '1.0.0']); $client->warning('Disk space low', ['availableGb' => 1.2]); $client->error('Payment failed', ['orderId' => 'ORD-9', 'reason' => 'declined']);
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
required | Your project API key from the TinyOwl dashboard. |
projectSecret |
string |
required | Your project secret for HMAC signing. |
baseUrl |
string |
https://be.tiny-owl-kit.io/api |
TinyOwl API base URL. |
timeout |
float |
5.0 |
HTTP request timeout in seconds. |
autoTraceId |
bool |
true |
Attach a stable UUID v4 trace ID to every event. |
defaultContext |
array |
[] |
Key/value pairs merged into every log call. |
service |
string |
unset | Service name, sent top-level and included in the HMAC signature when set. |
environment |
string |
unset | Deployment environment (e.g. production), sent and signed when set. |
Logging events
$client->info('User signed in', ['userId' => 'u-123']); $client->warning('Rate limit approaching', ['pct' => 90]); $client->error('Database unreachable', ['host' => 'db.prod']); $client->log('Order created', 'info', ['orderId' => 'ORD-1']);
Scoped loggers with withContext()
$reqLogger = $client->withContext(['requestId' => 'req-xyz', 'userId' => 'u-123']); $reqLogger->info('Request received'); $reqLogger->error('Validation failed', ['field' => 'email']);
The parent client is never modified. Each child gets a fresh trace ID.
Default context
$client = new TinyOwl([ 'apiKey' => $apiKey, 'projectSecret' => $secret, 'defaultContext' => ['service' => 'billing', 'env' => 'prod'], ]); $client->info('Invoice generated'); // Sent with context: {"service":"billing","env":"prod"}
Call-site context keys override defaultContext on conflict.
Auto trace ID
By default each TinyOwl instance generates a UUID v4 on construction and attaches it as
traceId to every event. Child instances created with withContext() receive their own
fresh trace ID. Invalid trace IDs are dropped with a warning (E_USER_WARNING), never fatal.
Opt out:
$client = new TinyOwl(['apiKey' => $apiKey, 'projectSecret' => $secret, 'autoTraceId' => false]);
Error handling
use TinyOwl\Observability\Exception\TinyOwlAuthException; use TinyOwl\Observability\Exception\TinyOwlNetworkException; use TinyOwl\Observability\Exception\TinyOwlTimeoutException; try { $client->info('Hello'); } catch (TinyOwlAuthException $e) { echo "Auth failed ({$e->statusCode}): {$e->getMessage()}"; } catch (TinyOwlTimeoutException $e) { echo 'Request timed out'; } catch (TinyOwlNetworkException $e) { echo "Network error: {$e->getMessage()}"; }
Framework middleware
The middleware adds method, endpoint (route template or path without query string),
routeTemplate, statusCode and durationMs to the event context. It sends an event only for
uncaught exceptions and responses with status >= 400. Request bodies, query strings, headers
and cookies are never sent, and SDK errors never break the application.
PSR-15
use TinyOwl\Observability\Integration\Psr15Middleware; $pipeline->pipe(new Psr15Middleware($client, static function ($request): ?string { $template = $request->getAttribute('routeTemplate'); return is_string($template) ? $template : null; }));
Place the middleware after your routing middleware, because the route resolver runs before the handler.
The resolver is optional; without it, endpoint is the request path.
Laravel
The package registers TinyOwl\Observability\Laravel\TinyOwlServiceProvider automatically. Set
TINYOWL_API_KEY, TINYOWL_PROJECT_SECRET, and optionally TINYOWL_SERVICE and
TINYOWL_ENVIRONMENT (falls back to APP_ENV). Then append the middleware in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware): void { $middleware->append(\TinyOwl\Observability\Laravel\TinyOwlMiddleware::class); })
Publish the config with php artisan vendor:publish --tag=tinyowl-config (optional).
Introspection
$config = $client->getConfig(); // [ // 'baseUrl' => 'https://be.tiny-owl-kit.io/api', // 'timeout' => 5.0, // 'autoTraceId' => true, // 'hasApiKey' => true, // 'hasProjectSecret' => true, // 'instanceTraceId' => '550e8400-...', // ] // Note: API key and project secret are never included.
Security
Every request is signed with HMAC-SHA256:
- A cryptographically-random 32-hex-char nonce is generated per request (
random_bytes). - The current UTC timestamp (ISO-8601, milliseconds) is included to prevent replay attacks.
- Canonical JSON uses
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODEso/and unicode match Node'sJSON.stringify(required for HMAC parity). - Empty
contextserializes as{}, not[]. - The backend rejects requests outside a ±60-second window and rejects reused nonces.
projectSecretis never logged or included ingetConfig().- Plain-HTTP
baseUrlover a non-localhost host emits a warning (OWASP A02).
Requirements
- PHP 8.1+
ext-curl,ext-json(no Composer runtime dependencies)
License
MIT — see LICENSE.