A simple Prometheus (aggregated) push gateway allowing stateless/serverless workloads, ephemeral and batch jobs to easily expose their metrics. Aggregated metrics are exposed via /metrics endpoint and optionally pushed to a remote Prometheus instance.
Powered by Cloudflare Workers, Durable Objects, and CRON Triggers.
PATCH /metrics/:metricName?label1=value1&label2=value2 to increment its value.GET /metrics.remote_write prometheus endpoint on each CRON Trigger execution. See Grafana Cloud (generous free tier) for a managed Prometheus with Grafana.Cloudflare Account with Paid Workers is needed.
yarnyarn deploy(optional) add following secrets (either via Cloudflare Dashboard or with npx wrangler@beta secret put)
SECRET_PROM_ENDPOINT (e.g. https://prometheus-us-central1.grafana.net/api/prom/push)SECRET_PROM_USER (e.g. 23333)SECRET_PROM_TOKEN (e.g. xxyy)There are a couple of easy ways to push your metrics, the POST /metrics endpoint is compatible with any prometheus client library configured to use it as a push gateway.
In case Cloudflare Workers are the only clients, its recommended to deploy this Workers with no routes attached to it and use Service bindings.
POST /metrics - accepts prometheus text-based format.PATCH /metrics/:metricName?label1=value1&label2=value2 - simplified counter increments that works well with no libraries needed.curl
curl -x PATCH https://worker.example.com/metrics/metric_name?foo=bar
POST request with body in prometheus text-based format
cat <<EOF | curl --data-binary @- https://worker.example.com/metrics
# TYPE some_metric counter
some_metric{label="val1"} 42
# TYPE another_metric gauge
# HELP another_metric Just an example.
another_metric 2398.283
EOF
Workers
worker_request counter that will keep track of request countries.
export default {
async fetch(request, env, ctx) {
ctx.waitUntil(
fetch(`https://worker.example.com/metrics/worker_request?country=${request.cf.country}&anotherLabel=value`, {
method: "PATCH"
})
)
return new Response("Hello World!")
}
}
import prom from 'promjs';
export default {
async fetch(request, env, ctx) {
const registry = prom()
const counter = registry.create('counter', 'my_counter', 'A counter for things')
// process tasks, e.g. orders
counter.inc({user: "user1", plan: "pro"})
counter.add(3, {user: "user2", plan: "free"})
counter.inc({user: "user3", plan: "pro"})
ctx.waitUntil(
fetch(`https://worker.example.com/metrics`, {
method: "POST",
body: JSON.stringify(registry.metrics())
})
)
}
}
DELETE /metrics/__all to delete all metricsDELETE /metrics/:metricName to delete a specific metric,DELETE /metrics/:metricName?foo=bar to delete a metric matching specific labels only (all labels need to match)counter and gauge metric types supported only. (histogram WIP)Inspired by Prometheus Push Gateway and Prometheus Aggregation Gateway