#!/usr/bin/env python3 import os from motor.motor_asyncio import AsyncIOMotorClient from sanic import Sanic, response, exceptions MONGODB_HOST = os.getenv("MONGODB_HOST") if not MONGODB_HOST: raise ValueError("No MONGODB_HOST specified") PROMETHEUS_BEARER_TOKEN = os.getenv("PROMETHEUS_BEARER_TOKEN") if not PROMETHEUS_BEARER_TOKEN: raise ValueError("No PROMETHEUS_BEARER_TOKEN specified") app = Sanic("exporter") @app.listener("before_server_start") async def setup_db(app, loop): app.ctx.db = AsyncIOMotorClient(MONGODB_HOST).get_default_database() async def wrap(i, prefix="wildduck_"): metrics_seen = set() async for name, tp, value, labels in i: if name not in metrics_seen: yield "# TYPE %s %s" % (name, tp) metrics_seen.add(name) yield "%s%s %d" % ( prefix + name, ("{%s}" % ",".join(["%s=\"%s\"" % j for j in labels.items()]) if labels else ""), value) async def fetch(): async for u in app.ctx.db.users.find(): labels = { "username": u["username"], "email": u["address"], } if u["lastLogin"]["time"]: yield "last_login", "gauge", u["lastLogin"]["time"].timestamp(), labels if u["storageUsed"]: yield "storage_used", "gauge", u["storageUsed"], labels if u["targets"]: yield "forwarding_addresses", "gauge", len(u["targets"]), labels yield "account_enabled", "gauge", not u["disabled"], labels @app.route("/metrics") async def view_export(request): if request.token != PROMETHEUS_BEARER_TOKEN: raise exceptions.Forbidden("Invalid bearer token") async def streaming_fn(response): async for line in wrap(fetch()): await response.write(line + "\n") return response.stream(streaming_fn, content_type="text/plain") app.run(host="0.0.0.0", port=3001)