Merge pull request #11 from Coffey-Labs/demo-alerts-and-dashboards
Add two storefronts, a payment gateway, and alerts worth waking up for
This commit is contained in:
@@ -25,9 +25,9 @@ other code.
|
|||||||
|
|
||||||
## The fleet
|
## The fleet
|
||||||
|
|
||||||
Fifty hosts, shaped like an estate rather than a stack: thirty-one Linux,
|
Fifty-four hosts, shaped like an estate rather than a stack: thirty-five
|
||||||
eighteen Windows, and one Linux host whose agent is gone so the Agents
|
Linux, eighteen Windows, and one Linux host whose agent is gone so the
|
||||||
page has something stale to show.
|
Agents page has something stale to show.
|
||||||
|
|
||||||
| Tier | Hosts |
|
| Tier | Hosts |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -35,6 +35,7 @@ page has something stale to show.
|
|||||||
| Application | `api-01`–`04`, `worker-01`–`03`, `arm-build-01` (aarch64) |
|
| Application | `api-01`–`04`, `worker-01`–`03`, `arm-build-01` (aarch64) |
|
||||||
| Data | `db-01/02` (Postgres), `mysql-01`, `cache-01/02` (Redis), `mq-01/02` (RabbitMQ), `search-01/02` (Elasticsearch) |
|
| Data | `db-01/02` (Postgres), `mysql-01`, `cache-01/02` (Redis), `mq-01/02` (RabbitMQ), `search-01/02` (Elasticsearch) |
|
||||||
| Platform | `k8s-node-01`–`03` (kubelet), `ci-01` (Jenkins), `vault-01`, `ldap-01` (OpenLDAP), `dns-01` (BIND), `backup-01`, `mail-01` |
|
| Platform | `k8s-node-01`–`03` (kubelet), `ci-01` (Jenkins), `vault-01`, `ldap-01` (OpenLDAP), `dns-01` (BIND), `backup-01`, `mail-01` |
|
||||||
|
| Commerce | `shop-mag-01/02` (Magento), `shop-woo-01` (WooCommerce), `pay-01` (payment gateway) |
|
||||||
| Windows | `DC-01/02`, `IIS-01`–`03`, `WIN-SQL-01/02`, `EXCH-01/02`, `FS-01/02`, `RDS-01/02`, `WIN-APP-01/02`, `PRINT-01`, `WSUS-01`, `SCCM-01` |
|
| Windows | `DC-01/02`, `IIS-01`–`03`, `WIN-SQL-01/02`, `EXCH-01/02`, `FS-01/02`, `RDS-01/02`, `WIN-APP-01/02`, `PRINT-01`, `WSUS-01`, `SCCM-01` |
|
||||||
|
|
||||||
The Windows share is the point of the proportions. An enterprise looking
|
The Windows share is the point of the proportions. An enterprise looking
|
||||||
@@ -49,9 +50,19 @@ Two hosts carry stories the alert rules fire on and must not be moved:
|
|||||||
and goes quiet, which is what `agent-legacy-01-unavailable` catches.
|
and goes quiet, which is what `agent-legacy-01-unavailable` catches.
|
||||||
`api-02` is the host the outage window hits.
|
`api-02` is the host the outage window hits.
|
||||||
|
|
||||||
**Volume.** Fifty hosts generate about 316 records/minute at
|
**Two storefronts, on purpose.** Magento and WooCommerce write about the
|
||||||
|
same events differently, so a commerce panel that groups by service rather
|
||||||
|
than assuming one shape is the honest way to build one. Both feed `pay-01`,
|
||||||
|
whose authorisations carry amount, gateway and decline reason -- which is
|
||||||
|
what lets the Commerce and Payments dashboards answer revenue, average
|
||||||
|
order value, funnel drop-off and why a card was refused out of the same log
|
||||||
|
lines the operators are already reading. Declines rise during the seeded
|
||||||
|
outage window alongside the 5xx rate, because the dependency trouble that
|
||||||
|
fails requests fails authorisations too.
|
||||||
|
|
||||||
|
**Volume.** Fifty-four hosts generate about 346 records/minute at
|
||||||
`-rate-scale 1`, and the nightly reset runs at `RATE_SCALE=0.5` over a
|
`-rate-scale 1`, and the nightly reset runs at `RATE_SCALE=0.5` over a
|
||||||
168-hour backfill -- roughly **1.9M records per reset**, against about
|
168-hour backfill -- roughly **2.2M records per reset**, against about
|
||||||
0.5M when the fleet was twelve hosts. ClickHouse is untroubled by that;
|
0.5M when the fleet was twelve hosts. ClickHouse is untroubled by that;
|
||||||
what it costs is reset time and disk on the demo box. `RATE_SCALE` is the
|
what it costs is reset time and disk on the demo box. `RATE_SCALE` is the
|
||||||
lever if either becomes a problem, and lowering it keeps every host and
|
lever if either becomes a problem, and lowering it keeps every host and
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "DC-01 not reporting",
|
||||||
|
"description": "No heartbeat from the primary domain controller. Everything that authenticates depends on this host",
|
||||||
|
"query": "earliest=-5m host=\"DC-01\" cairnobs.heartbeat=true",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "absence",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Backup job failures",
|
||||||
|
"description": "Backup jobs are erroring. Any run of these is worth attention: the failure is silent until the restore",
|
||||||
|
"query": "service=backup result=error earliest=-1h | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 4
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "CI build failures",
|
||||||
|
"description": "Builds are failing well above the usual rate, which usually means main is broken rather than any one change being bad",
|
||||||
|
"query": "service=jenkins result=FAILURE earliest=-30m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 12
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Checkout errors on WooCommerce",
|
||||||
|
"description": "Checkout is refusing customers above baseline -- an expired coupon campaign, a shipping rule change, or sessions timing out",
|
||||||
|
"query": "service=woocommerce event_kind=checkout_error earliest=-15m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 30
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "Orders have stopped",
|
||||||
|
"description": "No order has been placed on either storefront for fifteen minutes. At this estate's volume that is not a quiet spell, it is an outage somebody has not noticed yet",
|
||||||
|
"query": "event_kind=order earliest=-15m",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "absence",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Products out of stock",
|
||||||
|
"description": "Repeated attempts to buy items with nothing on hand: the catalogue and the warehouse disagree, and customers are finding out first",
|
||||||
|
"query": "service=magento event_kind=out_of_stock earliest=-30m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 40
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Storefront exceptions",
|
||||||
|
"description": "Uncaught exceptions in the storefront code. Every one of these is a customer looking at an error page mid-purchase",
|
||||||
|
"query": "event_kind=exception earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 3,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 14
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "Primary database silent",
|
||||||
|
"description": "db-01 has logged nothing for ten minutes. Postgres is never quiet on a live system, so silence means the service or the agent has stopped",
|
||||||
|
"query": "earliest=-10m host=\"db-01\" service=postgres",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "absence",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "DNS NXDOMAIN surge",
|
||||||
|
"description": "A jump in non-existent domain lookups: a typo-squat campaign, a misconfigured client, or malware working through a generated domain list",
|
||||||
|
"query": "service=bind dns_rcode=NXDOMAIN earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 3,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 18
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "Edge tier silent",
|
||||||
|
"description": "Neither load balancer has logged a request. Either traffic has stopped reaching the estate or both agents are down",
|
||||||
|
"query": "earliest=-10m service=haproxy",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "absence",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Elasticsearch GC pauses",
|
||||||
|
"description": "Old-generation collections are running long and often -- the heap is under pressure and search latency will follow",
|
||||||
|
"query": "service=elasticsearch event_kind=gc earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 5,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 20
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Exchange bounce rate",
|
||||||
|
"description": "Mail is bouncing above the usual rate: a directory out of step with reality, or a distribution list pointing at deleted mailboxes",
|
||||||
|
"query": "service=exchange event_kind=bounce earliest=-15m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 20
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "HAProxy backend 5xx surge",
|
||||||
|
"description": "The load balancers are returning 5xx from their backends well above the ~0.8 per ten minutes a healthy estate produces",
|
||||||
|
"query": "service=haproxy earliest=-10m | where status>=500 | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 2,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 4
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "IIS 5xx surge",
|
||||||
|
"description": "The Windows web tier is failing requests -- normally under one per ten minutes across all three IIS hosts",
|
||||||
|
"query": "service=iis earliest=-10m | where status>=500 | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 2,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 5
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Kubernetes image pulls failing",
|
||||||
|
"description": "Nodes cannot pull images: a registry outage, an expired pull secret, or a tag that was deleted out from under a deployment",
|
||||||
|
"query": "service=kubelet event_kind=image_pull_failed earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 5,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 30
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Kubernetes readiness probes failing",
|
||||||
|
"description": "Readiness probes are failing across the cluster at roughly double the normal rate -- pods are being pulled out of service",
|
||||||
|
"query": "service=kubelet event_kind=probe_failed earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 5,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 55
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "LDAP bind failures",
|
||||||
|
"description": "Failed LDAP binds are essentially zero in normal operation, so any run of them is worth waking up for",
|
||||||
|
"query": "service=openldap event_kind=bind_failed earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 5
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "SQL Server deadlocks",
|
||||||
|
"description": "Transactions are being chosen as deadlock victims well above baseline; something is taking locks in a new order",
|
||||||
|
"query": "service=mssql event_kind=deadlock earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 3,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 12
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "SQL Server I/O stalls",
|
||||||
|
"description": "SQL Server is reporting I/O requests over 15 seconds. The storage under the data files is not keeping up",
|
||||||
|
"query": "service=mssql event_kind=io_stall earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 5,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 35
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "SQL Server login failures",
|
||||||
|
"description": "Repeated failed logins against SQL Server -- credential stuffing against a service account, or an application with a stale password",
|
||||||
|
"query": "service=mssql event_kind=login_failed earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 12
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Card chargebacks",
|
||||||
|
"description": "Chargebacks above the usual trickle, which is the first visible sign of card testing or a fraud run getting through",
|
||||||
|
"query": "event_kind=chargeback earliest=-1h | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 8
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Payment decline rate elevated",
|
||||||
|
"description": "Declines are running well above the ~7.5% baseline. Either a gateway is unwell or something upstream is sending bad authorisations -- both cost orders directly",
|
||||||
|
"query": "auth_result=declined earliest=-15m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 5,
|
||||||
|
"notification_target_id": "__TARGET_OPS__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 120
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "RabbitMQ memory alarms",
|
||||||
|
"description": "Brokers are raising memory alarms, which blocks publishers -- the queue is not draining as fast as it is filling",
|
||||||
|
"query": "service=rabbitmq event_kind=alarm earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 3,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 15
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "RabbitMQ queue backing up",
|
||||||
|
"description": "A queue has passed 2,500 ready messages. Normal depth is a few hundred; this is the shape of consumers dying rather than traffic rising",
|
||||||
|
"query": "service=rabbitmq earliest=-15m | stats max(queue_depth) as max_depth",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 300,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_PLATFORM__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 2500
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "SMB access denials",
|
||||||
|
"description": "Share access is being denied repeatedly -- someone or something is reaching for files it has no rights to",
|
||||||
|
"query": "service=smb event_kind=share_denied earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 12
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Squid blocking outbound requests",
|
||||||
|
"description": "The outbound proxy is denying requests above baseline -- something on the inside is repeatedly reaching for a blocked destination",
|
||||||
|
"query": "service=squid status=403 earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 3,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 12
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "Vault authentication failures",
|
||||||
|
"description": "Something is failing to authenticate to Vault. Near-zero normally, and the one place where a handful of failures is already a story",
|
||||||
|
"query": "service=vault event_kind=auth_failed earliest=-10m | stats count",
|
||||||
|
"query_language": "spl",
|
||||||
|
"condition_type": "threshold",
|
||||||
|
"eval_interval_seconds": 60,
|
||||||
|
"for_minutes": 0,
|
||||||
|
"notification_target_id": "__TARGET_SECURITY__",
|
||||||
|
"enabled": true,
|
||||||
|
"comparator": "gt",
|
||||||
|
"threshold_value": 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"name": "Capacity and storage",
|
||||||
|
"description": "Disk, memory and the jobs that protect them -- what is filling up and what is backing it up",
|
||||||
|
"default_earliest": "-7d",
|
||||||
|
"default_latest": "now",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Peak disk used",
|
||||||
|
"query": "cairnobs.metrics=true | stats max(disk_used_bytes) as used",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Backup jobs",
|
||||||
|
"query": "service=backup event_kind=backup | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 3,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Backup failures",
|
||||||
|
"query": "service=backup result=error | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "SQL log backups",
|
||||||
|
"query": "service=mssql event_kind=log_backup | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 9,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Disk used by host",
|
||||||
|
"query": "cairnobs.metrics=true | stats max(disk_used_bytes) as used by host | sort -used",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 12,
|
||||||
|
"height": 6,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "host",
|
||||||
|
"value_column": "used"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Memory headroom",
|
||||||
|
"query": "cairnobs.metrics=true | stats max(mem_used_bytes) as peak, max(mem_total_bytes) as total by host | sort -peak | head 15",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 9,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Backup outcomes by job",
|
||||||
|
"query": "service=backup | stats count by backup_job, result",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 9,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "backup_job",
|
||||||
|
"value_column": "count",
|
||||||
|
"series_column": "result",
|
||||||
|
"stacked": "true"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Recent backup failures",
|
||||||
|
"query": "service=backup result=error | sort -timestamp | head 20 | fields timestamp, host, backup_job, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 14,
|
||||||
|
"width": 12,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 7
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
{
|
||||||
|
"name": "Commerce",
|
||||||
|
"description": "Orders, revenue and the checkout funnel across both storefronts",
|
||||||
|
"default_earliest": "-24h",
|
||||||
|
"default_latest": "now",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Orders",
|
||||||
|
"query": "event_kind=order | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Revenue",
|
||||||
|
"query": "event_kind=order | stats sum(order_total) as revenue",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 3,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Average order value",
|
||||||
|
"query": "event_kind=order | stats avg(order_total) as aov",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Items per order",
|
||||||
|
"query": "event_kind=order | stats avg(order_items) as items",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 9,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Revenue by storefront",
|
||||||
|
"query": "event_kind=order | stats sum(order_total) as revenue by service | sort -revenue",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "service",
|
||||||
|
"value_column": "revenue"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Orders by store view",
|
||||||
|
"query": "service=magento event_kind=order | stats count by store_view | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "store_view",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Checkout funnel",
|
||||||
|
"query": "service=magento event_kind=checkout | stats count by checkout_step | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "checkout_step",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Payment methods",
|
||||||
|
"query": "event_kind=order | stats count by payment_method | sort -count",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "payment_method",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Best sellers",
|
||||||
|
"query": "event_kind=order | stats count by sku | sort -count | head 10",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 13,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "sku",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Out of stock",
|
||||||
|
"query": "service=magento event_kind=out_of_stock | stats count by sku | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 13,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "sku",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Checkout errors",
|
||||||
|
"query": "service=woocommerce event_kind=checkout_error | sort -timestamp | head 20 | fields timestamp, host, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 18,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Storefront exceptions",
|
||||||
|
"query": "event_kind=exception | sort -timestamp | head 20 | fields timestamp, host, service, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 18,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 11
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
{
|
||||||
|
"name": "Fleet health",
|
||||||
|
"description": "Fifty hosts at a glance: who is loud, who is quiet, and who is running hot",
|
||||||
|
"default_earliest": "-24h",
|
||||||
|
"default_latest": "now",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Log events",
|
||||||
|
"query": "cairnobs.metrics!=true cairnobs.heartbeat!=true | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Errors",
|
||||||
|
"query": "severity=ERROR | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 3,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Warnings",
|
||||||
|
"query": "severity=WARN | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Fatal",
|
||||||
|
"query": "severity=FATAL | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 9,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Log volume by host",
|
||||||
|
"query": "cairnobs.metrics!=true cairnobs.heartbeat!=true | stats count by host | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 12,
|
||||||
|
"height": 6,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "host",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Busiest CPU",
|
||||||
|
"query": "cairnobs.metrics=true | stats avg(cpu_percent) as avg_cpu by host | sort -avg_cpu | head 12",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 9,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "host",
|
||||||
|
"value_column": "avg_cpu"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Fullest disks",
|
||||||
|
"query": "cairnobs.metrics=true | stats max(disk_used_bytes) as used by host | sort -used | head 12",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 9,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "host",
|
||||||
|
"value_column": "used"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Severity mix by service",
|
||||||
|
"query": "cairnobs.metrics!=true | stats count by service, severity",
|
||||||
|
"viz_type": "heatmap",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 14,
|
||||||
|
"width": 12,
|
||||||
|
"height": 6,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "service",
|
||||||
|
"y_column": "severity",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Agents by version",
|
||||||
|
"query": "cairnobs.heartbeat=true | stats count by agent_version | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 20,
|
||||||
|
"width": 6,
|
||||||
|
"height": 4,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "agent_version",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Quietest hosts",
|
||||||
|
"query": "cairnobs.metrics!=true cairnobs.heartbeat!=true | stats count by host | sort count | head 10",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 20,
|
||||||
|
"width": 6,
|
||||||
|
"height": 4,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 9
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"name": "Golden signals",
|
||||||
|
"description": "Traffic, errors, latency and saturation across everything that serves a request",
|
||||||
|
"default_earliest": "-6h",
|
||||||
|
"default_latest": "now",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Requests served",
|
||||||
|
"query": "| where status>=100 | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "5xx responses",
|
||||||
|
"query": "| where status>=500 | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 3,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "API latency (avg ms)",
|
||||||
|
"query": "service=api | stats avg(duration_ms) as avg_ms",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Slow Postgres statements",
|
||||||
|
"query": "service=postgres | where duration_ms>1000 | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 9,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Traffic by service and host",
|
||||||
|
"query": "| where status>=100 | stats count by service, host",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 12,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "service",
|
||||||
|
"value_column": "count",
|
||||||
|
"series_column": "host",
|
||||||
|
"stacked": "true"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Where the errors are",
|
||||||
|
"query": "severity=ERROR | stats count by service, host | sort -count | head 20",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "API latency by route",
|
||||||
|
"query": "service=api | stats avg(duration_ms) as avg_ms by route | sort -avg_ms | head 12",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "route",
|
||||||
|
"value_column": "avg_ms"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Status codes by service",
|
||||||
|
"query": "| where status>=100 | stats count by service, status",
|
||||||
|
"viz_type": "heatmap",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 13,
|
||||||
|
"width": 12,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "service",
|
||||||
|
"y_column": "status",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Slow operations, wherever they are",
|
||||||
|
"query": "| where duration_ms>1000 | stats count by service, host | sort -count | head 15",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 18,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Saturation by host",
|
||||||
|
"query": "cairnobs.metrics=true | stats avg(cpu_percent) as avg_cpu, max(mem_used_bytes) as peak_mem by host | sort -avg_cpu | head 15",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 18,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 9
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
{
|
||||||
|
"name": "Payments",
|
||||||
|
"description": "Authorisations, declines and the gateways behind them -- where money stops moving and why",
|
||||||
|
"default_earliest": "-24h",
|
||||||
|
"default_latest": "now",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Authorisations",
|
||||||
|
"query": "event_kind=authorization | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Declines",
|
||||||
|
"query": "auth_result=declined | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 3,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Refunds",
|
||||||
|
"query": "event_kind=refund | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Chargebacks",
|
||||||
|
"query": "event_kind=chargeback | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 9,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Approved and declined by gateway",
|
||||||
|
"query": "event_kind=authorization | stats count by gateway, auth_result",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "gateway",
|
||||||
|
"value_column": "count",
|
||||||
|
"series_column": "auth_result",
|
||||||
|
"stacked": "true"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Why cards are declined",
|
||||||
|
"query": "auth_result=declined | stats count by decline_reason | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "decline_reason",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Declined value",
|
||||||
|
"query": "auth_result=declined | stats sum(amount) as lost by gateway | sort -lost",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "gateway",
|
||||||
|
"value_column": "lost"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Gateway latency",
|
||||||
|
"query": "event_kind=authorization | stats avg(duration_ms) as avg_ms by gateway | sort -avg_ms",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "gateway",
|
||||||
|
"value_column": "avg_ms"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Decline reasons by gateway",
|
||||||
|
"query": "auth_result=declined | stats count by gateway, decline_reason",
|
||||||
|
"viz_type": "heatmap",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 13,
|
||||||
|
"width": 12,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "gateway",
|
||||||
|
"y_column": "decline_reason",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Chargebacks and refunds",
|
||||||
|
"query": "service=payments | where event_kind=chargeback | sort -timestamp | head 20 | fields timestamp, gateway, amount, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 18,
|
||||||
|
"width": 12,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 9
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"name": "Security posture",
|
||||||
|
"description": "Authentication, authorisation and the outbound edge -- every place a credential or a request gets refused",
|
||||||
|
"default_earliest": "-24h",
|
||||||
|
"default_latest": "now",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Failed SSH",
|
||||||
|
"query": "service=system auth_result=failed | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Failed Windows logons",
|
||||||
|
"query": "service=eventlog winevt.event_id=4625 | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 3,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Failed SQL logins",
|
||||||
|
"query": "service=mssql event_kind=login_failed | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Denied share access",
|
||||||
|
"query": "service=smb event_kind=share_denied | stats count",
|
||||||
|
"viz_type": "single_stat",
|
||||||
|
"position_x": 9,
|
||||||
|
"position_y": 0,
|
||||||
|
"width": 3,
|
||||||
|
"height": 3,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Source addresses probing SSH",
|
||||||
|
"query": "service=system auth_result=failed | stats count by remote_addr | sort -count | head 12",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "remote_addr",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Accounts targeted on Windows",
|
||||||
|
"query": "service=eventlog winevt.event_id=4625 | stats count by winevt.target_user | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 3,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "winevt.target_user",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Blocked outbound destinations",
|
||||||
|
"query": "service=squid status=403 | stats count by dest_host | sort -count",
|
||||||
|
"viz_type": "bar",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"x_column": "dest_host",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "NXDOMAIN by client",
|
||||||
|
"query": "service=bind dns_rcode=NXDOMAIN | stats count by remote_addr | sort -count | head 12",
|
||||||
|
"viz_type": "top_n",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 8,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"viz_config": {
|
||||||
|
"label_column": "remote_addr",
|
||||||
|
"value_column": "count"
|
||||||
|
},
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Failed LDAP binds",
|
||||||
|
"query": "service=openldap event_kind=bind_failed | sort -timestamp | head 20 | fields timestamp, host, bind_dn, remote_addr, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 13,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Vault authentication failures",
|
||||||
|
"query": "service=vault event_kind=auth_failed | sort -timestamp | head 20 | fields timestamp, host, vault_path, remote_addr, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 6,
|
||||||
|
"position_y": 13,
|
||||||
|
"width": 6,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Account lockouts",
|
||||||
|
"query": "service=eventlog winevt.event_id=4740 | sort -timestamp | head 20 | fields timestamp, host, winevt.target_user, message",
|
||||||
|
"viz_type": "table",
|
||||||
|
"position_x": 0,
|
||||||
|
"position_y": 18,
|
||||||
|
"width": 12,
|
||||||
|
"height": 5,
|
||||||
|
"query_language": "spl",
|
||||||
|
"sort_order": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -679,6 +679,12 @@ func primaryRecord(h *host, t time.Time, r *rand.Rand, c conditions) *logsv1.Log
|
|||||||
return exchangeRecord(h, t, r, c)
|
return exchangeRecord(h, t, r, c)
|
||||||
case "smb":
|
case "smb":
|
||||||
return smbRecord(h, t, r, c)
|
return smbRecord(h, t, r, c)
|
||||||
|
case "magento":
|
||||||
|
return magentoRecord(h, t, r, c)
|
||||||
|
case "woocommerce":
|
||||||
|
return wooRecord(h, t, r, c)
|
||||||
|
case "payments":
|
||||||
|
return paymentsRecord(h, t, r, c)
|
||||||
default:
|
default:
|
||||||
return newRecord(h, h.service, t, logsv1.Severity_SEVERITY_INFO, "heartbeat", nil)
|
return newRecord(h, h.service, t, logsv1.Severity_SEVERITY_INFO, "heartbeat", nil)
|
||||||
}
|
}
|
||||||
@@ -1057,3 +1063,236 @@ func smbRecord(h *host, t time.Time, r *rand.Rand, c conditions) *logsv1.LogReco
|
|||||||
map[string]string{"share": share, "winevt.target_user": user, "event_kind": "share_denied"})
|
map[string]string{"share": share, "winevt.target_user": user, "event_kind": "share_denied"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Commerce.
|
||||||
|
//
|
||||||
|
// Two storefronts and the gateway behind both, because the questions a
|
||||||
|
// business asks of its logs are not the questions an operator asks, and
|
||||||
|
// a demo that only answers the second one is only half a demo. Orders,
|
||||||
|
// revenue, average order value, where checkout is losing people and why
|
||||||
|
// a card was declined are all in the log line rather than in a separate
|
||||||
|
// metrics system -- which is the argument for keeping the two together.
|
||||||
|
//
|
||||||
|
// Two platforms rather than one on purpose: Magento and WooCommerce
|
||||||
|
// write differently about the same events, so a panel that groups by
|
||||||
|
// service rather than assuming one shape is the honest way to build one.
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
var (
|
||||||
|
storeViews = []string{"uk", "us", "de", "fr"}
|
||||||
|
|
||||||
|
skus = []struct {
|
||||||
|
sku, name string
|
||||||
|
price float64
|
||||||
|
}{
|
||||||
|
{"CH-1042", "Aeron-style task chair", 489.00},
|
||||||
|
{"DK-2201", "Standing desk 160x80", 629.00},
|
||||||
|
{"MN-3310", "27\" 4K monitor", 379.99},
|
||||||
|
{"KB-4407", "Mechanical keyboard, tactile", 129.50},
|
||||||
|
{"MS-5120", "Vertical ergonomic mouse", 74.95},
|
||||||
|
{"LT-6003", "Desk lamp, warm CCT", 59.00},
|
||||||
|
{"CB-7788", "Cable management tray", 24.99},
|
||||||
|
{"HS-8890", "Noise-cancelling headset", 219.00},
|
||||||
|
}
|
||||||
|
|
||||||
|
checkoutSteps = []string{"cart", "shipping", "payment", "review", "placed"}
|
||||||
|
|
||||||
|
gateways = []string{"stripe", "adyen", "paypal"}
|
||||||
|
|
||||||
|
declineReasons = []struct {
|
||||||
|
code, text string
|
||||||
|
weight int
|
||||||
|
}{
|
||||||
|
{"insufficient_funds", "Insufficient funds", 30},
|
||||||
|
{"do_not_honor", "Do not honour", 22},
|
||||||
|
{"expired_card", "Expired card", 14},
|
||||||
|
{"incorrect_cvc", "Incorrect CVC", 12},
|
||||||
|
{"lost_or_stolen", "Lost or stolen card", 6},
|
||||||
|
{"3ds_failed", "3-D Secure authentication failed", 16},
|
||||||
|
}
|
||||||
|
declineWeightTotal int
|
||||||
|
|
||||||
|
paymentMethods = []string{"card", "paypal", "apple_pay", "klarna"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for _, d := range declineReasons {
|
||||||
|
declineWeightTotal += d.weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pickDecline(r *rand.Rand) (string, string) {
|
||||||
|
n := r.Intn(declineWeightTotal)
|
||||||
|
for _, d := range declineReasons {
|
||||||
|
if n -= d.weight; n < 0 {
|
||||||
|
return d.code, d.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return declineReasons[0].code, declineReasons[0].text
|
||||||
|
}
|
||||||
|
|
||||||
|
// orderTotal builds a basket rather than drawing a number, so average
|
||||||
|
// order value moves the way a real one does -- driven by what is in the
|
||||||
|
// cart, not by a distribution somebody chose.
|
||||||
|
func orderTotal(r *rand.Rand) (float64, int, string) {
|
||||||
|
items := 1 + r.Intn(4)
|
||||||
|
total := 0.0
|
||||||
|
first := ""
|
||||||
|
for i := 0; i < items; i++ {
|
||||||
|
s := skus[r.Intn(len(skus))]
|
||||||
|
qty := 1
|
||||||
|
if r.Float64() < 0.18 {
|
||||||
|
qty = 2
|
||||||
|
}
|
||||||
|
total += s.price * float64(qty)
|
||||||
|
if i == 0 {
|
||||||
|
first = s.sku
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total, items, first
|
||||||
|
}
|
||||||
|
|
||||||
|
func magentoRecord(h *host, t time.Time, r *rand.Rand, c conditions) *logsv1.LogRecord {
|
||||||
|
store := pick(r, storeViews)
|
||||||
|
switch n := r.Intn(100); {
|
||||||
|
case n < 34:
|
||||||
|
// Checkout progress. The funnel narrows towards `placed`, which is
|
||||||
|
// what makes a "where are we losing people" panel say anything.
|
||||||
|
step := checkoutSteps[0]
|
||||||
|
switch f := r.Float64(); {
|
||||||
|
case f < 0.34:
|
||||||
|
step = "cart"
|
||||||
|
case f < 0.58:
|
||||||
|
step = "shipping"
|
||||||
|
case f < 0.76:
|
||||||
|
step = "payment"
|
||||||
|
case f < 0.88:
|
||||||
|
step = "review"
|
||||||
|
default:
|
||||||
|
step = "placed"
|
||||||
|
}
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("checkout step reached: %s quote_id=%d store=%s", step, 400000+r.Intn(99999), store),
|
||||||
|
map[string]string{"checkout_step": step, "store_view": store, "event_kind": "checkout"})
|
||||||
|
case n < 58:
|
||||||
|
total, items, sku := orderTotal(r)
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Order placed: increment_id=%d grand_total=%.2f items=%d store=%s method=%s",
|
||||||
|
2000000000+r.Intn(99999999), total, items, store, pick(r, paymentMethods)),
|
||||||
|
map[string]string{
|
||||||
|
"event_kind": "order", "order_total": fmt.Sprintf("%.2f", total),
|
||||||
|
"order_items": strconv.Itoa(items), "sku": sku, "store_view": store,
|
||||||
|
"currency": "GBP", "payment_method": pick(r, paymentMethods),
|
||||||
|
})
|
||||||
|
case n < 72:
|
||||||
|
s := skus[r.Intn(len(skus))]
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Product viewed: sku=%s name=%q store=%s", s.sku, s.name, store),
|
||||||
|
map[string]string{"event_kind": "product_view", "sku": s.sku, "store_view": store})
|
||||||
|
case n < 82:
|
||||||
|
idx := pick(r, []string{"catalog_product_price", "cataloginventory_stock", "catalogsearch_fulltext", "customer_grid"})
|
||||||
|
dur := 4 + r.Intn(180)
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Index %s has been rebuilt successfully in %02d:%02d:%02d", idx, 0, dur/60, dur%60),
|
||||||
|
map[string]string{"event_kind": "reindex", "indexer": idx, "duration_ms": strconv.Itoa(dur * 1000)})
|
||||||
|
case n < 90:
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Cron group %s finished, %d jobs run", pick(r, []string{"default", "index", "consumers"}), 1+r.Intn(20)),
|
||||||
|
map[string]string{"event_kind": "cron", "store_view": store})
|
||||||
|
case n < 96:
|
||||||
|
s := skus[r.Intn(len(skus))]
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_WARN,
|
||||||
|
fmt.Sprintf("Not enough items for sale: sku=%s requested=%d on_hand=%d", s.sku, 1+r.Intn(3), r.Intn(2)),
|
||||||
|
map[string]string{"event_kind": "out_of_stock", "sku": s.sku, "store_view": store})
|
||||||
|
default:
|
||||||
|
return newRecord(h, "magento", t, logsv1.Severity_SEVERITY_ERROR,
|
||||||
|
fmt.Sprintf("main.CRITICAL: Uncaught TypeError in %s: Argument #1 must be of type Quote, null given",
|
||||||
|
pick(r, []string{"Magento/Quote/Model/QuoteManagement.php", "Magento/Checkout/Model/Session.php", "Magento/Sales/Model/Order.php"})),
|
||||||
|
map[string]string{"event_kind": "exception", "store_view": store})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wooRecord(h *host, t time.Time, r *rand.Rand, c conditions) *logsv1.LogRecord {
|
||||||
|
switch n := r.Intn(100); {
|
||||||
|
case n < 38:
|
||||||
|
total, items, sku := orderTotal(r)
|
||||||
|
status := pick(r, []string{"processing", "completed", "on-hold"})
|
||||||
|
return newRecord(h, "woocommerce", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Order #%d status changed to %s (total %.2f, %d items)", 30000+r.Intn(9999), status, total, items),
|
||||||
|
map[string]string{
|
||||||
|
"event_kind": "order", "order_status": status, "order_total": fmt.Sprintf("%.2f", total),
|
||||||
|
"order_items": strconv.Itoa(items), "sku": sku, "currency": "GBP",
|
||||||
|
"payment_method": pick(r, paymentMethods),
|
||||||
|
})
|
||||||
|
case n < 58:
|
||||||
|
return newRecord(h, "woocommerce", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("REST API request: GET /wp-json/wc/v3/products?per_page=%d served in %dms", 10+r.Intn(90), 20+r.Intn(600)),
|
||||||
|
map[string]string{"event_kind": "api", "duration_ms": strconv.Itoa(20 + r.Intn(600))})
|
||||||
|
case n < 74:
|
||||||
|
return newRecord(h, "woocommerce", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Scheduled action completed: %s", pick(r, []string{"woocommerce_cleanup_sessions", "wc_admin_unsnooze_admin_notes", "woocommerce_scheduled_sales"})),
|
||||||
|
map[string]string{"event_kind": "cron"})
|
||||||
|
case n < 84:
|
||||||
|
s := skus[r.Intn(len(skus))]
|
||||||
|
return newRecord(h, "woocommerce", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("Stock reduced for %s: %d -> %d", s.sku, 5+r.Intn(40), r.Intn(5)),
|
||||||
|
map[string]string{"event_kind": "stock", "sku": s.sku})
|
||||||
|
case n < 93:
|
||||||
|
return newRecord(h, "woocommerce", t, logsv1.Severity_SEVERITY_WARN,
|
||||||
|
fmt.Sprintf("Checkout error: %s", pick(r, []string{
|
||||||
|
"Invalid billing postcode", "Coupon \"WELCOME10\" has expired",
|
||||||
|
"Shipping method not available for this address", "Session expired before payment",
|
||||||
|
})),
|
||||||
|
map[string]string{"event_kind": "checkout_error"})
|
||||||
|
default:
|
||||||
|
return newRecord(h, "woocommerce", t, logsv1.Severity_SEVERITY_ERROR,
|
||||||
|
"PHP Fatal error: Allowed memory size of 268435456 bytes exhausted in class-wc-order.php",
|
||||||
|
map[string]string{"event_kind": "exception"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func paymentsRecord(h *host, t time.Time, r *rand.Rand, c conditions) *logsv1.LogRecord {
|
||||||
|
gw := pick(r, gateways)
|
||||||
|
total, _, _ := orderTotal(r)
|
||||||
|
took := int(float64(90+r.Intn(900)) * c.latencyMult)
|
||||||
|
|
||||||
|
// Declines rise with the outage window: the same dependency trouble
|
||||||
|
// that fails API requests fails authorisations, which is what makes
|
||||||
|
// the decline-rate rule true at the same time as the 5xx one.
|
||||||
|
declineRate := 0.075
|
||||||
|
if c.apiErrorRate > 0 {
|
||||||
|
declineRate = 0.28
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case r.Float64() < declineRate:
|
||||||
|
code, text := pickDecline(r)
|
||||||
|
return newRecord(h, "payments", t, logsv1.Severity_SEVERITY_WARN,
|
||||||
|
fmt.Sprintf("authorization declined gateway=%s amount=%.2f currency=GBP reason=%s (%s) latency=%dms", gw, total, code, text, took),
|
||||||
|
map[string]string{
|
||||||
|
"event_kind": "authorization", "auth_result": "declined", "gateway": gw,
|
||||||
|
"decline_reason": code, "amount": fmt.Sprintf("%.2f", total),
|
||||||
|
"currency": "GBP", "duration_ms": strconv.Itoa(took),
|
||||||
|
})
|
||||||
|
case r.Float64() < 0.05:
|
||||||
|
return newRecord(h, "payments", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("refund issued gateway=%s amount=%.2f currency=GBP reason=%s", gw, total/2, pick(r, []string{"customer_request", "item_returned", "duplicate_charge"})),
|
||||||
|
map[string]string{"event_kind": "refund", "gateway": gw, "amount": fmt.Sprintf("%.2f", total/2), "currency": "GBP"})
|
||||||
|
case r.Float64() < 0.02:
|
||||||
|
return newRecord(h, "payments", t, logsv1.Severity_SEVERITY_ERROR,
|
||||||
|
fmt.Sprintf("chargeback received gateway=%s amount=%.2f currency=GBP network_reason=fraud", gw, total),
|
||||||
|
map[string]string{"event_kind": "chargeback", "gateway": gw, "amount": fmt.Sprintf("%.2f", total), "currency": "GBP"})
|
||||||
|
case r.Float64() < 0.10:
|
||||||
|
return newRecord(h, "payments", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("3-D Secure challenge issued gateway=%s amount=%.2f currency=GBP", gw, total),
|
||||||
|
map[string]string{"event_kind": "3ds_challenge", "gateway": gw, "amount": fmt.Sprintf("%.2f", total), "currency": "GBP"})
|
||||||
|
default:
|
||||||
|
return newRecord(h, "payments", t, logsv1.Severity_SEVERITY_INFO,
|
||||||
|
fmt.Sprintf("authorization approved gateway=%s amount=%.2f currency=GBP latency=%dms", gw, total, took),
|
||||||
|
map[string]string{
|
||||||
|
"event_kind": "authorization", "auth_result": "approved", "gateway": gw,
|
||||||
|
"amount": fmt.Sprintf("%.2f", total), "currency": "GBP",
|
||||||
|
"duration_ms": strconv.Itoa(took),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -580,6 +580,46 @@ var fleet = []host{
|
|||||||
agentVersion: agentVersion, sourceKind: "eventlog", sourceDetail: "channels=Security,System,Application",
|
agentVersion: agentVersion, sourceKind: "eventlog", sourceDetail: "channels=Security,System,Application",
|
||||||
batchMax: 500, batchFlushMS: 5000, heartbeatMS: 60000,
|
batchMax: 500, batchFlushMS: 5000, heartbeatMS: 60000,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "shop-mag-01", service: "magento",
|
||||||
|
os: "Ubuntu 24.04.1 LTS", kernel: "6.8.0-45-generic", arch: "x86_64",
|
||||||
|
cores: 16, memTotal: 32 << 30, diskTot: 400 << 30,
|
||||||
|
ipv4: "10.0.9.11", ipv6: "2600:3c02::f03c:94ff:fe1a:9011",
|
||||||
|
cpuBase: 51, memFrac: 0.69, diskFrac: 0.46,
|
||||||
|
eventsPerMin: 26, systemPerMin: 0.9,
|
||||||
|
agentVersion: agentVersion, sourceKind: "file", sourceDetail: "/var/www/shop/var/log/system.log",
|
||||||
|
batchMax: 500, batchFlushMS: 5000, heartbeatMS: 60000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shop-mag-02", service: "magento",
|
||||||
|
os: "Ubuntu 24.04.1 LTS", kernel: "6.8.0-45-generic", arch: "x86_64",
|
||||||
|
cores: 16, memTotal: 32 << 30, diskTot: 400 << 30,
|
||||||
|
ipv4: "10.0.9.12", ipv6: "2600:3c02::f03c:94ff:fe1a:9012",
|
||||||
|
cpuBase: 48, memFrac: 0.67, diskFrac: 0.44,
|
||||||
|
eventsPerMin: 24, systemPerMin: 0.9,
|
||||||
|
agentVersion: agentVersion, sourceKind: "file", sourceDetail: "/var/www/shop/var/log/system.log",
|
||||||
|
batchMax: 500, batchFlushMS: 5000, heartbeatMS: 60000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shop-woo-01", service: "woocommerce",
|
||||||
|
os: "Ubuntu 24.04.1 LTS", kernel: "6.8.0-45-generic", arch: "x86_64",
|
||||||
|
cores: 8, memTotal: 16 << 30, diskTot: 200 << 30,
|
||||||
|
ipv4: "10.0.9.21", ipv6: "2600:3c02::f03c:94ff:fe1a:9021",
|
||||||
|
cpuBase: 37, memFrac: 0.58, diskFrac: 0.39,
|
||||||
|
eventsPerMin: 17, systemPerMin: 0.7,
|
||||||
|
agentVersion: agentVersion, sourceKind: "file", sourceDetail: "/var/www/woo/wp-content/uploads/wc-logs/",
|
||||||
|
batchMax: 500, batchFlushMS: 5000, heartbeatMS: 60000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pay-01", service: "payments",
|
||||||
|
os: "Ubuntu 24.04.1 LTS", kernel: "6.8.0-45-generic", arch: "x86_64",
|
||||||
|
cores: 8, memTotal: 16 << 30, diskTot: 120 << 30,
|
||||||
|
ipv4: "10.0.9.31", ipv6: "2600:3c02::f03c:94ff:fe1a:9031",
|
||||||
|
cpuBase: 23, memFrac: 0.47, diskFrac: 0.28,
|
||||||
|
eventsPerMin: 19, systemPerMin: 0.6,
|
||||||
|
agentVersion: agentVersion, sourceKind: "journald", sourceDetail: "unit=shop-payments.service",
|
||||||
|
batchMax: 500, batchFlushMS: 5000, heartbeatMS: 60000,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "legacy-01", service: "nginx",
|
name: "legacy-01", service: "nginx",
|
||||||
os: "Ubuntu 20.04.6 LTS", kernel: "5.4.0-192-generic", arch: "x86_64",
|
os: "Ubuntu 20.04.6 LTS", kernel: "5.4.0-192-generic", arch: "x86_64",
|
||||||
|
|||||||
Reference in New Issue
Block a user