Flow PHP - Telemetrie
Observability - eine Möglichkeit, das Verhalten eines Systems anhand der Signale zu überwachen, die es aussendet.
Seit einigen Jahren dominiert ein Standard den Markt und gewinnt eine immer breitere Akzeptanz in APM-Diensten: OpenTelemetry.
OpenTelemetry liefert nicht nur eine Spezifikation, Protokolle oder Namenskonventionen. Es ist auch eine Sammlung von Bibliotheken und SDKs, die eine "einfache" Einführung in praktisch jedem System ermöglichen.
Warum habe ich mich also entschieden, meine eigene Bibliothek zu schreiben, anstatt vorhandene Lösungen zu nutzen?
Alles begann, als ich das DataFrame von Flow PHP (zusammen mit einigen Bibliotheken wie postgresql oder filesystem) mit OpenTelemetry integrieren wollte.
Das erste Problem, auf das ich stieß, waren die Abhängigkeiten. Das OpenTelemetry SDK benötigt:
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
Wenn man bedenkt, dass Flow PHP (in seiner Basisversion) nur benötigt:
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
Die Anzahl der Abhängigkeiten würde sich praktisch verdoppeln. Für ein Framework, das Benutzer zur Abhängigkeitsliste ihrer Systeme hinzufügen müssen, wird dies aufgrund potenzieller Versionskonflikte etwas problematisch.
Theoretisch könnte ich Flow nur von open-telemetry/api abhängig machen und das SDK als optionale Abhängigkeit
hinzufügen. Als ich jedoch begann, genauer zu untersuchen, wie die API aufgebaut ist, stellte ich fest, dass sie nicht
mit der Architektur kompatibel ist, die ich in Flow gewählt habe.
Das Hauptproblem für mich waren all diese globalen Zustände, Singletons und andere Mechanismen, die dazu gedacht sind, die Auto-Instrumentierung zu erleichtern. Beispiel: OpenTelemetry Globals.
Ich könnte wahrscheinlich versuchen, Globals zu umgehen, aber wenn ich mir die auf OpenTelemetry basierenden Instrumentierungsimplementierungen ansehe, würde ich schnell auf Probleme stoßen - das SDK ist sehr eng mit der API gekoppelt, und die API ist sehr eng mit dem globalen Zustand gekoppelt.
Hier musste ich eine Entscheidung treffen. Entweder den Flow-Code mit einer zusätzlichen Abstraktion trennen oder meine eigene API bauen, die gleichzeitig als Abstraktion für Telemetrie dienen würde.
Da die API selbst nicht so komplex ist, entschied ich mich, eine unabhängige API und eine
Transportschicht zu bauen, die dem OTLP-Protokoll entsprechen. So entstand eine kleine Bibliothek namens
flow-php/telemetry
Die flow-php/telemetry API ist sehr nicht-invasiv, basiert nicht auf globalen Zuständen, stützt sich
hauptsächlich auf Verträge und bietet sehr leichtgewichtige InMemory und Void Implementierungen,
die in Tests nützlich sind.
Aber die API allein reicht nicht aus, um Signale irgendwohin zu senden. Wir brauchen dafür einen Serializer und einen Transport (das ist es, was das OpenTelemetry SDK tut).
Flow bietet etwas sehr Ähnliches:
flow-php/telemetry-otlp-bridge.
Die Aufgabe dieser Bibliothek ist:
- Signale gemäß dem OTLP-Protokoll serialisieren
- Signale an OpenTelemetry Collector senden
flow-php/telemetry-otlp-bridge kann Signale serialisieren zu:
- JSON
- Protobuf - erfordert die protobuf-Erweiterung
Signale können übertragen werden über:
- curl - eingebauter vollständig asynchroner Transport
- gRPC - erfordert die gRPC-Erweiterung
- HTTP - erfordert
psr/http-clientundpsr/http-factoryImplementierungen
Signale
Was sind Signale?
Metriken - numerische Daten über den Systemzustand, z.B. Anfragen pro Sekunde, durchschnittliche Antwortzeit usw.
Traces / Spans - repräsentieren einzelne Operationen im System, zusammen mit ihren Attributen, Dauer usw.
Ein einzelner Trace ist eigentlich eine Sammlung von Spans, die miteinander verknüpft sind und einen Operationsbaum bilden.
Darüber hinaus kann ein Trace mit Metriken, Logs oder anderen Traces verknüpft werden, was ein vollständigeres Bild davon liefert, was im System passiert.
Logs - textuelle Daten über Ereignisse im System, die oft zusätzliche Attribute wie Log-Level oder Ereigniskontext enthalten.
Auto-Instrumentierung
Einer der Hauptvorteile von OpenTelemetry ist die Auto-Instrumentierung.
Gemäß dem Design sollten Bibliotheksautoren (wie ich) ihre Tools mit der OpenTelemetry API/SDK integrieren. Auf diese Weise erhalten wir automatisch grundlegende Telemetrie ohne zusätzlichen Aufwand, wenn wir die Serialisierung und Transportschicht in einem System konfigurieren, das solche Bibliotheken verwendet.
Wenn die PHP-Community die Vision der API/SDK-Ersteller teilen würde - in fast jedem Projekt müsste man nur das SDK konfigurieren, um einen Einblick zu bekommen, wie das System in der Produktion funktioniert.
Im Moment gibt es jedoch keine einzige Bibliothek, die nativ mit OpenTelemetry integriert ist.
Ich weiß nicht, warum das so ist. Umgebungen wie Java oder .NET haben eine viel bessere OpenTelemetry-Adoption als PHP.
Vielleicht hätte ich mich nicht entschieden, meine eigene Bibliothek zu schreiben, wenn das OpenTelemetry SDK populärer wäre.
Trotzdem habe ich langsam aber sicher begonnen, alle Bibliotheken, die ich pflege, mit flow-php/telemetry zu integrieren.
Ich habe auch mehrere Erweiterungen für bestehende Frameworks/Bibliotheken erstellt - was eine ebenso effektive Auto-Instrumentierung ermöglicht.
Bibliotheken mit nativer Integration:
flow-php/etlflow-php/filesystemflow-php/postgresql
Erweiterungen für bestehende Bibliotheken:
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
Hinweis: Wenn du dein eigenes Open-Source-Projekt entwickelst - melde dich.
Gemeinsam finden wir den einfachsten Weg, dein Tool mit Telemetrie zu integrieren.
Wir können das über native Integration oder Erweiterung machen.
OTEL Collector
OTEL Collector ist ein fantastisches Tool, das als zentraler Hub für den Empfang / die Verarbeitung /
das Weiterleiten von Signalen dient.
Indem du den Collector zwischen dein System und das APM (Application Performance Monitoring System) stellst, vermeidest du Vendor Lock-in.
Wir vermeiden auch unnötige, blockierende I/O-Operationen. Unsere Signale werden im Speicher gepuffert, und wenn der Puffer voll wird oder das Skript endet, werden sie an den Collector gesendet.
Unten findest du Beispiele für Collector-Konfigurationen aus dem Flow-Monorepo:
APM
Es gibt viele beliebte Lösungen auf dem Markt zur Überwachung von Systemen, die mit
dem OTLP-Protokoll kompatibel sind.
Dazu gehören sowohl kostenpflichtige als auch völlig kostenlose Tools, verfügbar als Dienste oder Open Source.
Bevor du jedoch den richtigen APM auswählst, lohnt es sich, dich mit dem OTLP-Protokoll selbst vertraut zu machen.
Natürlich bieten die meisten APMs ihre eigenen Bibliotheken zur Überwachung von Systemen an, aber ihre Verwendung bindet dich sehr eng an einen bestimmten Anbieter.
Meine bevorzugte Lösung (zumindest vorerst) für die lokale Entwicklung ist Aspire
Aspire ist auch als APM im Flow-Monorepo konfiguriert. Das Konfigurationsbeispiel findest du hier.
Demo
Du findest flow-php/telemetry Codebeispiele auf
https://flow-php.com.
Jedes davon kann im Browser mit dem Flow Playground ausgeführt werden.
Aber es gibt keinen besseren Weg, ein Tool zu lernen, als es selbst auszuprobieren, also ermutige ich alle Interessierten,
Aspire, OTEL Collector lokal einzurichten und flow-php/telemetry
zusammen mit flow-php/telemetry-otlp-bridge hinzuzufügen.
Dann bleibt nur noch, mit dem Aussenden von Signalen zu beginnen!
Unten ein Screenshot von lokalem Aspire, der die während der Testausführung des gesamten Flow-Monorepos gesammelte Telemetrie zeigt.