Awesome-claude-code check-observability-coverage
Analyzes PHP code for observability gaps. Detects missing structured logging, absent correlation IDs, missing health endpoints, unstructured log calls, and missing OpenTelemetry setup.
install
source · Clone the upstream repo
git clone https://github.com/dykyi-roman/awesome-claude-code
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/dykyi-roman/awesome-claude-code "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/check-observability-coverage" ~/.claude/skills/dykyi-roman-awesome-claude-code-check-observability-coverage && rm -rf "$T"
manifest:
skills/check-observability-coverage/SKILL.mdsource content
Observability Coverage Check
Analyze PHP code for observability gaps that hinder debugging, monitoring, and incident response in production.
Detection Patterns
1. Unstructured Logging
<?php declare(strict_types=1); // BAD: Raw error_log with unstructured output final class PaymentService { public function charge(Money $amount): void { error_log('Payment failed for amount ' . $amount->cents()); } } // BAD: echo/print_r/var_dump in production code final class OrderService { public function process(Order $order): void { echo 'Processing order: ' . $order->id(); print_r($order->items()); var_dump($order->status()); } } // GOOD: Structured logging with context final readonly class PaymentService { public function __construct( private LoggerInterface $logger, ) {} public function charge(Money $amount, UserId $userId): void { $this->logger->error('Payment charge failed', [ 'user_id' => $userId->toString(), 'amount_cents' => $amount->cents(), 'currency' => $amount->currency()->value, 'gateway' => 'stripe', 'timestamp' => (new DateTimeImmutable())->format(DateTimeInterface::RFC3339), ]); } }
2. Missing Correlation IDs
<?php declare(strict_types=1); // BAD: No request tracing across services final readonly class ApiClient { public function call(string $endpoint, array $payload): Response { return $this->httpClient->post($endpoint, [ 'json' => $payload, // No correlation ID forwarded ]); } } // BAD: Logger without correlation context final readonly class OrderHandler { public function handle(CreateOrderCommand $command): void { $this->logger->info('Order created'); // Which request triggered this? No way to trace. } } // GOOD: Correlation ID propagated across services final readonly class ApiClient { public function __construct( private HttpClientInterface $httpClient, private CorrelationIdProvider $correlationProvider, ) {} public function call(string $endpoint, array $payload): Response { return $this->httpClient->post($endpoint, [ 'json' => $payload, 'headers' => [ 'X-Request-ID' => $this->correlationProvider->get(), 'X-Correlation-ID' => $this->correlationProvider->get(), ], ]); } } // GOOD: Logger processor adds correlation ID automatically final readonly class CorrelationIdProcessor { public function __construct( private CorrelationIdProvider $correlationProvider, ) {} public function __invoke(LogRecord $record): LogRecord { return $record->with(extra: [ 'correlation_id' => $this->correlationProvider->get(), ]); } }
3. Missing Health Endpoints
<?php declare(strict_types=1); // BAD: No health check endpoints -- load balancer cannot detect unhealthy instances // Application has NO /health, /ready, or /live routes // GOOD: Comprehensive health check final readonly class HealthAction { public function __construct( private PDO $database, private \Redis $redis, private AMQPConnection $queue, ) {} public function __invoke(): JsonResponse { $checks = [ 'database' => $this->checkDatabase(), 'redis' => $this->checkRedis(), 'queue' => $this->checkQueue(), ]; $healthy = !in_array(false, $checks, true); return new JsonResponse( data: $checks, status: $healthy ? 200 : 503, ); } private function checkDatabase(): bool { try { $this->database->query('SELECT 1'); return true; } catch (\PDOException) { return false; } } private function checkRedis(): bool { try { return $this->redis->ping() === true; } catch (\RedisException) { return false; } } private function checkQueue(): bool { try { return $this->queue->isConnected(); } catch (\AMQPException) { return false; } } }
4. Missing Metrics Endpoint
<?php declare(strict_types=1); // BAD: No Prometheus metrics exposed // No /metrics endpoint, no counter/histogram tracking // GOOD: Prometheus metrics endpoint with business metrics final readonly class MetricsAction { public function __construct( private CollectorRegistry $registry, ) {} public function __invoke(): Response { $renderer = new RenderTextFormat(); $result = $renderer->render($this->registry->getMetricFamilySamples()); return new Response($result, 200, [ 'Content-Type' => RenderTextFormat::MIME_TYPE, ]); } } // GOOD: Track business metrics final readonly class OrderService { public function __construct( private Counter $ordersCreated, private Histogram $orderProcessingDuration, ) {} public function create(OrderData $data): Order { $start = microtime(true); try { $order = $this->processOrder($data); $this->ordersCreated->incBy(1, ['status' => 'success']); return $order; } catch (\Throwable $e) { $this->ordersCreated->incBy(1, ['status' => 'failure']); throw $e; } finally { $this->orderProcessingDuration->observe( microtime(true) - $start, ['operation' => 'create'], ); } } }
5. Missing Tracing / OpenTelemetry Setup
<?php declare(strict_types=1); // BAD: No tracing spans in cross-service communication final readonly class OrderService { public function create(OrderData $data): Order { $order = $this->repository->save(Order::create($data)); $this->paymentService->charge($order->total()); $this->inventoryService->reserve($order->items()); // Cannot trace which step failed or which was slow return $order; } } // GOOD: OpenTelemetry spans for cross-service calls final readonly class OrderService { public function __construct( private TracerInterface $tracer, private OrderRepository $repository, private PaymentService $paymentService, private InventoryService $inventoryService, ) {} public function create(OrderData $data): Order { $span = $this->tracer->spanBuilder('order.create') ->setAttribute('order.items_count', count($data->items)) ->startSpan(); try { $order = $this->repository->save(Order::create($data)); $span->setAttribute('order.id', $order->id()->toString()); $paymentSpan = $this->tracer->spanBuilder('payment.charge')->startSpan(); try { $this->paymentService->charge($order->total()); } finally { $paymentSpan->end(); } $inventorySpan = $this->tracer->spanBuilder('inventory.reserve')->startSpan(); try { $this->inventoryService->reserve($order->items()); } finally { $inventorySpan->end(); } return $order; } catch (\Throwable $e) { $span->recordException($e); $span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage()); throw $e; } finally { $span->end(); } } }
Grep Patterns
# Unstructured logging Grep: "error_log\(|print_r\(|var_dump\(|var_export\(" --glob "**/*.php" Grep: "echo\s+['\"]|echo\s+\\\$" --glob "**/src/**/*.php" # Check for PSR-3 Logger usage Grep: "LoggerInterface|LoggerAwareInterface" --glob "**/*.php" # Missing correlation ID propagation Grep: "X-Request-ID|X-Correlation-ID|correlation_id" --glob "**/*.php" # Health check endpoints Grep: "/health|/ready|/live|HealthCheck|HealthAction" --glob "**/*.php" # Metrics endpoints Grep: "/metrics|PrometheusMetrics|CollectorRegistry|Counter::class|Histogram::class" --glob "**/*.php" # OpenTelemetry / tracing Grep: "TracerInterface|spanBuilder|OpenTelemetry|Jaeger|Zipkin" --glob "**/*.php" # Structured context in log calls Grep: "->info\(|->error\(|->warning\(|->debug\(|->critical\(" --glob "**/*.php"
Severity Classification
| Pattern | Severity |
|---|---|
| var_dump/print_r in production code | 🔴 Critical |
| No health check endpoints | 🔴 Critical |
| Missing correlation ID propagation | 🟠 Major |
| No structured logging (error_log only) | 🟠 Major |
| No metrics/Prometheus endpoint | 🟠 Major |
| Missing OpenTelemetry tracing | 🟡 Minor |
| Log messages without context array | 🟡 Minor |
Output Format
### Observability Gap: [Brief Description] **Severity:** 🔴/🟠/🟡 **Location:** `file.php:line` **Type:** [Unstructured Log|Missing Correlation|No Health Check|No Metrics|No Tracing] **Issue:** [Description of the observability gap] **Impact:** - Cannot trace requests across services - No alerting on failures - Debugging in production is guesswork **Code:** ```php // Current code without observability
Fix:
// With proper observability
## When This Is Acceptable - **Development/test environment** -- var_dump/print_r may be used temporarily during local debugging - **CLI commands** -- Console output via `echo` is standard for CLI tools - **Early prototype stage** -- Before production deployment, minimal logging is acceptable - **Single-service monolith** -- Correlation IDs are less critical when there are no cross-service calls ### False Positive Indicators - Code is in a test file or fixture - echo/print_r is inside a Console Command `execute()` method - Health check exists but is registered in routing config, not discoverable via grep