Observabilidad en PHP: cómo dejar de adivinar y empezar a verlo todo con claridad
\n\nCuando tu aplicación PHP comienza a ralentizarse, arrojar errores 500 o simplemente comportarse de manera extraña, la primera reacción es revisar los registros. Pero los registros (incluso perfectamente estructurados) son solo una parte del rompecabezas. El desarrollo moderno requiere mucho más: necesitas saber cuánto tiempo tomó cada solicitud, qué servicios externos están ralentizando el sistema y cuánta memoria consume un endpoint específico.
\n\nTodo esto se trata de observabilidad. En este artículo, analizaremos los tres pilares de la observabilidad en PHP: trazado, métricas y registros. Aprenderá a configurar un monitoreo real con ejemplos de código y obtendrá recetas listas para implementar hoy mismo.
\n\n¿Qué es la observabilidad y por qué la necesita un desarrollador PHP?
\n\nLa observabilidad (O11y) es la capacidad de comprender el estado interno de un sistema a partir de sus datos de salida externos. En el mundo de PHP, esto significa que puede responder preguntas como:
\n\n- \n
- ¿Por qué una solicitud API específica tomó 5 segundos? \n
- ¿Qué consulta SQL fue la más lenta en la última hora? \n
- ¿Cuántas solicitudes por segundo está manejando su servidor ahora mismo? \n
- ¿Dónde exactamente se está "fugando" la memoria? \n
Sin observabilidad, trabaja a ciegas. Con ella, ve cada movimiento de su aplicación. Y sí, esto no solo se aplica a microservicios en Go o Java. Un monolito PHP también se puede y se debe "observar".
\n\nTrazado en PHP: trazado distribuido con OpenTelemetry
\n\nEl trazado es la historia de una solicitud. Ves cómo la solicitud atraviesa tu aplicación, qué funciones se llaman, qué API externas se invocan y cuánto tiempo se dedica a cada paso. En PHP, se utiliza el estándar OpenTelemetry para esto.
\n\nInstalación de OpenTelemetry para PHP
\n\nPrimero, instalemos los paquetes necesarios a través de Composer:
\n\ncomposer require open-telemetry/opentelemetry\ncomposer require open-telemetry/transport-grpc\ncomposer require open-telemetry/exporter-otlp\n\n\nAhora configuremos el trazador básico. Creemos un archivo tracing.php:
// tracing.php\nuse OpenTelemetry\\API\\Globals;\nuse OpenTelemetry\\SDK\\Trace\\TracerProvider;\nuse OpenTelemetry\\SDK\\Trace\\SpanProcessor\\BatchSpanProcessor;\nuse OpenTelemetry\\SDK\\Trace\\Exporter\\ConsoleSpanExporter;\n\nrequire 'vendor/autoload.php';\n\n// Creamos un exportador (por ahora a la consola)\n$exporter = new ConsoleSpanExporter();\n$processor = new BatchSpanProcessor($exporter);\n$tracerProvider = new TracerProvider($processor);\n\n// Registramos el trazador global\nGlobals::setTracerProvider($tracerProvider);\n\n// Obtenemos un trazador con el nombre de su aplicación\n$tracer = Globals::tracerProvider()->getTracer('my-php-app');\n\n\nCreación de spans en código real
\n\nSupongamos que tiene un controlador que procesa un pedido. Envolvamos las operaciones clave en spans:
\n\n// OrderController.php\nclass OrderController {\n public function createOrder(array $data) {\n $tracer = Globals::tracerProvider()->getTracer('order-service');\n \n // Span principal — toda la solicitud\n $span = $tracer->spanBuilder('POST /orders')->startSpan();\n $span->setAttribute('order.id', $data['id'] ?? 'unknown');\n \n try {\n // Span para validar usuario\n $userSpan = $tracer->spanBuilder('validate.user')->startSpan();\n $user = $this->validateUser($data['user_id']);\n $userSpan->end();\n \n // Span para guardar en BD\n $dbSpan = $tracer->spanBuilder('save.order')->startSpan();\n $order = $this->saveOrder($data);\n $dbSpan->end();\n \n // Span para enviar email\n $emailSpan = $tracer->spanBuilder('send.email')