Flow PHP - Telemetria
L'osservabilità - un modo per monitorare il comportamento di un sistema in base ai segnali che emette.
Da qualche anno, uno standard domina il mercato e guadagna un'adozione sempre più ampia nei servizi APM: OpenTelemetry.
OpenTelemetry fornisce non solo una specifica, protocolli o convenzioni di denominazione. È anche un insieme di librerie e SDK che permettono un'adozione "facile" in praticamente qualsiasi sistema.
Allora perché ho deciso di scrivere la mia libreria invece di usare le soluzioni esistenti?
Tutto è iniziato quando ho voluto integrare il DataFrame di Flow PHP (insieme ad alcune librerie come postgresql o filesystem) con OpenTelemetry.
Il primo problema che ho incontrato sono state le dipendenze. L'SDK OpenTelemetry richiede:
php: ^8.1ext-json: *nyholm/psr7-server: ^1.1open-telemetry/api: ^1.7open-telemetry/context: ^1.4open-telemetry/sem-conv: ^1.0php-http/discovery: ^1.14psr/http-client: ^1.0psr/http-client-implementation: ^1.0psr/http-factory-implementation: ^1.0psr/http-message: ^1.0.1|^2.0psr/log: ^1.1|^2.0|^3.0ramsey/uuid: ^3.0 || ^4.0symfony/polyfill-mbstring: ^1.23symfony/polyfill-php82: ^1.26tbachert/spi: ^1.0.5
Considerando che attualmente Flow PHP (nella sua versione base) richiede solo:
php: ~8.3.0 || ~8.4.0 || ~8.5.0composer-runtime-api: ^2.0ext-json: *brick/math: ^0.14.2flow-php/array-dot: 0.32.0flow-php/filesystem: 0.32.0flow-php/types: 0.32.0psr/clock: ^1.0psr/simple-cache: ^1.0 || ^2.0 || ^3.0symfony/string: ^6.4 || ^7.3 || ^8.0webmozart/glob: ^3.0 || ^4.0
Il numero di dipendenze raddoppierebbe praticamente. Per un framework che gli utenti devono aggiungere alla lista delle dipendenze dei loro sistemi, questo diventa un po' problematico a causa dei potenziali conflitti di versione.
Teoricamente, potrei far dipendere Flow solo da open-telemetry/api e aggiungere l'SDK come dipendenza
opzionale. Tuttavia, quando ho iniziato a esaminare più da vicino come è costruita l'API, ho realizzato che non era
compatibile con l'architettura che ho adottato in Flow.
Il problema principale per me erano tutti quegli stati globali, singleton e altri meccanismi pensati per facilitare l'auto-strumentazione. Esempio: OpenTelemetry Globals.
Probabilmente potrei provare ad aggirare Globals, ma guardando le implementazioni di strumentazione basate su OpenTelemetry, incontrerei rapidamente problemi - l'SDK è molto strettamente accoppiato con l'API, e l'API è molto strettamente accoppiata con lo stato globale.
Qui ho dovuto fare una scelta. O separare il codice di Flow usando un'astrazione aggiuntiva, o costruire la mia API che servirebbe anche come astrazione per la telemetria.
Dato che l'API stessa non è così complessa, ho deciso di costruire un'API indipendente e un
layer di trasporto conformi al protocollo OTLP. È così che è nata una piccola libreria chiamata
flow-php/telemetry
L'API flow-php/telemetry è molto non invasiva, non si basa su stati globali, si affida
principalmente a contratti, e fornisce implementazioni molto leggere InMemory e Void
utili nei test.
Ma l'API da sola non è sufficiente per inviare segnali da qualche parte. Abbiamo bisogno di un serializzatore e di un trasporto per questo (questo è ciò che fa l'SDK OpenTelemetry).
Flow fornisce qualcosa di molto simile:
flow-php/telemetry-otlp-bridge.
Il compito di questa libreria è:
- Serializzare i segnali secondo il protocollo OTLP
- Inviare i segnali a OpenTelemetry Collector
flow-php/telemetry-otlp-bridge può serializzare i segnali in:
- JSON
- Protobuf - richiede l'estensione protobuf
I segnali possono essere trasmessi tramite:
- curl - trasporto completamente asincrono integrato
- gRPC - richiede l'estensione gRPC
- HTTP - richiede le implementazioni
psr/http-clientepsr/http-factory
Segnali
Cosa sono i segnali?
Metriche - dati numerici sullo stato del sistema, ad es. richieste al secondo, tempo di risposta medio, ecc.
Traces / Spans - rappresentano operazioni individuali nel sistema, con i loro attributi, durata, ecc.
Una Trace è in realtà una collezione di Spans collegati tra loro, formando un albero di operazioni.
Inoltre, una Trace può essere collegata a metriche, log o altre Traces, fornendo un'immagine più completa di ciò che sta accadendo nel sistema.
Log - dati testuali sugli eventi nel sistema, spesso contenenti attributi aggiuntivi come il livello di log o il contesto dell'evento.
Auto-strumentazione
Uno dei principali vantaggi di OpenTelemetry è l'auto-strumentazione.
Secondo il design, gli autori di librerie (come me) dovrebbero integrare i loro strumenti con l'API/SDK OpenTelemetry. In questo modo, se configuriamo la serializzazione e il layer di trasporto in un sistema che usa tali librerie, otteniamo automaticamente una telemetria di base senza sforzo aggiuntivo.
Se la comunità PHP condividesse la visione dei creatori dell'API/SDK - in quasi ogni progetto basterebbe configurare l'SDK per avere un'idea di come funziona il sistema in produzione.
Al momento, però, non esiste una singola libreria nativamente integrata con OpenTelemetry.
Non so perché. Ambienti come Java o .NET hanno un'adozione di OpenTelemetry molto migliore rispetto a PHP.
Forse se l'SDK OpenTelemetry fosse più popolare, non avrei deciso di scrivere la mia libreria.
Tuttavia, lentamente ma inesorabilmente, ho iniziato a integrare tutte le librerie che mantengo con flow-php/telemetry.
Ho anche creato diverse estensioni per framework/librerie esistenti - permettendo un'auto-strumentazione altrettanto efficace.
Librerie con integrazione nativa:
flow-php/etlflow-php/filesystemflow-php/postgresql
Estensioni per librerie esistenti:
flow-php/psr7-telemetry-bridgeflow-php/psr18-telemetry-bridgeflow-php/monolog-telemetry-bridgeflow-php/symfony-http-foundation-telemetry-bridgeflow-php/symfony-telemetry-bundleflow-php/phpunit-telemetry-bridge
Nota: Se stai sviluppando il tuo progetto open source - contattami.
Insieme troveremo il modo più semplice per integrare il tuo strumento con la telemetria.
Possiamo farlo tramite integrazione nativa o estensione.
OTEL Collector
OTEL Collector è uno strumento fantastico che serve come hub centrale per la ricezione / elaborazione /
inoltro dei segnali.
Posizionando il collector tra il tuo sistema e l'APM (Application Performance Monitoring System), eviti il vendor lock-in.
Evitiamo anche operazioni I/O inutili e bloccanti. I nostri segnali vengono bufferizzati in memoria, e quando il buffer si riempie o lo script termina, vengono inviati al collector.
Di seguito, esempi di configurazione del collector dal monorepo Flow:
APM
Esistono molte soluzioni popolari sul mercato per monitorare sistemi compatibili con
il protocollo OTLP.
Questi includono strumenti sia a pagamento che completamente gratuiti, disponibili come servizi o open source.
Prima di scegliere l'APM giusto, però, vale la pena familiarizzare con il protocollo OTLP stesso.
Naturalmente, la maggior parte degli APM fornisce le proprie librerie per monitorare i sistemi, ma usarle ti lega molto strettamente a un fornitore specifico.
La mia soluzione preferita (almeno per ora) per lo sviluppo locale è Aspire
Aspire è anche configurato come APM nel monorepo Flow. Puoi trovare l'esempio di configurazione qui.
Demo
Puoi trovare esempi di codice flow-php/telemetry su
https://flow-php.com.
Ognuno di essi può essere eseguito nel browser usando il Playground di Flow.
Ma non c'è modo migliore di imparare uno strumento che metterci le mani sopra, quindi incoraggio tutti gli interessati
a installare Aspire, OTEL Collector localmente, e aggiungere flow-php/telemetry
insieme a flow-php/telemetry-otlp-bridge.
Poi non resta che iniziare a emettere segnali!
Di seguito, uno screenshot di Aspire locale che mostra la telemetria raccolta durante l'esecuzione dei test dell'intero monorepo Flow.