Assertion-Modus: die vollständige Referenz
Der Assertion-Modus ersetzt die festen Bestanden/Fehlgeschlagen-Felder eines Monitors durch eine Liste selbst geschriebener Regeln, wobei jede Regel genau benennt, was an der Antwort wahr sein muss. Die meisten Uptime-Monitore beurteilen eine Prüfung anhand einer Handvoll fester Felder: ein Statuscode, der als "down" gilt, eine Liste zu ignorierender Status, vielleicht ein Schlüsselwort, das irgendwo im Body erscheinen muss. Diese Felder können nicht ausdrücken, dass eine API legitim mit 404 für eine fehlende Ressource antwortet, dass eine Seite 200 mit einer leeren Hülle zurückgibt, weil die Datenbank ein Timeout hatte, oder dass eine Weiterleitungskette beginnt, über die Domain eines anderen zu laufen.
Der Assertion-Modus von HostTracker ersetzt diese festen Felder durch eine kleine, selbst geschriebene Regelsprache, Regel für Regel, direkt im Monitor-Editor. Jede Regel sagt genau, was an der Antwort wahr sein muss: der Status, ein Teil des Bodys, ein Header, wie lange es gedauert hat, wo eine Weiterleitung endete. Eine Prüfung schlägt nur fehl, wenn eine Ihrer Regeln fehlschlägt. Es ist dieselbe Sprache für eine Website, eine REST-API, eine Datenbankabfrage, einen TCP-Port, ein Ping-Ziel, einen SNMP-Zähler oder eine generische Counter-Prüfung: ein Satz Subjekte, ein Satz Prädikate, überall dort wiederverwendet, wo der Modus vorkommt. Diese Anleitung behandelt jedes Subjekt, jeden Operator, jeden Reducer, jede Sugar-Abkürzung und jede Syntaxform, die die Engine implementiert.
Was der Assertion-Modus ersetzt
Die klassischen Response-Validation-Felder sind weiterhin vorhanden, wenn der Assertion-Modus aus ist: ein erwarteter Statusbereich, eine Liste zu ignorierender Status, ein Schlüsselwort, das (nicht) vorkommen darf. Der Modus ist ein Schalter, der diesen festen Satz gegen eine selbst gebaute Regelliste tauscht, und ihn vor dem Speichern wieder auszuschalten, stellt die klassischen Felder unverändert wieder her. Kein Monitor ist dauerhaft an einen Modus gebunden.
Eine Sprache statt eines Haufens typspezifischer Einzellösungen fügt sich zusammen. Eine Prüfung "muss JSON sein und ein Feld enthalten", eine Prüfung "muss weiterhin 'Add to cart' sagen" und eine Prüfung "darf nicht langsamer geworden sein" sind drei Regeln in derselben Syntax, in derselben Liste, jede für sich bestehend oder fehlschlagend.
Ihn einschalten
Der Assertion-Modus liegt im Abschnitt Response Validation des Monitor-Editors, für HTTP-(Website-) und API-Monitore. Legen Sie den Schalter um, und die klassischen Felder werden durch eine nummerierte Regelliste ersetzt, mit einer Schaltfläche "Add rule" und einem Live-Zähler, der zeigt, wie viele Regeln Sie im Verhältnis zum Limit Ihres Plans bereits verwendet haben.

Jede Regel wird beim Aufbau validiert: Eine noch nicht fertige Regel trägt einen eigenen "unfinished"-Status, und eine Regel, die der Editor auswerten kann, wird als vollständig markiert. Jede Regel hat außerdem eine kleine </>-Schaltfläche, die ihre Klartext-Quellform öffnet, sodass Sie den genauen Ausdruck statt nur der Kästchen-für-Kästchen-Ansicht lesen oder kopieren können.
Das mentale Modell: Subjekt, Prädikat, Wert
Jede Regel hat dieselbe Form: ein Subjekt (der Statuscode, der Body, ein Header, die Antwortzeit), ein Prädikat (gleich, enthält, existiert, ist kleiner als) und, für die meisten Prädikate, einen Wert, mit dem verglichen wird. status eq 200 ist Subjekt, Prädikat und Wert; body.json exists ist ein Subjekt und ein Prädikat ohne Wert, weil "exists" nichts weiter braucht.
Eine Regel im Editor zu bauen beginnt damit, auszuwählen, was geprüft werden soll, dann die Bedingung, dann den Wert:

Regeln in der Liste werden mit UND verknüpft: Eine Prüfung schlägt fehl, wenn eine einzelne Regel fehlschlägt, und besteht nur, wenn jede Regel besteht. Ein ODER gibt es noch nicht, um also einen von mehreren Status oder Strings zu akzeptieren, drücken Sie das innerhalb einer einzigen Regel aus (status in [200, 301, 302], body containsAny ["In stock", "Pre-order"]). Eine fehlschlagende Regel nennt sich selbst im Alarm, geben Sie also jeder, wo möglich, eine kurze Bezeichnung, und der Alarm sagt, welche Erwartung gebrochen wurde.
Was Sie prüfen können
Die Auswahl gruppiert Subjekte in das eigene Dokument der Antwort (den Body, die Header) und ihren Transport (Status, Timing, Weiterleitungen, TLS). Einige der wichtigsten Subjekte, mit der vollständigen Liste in der folgenden Referenz:
| Subjekt | Was es ist | Beispiel |
|---|---|---|
status | Der HTTP-Statuscode der endgültigen Antwort, nach Verfolgung aller Weiterleitungen | status isOk |
body | Der Antwort-Body als Klartext | body contains "Add to cart" |
body.json / body.xml / body.html / body.yaml | Der Body, aber nur, wenn er sich tatsächlich als dieses Format parsen lässt, andernfalls ist das Subjekt schlicht nicht vorhanden | body.json exists |
body.json.path("$.a") | Eine JSONPath-Abfrage in einen JSON-Body (dieselbe Abfragesprache funktioniert auch über YAML, da YAML in dasselbe Objektmodell geparst wird) | body.json.path("$.status") eq "ok" |
body.size / body.hash | Die Antwortgröße in Bytes und ein Fingerabdruck des Body-Inhalts | body.size gt 1kb |
header("Name") | Ein Antwort-Header, nach Namen nachgeschlagen | header("Content-Type") isJson |
time sowie time.connect / time.tls / time.dns / time.head | Gesamte Antwortzeit, und ihre Netzwerk-, TLS-Handshake-, DNS- und Server-Denkzeit-Phasen | time lt 2s |
redirects / requests | Die von einer Prüfung durchlaufenen Sprünge, und die vollständige Kette (einschließlich der finalen Antwort) | redirects.count eq 0 |
url und seine Teile (.host, .scheme, .path…) | Die finale URL, bei der die Prüfung gelandet ist | url.scheme eq "https" |
cert.days.left | Verbleibende Tage bis zum Ablauf des TLS-Zertifikats | cert.days.left gt 14 |
Zahlen akzeptieren Einheiten-Suffixe, damit Sie nie von Hand umrechnen müssen: 2s bedeutet 2000 Millisekunden, 1kb bedeutet 1024 Byte. Textvergleiche unterscheiden Groß- und Kleinschreibung, sofern Sie der Regel nicht nocase hinzufügen.
Die vollständige Referenz
Im Folgenden finden Sie jedes Subjekt, jeden Operator, jeden Reducer, jede Sugar-Abkürzung, jeden Filter und jede Syntaxform, die die Assertion-Engine für HTTP- und API-Prüfungen registriert, direkt aus der eigenen Registry der Engine (denselben Daten, die auch die Auswahllisten und die Dokumentation des Regel-Editors speisen). Einträge, die noch nicht allgemein verfügbar sind, tragen einen Hinweis in Klammern: reservierte Felder, die noch von keinem Agenten befüllt werden, Kandidaten, die noch auf Agenten ausgerollt werden, und die zustandsbehaftete Familie, die einen bestimmten Plan braucht. Alles ohne einen solchen Hinweis ist heute auf jedem Plan mit Assertion-Modus live.
Die Referenz folgt der eigenen Gruppierung der Engine: Antwortfelder, dann die Wege, in einen Body hineinzugreifen, dann Timing, Netzwerk und TLS, dann die Operatoren, Reducer und Abkürzungen, die auf allen davon funktionieren, dann die Prüfungsarten jenseits von HTTP, die dieselbe Sprache sprechen.
Antwort
Die Subjekte, die die Antwort selbst beschreiben: ihr Status, ihr roher Body, ihre Header, die URL, von der sie tatsächlich geantwortet hat, und die Kette der Requests, die dorthin geführt haben.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
body | Der Antwort-Body als dekodierter Text. | string | body contains "Welcome" |
header | Ein Antwort-Header der finalen Antwort, nach Namen. | string | header("Content-Type") isJson |
redirects | Die verfolgten Weiterleitungssprünge, die Request-Kette ohne ihr letztes Element. | collection | redirects.count eq 0 |
status | Der HTTP-Statuscode der finalen Antwort, nach Verfolgung der Weiterleitungen. | number | status isOk |
body.size | Die Größe des heruntergeladenen Bodys, in Byte. | number | body.size lt 500000 |
url | Die URL, die nach allen Weiterleitungen tatsächlich geantwortet hat. Vergleichen Sie eine url als Text, oder lesen Sie ihre Teile (url.host, .port, .path, .query, .scheme). | url | url.host eq "www.example.com" |
requests | Jeder Request dieser Prüfung, in Reihenfolge, die Weiterleitungssprünge und dann der finale Request. | collection | requests.count le 2 |
url.host | Der Host-Teil der URL. | string | url.host eq "example.com" |
url.path | Der Pfad-Teil der URL. | string | url.path startsWith "/api" |
url.port | Der Port-Teil der URL. | number | url.port eq 443 |
url.query | Der Query-String der URL. | string | url.query contains "token" |
url.scheme | Das Schema der URL (http oder https). | string | url.scheme eq "https" |
body.hash | Ein Digest des Antwort-Bodys, vergleichen Sie ihn mit previous.body.hash, um Inhaltsänderungen zu erkennen. | string | body.hash eq previous.body.hash |
httpVersion (reserviert, noch nicht befüllt) | Die ausgehandelte HTTP-Version. | string | httpVersion eq "HTTP/2" |
setCookie | Die Set-Cookie-Werte der finalen Antwort, ein Eintrag pro Cookie, gleichen Sie einen mit contains oder matches ab, zählen Sie mit .count. | collection | setCookie exists |
steps (noch nicht verfügbar) | Die Schritte einer mehrstufigen Prüfung. Noch nicht verfügbar. | collection | steps.count le 1 |
url.original | Die URL, mit der die Prüfung konfiguriert wurde, vor jeder Weiterleitung. Eine url wie url selbst: sie hat dieselben Teile (url.original.host, .port, .path, .query, .scheme). | url | url.original.scheme eq "http" |
url.original.host | Der Host-Teil der URL. | string | url.original.host eq "example.com" |
url.original.path | Der Pfad-Teil der URL. | string | url.original.path startsWith "/start" |
url.original.port | Der Port-Teil der URL. | number | url.original.port eq 80 |
url.original.query | Der Query-String der URL. | string | url.original.query contains "utm_" |
url.original.scheme | Das Schema der URL (http oder https). | string | url.original.scheme eq "http" |
Zwei Aliasse: redirectCount ist die Kurzform von redirects.count, redirectCount eq 0 liest sich etwas natürlicher als die entsprechende Langform, wenn Sie meinen "die Prüfung hat direkt geantwortet, keine Weiterleitungen". Und setCookie auf oberster Ebene ist die Cookie-Liste der finalen Antwort; derselbe Name taucht pro Sprung innerhalb von requests/redirects erneut auf (siehe Sprung-Subjekte unten) und hält dann nur die Cookies dieses einen Requests.
Werte aus dem Body extrahieren
Diese Subjekte lösen sich nur auf, wenn der Body das von ihnen benannte Format ist: ein JSON-Namensraum auf einer HTML-Seite ist schlicht nicht vorhanden, kein typisierter Fehler. Das macht exists zum Standardweg, das Format zu prüfen, und body.json exists fragt, ob sich der Body als JSON parsen ließ. Die vier Format-Namensräume tragen jeweils eine passende .path(...)-Abfragefunktion (JSONPath für json/yaml, XPath für xml, CSS-Selektoren für html) und ein passendes .regex(...), dazu ein formatunabhängiges body.regex(...), das den rohen Text unabhängig vom Format liest. body.html.a, body.html.script und body.html.link sind fertige Sammlungen jedes Links, Scripts und <link>-Elements der Seite; siehe Elementbereiche unten für die je Element verfügbaren Felder.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
body.json | Der Antwort-Body, aber nur, wenn er wirklich JSON ist. | string | body.json exists |
body.json.path | Fragt den JSON-Body mit einem JSONPath-Ausdruck ab. | json | body.json.path("$.status") eq "ok" |
body.html | Der Antwort-Body, aber nur, wenn er wirklich HTML ist. | string | body.html exists |
body.html.path | Wählt Seitenelemente mit einem CSS-Selektor aus. | collection | body.html.path("h1").first contains "Welcome" |
body.regex | Vergleicht den Text mit einem regulären Ausdruck und übernimmt, was er erfasst hat. | string | body.regex("order-(?<id>\\d+)") exists |
body.xml | Der Antwort-Body, aber nur, wenn er wirklich XML ist. | string | body.xml exists |
body.xml.path | Fragt den XML-Body mit einem XPath-Ausdruck ab. | string | body.xml.path("//item/@id") exists |
body.html.a | Jeder Link auf der Seite. | collection | body.html.a.count gt 0 |
body.html.link | Jedes <link>, das die Seite deklariert. Eine bloße Nennung meint die Link-Ziele. | collection | body.html.link.count gt 0 |
body.html.script | Jedes Script, das die Seite lädt. Eine bloße Nennung meint die Script-Quellen. | collection | body.html.script.count gt 0 |
body.yaml | Der Antwort-Body, aber nur, wenn er wirklich YAML ist. | string | body.yaml exists |
body.yaml.path | Fragt den YAML-Body mit einem JSONPath-Ausdruck ab. | json | body.yaml.path("$.services.web.image") contains ":1." |
body.html.regex | Vergleicht den Text mit einem regulären Ausdruck und übernimmt, was er erfasst hat. | string | body.regex("order-(?<id>\\d+)") exists |
body.json.regex | Vergleicht den Text mit einem regulären Ausdruck und übernimmt, was er erfasst hat. | string | body.regex("order-(?<id>\\d+)") exists |
body.xml.regex | Vergleicht den Text mit einem regulären Ausdruck und übernimmt, was er erfasst hat. | string | body.regex("order-(?<id>\\d+)") exists |
body.yaml.regex | Vergleicht den Text mit einem regulären Ausdruck und übernimmt, was er erfasst hat. | string | body.regex("order-(?<id>\\d+)") exists |
Ein Detail der Regex-Familie ist erwähnenswert: benannte Erfassungsgruppen ((?<id>...)) innerhalb einer .regex(...)-Regel schreiben in einen prüfungsweiten Bindungs-Namensraum, den eine spätere Regel mit bind("id") oder bind[0] nach Position zurücklesen kann, siehe Erfasste Werte unten.
Erfasste Werte
Ein Subjekt lebt in seiner eigenen Registry-Gruppe, weil es nicht direkt die Antwort liest, sondern das, was eine frühere Regel daraus erfasst hat.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
bind | Ein Wert, den eine frühere Regex-Regel erfasst hat, nach Erfassungsname oder nach Position. | string | bind("id") gt 1000 |
Kombinieren Sie es mit einer früheren Regex-Regel in der Liste: Eine Regel wie body.regex("order-(?<id>\d+)") exists erfasst die Bestell-ID unter dem Namen id, und eine spätere Regel kann sie mit bind("id") gt 1000 oder, für eine positionale (unbenannte) Erfassung, mit bind[0] prüfen.
Timing
Eine Gesamtzeit plus eine Aufschlüsselung nach Phase, sodass ein "langsamer geworden"-Alarm auch sagen kann, welcher Teil langsamer wurde.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
time | Gesamte Antwortzeit: Verbindungsaufbau + TLS + Warten + Download. Die DNS-Auflösung ist nicht enthalten. | number | time lt 2000 |
ttfb (reserviert, noch nicht befüllt) | Time to first byte. | number | ttfb lt 200 |
time.connect | Zeit für den Aufbau der TCP-Verbindung. | number | time.connect lt 500 |
time.data | Zeit für den Download des Antwort-Bodys. | number | time.data lt 1000 |
time.dns | Zeit für die Auflösung des Domainnamens. Nicht im bloßen time-Subjekt enthalten. | number | time.dns lt 200 |
time.head | Wartezeit auf die Antwort-Header. | number | time.head lt 500 |
time.tls | Zeit für den TLS-Handshake. | number | time.tls lt 300 |
Das bloße Subjekt time ist Verbindungsaufbau + TLS + Warten-auf-Header + Herunterladen des Bodys. Die DNS-Auflösung ist ausgeschlossen und lebt nur im Geschwisterfeld time.dns, weil DNS oft zwischengespeichert ist und sonst denselben Endpunkt zwischen Prüfungen inkonsistent schnell oder langsam erscheinen ließe.
Netzwerk: DNS und Verbindung
dns und conn sind reine Namensräume in der Registry, eine Gruppe ohne eigenen Wert, sodass eine Regel nie auf dem bloßen Namen enden kann (Editor und Engine lehnen beide eine Kette ab, die allein auf dns oder conn endet, und verweisen auf die echten Felder unten).
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
conn | Die von der Prüfung aufgebaute Netzwerkverbindung, die Adresse, die die Antwort ausgeliefert hat, und etwaige Verbindungsfehler. | string | - |
conn.failed | Wie viele der aufgelösten Adressen die Verbindung nicht aufbauen konnten. | number | conn.failed eq 0 |
conn.failedIps (Kandidat, wird auf Agenten ausgerollt) | Die aufgelösten Adressen, bei denen die Verbindung fehlschlug. | collection | conn.failedIps.count eq 0 |
conn.ip | Die IP-Adresse, die die Antwort tatsächlich ausgeliefert hat. | string | conn.ip eq "203.0.113.10" |
conn.ip.family (Kandidat, wird auf Agenten ausgerollt) | Ob die Antwort über IPv4 oder IPv6 ausgeliefert wurde. | string | conn.ip.family eq "IPv4" |
dns | DNS-Auflösung des überwachten Namens, auf welche Adressen er aufgelöst wurde und welcher Server geantwortet hat. | string | - |
dns.ips | Alle IP-Adressen, auf die der Domainname bei dieser Prüfung aufgelöst wurde. | collection | dns.ips contains "1.2.3.4" |
dns.server | Der DNS-Server, der die Auflösung beantwortet hat. | string | dns.server eq "8.8.8.8" |
TLS und Zertifikat
Wie dns und conn sind auch tls und cert reine Namensräume, die Zertifikats-/Handshake-Fakten liegen eine Ebene tiefer.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
cert.days.left | Verbleibende Tage bis zum Ablauf des Seitenzertifikats. | number | cert.days.left gt 14 |
cert | Das vom Server präsentierte Zertifikat, Tage bis zum Ablauf, Aussteller und Identitätsfelder. | string | - |
cert.days | Tage seit Ausstellung des Zertifikats. | number | cert.days gt 0 |
cert.issuer | Die Zertifizierungsstelle, die das Seitenzertifikat ausgestellt hat. | string | cert.issuer contains "Let's Encrypt" |
cert.san | Die Domainnamen, für die das Zertifikat gültig ist. | collection | cert.san contains "www.example.com" |
cert.serial | Die Seriennummer des Zertifikats. | string | cert.serial exists |
cert.subject (reserviert, noch nicht befüllt) | Der Subject des Zertifikats. Noch nicht erfasst, kommt mit einer künftigen Agentenversion. | string | cert.subject contains "example.com" |
tls | Der TLS-Handshake der gesicherten Verbindung, ausgehandelte Protokollversion und Cipher. | string | - |
tls.cipher | Die ausgehandelte Cipher Suite. | string | tls.cipher contains "AES" |
tls.protocol | Die ausgehandelte TLS-Version. | string | tls.protocol eq "Tls13" |
cert.subject ist hier der Vollständigkeit halber aufgeführt, aber noch nicht befüllt, siehe den Abschnitt Ehrlichkeit unten dafür, was "reserviert" in der Praxis bedeutet. Die alltägliche Zertifikatsregel ist cert.days.left gt 14, behandelt im Abschnitt mit den ausgearbeiteten Regeln.
Sprung-Subjekte: Felder innerhalb von requests und redirects
requests und redirects sind Sammlungen, und jedes Element ist ein vollständiger Request-/Response-Datensatz mit einem eigenen, reduzierten Feldsatz. Nicht jedes Subjekt der obersten Ebene ergibt pro Sprung Sinn (ein Sprung hat kein eigenes Zertifikat), und ein Feld existiert nur pro Sprung: hstsHeader, der Strict-Transport-Security-Header dieses Requests in der Kette.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
status | Der Statuscode dieses Requests in der Kette. | number | status eq 200 |
url | Die URL, an die dieser Request ging. | url | url startsWith "https://" |
header | Ein Antwort-Header dieses Requests. Ein Sprung führt einen reduzierten Header-Datensatz mit einem Wert je Header, listenförmige Prädikate (containsAny, containsAll, unique) gelten nur für den Header der finalen Antwort. | string | header("Content-Type") contains "json" |
hstsHeader | Der Strict-Transport-Security-Header dieses Requests. | string | requests[0].hstsHeader exists |
setCookie | Die Set-Cookie-Header-Werte dieses Requests. | collection | setCookie exists |
url.original | Die URL, mit der die Prüfung konfiguriert wurde, vor jeder Weiterleitung. Eine url wie url selbst: sie hat dieselben Teile (url.original.host, .port, .path, .query, .scheme). | url | url.original.scheme eq "http" |
url.host | Der Host-Teil der URL. | string | url.host eq "example.com" |
url.port | Der Port-Teil der URL. | number | url.port eq 443 |
url.path | Der Pfad-Teil der URL. | string | url.path startsWith "/api" |
url.query | Der Query-String der URL. | string | url.query contains "token" |
url.scheme | Das Schema der URL (http oder https). | string | url.scheme eq "https" |
url.original.host | Der Host-Teil der URL. | string | url.original.host eq "example.com" |
url.original.port | Der Port-Teil der URL. | number | url.original.port eq 80 |
url.original.path | Der Pfad-Teil der URL. | string | url.original.path startsWith "/start" |
url.original.query | Der Query-String der URL. | string | url.original.query contains "utm_" |
url.original.scheme | Das Schema der URL (http oder https). | string | url.original.scheme eq "http" |
Das pro-Sprung header(...) führt einen reduzierten Datensatz mit einem Wert je Header: listenförmige Prädikate wie containsAny oder unique ergeben nur bei der vollständigen Header-Map der finalen Antwort Sinn, sie gelten also dort und nicht pro Sprung. Die beiden Sammlungen bedeuten Unterschiedliches. requests ist die gesamte Kette einschließlich der finalen Antwort, die geantwortet hat, also nie leer, selbst bei einem direkten 200, während redirects nur die auf dem Weg dorthin verfolgten Sprünge sind, leer, wenn nichts weitergeleitet wurde.
Elementbereiche: Felder innerhalb eines Sammlungselements
Ein Elementbereich ist der Feldsatz, der für EIN Element einer Sammlung verfügbar ist, nach Indizierung hinein (requests[0]) oder beim Filtern mit where(current. ...) (siehe den nächsten Abschnitt). Sechs Sammlungen tragen einen eigenen Elementbereich: Die vier HTML-Element-Sammlungen teilen sich eine Form, und requests/redirects teilen sich die oben dokumentierte Sprung-Subjekt-Form.
| Elementbereich | Wie ein Element aussieht |
|---|---|
body.html.path | Ein von einer body.html.path("selector")-CSS-Abfrage getroffenes Element. |
body.html.a | Ein <a>-Link-Element aus body.html.a. |
body.html.script | Ein <script>-Element aus body.html.script. |
body.html.link | Ein <link>-Element aus body.html.link. |
requests | Ein Request in der vollständigen Kette (siehe die Sprung-Subjekt-Tabelle oben). |
redirects | Ein verfolgter Weiterleitungssprung (dieselben Felder wie requests, siehe oben). |
Die gemeinsame Form für body.html.path(...), body.html.a, body.html.script und body.html.link, eine Zeile pro Link, Script-Tag oder <link>-Element:
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
url | Das Link-Ziel genau so, wie es das Markup schreibt. | string | body.html.a.url contains "/checkout" |
text | Der sichtbare Text des Elements. | string | body.html.a.text contains "Sign in" |
absolute | Das Link-Ziel, gegen die Seiten-URL zu einer absoluten Adresse aufgelöst, eine Liste von URLs, mit den URL-Teilen je Element verfügbar (.host, .path, .port, .query, .scheme). | url | body.html.a.absolute.scheme in ["https"] |
rel | Das rel-Attribut des Elements. | string | body.html.a.where(current.rel eq "nofollow").count eq 0 |
absolute.host | Der Host-Teil der URL. | string | body.html.a.absolute.host.unique.count le 3 |
absolute.port | Der Port-Teil der URL. | number | body.html.a.absolute.port in [443] |
absolute.path | Der Pfad-Teil der URL. | string | body.html.a.absolute.path startsWith "/" |
absolute.query | Der Query-String der URL. | string | body.html.a.absolute.query not contains "session" |
absolute.scheme | Das Schema der URL (http oder https). | string | body.html.a.absolute.scheme in ["https"] |
Zwei Standardregeln gelten, bevor Sie eine Regel gegen diese Sammlungen schreiben. Eine bloße Nennung von body.html.a (oder .script/.link) meint ihre .url-Projektion, sodass body.html.a.count gt 0 Hrefs zählt, und eine gespeicherte Regel liest sich mit ausgeschriebenem .url zurück, weil eine gespeicherte Regel immer genau sagt, was sie auswertet. Und .url ist das rohe href-Attribut genau so, wie das Markup es geschrieben hat, ein relativer Link bleibt also relativ; .absolute ist derselbe Link, gegen die eigene URL der Seite aufgelöst, was Sie wollen, wenn Sie Schema oder Host ausgehender Links prüfen (body.html.a.absolute.scheme in ["https"]).
Operatoren
Die Prädikat-Hälfte jeder Regel: welcher Test gegen das Subjekt läuft. Alle 15 werden über jede Prüfungsart hinweg geteilt, und welche zu einem gegebenen Subjekt passen, hängt von dessen Typ ab (ein Statuscode nimmt lt/gt, ein Body nimmt contains, eine Sammlung nimmt exists/unique/in).
| Name | Was es ist | Gilt für | Beispiel |
|---|---|---|---|
eq | Gleich. Gegenüber einem json("…")/xml("…")/yaml("…")-Literal ist es ein EXAKTER struktureller Abgleich: gleiche Schlüssel (beliebige Reihenfolge), Arrays in Reihenfolge, gleiche Werte. | beliebiger Typ | body.json eq json("{\"ok\": true}") |
lt | Kleiner als. | Zahlen | time lt 2000 |
le | Kleiner oder gleich. | Zahlen | cert.days.left le 30 |
gt | Größer als. | Zahlen | body.size gt 0 |
ge | Größer oder gleich. | Zahlen | status ge 500 |
contains | Enthält den Text, bei einer Liste besteht es, wenn der Wert eines der Elemente IST (exakter Abgleich, verwenden Sie matches, um innerhalb von Elementen zu suchen). Gegenüber einem json("…")/xml("…")/yaml("…")-Literal ist es STRUKTURELLE Enthaltenheit: alles, was das Literal angibt, muss vorhanden sein und übereinstimmen, die Antwort darf mehr haben. | Text oder Liste | body.json contains json("{\"status\": \"ok\"}") |
startsWith | Beginnt mit dem Text, bei einer Liste, wenn IRGENDEIN Element das tut. | Text | url.path startsWith "/api" |
endsWith | Endet mit dem Text, bei einer Liste, wenn IRGENDEIN Element das tut. | Text | url.path endsWith ".json" |
matches | Trifft den regulären Ausdruck irgendwo im Text, bei einer Liste, wenn IRGENDEIN Element das tut. | Text | body matches "order-\\d+" |
containsAny | Enthält mindestens einen der aufgelisteten Werte (bei einer Liste: ein Element ENTSPRICHT einem davon). | Liste | body containsAny ["In stock", "Pre-order"] |
containsAll | Enthält jeden der aufgelisteten Werte (bei einer Liste: jeder Wert entspricht einem Element, die Belege dürfen unterschiedlich sein). | Liste | body.html.a.text containsAll ["Home", "Contact"] |
in | Ist einer der aufgelisteten Werte oder Bereiche, bei einer Liste, jedes Element ist es. | Wert(e) oder Bereich | status in [200, 301, 302] |
exists | Das Subjekt ist vorhanden. | beliebiges Subjekt | header("ETag") exists |
isNumber | Der Wert liest sich als Zahl. | Text oder beliebig | bind("id") isNumber |
unique | Die Liste hat keine Duplikate. | Liste | dns.ips unique |
Zwei Operatoren bedeuten etwas Engeres, als sie klingen. contains bei einer Liste ist exakte Mitgliedschaft: Der Wert muss einem ganzen Element entsprechen, nicht als Teilstring darin vorkommen, greifen Sie also zu matches, wenn Sie Teilstring-Verhalten über Elemente einer Sammlung wollen. Und eq gegenüber einem json(...)/xml(...)/yaml(...)-Literal ist ein vollständiger struktureller Abgleich (gleiche Schlüssel, Arrays in derselben Reihenfolge, gleiche Werte), während contains gegenüber demselben Literaltyp strukturelle Enthaltenheit ist: alles, was das Literal angibt, muss vorhanden sein, aber die echte Antwort darf mehr tragen.
Reducer
Ein Reducer reduziert eine Sammlung auf einen Wert und kann nur am Ende einer Kette stehen. Die folgenden Beispiele verwenden requests.status, die eine natürlich numerische Sammlung, die HTTP-Subjekte bilden, als Illustration; dieselben acht Reducer funktionieren über jeder numerischen Sammlung in jeder Prüfungsart (die Umlaufzeiten einer Ping-Prüfung, eine gefilterte Menge Datenbankzeilen).
| Name | Was es reduziert | Gilt für | Beispiel |
|---|---|---|---|
count | Wie viele Elemente die Liste hat. | beliebige Sammlung | requests.count le 2 |
min | Die kleinste Zahl in der Liste. | numerische Sammlung | requests.status.min ge 200 |
max | Die größte Zahl in der Liste. | numerische Sammlung | requests.status.max lt 500 |
sum | Die Summe der Zahlen in der Liste. | numerische Sammlung | requests.status.sum gt 0 |
avg | Der Durchschnitt der Zahlen in der Liste. | numerische Sammlung | requests.status.avg lt 300 |
first | Das erste Element der Liste. | beliebige Sammlung | redirects.status.first eq 301 |
last | Das letzte Element der Liste. | beliebige Sammlung | requests.status.last eq status |
unique | Die Liste ohne Duplikate. | beliebige Sammlung | dns.ips.unique.count le 3 |
unique ist doppelt belegt, und die Schreibweise unterscheidet die zwei Bedeutungen. Als Operator ohne Punkt geschrieben (x unique) ist es ein Prädikat, "diese Sammlung hat keine Duplikate", und es scheitert, indem es die Wiederholung benennt. Als Reducer mit Punkt geschrieben (x.unique) ist es eine Transformation, die deduplizierte Sammlung selbst, die immer noch etwas danach braucht (x.unique.count eq 1). Ein baumelndes x.unique ohne etwas Folgendem ist ein Syntaxfehler, weil die Engine nicht raten kann, welche der beiden Bedeutungen Sie meinten.
Sugar
Sugar-Prädikate sind Abkürzungen, die die Engine in gewöhnliche Kern-Prädikate umwandelt, bevor die Regel je an einen Agenten gesendet wird, ein Sugar-Prädikat hinzuzufügen kostet nichts extra (kein Agent-Bundle, keine Versionsuntergrenze), weil es bereits wie eine gewöhnliche Regel aussieht, sobald es auf der Leitung ankommt. Bevorzugen Sie Sugar, wo immer es existiert: Es liest sich besser und ist exakt gleichwertig damit, die lange Form von Hand auszuschreiben.
| Name | Was es bedeutet | Gilt für | Beispiel |
|---|---|---|---|
isOk | Der Status ist ein Erfolgscode (200-299). | status | - |
isRedirect | Der Status ist ein Weiterleitungscode (300-399). | status | status isRedirect |
isClientError | Der Status ist ein Client-Fehler (400-499). | status | status isClientError |
isServerError | Der Status ist ein Server-Fehler (500-599). | status | status isServerError |
isJson | Der Inhalt ist wirklich JSON. | Dokument oder Medientyp-Text | header("Content-Type") isJson |
isXml | Der Inhalt ist wirklich XML, der Body parst als XML, oder ein Header nennt einen XML-Medientyp. | Dokument oder Medientyp-Text | body isXml |
isHtml | Der Inhalt ist wirklich HTML, der Body parst als HTML, oder ein Header nennt einen HTML-Medientyp. | Dokument oder Medientyp-Text | body isHtml |
isForm | Ein Medientyp nennt eine Formularkodierung: application/x-www-form-urlencoded oder multipart/form-data. Anders als isJson/isXml/isHtml gibt es keinen passenden Body-Namensraum, das gilt also nur für einen Medientyp-Wert wie einen Content-Type-Header. | nur Medientyp-Text | header("Content-Type") isForm |
isEmpty | Nichts da: ein leerer Text, eine Größe von null oder eine leere Liste. | Text, Größe oder Liste | body isEmpty |
isNull | Der Wert ist null. | beliebiger Wert | body.json.path("$.deletedAt") isNull |
absent | Das Subjekt ist überhaupt nicht vorhanden. | beliebiges Subjekt | header("X-Debug") absent |
between | Eine Zahl innerhalb eines einschließenden Bereichs. | Zahlen (2 Operanden) | body.size between 50 and 500kb |
isJson/isXml/isHtml sind absichtlich kontextabhängig, und worauf Sie sie schreiben, entscheidet, welche Bedeutung gilt. Auf dem Dokument selbst geschrieben (body isJson) bedeuten sie "lässt sich das als dieses Format parsen", genau wie body.json exists. Auf einem Textwert geschrieben, der einen Medientyp nennt, etwa einem Header (header("Content-Type") isJson), bedeuten sie "nennt dies die MIME-Familie dieses Formats"; siehe MIME-Familien unten dafür, welche Medientypen infrage kommen. isForm hat nur die zweite Bedeutung, da es keinen body.form-Namensraum zum Parsen gibt, sodass body isForm ein statischer Fehler ist statt einer Regel, die nie wahr sein könnte.
Der Filter where(...)
Behält nur die passenden Elemente und macht von dort weiter. Es ist die einzige Filterkonstruktion der Sprache und hat genau eine Form: where(current.<feld> <bedingung> <wert>).
Filtern unterscheidet sich von Projizieren: requests.url contains "cart" fragt, ob ein solcher Sprung irgendwo in der Kette existiert, während requests.where(current.url contains "cart").status fragt, was dieser Sprung zurückgegeben hat. Filtern reduziert die Liste nicht, schließen Sie also mit .first, einer in [...]-Prüfung oder .count ab. Es geht nur eine Ebene tief und nimmt genau eine Bedingung.
| Teil | Was er benennt | Hinweise |
|---|---|---|
current.<feld> | Ein Feld jedes gefilterten Elements, geschrieben nach current. | Das ist die Hälfte, die am häufigsten falsch gemacht wird: current.url ist die URL des einzelnen Sprungs, nicht die der finalen Antwort. Nur die eigenen Felder des Elements können nach current. folgen. |
| die Bedingung | Jedes gewöhnliche Prädikat, einschließlich not und nocase | Jede Bedingung, die auf oberster Ebene einer Regel funktioniert, funktioniert auch hier. |
| der Wert | Womit das Feld jedes Elements verglichen wird | Meist ein einfacher Wert. Ein hier geschriebenes Subjekt wird aus der ÄUSSEREN Antwort gelesen, nicht aus dem Element, where(current.url eq url.original) vergleicht jeden Sprung mit der URL, mit der die Prüfung konfiguriert wurde, weshalb die Element-Seite current. ausdrücklich sagen muss. |
Beispiel: requests.where(current.url contains "cart").status.first eq 301, behält nur die Sprünge, deren URL "cart" enthält, und sieht sich dann den Status des ersten passenden an.
Zwei weitere Grenzen, beide vom Parser mit eigener Meldung erzwungen: where kann nicht in ein anderes where verschachtelt werden, und sein Argument ist genau eine Bedingung, ohne and/or darin. Es gilt nur für eine Sammlung, deren Elemente benannte Felder tragen (requests, body.html.a), nicht für eine schlichte projizierte Liste wie dns.ips, wo es nichts gibt, das current. benennen könnte.
Syntaxreferenz
Alles Weitere, was die Grammatik definiert: wie Sie eine Regel benennen, wie Sie Bereiche und Listen schreiben, was ein bloßes Subjekt bedeutet, und die kleinen Interpunktionsformen, die in vielen Regeln auftauchen.
| Form | Was sie tut | Art | Beispiel |
|---|---|---|---|
where | Behält nur die passenden Elemente und macht von dort weiter. | Sammlungsfilter | requests.where(current.url contains "cart").status.first eq 301 |
not | Kehrt die folgende Bedingung um. | Flag | body not contains "error" |
nocase | Vergleicht Text ohne Rücksicht auf Groß- und Kleinschreibung. | Flag | body contains "welcome" nocase |
current | Benennt innerhalb von where(…) das eine gefilterte Element. | Elementreferenz (innerhalb where) | requests.where(current.status eq 301) |
range | Ein Zahlenbereich, beide Enden EINGESCHLOSSEN. | Zahlenbereich | status in [200..299] |
list | Eine Liste von Werten zum Vergleich. | Wertliste | status in [200, 301, 302] |
docLiteral | Ein typisierter Dokumentwert für strukturellen Vergleich: der zitierte Text wird beim Speichern der Regel als JSON, XML oder YAML geparst. | json/xml/yaml-Text | body.json contains json("{\"user\": {\"active\": true}}") |
implicitExists | Eine Regel, die nur ein Subjekt nennt, prüft, dass es vorhanden ist: allein body.json zu schreiben speichert als body.json exists. | reine Subjektzeile | body.json |
index | Wählt EIN Element einer Liste nach Position aus, gezählt ab 0. | nullbasierte Ganzzahl | requests[0].status |
label | Ein optionaler Name für die Regel, geschrieben vor einem Doppelpunkt. | Name vor einem Doppelpunkt | "json ok": body.json exists |
comment | Alles nach # ist eine Notiz für Sie selbst; es beeinflusst die Prüfung nicht. | nachgestellter #-Text | status isOk # the happy path |
Einige verdienen eine Erklärung in Klartext. label gibt einer Regel einen Namen, der im Alarm statt des gerenderten Ausdrucks erscheint; schreiben Sie ihn vor einem Doppelpunkt, wie in "json ok": body.json exists. implicitExists bedeutet, dass eine Zeile, die nur ein Subjekt und kein Prädikat nennt, eine Kurzform für exists ist: body.json zu schreiben speichert als body.json exists, und beim erneuten Öffnen der Regel erscheint die ausgeschriebene Form. range und list sind die zwei Formen, die ein Wert nach in annehmen kann: ein Bereich ([200..299], beide Enden eingeschlossen) oder eine explizite Liste ([200, 301, 302]), und ein Wert kann auf beiden Seiten offen sein ([400..]). docLiteral ist der json(...)/xml(...)/yaml(...)-Wrapper für strukturellen Vergleich mit dem Body (behandelt oben unter Operatoren und unten in den ausgearbeiteten Regeln). index wählt ein Element einer Sammlung nach Position, ab 0, genauso wie bind[0] eine positionale Erfassung liest. current hat nur innerhalb eines where(...)-Filters eine Bedeutung, behandelt im eigenen Abschnitt oben.
Wrapper: previous, delta und delta2
Drei Formen, die rückwärts blicken statt nur auf die aktuelle Antwort, jede an Pläne mit zustandsbehafteten Assertions gebunden, und jede vollständig erklärt unter "Gegen die vorherige Prüfung vergleichen" weiter unten.
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
delta (braucht einen Plan mit zustandsbehafteten Assertions) | Wie stark sich ein numerischer Wert seit der vorherigen Prüfung geändert hat, optional als Rate pro ms/s/m/h. | number | delta(body.size, h) lt 1mb |
delta2 (braucht einen Plan mit zustandsbehafteten Assertions) | Die Änderung der Änderung, beschleunigt sich ein Wert? | number | delta2(time) gt 0 |
previous (braucht einen Plan mit zustandsbehafteten Assertions) | Der Wert, den dieses Subjekt bei der vorherigen Prüfung hatte. | any | status eq previous.status |
Flags: not und nocase
Zwei Modifikatoren, die sich an eine Regel anhängen, statt selbst als Subjekt oder Prädikat zu stehen.
| Flag | Was es tut | Art | Beispiel |
|---|---|---|---|
not | Kehrt die folgende Bedingung um. | Flag (an eine Regel angehängt) | body not contains "error" |
nocase | Vergleicht Text ohne Rücksicht auf Groß- und Kleinschreibung. | Flag (an eine Regel angehängt) | body contains "welcome" nocase |
not verbindet sich mit dem folgenden Prädikat, statt die ganze Regel als Einheit zu negieren, x not != y liest sich als x eq y, nicht als doppelte Verneinung. Wenn ein Subjekt komplett fehlen könnte, bevorzugen Sie das Sugar absent gegenüber not exists: Beide bedeuten exakt dasselbe, aber absent sagt es direkt. nocase ändert nur bei einem wirklich textuellen Vergleich etwas; der Editor (und diese Referenz) lassen Sie es trotzdem überall schreiben, aber bei einem numerischen Test wie status eq 200 nocase ist es stillschweigend bedeutungslos.
MIME-Familien
Die Nachschlagetabellen hinter isJson, isXml, isHtml und isForm, wenn sie auf einen Medientyp-Wert wie einen Header angewendet werden. Jede Familie trifft den offensichtlichen Medientyp der obersten Ebene plus, für JSON und XML, jeden Typ, der auf das passende strukturierte Syntax-Suffix endet, was header("Content-Type") isJson korrekt bei application/problem+json oder application/vnd.api+json und nicht nur beim reinen application/json bestehen lässt.
| Familie | Trifft | Zugehöriges Sugar | Beispiel |
|---|---|---|---|
json | application/json, text/json, oder jeder Medientyp, der auf +json endet (application/problem+json, application/vnd.api+json, application/ld+json, …). | Sugar: isJson | header("Content-Type") isJson |
xml | application/xml, text/xml, oder jeder Medientyp, der auf +xml endet (image/svg+xml, application/atom+xml, …). | Sugar: isXml | header("Content-Type") isXml |
html | text/html oder application/xhtml+xml. | Sugar: isHtml | header("Content-Type") isHtml |
form | application/x-www-form-urlencoded oder multipart/form-data. | Sugar: isForm | header("Content-Type") isForm |
Der Abgleich ignoriert alles ab dem ersten ; (sodass application/json; charset=utf-8 weiterhin trifft) und unterscheidet nicht zwischen Groß- und Kleinschreibung.
Subjekte für Datenbank-, Counter-, SNMP-, Port- und Ping-Prüfungen
Jeder Operator, Reducer, jede Sugar-Abkürzung und Syntaxform oben ist für diese fünf Prüfungsarten identisch: Die Engine registriert dieselben 15 Operatoren, 8 Reducer, 12 Sugar-Prädikate und 11 Syntaxformen für alle sechs Arten ohne Ausnahme. Was sich je Art unterscheidet, ist die Subjektliste. Jede Art stellt einen kleinen Satz eigener Subjekte für das bereit, was sie misst, plus eine Teilmenge der oben bereits dokumentierten Netzwerk-/Timing-/TLS-Subjekte, deren Bedeutung sich an die Art anpasst (das time einer Datenbankprüfung ist Verbindungsaufbau-plus-Abfrage statt Verbindungsaufbau-plus-TLS-plus-Download, da es keine HTTP-Antwort zum Herunterladen gibt).
Datenbankprüfungen
Teilt sich time, time.connect und time.dns mit den oben dokumentierten Subjekten (neu bezogen auf eine Datenbankverbindung: Verbindungsaufbau-plus-Abfrage, DNS-Auflösung des Datenbank-Hosts). Eigen für Datenbankprüfungen:
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
scalar | Der einzelne Wert, den die Abfrage zurückgegeben hat (Scalar-Modus). | any | - |
rows | Die Zeilen, die die Abfrage betroffen oder zurückgegeben hat (NonQuery-Modus). | collection | - |
time.query | Zeit, die die Abfrage selbst gebraucht hat. | number | - |
Ein ausgearbeitetes Beispiel steht im nächsten Abschnitt: Verwenden Sie rows.count, um zu prüfen, wie viele Zeilen eine Abfrage betroffen oder zurückgegeben hat, und scalar, um einen einzelnen zurückgegebenen Wert direkt zu prüfen.
Counter-Prüfungen
Teilt sich time mit den oben dokumentierten Subjekten, speziell als Feld mit Kandidatenstatus für Counter-Prüfungen (Umlaufzeit des Counter-Requests; siehe den Abschnitt Ehrlichkeit dafür, was "Kandidat" bedeutet). Eigen für Counter-Prüfungen:
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
value | Der gemessene Counter-Wert. | number | - |
SNMP-Prüfungen
Teilt sich time und time.dns mit den oben dokumentierten Subjekten (Hostname-Auflösung plus der SNMP-Request selbst). Eigen für SNMP-Prüfungen:
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
time.request | Zeit, die der SNMP-Request gebraucht hat. | number | - |
value | Der von der OID zurückgegebene Wert. | any | - |
value.type (Kandidat, wird auf Agenten ausgerollt) | Der SNMP-Datentypname des zurückgegebenen Werts. | string | - |
value.type hat Kandidatenstatus, siehe den Abschnitt Ehrlichkeit. Hinweis zur Verfügbarkeit: Der Response-Validation-Schalter für den Assertion-Modus hat den SNMP-Monitor-Editor noch nicht erreicht. Er zeigt ein "Coming soon"-Panel, das diese zwei Subjekte nennt, statt einer funktionierenden Regelliste. Die Subjekte und der Evaluator darunter sind bereits registriert; nur die SNMP-Editor-Oberfläche steht noch aus.
Port-Prüfungen
Die größte gemeinsame Liste jeder Nicht-HTTP-Art. Eine TCP-Port-Prüfung trägt im Wesentlichen dieselben Verbindungs-, DNS-, TLS- und Zertifikatssubjekte wie eine HTTP-Prüfung, da eine Port-Prüfung ebenfalls TLS aushandeln kann: bind, cert und seine sechs Unterfelder, conn und seine vier Unterfelder, dns und seine zwei Unterfelder, time/time.connect/time.dns/time.tls, und tls/tls.cipher/tls.protocol, alle in den obigen Abschnitten mit derselben Bedeutung dokumentiert. Eigen für Port-Prüfungen:
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
banner | Die nach dem Verbindungsaufbau vom Socket gelesenen Daten. | string | - |
banner.hash (Kandidat, wird auf Agenten ausgerollt) | Ein Digest des Banners, vergleichen Sie ihn mit previous.banner.hash, um Dienständerungen zu erkennen. | string | - |
banner.regex | Vergleicht den Text mit einem regulären Ausdruck und übernimmt, was er erfasst hat. | string | body.regex("order-(?<id>\\d+)") exists |
banner.size (Kandidat, wird auf Agenten ausgerollt) | Die Größe des Banners, in Byte. | number | - |
banner.hash und banner.size haben Kandidatenstatus, siehe den Abschnitt Ehrlichkeit. banner.regex ist bereits heute live und funktioniert genau wie body.regex(...) bei einer HTTP-Prüfung: benannte Erfassungen speisen denselben bind(...)-Namensraum.
Ping-Prüfungen
Teilt sich die Verbindungs- und DNS-Subjekte mit den obigen Abschnitten: conn, conn.failed, conn.failedIps, conn.ip, conn.ip.family, dns, dns.ips, dns.server, time (hier die durchschnittliche Umlaufzeit der erfolgreichen Antworten) und time.dns. Eigen für Ping-Prüfungen:
| Name | Was es ist | Typ/Werte | Beispiel |
|---|---|---|---|
loss | Der Prozentsatz der Echo-Requests, die keine Antwort erhielten. | number | loss eq 0 |
reply | Die erfolgreichen Echo-Antworten. | collection | - |
sentCount (Kandidat, wird auf Agenten ausgerollt) | Wie viele Echo-Requests gesendet wurden. | number | - |
time.max | Die Umlaufzeit der langsamsten Antwort. | number | - |
time.min | Die Umlaufzeit der schnellsten Antwort. | number | - |
ttl | Die TTL der ersten erfolgreichen Antwort, vergleichen Sie mit previous.ttl, um Routenänderungen zu erkennen. | number | - |
sentCount hat Kandidatenstatus, siehe den Abschnitt Ehrlichkeit. ttl ist auch abseits einer strikten Bestanden/Fehlgeschlagen-Regel gut zu kennen: Es mit previous.ttl zu kombinieren (siehe Wrapper oben) zeigt einen Routenwechsel zwischen Prüfungen an, da die Time-to-live eines Pakets bei Ankunft widerspiegelt, wie viele Hops es durchlaufen hat.
Regeln, die man tatsächlich schreiben will
Die Seite ist erreichbar und sagt tatsächlich das Richtige
Ein Statuscode allein kann Ihnen nicht sagen, ob eine Seite korrekt gerendert hat, eine 200-mit-leerer-Hülle-Antwort ist der klassische stille Fehler, den eine reine Statusprüfung übersieht.
status isOk
body contains "Add to cart"
Eine API antwortet mit JSON und dem erwarteten Feld
Prüfen Sie, dass der Endpunkt noch JSON spricht, und greifen Sie dann hinein. Die erste Zeile fängt eine HTML-Fehlerseite ab, die dort ausgeliefert wird, wo JSON stehen sollte, eine häufige Ausfallform hinter einem Proxy; die zweite prüft den eigenen gemeldeten Gesundheitszustand der API, nicht nur ihren Transportstatus.
body isJson
body.json.path("$.status") eq "ok"
Die Antwort wurde langsamer, als sie sein sollte
Schlüsseln Sie es auf, wenn Sie wissen müssen, was zu reparieren ist, statt nur dass etwas langsam ist, time.connect, time.tls, time.dns und time.head trennen Netzwerk-, Handshake-, Resolver- und Anwendungszeit.
time lt 2s
Eine Weiterleitung hat Besucher von der eigenen Domain weggeschickt
Vergleicht, wo die Prüfung gelandet ist, mit der von Ihnen konfigurierten URL, sie schlägt in dem Moment fehl, in dem eine Weiterleitungskette, gekapert, falsch konfiguriert oder einfach durch ein Deployment geändert, irgendwo landet, das Sie nicht beabsichtigt haben.
url.host eq url.original.host
Die Seite hat sich seit der letzten Prüfung geändert
Nützlich bei Inhalten, die zwischen Prüfungen stabil bleiben sollten, Preise, Bedingungen, eine veröffentlichte Richtlinie, und dient nebenbei als einfaches Defacement-Signal.
previous.body.hash eq body.hash
Eine Zahl innerhalb eines Bereichs, kurz geschrieben
Fängt sowohl eine abgespeckte als auch eine ausgeuferte Seite in einer einzigen Regel ab, ohne einen expliziten Bereich auszuschreiben.
body.size between 50 and 500kb
Ein bald ablaufendes Zertifikat
Die mit Abstand häufigste TLS-Prüfung, und eine, die ein einfacher Up/Down-Monitor überhaupt nicht ausdrücken kann, ein Zertifikat, das heute noch validiert, aber nächste Woche verfällt, gibt sonst kein Warnzeichen. Kombinieren Sie es mit cert.issuer, wenn Sie zusätzlich ein still von der falschen Stelle ausgestelltes Zertifikat erfassen wollen.
cert.days.left gt 14
Eine Datenbankabfrage, die die falsche Anzahl Zeilen zurückgibt
Bei einer Datenbankprüfung ist rows die Sammlung, die eine Abfrage betroffen oder zurückgegeben hat, sie zu zählen fängt ab, dass eine Abfrage, die genau einen Kontodatensatz zurückgeben sollte, still anfängt, null zurückzugeben (gelöscht, oder ein kaputter Join) oder mehr als einen (ein Duplikat, das nicht existieren sollte).
rows.count eq 1
Ein SNMP-Wert, der aus dem Bereich driftet
Ein SNMP-Gerät, das überhaupt antwortet, ist nicht dasselbe wie ein gesundes, eine überwachte OID (Speicherauslastung, Warteschlangentiefe, Temperatur) kann weit über einen sicheren Schwellenwert steigen, während das Gerät selbst einwandfrei erreichbar bleibt. Den abgefragten value gegen einen Bereich zu vergleichen, erfasst, was eine reine Erreichbarkeitsprüfung nicht kann.
value lt 90
Ein TCP-Dienst, der mit dem falschen Banner antwortet
Ein Port, der Verbindungen annimmt, ist nicht dasselbe wie der richtige, darauf lauschende Dienst, ein hängender Prozess, ein falsch konfigurierter Proxy oder ein herabgestufter/ausgetauschter Dienst können alle trotzdem einen TCP-Handshake abschließen. Den Begrüßungstext zu lesen, den ein Dienst direkt nach dem Verbindungsaufbau sendet (ein SMTP-, FTP- oder ähnliches Banner), erfasst, was eine bloße Port-offen-Prüfung nicht kann.
banner contains "220"
Gegen die vorherige Prüfung vergleichen
Eine Handvoll Subjekte blickt rückwärts statt nur auf die aktuelle Antwort: Stellen Sie previous. vor ein Subjekt, um zu lesen, was es bei der letzten Prüfung war, oder umschließen Sie es mit delta(...), um den Unterschied zwischen damals und jetzt zu erhalten, oder mit delta2(...), um die Änderung dieser Änderung zu erhalten, nützlich, um einen sich beschleunigenden statt driftenden Wert zu erkennen (eine Antwortzeit, die mit jeder Prüfung schneller steigt). previous.body.hash eq body.hash oben ist genau das, "stimmt der Fingerabdruck des Bodys mit dem von letztem Mal überein". delta(body.size, h) liest die Änderung als Rate pro Stunde statt als rohe Differenz, für Werte, von denen Sie stetiges statt sprunghaftes Wachstum erwarten.
Beim ersten Lauf einer solchen Regel gibt es nichts zum Vergleichen, sie wird also übersprungen statt zu scheitern: Die Prüfung wird als "warming up" markiert, nicht als defekt, und das Hinzufügen der Regel löst keinen falschen Alarm aus. Warming gilt nur, wenn die aktuelle Lesung stimmt und nur die Basislinie fehlt. Scheitert die aktuelle Lesung selbst (etwa weil der Body diesmal kein gültiges JSON ist), scheitert die Regel genau wie ihre einfache, nicht-previous.-Version es täte. Eine solche Regel braucht außerdem, dass sich der Wert zwischen Prüfungen ändert, was sie bei einer Prüfmethode bedeutungslos macht, die den Zustand nie weiterbewegt, etwa ein HEAD-Request.
Der Vergleich mit der vorherigen Prüfung braucht einen höherstufigen Plan; ohne ihn erscheinen diese Subjekte im Editor ausgegraut, mit einer Erklärung.
Grenzen, und was absichtlich fehlt
Wie viele Regeln Sie hinzufügen können, hängt von Ihrem Plan ab, angezeigt als Live-Zähler neben "Add rule". Die Engine erzwingt zudem eine absolute Obergrenze für jede Prüfung, unabhängig vom Plan, eine Absicherung gegen Missbrauch statt eine Grenze für den gewöhnlichen Gebrauch: Eine Prüfung mit annähernd so vielen Regeln wäre bereits ungewöhnlich groß.
Es gibt keine Möglichkeit, eigenes JavaScript für eine Regel zu schreiben, und das wird es nie geben. Jede Regel wird aus einem festen, benannten Wortschatz von Subjekten und Prädikaten gebaut statt aus einer offenen Skript-Sandbox, was eine Regel auf einen Blick lesbar hält und sicher macht, auf jedem Monitoring-Agenten weltweit zu laufen.
Nicht jede Regel funktioniert überall, sobald sie geschrieben ist: Manche neueren Bedingungen brauchen eine aktuelle Version des Monitoring-Agenten, der Ihre Prüfung ausführt, eine neue Fähigkeit braucht also Zeit, um jede Prüfung Ihres Kontos zu erreichen, während Agenten sich im Hintergrund aktualisieren. Regeln, die auf dem stabilen Kern der Sprache aufbauen, sind davon nicht betroffen.
Eine Handvoll Einträge in dieser Referenz ist noch nicht vollständig live, und jeder ist in seiner Tabelle oben entsprechend gekennzeichnet:
- Reserviert (deklariert, aber von keinem Agenten befüllt):
ttfb,httpVersion,cert.subject. - Kandidat (wird bereits ausgerollt, aber noch nicht auf jedem Agenten):
conn.ip.familyundconn.failedIpsbei HTTP-, Port- und Ping-Prüfungen,value.typebei SNMP-Prüfungen,banner.hashundbanner.sizebei Port-Prüfungen,sentCountbei Ping-Prüfungen, und das eigenetimeder Counter-Prüfung. - Noch nicht verfügbar:
steps, reserviert für eine künftige mehrstufige Prüfungsart. - Braucht einen Plan mit zustandsbehafteten Assertions: die gesamte Wrapper-Familie
previous/delta/delta2, bei jeder Prüfungsart.
Alles Übrige in dieser Referenz ist heute auf jedem Plan mit Assertion-Modus live.
Wo der Assertion-Modus heute funktioniert
Der Response-Validation-Schalter für den Assertion-Modus erscheint bei HTTP-(Website-) und API-Monitoren, die sich denselben Regel-Editor und dieselbe Syntax teilen. Dieselbe Sprache und derselbe Evaluator laufen bereits unter Datenbank-, Counter-, Port-, Ping- und SNMP-Prüfungen auf Protokollebene: Die Registry oben bedient alle sechs Prüfungsarten, und eine für eine gespeicherte Regel liest sich gleich, falls der Modus später den Editor einer anderen Art erreicht.
SNMP zeigt die deutlichste Vorschau: Sein Editor hat bereits ein Assertion-Rules-Panel, das value und value.type nennt, mit der Kennzeichnung "Coming soon", noch ohne funktionierende Regelliste dahinter. Datenbank-, Counter- und Ping-Prüfungen zeigen überhaupt kein Assertion-Modus-Panel, obwohl ihre Subjekte in der Engine darunter genauso live sind.
Eine echte Prüfung einrichten
Wenn Sie eine REST-API statt einer reinen Webseite validieren, beginnen Sie bei API-Response-Validierung, die Erreichbarkeit, Antwortzeit und Body Ihrer eigenen API gemeinsam abdeckt. Für eine Seite oder Website, die überall dort erreichbar bleiben und korrekt antworten muss, wo Ihre Nutzer sind, ist globales Uptime-Monitoring von über 300 Standorten das größere Bild, in das der Assertion-Modus sich einfügt. Um Regeln über Code statt über das Panel zu setzen, erstellt und aktualisiert HostTrackers eigene API v2 die Assertion-Regeln eines Monitors genauso wie der Editor. Für die Zertifikatsablauf-Regel oben und alles andere zu TLS deckt Domain- und TLS-Zertifikats-Monitoring die Prüfungsart ab, auf der cert.days.left und die anderen TLS-Subjekte leben. Um eine Regel jetzt sofort gegen eine echte URL auszuwerten, ohne Konto, nutzen Sie das kostenlose HTTP-Check-Tool. Für die Statuscodes, mit denen das Subjekt status vergleicht, beginnen Sie bei 200 OK, und die Monitoring-Anleitungen behandeln die Einrichtung jeder Prüfungsart, unter der diese Sprache läuft.
Häufig gestellte Fragen
Was passiert mit meinen alten Statuscode- und Schlüsselwort-Einstellungen, wenn ich den Assertion-Modus einschalte?
Sie werden durch die Regelliste ersetzt, solange der Modus an ist. Ihn vor dem Speichern wieder auszuschalten, stellt die klassischen Felder exakt wie zuvor wieder her: Nichts wird gelöscht, die beiden Modi können nur nicht gleichzeitig aktiv sein.
Kann ich mehrere Bedingungen in einem Monitor kombinieren?
Ja: Jede Regel in der Liste muss bestehen. Ein "ODER" zwischen getrennten Regeln gibt es noch nicht, um also einen von mehreren Werten zu akzeptieren, verwenden Sie eine einzelne Regel mit in [...] oder containsAny [...].
Wird der Assertion-Modus mir jemals erlauben, ein eigenes Skript zu schreiben?
Nein. Jede Regel wird aus dem festen Satz von Subjekten und Prädikaten der Sprache zusammengesetzt. Es gibt kein eigenes JavaScript, mit Absicht.
Was passiert beim ersten Mal, wenn ich eine Regel hinzufüge, die mit der vorherigen Prüfung vergleicht?
Es gibt noch nichts zum Vergleichen, sie wird also als "warming up" markiert und übersprungen statt als fehlgeschlagen gewertet. Eine "hat sich das geändert"-Regel hinzuzufügen löst nie von selbst einen falschen Alarm aus.
Brauche ich einen bestimmten Plan, um den Assertion-Modus zu nutzen?
Der Modus selbst und die alltäglichen Regeln (Status, Body, Header, Timing, Weiterleitungen) sind breit verfügbar; wie viele Regeln Sie gleichzeitig hinzufügen können, hängt von Ihrem Plan ab und wird live im Editor angezeigt. Regeln, die mit einer vorherigen Prüfung vergleichen (previous, delta, delta2), brauchen einen höherstufigen Plan und sind ausgegraut, mit einer Erklärung.
Funktioniert der Assertion-Modus bei API-Monitoren genauso wie bei Website-Monitoren?
Ja. HTTP- und API-Monitore teilen sich denselben Regel-Editor, dieselben Subjekte und Prädikate, und eine für einen geschriebene Regel liest sich beim anderen identisch.
Was macht where(...) eigentlich, und wann brauche ich es?
Es filtert eine Sammlung auf die zu einer Bedingung passenden Elemente herunter, bevor Sie ein Feld des Ergebnisses lesen. Verwenden Sie es, wenn eine einfache Projektion wie requests.status nicht spezifisch genug ist und Sie einen bestimmten Sprung, Link oder ein bestimmtes Element statt der ganzen Sammlung brauchen (siehe den Abschnitt oben).
Kann ich heute schon eine Regel gegen eine Datenbankabfrage, einen SNMP-Wert, ein Port-Banner oder ein Ping-Ergebnis schreiben?
Die Sprache und der Evaluator unterstützen alle davon, und die Subjekttabellen oben decken jede einzelne ab. Der Regel-Editor tut es nicht: Nur HTTP- und API-Monitore zeigen heute den Response-Validation-Schalter. SNMP zeigt eine "Coming soon"-Vorschau seiner zwei Subjekte, und Datenbank-, Counter- und Ping-Prüfungen zeigen überhaupt kein Assertion-Modus-Panel.
Warum sagen manche Subjekte in dieser Referenz "reserviert" oder "Kandidat" statt einfach normal aufgelistet zu sein?
"Reserviert" bedeutet, das Feld ist in der Registry deklariert, aber noch von keinem Agenten befüllt; "Kandidat" bedeutet, es wird bereits auf Agenten ausgerollt, hat aber noch nicht alle erreicht. Beides wird überall dort, wo es in dieser Referenz auftaucht, und in der zusammenfassenden Liste im Abschnitt Grenzen oben eigens gekennzeichnet.