Przejdź do głównej zawartości

Kontrakty / Obserwowalność

Obszar obserwowalności definiuje kontrakty, które udostępniają stan silnika w czasie wykonywania: ContextAwareExceptionInterface dla ustrukturyzowanego kontekstu błędu, SpectrumInterface dla opcjonalnego procesu pomocniczego akceleracji, JobNotificationInterface dla strumieniowego postępu zadań oraz typ wyliczeniowy DegradationPolicy dla zachowania w razie utraty funkcji.

Okno terminala
composer require nextpdf/core:^3

ContextAwareExceptionInterface jest kontraktem diagnostycznym. Implementuje go każdy wyjątek domenowy NextPDF. Każdy przechwycony wyjątek NextPDF można rzutować na ten kontrakt i pobrać ustrukturyzowany kontekst dla narzędzia do monitorowania wydajności aplikacji (APM), potoku logowania albo reportera błędów. Kontekst jest tablicą asocjacyjną z kluczami w stylu snake_case i zawiera wyłącznie wartości prymitywne. Nie zawiera zagnieżdżonych obiektów. Dzięki temu serializuje się przewidywalnie do ładunku w formacie JavaScript Object Notation (JSON) lub ładunku APM. Nie trzeba parsować komunikatu wyjątku, aby odzyskać dane diagnostyczne. Jest stable od wersji 3.1.0.

SpectrumInterface jest kontraktem dla opcjonalnego procesu pomocniczego akceleracji. Spectrum wykonuje pracę równolegle na procesorze centralnym (CPU), przenosząc wykrywanie sprzętu, parsowanie formatu Portable Document Format (PDF) oraz kompresję obrazów do lokalnego procesu pomocniczego. Kontrakt raportuje dostępność za bezpiecznikiem (circuit breaker), więc częste kontrole stanu nie pogłębiają awarii, gdy proces pomocniczy nie działa. Sprawdza możliwości sprzętowe i zapisuje wynik w pamięci podręcznej. Udostępnia bieżący budżet zasobów. Zapewnia także ogólny transport żądań dla modułów wyższego poziomu. Silnik działa również bez procesu pomocniczego. Kontrakt sprawia, że akcelerację można wstrzyknąć jako opcję, zamiast traktować ją jako twardą zależność. JobNotificationInterface strumieniuje typowane zdarzenia zadań z punktu końcowego server-sent-events procesu pomocniczego jako generator. Generator kończy pracę, gdy nadejdzie zdarzenie końcowe lub strumień zostanie zamknięty.

DegradationPolicy jest typem wyliczeniowym określającym zachowanie w razie utraty funkcji. Gdy funkcja ulega degradacji, polityka na podstawie skutków decyduje, czy rzucić wyjątek, ostrzec, czy gromadzić zdarzenia po cichu. Strict rzuca wyjątek, gdy skutek oznacza ryzyko zgodności, utratę semantyki lub blokadę działania. Stosuj ją w środowiskach regulowanych, gdzie poprawność wyniku jest obowiązkowa. Balanced, wartość domyślna, emituje ustrukturyzowane ostrzeżenia i kontynuuje przy ograniczonej degradacji, a wyjątek rzuca tylko przy skutku blokującym. Stosuj ją w większości wdrożeń produkcyjnych. Permissive gromadzi każde zdarzenie po cichu i nigdy nie rzuca wyjątku. Stosuj ją w trybie podglądu lub szkicu, gdzie akceptowalny jest wynik realizowany na zasadzie best-effort. Typy SpectrumInterface, JobNotificationInterface oraz DegradationPolicyexperimental. Ich obietnica kompatybilności jest słabsza niż w przypadku ContextAwareExceptionInterface.

TypRodzajKluczowe składoweStabilnośćOd wersji
ContextAwareExceptionInterfaceinterfejsgetContext(): array<string, mixed>stable3.1.0
SpectrumInterfaceinterfejsisAvailable(), probe(), getBudget(), request()experimental2.1.0
JobNotificationInterfaceinterfejsstreamEvents(string): Generator<int, JobEvent>experimental2.2.0
DegradationPolicyenum (string)Strict, Balanced, Permissiveexperimental2.3.0

getContext() zwraca wyłącznie prymitywy lub listy prymitywów. streamEvents() generuje obiekty JobEvent aż do zdarzenia końcowego. SpectrumInterface::request() zwraca surową treść odpowiedzi jako string. probe() zwraca HardwareReport, a getBudget() zwraca 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());
}

Ustrukturyzowany kontekst trafia do wpisu logu bez potrzeby parsowania komunikatu.

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

Dopuszczenie wartości null dla SpectrumInterface sprawia, że akceleracja jest opcjonalna. Kontrakt udostępnia jedną metodę transportu, request(), która zwraca surową treść odpowiedzi jako string. Parser wyższego poziomu przekształca tę treść w NextPDF\Accelerator\BatchResult. Konkretny SpectrumClient dodaje typowane funkcje pomocnicze, takie jak parseBatch(), które opakowują request() i zwracają bezpośrednio BatchResult. Te funkcje pomocnicze nie są częścią zamrożonego kontraktu. Polityka degradacji rozstrzyga, czy brak procesu pomocniczego jest krytyczny.

  • Nie każdy \Throwable jest wyjątkiem NextPDF. Przed wywołaniem getContext() zabezpiecz kod za pomocą instanceof ContextAwareExceptionInterface.
  • getContext() zgodnie z kontraktem zwraca wyłącznie prymitywy. Jeśli oczekujesz zagnieżdżonych obiektów, założenie jest błędne; kontrakt gwarantuje wartości bezpieczne dla JSON.
  • SpectrumInterface::isAvailable() działa za bezpiecznikiem (circuit breaker) i można ją wywoływać często, ale wynik true oznacza kontrolę w danym punkcie czasu. Obsłuż sytuację, w której proces pomocniczy przestaje działać między kontrolą a wywołaniem.
  • JobNotificationInterface::streamEvents() jest generatorem. Dwukrotne iterowanie po nim nie odtwarza zdarzeń. Skonsumuj go jednokrotnie.
  • DegradationPolicy::Permissive nigdy nie rzuca wyjątku. W tym trybie degradacja wpływająca na zgodność przechodzi po cichu. Nie stosuj go dla wyniku podlegającego regulacjom.

Kontrakty obserwowalności dodają pomijalny koszt. getContext() zwraca wcześniej zbudowaną tablicę. isAvailable() to buforowana sonda stanu zabezpieczona bezpiecznikiem (circuit breaker). Kontrakt wymaga, aby implementacje buforowały wynik sondy przez co najmniej 30 sekund, więc ścieżka krytyczna nie wywołuje procesu pomocniczego wielokrotnie. Tempo streamEvents() wyznaczają zdarzenia procesu pomocniczego, a nie silnik. Wartość performance_budget wynosząca 1500 ms czasu rzeczywistego i 64 MB szczytowego zużycia jest wyznaczana przez bazową pracę, którą kontrakty obserwują, a nie przez same kontrakty. Profil odtwarzalności to structural. Strumienie zdarzeń i konteksty wyjątków zawierają znaczniki czasu. Dwa uruchomienia różnią się w tych polach, podczas gdy struktura pozostaje identyczna.

Ustrukturyzowany kontekst wyjątku jest powierzchnią wycieku danych, jeśli zawiera sekrety. Kontrakt ogranicza kontekst do prymitywów, co ogranicza przypadkowy wyciek obiektów. Mimo to musisz wyczyścić wrażliwe wartości, zanim kontekst trafi do odbiornika logów. Jest to obowiązek bezpiecznej telemetrii w polityce logowania projektu. Proces pomocniczy akceleracji to odrębny proces dostępny za pośrednictwem transportu. Metoda żądania przenosi deklaracje zakresu na potrzeby autoryzacji. Musisz traktować granicę procesu pomocniczego jako granicę zaufania. Polityka degradacji ustawiona na Permissive może maskować utratę funkcji istotną dla bezpieczeństwa. Stosuj Strict tam, gdzie poprawność wyniku stanowi mechanizm kontrolny. Traktuj kontekst wyjątku, zdarzenia zadań oraz odpowiedzi procesu pomocniczego jako dane, które mogą zostać zalogowane, i odpowiednio je czyść.

Ta strona nie formułuje bezpośredniego twierdzenia normatywnego. Kontrakty obserwowalności udostępniają stan silnika i nie implementują znormalizowanego protokołu, którego klauzule silnik musiałby przytaczać. Wspomniane powyżej obowiązki bezpiecznej telemetrii i czyszczenia logów wynikają z wewnętrznej polityki logowania projektu, a nie z zewnętrznego standardu. Gdy sama obserwowana operacja jest znormalizowana — podpis, dokument PDF/A — jej zgodność jest udokumentowana na stronie dotyczącej podpisywania lub ekstrakcji.