Pular para o conteúdo

Contracts / Observabilidade

O domínio de observabilidade define os contracts que expõem o estado de runtime do engine: ContextAwareExceptionInterface para contexto de erro estruturado, SpectrumInterface para o sidecar de aceleração opcional, JobNotificationInterface para progresso de jobs em fluxo, e o enum DegradationPolicy para o comportamento em caso de perda de capacidade.

Terminal window
composer require nextpdf/core:^3

ContextAwareExceptionInterface é o contract de diagnóstico. Toda exceção de domínio do NextPDF implementa esse contract. Você pode fazer o cast de qualquer exceção do NextPDF capturada para este contract e recuperar contexto estruturado para uma ferramenta de Application Performance Monitoring (APM), um pipeline de logging ou um reportador de erros. O contexto é um array associativo com chaves em snake_case e somente valores primitivos. Ele não contém objetos aninhados. Como resultado, ele é serializado de forma previsível para um payload em JavaScript Object Notation (JSON) ou um payload de APM. Você não precisa fazer o parsing de uma mensagem de exceção para recuperar dados de diagnóstico. Esse contract está stable desde a 3.1.0.

SpectrumInterface é o contract do sidecar de aceleração opcional. O Spectrum executa o trabalho em paralelo na central processing unit (CPU), transferindo a detecção de hardware, o parsing de Portable Document Format (PDF) e a compressão de imagens para um processo sidecar local. O contract informa a disponibilidade por trás de um circuit breaker, para que health checks frequentes não amplifiquem uma falha quando o sidecar está fora do ar. Ele sonda as capacidades de hardware e armazena o resultado em cache. Também expõe o orçamento de recursos ativo e fornece um transporte de requisições genérico para módulos de nível mais alto. O engine funciona sem o sidecar. O contract torna a aceleração uma opção injetável, e não uma dependência rígida. JobNotificationInterface transmite eventos de job tipados a partir do endpoint de server-sent-events do sidecar como um generator. O generator para quando um evento terminal chega ou o fluxo é encerrado.

DegradationPolicy é o enum de comportamento para perda de capacidade. Quando uma capacidade se degrada, a política decide, com base no impacto, se lança uma exceção, emite um aviso ou coleta silenciosamente. Strict lança uma exceção quando o impacto é um risco de compliance, uma perda semântica ou um bloqueio. Use-a em um ambiente regulado em que a correção da saída seja obrigatória. Balanced, o padrão, emite avisos estruturados e continua diante de uma degradação limitada, lançando uma exceção apenas em caso de impacto bloqueante. Use-a na maioria das implantações em produção. Permissive coleta todos os eventos silenciosamente e nunca lança uma exceção. Use-a no modo de preview ou rascunho, em que uma saída de melhor esforço seja aceitável. Os tipos SpectrumInterface, JobNotificationInterface e DegradationPolicy são experimental. A promessa de compatibilidade desses tipos é mais fraca do que a de ContextAwareExceptionInterface.

TipoCategoriaMembros principaisEstabilidadeDesde
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() retorna apenas primitivos ou listas de primitivos. streamEvents() produz objetos JobEvent até um evento terminal. SpectrumInterface::request() retorna o corpo bruto da resposta como uma string. probe() retorna um HardwareReport, e getBudget() retorna um 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());
}

O contexto estruturado vai para o registro de log sem exigir o parsing da mensagem.

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 '';
}
}

O SpectrumInterface anulável torna a aceleração opcional. O contract expõe um método de transporte, request(), que retorna o corpo bruto da resposta como uma string. Um parser de nível mais alto transforma esse corpo em um NextPDF\Accelerator\BatchResult. O SpectrumClient concreto adiciona helpers tipados, como parseBatch(), que encapsulam request() e retornam BatchResult diretamente. Esses helpers não fazem parte do contract congelado. A política de degradação decide se a ausência de um sidecar é fatal.

  • Nem todo \Throwable é uma exceção do NextPDF. Proteja com instanceof ContextAwareExceptionInterface antes de chamar getContext().
  • getContext() retorna apenas primitivos por contract. Se você espera objetos aninhados, essa suposição está errada; o contract garante valores seguros para JSON.
  • SpectrumInterface::isAvailable() roda por trás de um circuit breaker e é seguro chamar com frequência, mas um resultado true é uma verificação pontual. Trate a possibilidade de o sidecar cair entre a verificação e a chamada.
  • JobNotificationInterface::streamEvents() é um generator. Iterá-lo duas vezes não repete os eventos. Consuma-o apenas uma vez.
  • DegradationPolicy::Permissive nunca lança uma exceção. Nesse modo, uma degradação que afeta a compliance passa silenciosamente. Não a use para saída regulada.

Os contracts de observabilidade adicionam um custo desprezível. getContext() retorna um array pré-construído. isAvailable() é uma sonda de saúde em cache e protegida por circuit breaker. O contract exige que as implementações mantenham o resultado da sonda em cache por pelo menos 30 segundos, para que um caminho crítico não chame o sidecar repetidamente. streamEvents() é limitado pela taxa de eventos do sidecar, não pelo engine. O performance_budget de 1500 ms de tempo de parede e 64 MB de pico é definido pelo trabalho subjacente que os contracts observam, não pelos próprios contracts. O perfil de reprodutibilidade é structural. Os fluxos de eventos e os contextos de exceção incluem timestamps. Duas execuções diferem nesses campos, enquanto a estrutura permanece idêntica.

O contexto estruturado de exceção é uma superfície de exfiltração de dados caso carregue segredos. O contract restringe o contexto a primitivos, o que limita o vazamento acidental de objetos. Ainda assim, você deve sanitizar valores sensíveis antes que o contexto chegue a um destino de log. Essa é a obrigação de telemetria segura na política de logging do projeto. O sidecar de aceleração é um processo separado, acessado por meio de um transporte. O método de requisição carrega scope claims para autorização. Você deve tratar o limite do sidecar como um limite de confiança. Uma política de degradação definida como Permissive pode mascarar uma perda de capacidade relevante para a segurança. Use Strict quando a correção da saída for um controle. Trate o contexto de exceção, os eventos de job e as respostas do sidecar como dados que podem ser registrados em log, e sanitize-os conforme necessário.

Esta página não faz nenhuma afirmação normativa direta. Os contracts de observabilidade expõem o estado do engine e não implementam um protocolo padronizado cujas cláusulas o engine deva citar. As obrigações de telemetria segura e de sanitização de logs mencionadas acima derivam da política interna de logging do projeto, não de um padrão externo. Quando uma operação observada é, em si, padronizada — uma assinatura, um documento PDF/A —, sua conformidade fica documentada na página de assinatura ou de extração.