diff options
| author | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-05-14 01:06:49 +0300 |
|---|---|---|
| committer | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-05-14 10:04:56 +0300 |
| commit | 5358369d7b7965deda010267076b25e25c4c044a (patch) | |
| tree | 84249be2fc616452cb598e77d153bd0f42a44b94 | |
| parent | d200bdcb39f754ac360b78bf408a3c66f50e8a8c (diff) | |
| download | accel-ppp-5358369d7b7965deda010267076b25e25c4c044a.tar.gz accel-ppp-5358369d7b7965deda010267076b25e25c4c044a.zip | |
metrics: render JSON when format=json
Replace the previous `{}` stub with a structured JSON document carrying
the same fields as the Prometheus exposition: build info, uptime,
CPU%, RSS/virtual memory, the triton core counters, session counts by
state, and a `protocols` object whose keys are only present for
modules that are actually loaded.
Strings are emitted through a small helper that escapes the JSON
control characters (\b, \f, \n, \r, \t, \", \\) and falls back to
\u00XX for other bytes below 0x20, so the version string and any
future textual labels survive without producing invalid JSON. The
output passes `python3 -m json.tool` against a running daemon.
Content-Type is already set to application/json by content_type(), so
no transport changes are needed.
Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
| -rw-r--r-- | accel-pppd/extra/metrics.c | 101 |
1 files changed, 99 insertions, 2 deletions
diff --git a/accel-pppd/extra/metrics.c b/accel-pppd/extra/metrics.c index 58d83707..d0fbe6e5 100644 --- a/accel-pppd/extra/metrics.c +++ b/accel-pppd/extra/metrics.c @@ -548,6 +548,104 @@ static void render_prometheus(struct strbuf *sb) } } +static void append_json_string(struct strbuf *sb, const char *s) +{ + strbuf_appendf(sb, "\""); + for (; *s; s++) { + switch (*s) { + case '"': + strbuf_appendf(sb, "\\\""); + break; + case '\\': + strbuf_appendf(sb, "\\\\"); + break; + case '\b': + strbuf_appendf(sb, "\\b"); + break; + case '\f': + strbuf_appendf(sb, "\\f"); + break; + case '\n': + strbuf_appendf(sb, "\\n"); + break; + case '\r': + strbuf_appendf(sb, "\\r"); + break; + case '\t': + strbuf_appendf(sb, "\\t"); + break; + default: + if ((unsigned char)*s < 0x20) + strbuf_appendf(sb, "\\u%04x", (unsigned)*s); + else + strbuf_appendf(sb, "%c", *s); + break; + } + } + strbuf_appendf(sb, "\""); +} + +static void emit_json_proto(struct strbuf *sb, const char *name, int *first, + unsigned int starting, unsigned int active) +{ + if (!*first) + strbuf_appendf(sb, ","); + *first = 0; + strbuf_appendf(sb, "\"%s\":{\"starting\":%u,\"active\":%u}", + name, starting, active); +} + +static void render_json(struct strbuf *sb) +{ + struct accel_stats s; + int first = 1; + + gather_stats(&s); + + strbuf_appendf(sb, "{"); + strbuf_appendf(sb, "\"build\":{\"version\":"); + append_json_string(sb, ACCEL_PPP_VERSION); + strbuf_appendf(sb, "},"); + + strbuf_appendf(sb, "\"uptime_seconds\":%llu,", (unsigned long long)s.uptime); + strbuf_appendf(sb, "\"cpu_percent\":%u,", s.cpu); + strbuf_appendf(sb, "\"memory\":{\"rss_bytes\":%lu,\"virt_bytes\":%lu},", + s.rss_bytes, s.virt_bytes); + + strbuf_appendf(sb, "\"core\":{"); + strbuf_appendf(sb, "\"mempool_allocated_bytes\":%" PRIu64 ",", s.core.mempool_allocated); + strbuf_appendf(sb, "\"mempool_available_bytes\":%" PRIu64 ",", s.core.mempool_available); + strbuf_appendf(sb, "\"threads\":%u,", s.core.thread_count); + strbuf_appendf(sb, "\"threads_active\":%u,", s.core.thread_active); + strbuf_appendf(sb, "\"contexts\":%u,", s.core.context_count); + strbuf_appendf(sb, "\"contexts_sleeping\":%u,", s.core.context_sleeping); + strbuf_appendf(sb, "\"contexts_pending\":%u,", s.core.context_pending); + strbuf_appendf(sb, "\"md_handlers\":%u,", s.core.md_handler_count); + strbuf_appendf(sb, "\"md_handlers_pending\":%u,", s.core.md_handler_pending); + strbuf_appendf(sb, "\"timers\":%u,", s.core.timer_count); + strbuf_appendf(sb, "\"timers_pending\":%u", s.core.timer_pending); + strbuf_appendf(sb, "},"); + + strbuf_appendf(sb, + "\"sessions\":{\"starting\":%u,\"active\":%u,\"finishing\":%u},", + s.sessions.starting, s.sessions.active, s.sessions.finishing); + + strbuf_appendf(sb, "\"protocols\":{"); + if (triton_module_loaded("pppoe")) + emit_json_proto(sb, "pppoe", &first, pppoe_stat_starting(), pppoe_stat_active()); + if (triton_module_loaded("l2tp")) + emit_json_proto(sb, "l2tp", &first, l2tp_stat_starting(), l2tp_stat_active()); + if (triton_module_loaded("pptp")) + emit_json_proto(sb, "pptp", &first, pptp_stat_starting(), pptp_stat_active()); + if (triton_module_loaded("sstp")) + emit_json_proto(sb, "sstp", &first, sstp_stat_starting(), sstp_stat_active()); + if (triton_module_loaded("ipoe")) + emit_json_proto(sb, "ipoe", &first, ipoe_stat_starting(), ipoe_stat_active()); + strbuf_appendf(sb, "}"); + + strbuf_appendf(sb, "}\n"); +} + static int write_all(int fd, const char *buf, int len) { int n, total = 0; @@ -605,8 +703,7 @@ static void serve_metrics(struct metrics_client_t *cln) render_prometheus(&sb); break; case METRICS_FORMAT_JSON: - default: - strbuf_appendf(&sb, "{}\n"); + render_json(&sb); break; } |
