summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-05-14 01:06:49 +0300
committerDenys Fedoryshchenko <denys.f@collabora.com>2026-05-14 10:04:56 +0300
commit5358369d7b7965deda010267076b25e25c4c044a (patch)
tree84249be2fc616452cb598e77d153bd0f42a44b94
parentd200bdcb39f754ac360b78bf408a3c66f50e8a8c (diff)
downloadaccel-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.c101
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;
}