monkeyscloud / monkeyslegion-telemetry
Comprehensive telemetry package with PSR-3 logging, metrics (Prometheus/StatsD), and distributed tracing for MonkeysLegion projects
Installs: 179
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
pkg:composer/monkeyscloud/monkeyslegion-telemetry
Requires
- php: ^8.4
- psr/log: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- promphp/prometheus_client_php: ^2.14
- psr/http-message: ^2.0
- psr/http-server-middleware: ^1.0
Suggests
- ext-redis: Recommended for Prometheus storage with Redis
- promphp/prometheus_client_php: Required for PrometheusMetrics adapter (^2.14)
- psr/http-message: Required for HTTP middleware (^2.0)
- psr/http-server-middleware: Required for PSR-15 middleware (^1.0)
This package is auto-updated.
Last update: 2025-12-10 23:46:17 UTC
README
A comprehensive telemetry package for PHP 8.4+ providing:
- Metrics - Counter, Gauge, Histogram, Summary with Prometheus and StatsD adapters
- Distributed Tracing - W3C Trace Context compatible spans and trace propagation
- Logging - PSR-3 compatible logging with automatic trace correlation
Table of Contents
- Installation
- Quick Start
- Metrics
- Distributed Tracing
- Logging
- Direct Component Usage
- PSR-15 Middleware
- PHP 8 Attributes
- Complete Application Example
- Prometheus Endpoint
- Configuration Reference
- Testing
- License
Installation
composer require monkeyscloud/monkeyslegion-telemetry
Optional Dependencies
# For Prometheus support composer require promphp/prometheus_client_php # For PSR-15 middleware composer require psr/http-message psr/http-server-middleware
Quick Start
<?php use MonkeysLegion\Telemetry\Telemetry; use MonkeysLegion\Telemetry\Tracing\SpanKind; // Initialize once (e.g., in bootstrap.php) Telemetry::init([ 'metrics' => [ 'driver' => 'statsd', // 'null', 'memory', 'statsd', 'prometheus' 'host' => '127.0.0.1', 'port' => 8125, 'namespace' => 'myapp', ], 'tracing' => [ 'enabled' => true, 'service_name' => 'myapp', 'sample_rate' => 1.0, 'exporter' => 'console', // 'console', 'http', 'none' ], 'logging' => [ 'stream' => 'php://stderr', 'level' => 'debug', 'json' => true, ], ]); // Record metrics Telemetry::counter('http_requests_total', 1, ['method' => 'GET', 'status' => '200']); Telemetry::histogram('http_request_duration_seconds', 0.123, ['endpoint' => '/api/users']); Telemetry::gauge('active_connections', 42); // Distributed tracing $result = Telemetry::trace('fetch-user', function () use ($userId) { return $this->userRepository->find($userId); }, SpanKind::CLIENT, ['user.id' => $userId]); // Logging with trace correlation Telemetry::log()->info('User fetched', ['user_id' => $userId]);
Metrics
Counter (monotonically increasing)
Counters are cumulative metrics that only increase (or reset to zero on restart).
use MonkeysLegion\Telemetry\Telemetry; // Simple increment Telemetry::counter('http_requests_total'); // Increment by value with labels Telemetry::counter('http_requests_total', 1, [ 'method' => 'POST', 'endpoint' => '/api/users', 'status' => '201', ]); // Count errors Telemetry::counter('errors_total', 1, ['type' => 'validation']); // Count multiple at once Telemetry::counter('bytes_processed', 1024);
Gauge (point-in-time value)
Gauges represent a single numerical value that can go up or down.
// Set absolute value Telemetry::gauge('queue_size', 42); Telemetry::gauge('memory_usage_bytes', memory_get_usage()); Telemetry::gauge('active_connections', $connectionPool->count()); // With labels Telemetry::gauge('cache_items', 1500, ['cache' => 'redis']); Telemetry::gauge('temperature_celsius', 23.5, ['location' => 'server_room']);
Histogram (distribution/timing)
Histograms sample observations and count them in configurable buckets.
// Record a value (e.g., response time in seconds) Telemetry::histogram('http_request_duration_seconds', 0.125); // With labels Telemetry::histogram('db_query_duration_seconds', 0.045, [ 'query_type' => 'select', 'table' => 'users', ]); // Custom buckets for specific use cases Telemetry::histogram('file_size_bytes', 1024, [], [100, 1000, 10000, 100000, 1000000]); // Response size tracking Telemetry::histogram('http_response_size_bytes', strlen($response->getBody()));
Timer Helper
Convenient way to measure operation duration.
// Start timer $stopTimer = Telemetry::timer('operation_duration_seconds'); // ... do work ... $result = $this->heavyOperation(); sleep(1); // simulate work // Stop and record (returns duration in seconds) $duration = $stopTimer(['operation' => 'heavy_task']); // $duration = 1.002345 // Use in try/finally for guaranteed recording $stop = Telemetry::timer('risky_operation_seconds'); try { $this->riskyOperation(); } finally { $stop(['status' => 'completed']); }
Distributed Tracing
Simple Tracing with Callback
The trace() method automatically creates a span, handles exceptions, and records duration.
use MonkeysLegion\Telemetry\Telemetry; use MonkeysLegion\Telemetry\Tracing\SpanKind; // Basic trace $user = Telemetry::trace('fetch-user', function () use ($userId) { return $this->userRepository->find($userId); }); // With span kind and attributes $user = Telemetry::trace('fetch-user', function () use ($userId) { return $this->userRepository->find($userId); }, SpanKind::CLIENT, [ 'user.id' => $userId, 'db.system' => 'mysql', ]); // Nested traces (automatic parent-child relationship) $result = Telemetry::trace('process-order', function () use ($order) { // Child span 1 $inventory = Telemetry::trace('check-inventory', function () use ($order) { return $this->inventory->check($order->items); }, SpanKind::CLIENT); // Child span 2 $payment = Telemetry::trace('process-payment', function () use ($order) { return $this->payment->charge($order); }, SpanKind::CLIENT); // Child span 3 Telemetry::trace('send-confirmation', function () use ($order) { $this->mailer->sendOrderConfirmation($order); }, SpanKind::PRODUCER); return ['inventory' => $inventory, 'payment' => $payment]; });
Manual Span Management
For complex scenarios where you need full control.
use MonkeysLegion\Telemetry\Telemetry; use MonkeysLegion\Telemetry\Tracing\SpanKind; use MonkeysLegion\Telemetry\Tracing\SpanStatus; $span = Telemetry::startSpan('complex-operation', SpanKind::INTERNAL); $span->setAttribute('operation.type', 'batch'); $span->setAttribute('batch.size', 100); try { // Add events for important moments $span->addEvent('validation-started'); $this->validate($data); $span->addEvent('validation-completed'); $span->addEvent('processing-started', ['items' => count($items)]); foreach ($items as $index => $item) { $this->process($item); // Add event for milestones if ($index % 100 === 0) { $span->addEvent('progress', ['processed' => $index]); } } $span->addEvent('processing-completed'); $span->setStatus(SpanStatus::OK); } catch (ValidationException $e) { $span->recordException($e); $span->setStatus(SpanStatus::ERROR, 'Validation failed'); throw $e; } catch (Throwable $e) { $span->recordException($e); $span->setStatus(SpanStatus::ERROR, $e->getMessage()); throw $e; } finally { $span->end(); Telemetry::flush(); // Send to exporter }
Get Current Trace Context
Access trace information for correlation.
// Get current trace ID (for logging, external calls) $traceId = Telemetry::traceId(); // "a1b2c3d4e5f67890a1b2c3d4e5f67890" // Get active span for adding attributes $span = Telemetry::activeSpan(); if ($span) { $span->setAttribute('custom.attribute', 'value'); $span->addEvent('checkpoint', ['data' => 'info']); } // Inject trace context into outgoing HTTP request $tracer = Telemetry::tracer(); $headers = $tracer->inject([]); // $headers = ['traceparent' => '00-{trace_id}-{span_id}-01'] // Make HTTP call with trace propagation $response = $httpClient->request('GET', $url, [ 'headers' => $headers, ]);
Logging with Trace Correlation
Logs are automatically enriched with trace context.
use MonkeysLegion\Telemetry\Telemetry; $logger = Telemetry::log(); // Basic logging (trace_id and span_id auto-injected when in a trace) $logger->debug('Starting operation', ['step' => 1]); $logger->info('User logged in', ['user_id' => 123, 'ip' => '192.168.1.1']); $logger->notice('Configuration changed', ['key' => 'cache_ttl', 'value' => 3600]); $logger->warning('Rate limit approaching', ['current' => 95, 'limit' => 100]); $logger->error('Payment failed', ['order_id' => 456, 'reason' => 'insufficient_funds']); $logger->critical('Database connection lost', ['host' => 'db-master']); $logger->alert('Disk space critical', ['free_percent' => 2]); $logger->emergency('System shutdown initiated', ['reason' => 'hardware_failure']); // With exception try { $this->riskyOperation(); } catch (Exception $e) { $logger->error('Operation failed', [ 'exception' => $e, 'context' => 'processing order', 'order_id' => $orderId, ]); } // Set default context for all logs $logger->setDefaultContext([ 'service' => 'user-api', 'version' => '2.0.0', 'environment' => 'production', ]); // Add custom processor $logger->addProcessor(function (array $record): array { $record['context']['hostname'] = gethostname(); $record['context']['pid'] = getmypid(); $record['context']['memory_mb'] = round(memory_get_usage() / 1024 / 1024, 2); return $record; });
JSON Output Example:
{
"timestamp": "2025-01-15T10:30:00+00:00",
"level": "INFO",
"message": "User logged in",
"user_id": 123,
"ip": "192.168.1.1",
"trace_id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
"span_id": "1234567890abcdef",
"service": "user-api",
"hostname": "web-server-01"
}
Direct Component Usage
InMemoryMetrics (Testing)
Perfect for unit tests and single-request metrics.
use MonkeysLegion\Telemetry\Metrics\InMemoryMetrics; $metrics = new InMemoryMetrics('test'); // Record metrics $metrics->counter('requests', 1, ['method' => 'GET']); $metrics->counter('requests', 4, ['method' => 'GET']); $metrics->counter('requests', 2, ['method' => 'POST']); $metrics->gauge('temperature', 23.5); $metrics->histogram('duration', 0.1); $metrics->histogram('duration', 0.2); $metrics->histogram('duration', 0.15); // Assert in tests $this->assertEquals(5.0, $metrics->getCounter('requests', ['method' => 'GET'])); $this->assertEquals(2.0, $metrics->getCounter('requests', ['method' => 'POST'])); $this->assertEquals(23.5, $metrics->getGauge('temperature')); // Get histogram statistics $stats = $metrics->getHistogramStats('duration'); $this->assertEquals(3, $stats['count']); $this->assertEquals(0.45, $stats['sum']); $this->assertEquals(0.15, $stats['mean']); $this->assertEquals(0.1, $stats['min']); $this->assertEquals(0.2, $stats['max']); $this->assertArrayHasKey(50, $stats['percentiles']); // p50 $this->assertArrayHasKey(99, $stats['percentiles']); // p99 // Get all metrics $all = $metrics->getMetrics(); // ['counters' => [...], 'gauges' => [...], 'histograms' => [...], 'summaries' => [...]] // Export as Prometheus format echo $metrics->exportPrometheus(); /* # TYPE test_requests counter test_requests{method="GET"} 5 test_requests{method="POST"} 2 # TYPE test_temperature gauge test_temperature 23.5 # TYPE test_duration histogram test_duration_count 3 test_duration_sum 0.45 ... */ // Reset for next test $metrics->reset();
PrometheusMetrics
Production-ready Prometheus integration.
use MonkeysLegion\Telemetry\Metrics\PrometheusMetrics; use Prometheus\Storage\Redis; use Prometheus\Storage\APC; use Prometheus\Storage\InMemory; // With Redis storage (recommended for production) $adapter = new Redis([ 'host' => '127.0.0.1', 'port' => 6379, 'password' => null, 'database' => 0, ]); $metrics = new PrometheusMetrics($adapter, 'myapp'); // With APC storage (single server) $metrics = new PrometheusMetrics(new APC(), 'myapp'); // In-memory (single request, testing) $metrics = new PrometheusMetrics(new InMemory(), 'myapp'); // Record metrics $metrics->counter('http_requests_total', 1, ['method' => 'GET', 'status' => '200']); $metrics->counter('http_requests_total', 1, ['method' => 'POST', 'status' => '201']); $metrics->gauge('goroutines', 42); $metrics->histogram('http_request_duration_seconds', 0.125, ['handler' => '/api/users']); // Set default labels (added to all metrics) $metrics->setDefaultLabels([ 'instance' => gethostname(), 'job' => 'api-server', ]); // Render for /metrics endpoint header('Content-Type: text/plain; version=0.0.4'); echo $metrics->render(); // Wipe all metrics (useful for testing) $metrics->wipe();
StatsDMetrics
StatsD/DogStatsD/Telegraf support.
use MonkeysLegion\Telemetry\Metrics\StatsDMetrics; // Standard StatsD $metrics = new StatsDMetrics( host: '127.0.0.1', port: 8125, namespace: 'myapp', ); // DogStatsD (with tags support) $metrics = new StatsDMetrics( host: 'localhost', port: 8125, namespace: 'myapp', dogstatsd: true, sampleRate: 1.0, ); // With sampling (reduce network traffic) $metrics = new StatsDMetrics( host: 'statsd.internal', port: 8125, namespace: 'myapp', dogstatsd: true, sampleRate: 0.5, // Only send 50% of metrics ); // Record metrics $metrics->counter('events', 1, ['type' => 'click']); $metrics->gauge('queue_depth', 42, ['queue' => 'emails']); $metrics->histogram('response_time', 0.123, ['endpoint' => '/api']); // StatsD-specific: set metric (unique values) $metrics->set('unique_users', $userId); // StatsD-specific: increment/decrement gauge $metrics->gaugeIncrement('connections', 1); $metrics->gaugeIncrement('connections', -1); // Close connection when done (optional, auto-closes on destruct) $metrics->close();
Tracer with HTTP Exporter
Send traces to Jaeger, Zipkin, or any OTLP-compatible backend.
use MonkeysLegion\Telemetry\Tracing\Tracer; use MonkeysLegion\Telemetry\Tracing\SpanKind; use MonkeysLegion\Telemetry\Tracing\Exporter\JsonHttpExporter; use MonkeysLegion\Telemetry\Tracing\Exporter\ConsoleExporter; use MonkeysLegion\Telemetry\Tracing\Exporter\InMemoryExporter; // Create tracer $tracer = new Tracer( serviceName: 'my-service', sampleRate: 1.0, // 100% sampling ); // Add OTLP HTTP exporter (Jaeger, Tempo, etc.) $tracer->addExporter(new JsonHttpExporter( endpoint: 'http://localhost:4318/v1/traces', headers: [ 'Authorization' => 'Bearer your-token', ], timeout: 5, async: true, // Fire and forget (non-blocking) )); // Or console exporter for debugging $tracer->addExporter(new ConsoleExporter( stream: STDERR, prettyPrint: true, )); // Or in-memory for testing $inMemory = new InMemoryExporter(); $tracer->addExporter($inMemory); // Use the tracer $result = $tracer->trace('operation', function () { return doWork(); }, SpanKind::INTERNAL, ['key' => 'value']); // Flush all spans to exporters $tracer->flush(); // For testing: get exported spans $spans = $inMemory->getSpans(); $spansAsArrays = $inMemory->getSpansAsArrays();
PSR-15 Middleware
Automatic HTTP Metrics
use MonkeysLegion\Telemetry\Middleware\RequestMetricsMiddleware; use MonkeysLegion\Telemetry\Metrics\PrometheusMetrics; $metrics = new PrometheusMetrics($adapter, 'myapp'); $middleware = new RequestMetricsMiddleware( metrics: $metrics, includeRoute: true, // Add route pattern as label includeMethod: true, // Add HTTP method as label includeStatus: true, // Add status code as label ); // Add to your middleware stack (Slim, Mezzio, etc.) $app->add($middleware); // Automatically records for every request: // - http_requests_total{method="GET",route="/users/{id}",status="200"} counter // - http_request_duration_seconds{...} histogram // - http_requests_in_progress gauge
Automatic Request Tracing
use MonkeysLegion\Telemetry\Middleware\RequestTracingMiddleware; use MonkeysLegion\Telemetry\Tracing\Tracer; $tracer = new Tracer('my-api'); $middleware = new RequestTracingMiddleware( tracer: $tracer, propagateContext: true, // Add traceparent header to response ); $app->add($middleware); // Automatically for every request: // - Extracts trace context from incoming 'traceparent' header // - Creates root span named "{METHOD} {PATH}" // - Sets HTTP attributes (method, url, status_code, user_agent, etc.) // - Records exceptions // - Injects trace context into response headers // Access span in your handlers: $span = $request->getAttribute('telemetry.span'); $traceId = $request->getAttribute('telemetry.trace_id');
PHP 8 Attributes
Use attributes for declarative instrumentation (requires AOP integration).
use MonkeysLegion\Telemetry\Attribute\Timed; use MonkeysLegion\Telemetry\Attribute\Counted; use MonkeysLegion\Telemetry\Attribute\Traced; use MonkeysLegion\Telemetry\Tracing\SpanKind; class UserService { // Automatic timing + counting + tracing #[Timed('user_fetch_duration_seconds')] #[Counted('user_fetch_total')] #[Traced('UserService::fetchUser', kind: SpanKind::CLIENT)] public function fetchUser(int $id): User { return $this->repository->find($id); } // Just timing with auto-generated name #[Timed] // Creates: user_service_save_user_duration public function saveUser(User $user): void { $this->repository->save($user); } // Tracing with custom attributes #[Traced(attributes: ['db.system' => 'mysql', 'db.operation' => 'query'])] public function searchUsers(string $query): array { return $this->repository->search($query); } // Counting with labels #[Counted('api_calls_total', labels: ['service' => 'external'])] public function callExternalApi(): Response { return $this->httpClient->get('https://api.example.com'); } }
Complete Application Example
Bootstrap
<?php // bootstrap.php use MonkeysLegion\Telemetry\Telemetry; // Load configuration from environment $config = [ 'metrics' => [ 'driver' => getenv('METRICS_DRIVER') ?: 'prometheus', 'namespace' => getenv('METRICS_NAMESPACE') ?: 'myapp', ], 'tracing' => [ 'enabled' => getenv('TRACING_ENABLED') !== 'false', 'service_name' => getenv('SERVICE_NAME') ?: 'myapp-api', 'sample_rate' => (float) (getenv('TRACING_SAMPLE_RATE') ?: 1.0), 'exporter' => getenv('TRACING_EXPORTER') ?: 'http', 'endpoint' => getenv('OTLP_ENDPOINT') ?: 'http://localhost:4318/v1/traces', ], 'logging' => [ 'stream' => getenv('LOG_STREAM') ?: 'php://stderr', 'json' => getenv('LOG_FORMAT') === 'json', 'level' => getenv('LOG_LEVEL') ?: 'info', ], ]; // Initialize telemetry Telemetry::init($config); // Set default labels for all metrics Telemetry::metrics()->setDefaultLabels([ 'service' => $config['tracing']['service_name'], 'instance' => gethostname(), ]); // Register shutdown to flush traces register_shutdown_function(function () { Telemetry::flush(); }); // Log startup Telemetry::log()?->info('Application started', [ 'php_version' => PHP_VERSION, 'environment' => getenv('APP_ENV') ?: 'development', ]);
Controller Example
<?php // UserController.php use MonkeysLegion\Telemetry\Telemetry; use MonkeysLegion\Telemetry\Tracing\SpanKind; class UserController { public function __construct( private UserRepository $userRepo, private CacheInterface $cache, ) {} public function show(int $id): Response { $logger = Telemetry::log(); return Telemetry::trace('UserController::show', function () use ($id, $logger) { $logger?->info('Fetching user', ['user_id' => $id]); // Try cache first $cacheKey = "user:{$id}"; $user = Telemetry::trace('cache-lookup', function () use ($cacheKey) { return $this->cache->get($cacheKey); }, SpanKind::CLIENT, ['cache.key' => $cacheKey]); if ($user) { Telemetry::counter('cache_hits_total', 1, ['type' => 'user']); $logger?->debug('Cache hit', ['user_id' => $id]); } else { Telemetry::counter('cache_misses_total', 1, ['type' => 'user']); // Fetch from database $user = Telemetry::trace('database-fetch', function () use ($id) { return $this->userRepo->find($id); }, SpanKind::CLIENT, ['db.operation' => 'select']); if ($user) { // Store in cache Telemetry::trace('cache-store', function () use ($cacheKey, $user) { $this->cache->set($cacheKey, $user, 3600); }, SpanKind::CLIENT); } } if (!$user) { Telemetry::counter('user_not_found_total'); $logger?->warning('User not found', ['user_id' => $id]); return new Response(404, json_encode([ 'error' => 'User not found', 'trace_id' => Telemetry::traceId(), ])); } Telemetry::counter('user_fetched_total', 1, ['status' => 'success']); return new Response(200, json_encode([ 'data' => $user, 'meta' => ['trace_id' => Telemetry::traceId()], ])); }, SpanKind::SERVER, ['user.id' => $id]); } public function create(Request $request): Response { $logger = Telemetry::log(); $stopTimer = Telemetry::timer('user_creation_duration_seconds'); try { return Telemetry::trace('UserController::create', function () use ($request, $logger) { $data = json_decode($request->getBody(), true); // Validate Telemetry::trace('validation', function () use ($data) { $this->validator->validate($data, UserSchema::class); }); // Create user $user = Telemetry::trace('create-user', function () use ($data) { return $this->userRepo->create($data); }, SpanKind::CLIENT); Telemetry::counter('users_created_total'); $logger?->info('User created', ['user_id' => $user->id]); return new Response(201, json_encode($user)); }, SpanKind::SERVER); } catch (ValidationException $e) { Telemetry::counter('validation_errors_total', 1, ['entity' => 'user']); $logger?->warning('Validation failed', ['errors' => $e->getErrors()]); return new Response(400, json_encode(['errors' => $e->getErrors()])); } finally { $stopTimer(['operation' => 'create_user']); } } }
Service Example
<?php // PaymentService.php use MonkeysLegion\Telemetry\Telemetry; use MonkeysLegion\Telemetry\Tracing\SpanKind; class PaymentService { public function processPayment(Order $order): PaymentResult { $logger = Telemetry::log(); return Telemetry::trace('PaymentService::processPayment', function () use ($order, $logger) { $span = Telemetry::activeSpan(); $span?->setAttributes([ 'order.id' => $order->id, 'order.total' => $order->total, 'order.currency' => $order->currency, ]); $logger?->info('Processing payment', [ 'order_id' => $order->id, 'amount' => $order->total, ]); // Call payment gateway $result = Telemetry::trace('payment-gateway-call', function () use ($order) { return $this->gateway->charge([ 'amount' => $order->total, 'currency' => $order->currency, 'source' => $order->paymentMethod, ]); }, SpanKind::CLIENT, [ 'http.method' => 'POST', 'http.url' => 'https://api.stripe.com/v1/charges', 'peer.service' => 'stripe', ]); if ($result->success) { Telemetry::counter('payments_total', 1, ['status' => 'success']); Telemetry::histogram('payment_amount', $order->total, [ 'currency' => $order->currency, ]); $logger?->info('Payment successful', [ 'order_id' => $order->id, 'transaction_id' => $result->transactionId, ]); } else { Telemetry::counter('payments_total', 1, ['status' => 'failed']); Telemetry::counter('payment_failures_total', 1, [ 'reason' => $result->errorCode, ]); $logger?->error('Payment failed', [ 'order_id' => $order->id, 'error' => $result->errorMessage, 'code' => $result->errorCode, ]); } return $result; }, SpanKind::INTERNAL, ['payment.provider' => 'stripe']); } }
Prometheus /metrics Endpoint
<?php // public/metrics.php use MonkeysLegion\Telemetry\Metrics\PrometheusMetrics; use Prometheus\Storage\Redis; // Use same storage as your application $adapter = new Redis([ 'host' => getenv('REDIS_HOST') ?: '127.0.0.1', 'port' => (int) (getenv('REDIS_PORT') ?: 6379), ]); $metrics = new PrometheusMetrics($adapter, 'myapp'); // Add some runtime metrics $metrics->gauge('php_info', 1, [ 'version' => PHP_VERSION, ]); // Output Prometheus format header('Content-Type: text/plain; version=0.0.4; charset=utf-8'); echo $metrics->render();
Configuration Reference
Metrics Configuration
| Option | Type | Default | Description |
|---|---|---|---|
driver |
string | 'null' |
null, memory, statsd, prometheus |
namespace |
string | 'app' |
Metric name prefix |
host |
string | '127.0.0.1' |
StatsD host |
port |
int | 8125 |
StatsD port |
dogstatsd |
bool | false |
Enable DogStatsD tags |
sample_rate |
float | 1.0 |
Sampling rate (0.0-1.0) |
prometheus_adapter |
Adapter | InMemory |
Prometheus storage adapter |
Tracing Configuration
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable tracing |
service_name |
string | 'app' |
Service name for spans |
sample_rate |
float | 1.0 |
Sampling rate (0.0-1.0) |
exporter |
string | 'console' |
console, http, none |
endpoint |
string | - | OTLP HTTP endpoint URL |
headers |
array | [] |
Additional HTTP headers |
Logging Configuration
| Option | Type | Default | Description |
|---|---|---|---|
stream |
string|resource | 'php://stderr' |
Log output destination |
level |
string | 'debug' |
Minimum log level |
json |
bool | false |
Use JSON format |
pretty |
bool | false |
Pretty print JSON |
Environment Variables Example
# Metrics METRICS_DRIVER=prometheus METRICS_NAMESPACE=myapp # Tracing TRACING_ENABLED=true TRACING_SAMPLE_RATE=0.1 TRACING_EXPORTER=http OTLP_ENDPOINT=http://tempo:4318/v1/traces SERVICE_NAME=user-api # Logging LOG_STREAM=php://stderr LOG_FORMAT=json LOG_LEVEL=info # Redis (for Prometheus storage) REDIS_HOST=redis REDIS_PORT=6379
Testing
# Run tests composer test # Run tests with coverage composer test:coverage # Static analysis composer analyse
Testing with InMemoryMetrics
use MonkeysLegion\Telemetry\Telemetry; use MonkeysLegion\Telemetry\Metrics\InMemoryMetrics; use PHPUnit\Framework\TestCase; class MyServiceTest extends TestCase { private InMemoryMetrics $metrics; protected function setUp(): void { $this->metrics = new InMemoryMetrics('test'); Telemetry::setMetrics($this->metrics); } protected function tearDown(): void { Telemetry::reset(); } public function testItRecordsMetrics(): void { $service = new MyService(); $service->doSomething(); $this->assertEquals(1.0, $this->metrics->getCounter('operations_total')); $this->assertNotNull($this->metrics->getHistogramStats('operation_duration')); } }
License
MIT License. See LICENSE for details.
🤝 Contributing
- Fork 🍴
- Create a feature branch 🌱
- Submit a PR 🚀
Happy hacking with MonkeysLegion! 🎉
Contributors
![]() Jorge Peraza |
![]() Amanar Marouane |

