Ir al contenido

Contratos / Observabilidad

El dominio de observabilidad agrupa los contratos que exponen el estado de ejecución del motor: ContextAwareExceptionInterface para el contexto estructurado de errores, SpectrumInterface para el sidecar opcional de aceleración, JobNotificationInterface para el progreso de los trabajos transmitido por flujo y el enum DegradationPolicy para el comportamiento ante la pérdida de capacidades.

Ventana de terminal
composer require nextpdf/core:^3

ContextAwareExceptionInterface es el contrato de diagnóstico. Todas las excepciones de dominio de NextPDF lo implementan. Toda excepción de NextPDF capturada puede tratarse como este tipo para recuperar contexto estructurado destinado a una herramienta de supervisión del rendimiento de aplicaciones (APM), una canalización de registro o un sistema de reporte de errores. El contexto es un arreglo asociativo con claves en snake_case y solo valores primitivos. No contiene objetos anidados. Por eso, se serializa como una carga útil JSON o de APM sin sorpresas. Esto elimina la necesidad de analizar el mensaje de una excepción para recuperar los datos de diagnóstico. Es stable desde la versión 3.1.0.

SpectrumInterface es el contrato del sidecar opcional de aceleración. Spectrum es un motor con paralelismo de CPU que delega la detección de hardware, el análisis de PDF y la compresión de imágenes a un proceso sidecar local. El contrato informa sobre la disponibilidad detrás de un cortacircuitos, de modo que una comprobación de estado frecuente no propague un fallo en cascada cuando el sidecar está caído. Sondea las capacidades del hardware y almacena el resultado en caché. Expone el presupuesto de recursos activo. Proporciona un transporte de solicitudes general para los módulos de nivel superior. El motor funciona sin el sidecar. El contrato existe para que la aceleración sea una opción inyectable, no una dependencia obligatoria. JobNotificationInterface transmite eventos de trabajo tipados desde el endpoint de eventos enviados por el servidor del sidecar como generador. El generador termina cuando llega un evento terminal o cuando se cierra el flujo.

DegradationPolicy es el enum de comportamiento ante la pérdida de capacidades. Cuando una capacidad se degrada, la política decide si lanzar una excepción, advertir o recolectar en silencio, y la decisión tiene en cuenta el impacto. Strict lanza una excepción cuando el impacto es un riesgo de cumplimiento, una pérdida semántica o un bloqueo. Es la opción para entornos regulados donde la corrección de la salida es obligatoria. Balanced, el valor predeterminado, emite advertencias estructuradas y continúa ante una degradación acotada; solo lanza una excepción cuando el impacto es un bloqueo. Es la opción para la mayoría de los despliegues en producción. Permissive recolecta todos los eventos en silencio y nunca lanza una excepción. Es la opción para un modo de vista previa o de borrador donde se acepta una salida de mejor esfuerzo. Los tipos SpectrumInterface, JobNotificationInterface y DegradationPolicy son experimental. Su garantía de compatibilidad es más débil que la de ContextAwareExceptionInterface.

TipoClaseMiembros claveEstabilidadDesde
ContextAwareExceptionInterfaceinterfacegetContext(): array<string, mixed>stable3.1.0
SpectrumInterfaceinterfaceisAvailable(), probe(), getBudget(), request()experimental2.1.0
JobNotificationInterfaceinterfacestreamEvents(string): Generator<int, JobEvent>experimental2.2.0
DegradationPolicyenum (string)Strict, Balanced, Permissiveexperimental2.3.0

getContext() devuelve únicamente primitivos o listas de primitivos. streamEvents() produce objetos JobEvent hasta que llega un evento terminal. SpectrumInterface::request() devuelve el cuerpo de respuesta sin procesar como un string. probe() devuelve un HardwareReport y getBudget() devuelve un SpectrumBudget.

examples/contracts/observability-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\ContextAwareExceptionInterface;
use Psr\Log\LoggerInterface;
/**
* Log a NextPDF exception with its structured context.
*
* @param \Throwable $e A caught exception.
* @param LoggerInterface $logger A PSR-3 logger.
*/
function logWithContext(\Throwable $e, LoggerInterface $logger): void
{
if ($e instanceof ContextAwareExceptionInterface) {
$logger->error($e->getMessage(), $e->getContext());
return;
}
$logger->error($e->getMessage());
}

El contexto estructurado se envía al registro de trazas sin necesidad de analizar el mensaje.

examples/contracts/observability-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\DegradationPolicy;
use NextPDF\Contracts\SpectrumInterface;
use Psr\Log\LoggerInterface;
final readonly class AcceleratedParseService
{
public function __construct(
private ?SpectrumInterface $spectrum,
private DegradationPolicy $policy,
private LoggerInterface $logger,
) {}
/**
* Send a parse batch to the sidecar when healthy, otherwise fall back.
*
* @param list<array{id: string, data: string}> $documents PDF binaries with caller IDs.
*
* @return string Raw sidecar response body; decode with a batch-result parser.
*/
public function parse(array $documents): string
{
if ($this->spectrum?->isAvailable() === true) {
return $this->spectrum->request(
'POST',
'/v1/parse',
json: ['documents' => $documents],
scope: ['parse'],
);
}
if ($this->policy === DegradationPolicy::Strict) {
throw new \RuntimeException('Accelerator required under strict policy.');
}
$this->logger->info('Accelerator unavailable; using PHP fallback.');
return $this->phpFallback($documents);
}
/** @param list<array{id: string, data: string}> $documents @return string */
private function phpFallback(array $documents): string
{
// Pure-PHP parse path omitted for brevity.
return '';
}
}

Permitir null en SpectrumInterface hace que la aceleración sea opcional. El contrato expone un único método de transporte, request(), que devuelve el cuerpo de respuesta sin procesar como un string. Un analizador de nivel superior convierte ese cuerpo en un NextPDF\Accelerator\BatchResult. El SpectrumClient concreto añade ayudantes tipados como parseBatch() que envuelven request() y devuelven directamente un BatchResult. Esos ayudantes no forman parte del contrato congelado. La política de degradación decide si la ausencia de un sidecar es fatal.

  • No todo \Throwable es una excepción de NextPDF. Proteger siempre con instanceof ContextAwareExceptionInterface antes de llamar a getContext().
  • getContext() devuelve únicamente primitivos por contrato. Un consumidor que espera objetos anidados parte de una suposición errónea; el contrato garantiza valores seguros para JSON.
  • SpectrumInterface::isAvailable() está detrás de un cortacircuitos y es seguro llamarlo con frecuencia, pero un resultado true es una comprobación puntual. Gestionar el caso de un sidecar que se cae entre la comprobación y la llamada.
  • JobNotificationInterface::streamEvents() es un generador. Iterarlo dos veces no reproduce de nuevo los eventos. Consumirlo una sola vez.
  • DegradationPolicy::Permissive nunca lanza una excepción. En ese modo, una degradación que afecta al cumplimiento pasa en silencio. No usarlo para salida regulada.

Los contratos de observabilidad añaden una sobrecarga insignificante. getContext() devuelve un arreglo ya construido. isAvailable() es un sondeo de estado en caché y protegido por un cortacircuitos. El contrato exige que las implementaciones almacenen en caché el resultado del sondeo durante al menos 30 segundos, de modo que una ruta crítica no llame al sidecar de forma repetida. streamEvents() está acotado por la tasa de eventos del sidecar, no por el motor. El performance_budget de 1500 ms de tiempo de reloj y 64 MB de pico lo determina el trabajo subyacente que los contratos observan, no los contratos en sí. El perfil de reproducibilidad es structural. Tanto un flujo de eventos como el contexto de una excepción incluyen marcas de tiempo. Dos ejecuciones difieren en esos campos, mientras que la estructura permanece idéntica.

El contexto estructurado de una excepción es una superficie de exfiltración de datos si transporta secretos. El contrato restringe el contexto a primitivos, lo que limita la fuga accidental de objetos. Aun así, un despliegue debe depurar los valores sensibles antes de que el contexto llegue a un destino de registro. Esta es la obligación de telemetría segura de la política de registro del proyecto. El sidecar de aceleración es un proceso separado al que se accede a través de un transporte. El método de solicitud transporta declaraciones de ámbito para la autorización. Un despliegue debe tratar el límite del sidecar como un límite de confianza. Una política de degradación establecida en Permissive puede enmascarar una pérdida de capacidad relevante para la seguridad. Usar Strict cuando la corrección de la salida sea un control. Tratar el contexto de las excepciones, los eventos de trabajo y las respuestas del sidecar como datos que podrían registrarse, y depurarlos en consecuencia.

Esta página no formula ninguna declaración normativa directa. Los contratos de observabilidad exponen el estado del motor y no implementan un protocolo estandarizado cuyas cláusulas el motor deba citar. Las obligaciones de telemetría segura y de depuración de registros mencionadas arriba derivan de la política interna de registro del proyecto, no de una norma externa. Cuando una operación observada esté a su vez estandarizada —una firma, un documento PDF/A—, su conformidad se documenta en la página de firma o de extracción.

  • Contratos: 41 interfaces públicas (SPI) — el panorama de la SPI y los niveles de estabilidad.
  • Observabilidad — el módulo de estado en tiempo de ejecución que estos contratos exponen.
  • Acelerador — el cliente del sidecar Spectrum que respalda SpectrumInterface.
  • Excepción — excepciones que implementan ContextAwareExceptionInterface.
  • Rendimiento — los presupuestos contra los que se ejecuta el trabajo observado.