cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
metrics.c
Go to the documentation of this file.
1
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <math.h>
15
16/* -------------------------------------------------------------------------
17 * Internal registry layout
18 * ---------------------------------------------------------------------- */
19
22};
23
24static const cwist_metric_t metric_defaults[CWIST_METRIC_COUNT] = {
25 [CWIST_METRIC_URING_SQES_PENDING] = {.name = "cwist_io_uring_sqes_pending",
26 .help = "Active io_uring SQEs waiting for kernel pickup",
27 .type = CWIST_METRIC_COUNTER},
28 [CWIST_METRIC_URING_BUFFERS_ACTIVE] = {.name = "cwist_io_uring_buffers_active",
29 .help = "In-flight fixed buffers in io_uring pool",
30 .type = CWIST_METRIC_GAUGE},
31 [CWIST_METRIC_STREAMS_HTTP2] = {.name = "cwist_streams_http2",
32 .help = "Active HTTP/2 streams",
33 .type = CWIST_METRIC_GAUGE},
34 [CWIST_METRIC_STREAMS_HTTP3] = {.name = "cwist_streams_http3",
35 .help = "Active HTTP/3 streams",
36 .type = CWIST_METRIC_GAUGE},
37 [CWIST_METRIC_FLOW_WINDOW_HEALTH] = {.name = "cwist_flow_window_health",
38 .help = "Flow-control window health (0-100)",
39 .type = CWIST_METRIC_GAUGE},
40 [CWIST_METRIC_VALIDATION_ERRORS] = {.name = "cwist_validation_errors",
41 .help = "Validation error frequency",
42 .type = CWIST_METRIC_COUNTER},
43 [CWIST_METRIC_REQUESTS_TOTAL] = {.name = "cwist_requests_total",
44 .help = "Total HTTP requests handled",
45 .type = CWIST_METRIC_COUNTER},
46 [CWIST_METRIC_REQUEST_DURATION_NS] = {.name = "cwist_request_duration_ns",
47 .help = "Request duration sum in nanoseconds",
48 .type = CWIST_METRIC_COUNTER},
50 {.name = "cwist_http_header_overflow_total",
51 .help = "HTTP/1.1 connections dropped because headers exceeded the read buffer",
52 .type = CWIST_METRIC_COUNTER},
54 {.name = "cwist_h2_headers_dropped_total",
55 .help = "HTTP/2 header fields dropped due to unresolvable HPACK index",
56 .type = CWIST_METRIC_COUNTER},
58 {.name = "cwist_http_continuation_shed_total",
59 .help = "Pipelined HTTP/1.1 continuations dropped due to full reactor queue",
60 .type = CWIST_METRIC_COUNTER},
62 {.name = "cwist_tls_handshakes_total",
63 .help = "TLS handshakes completed (full + resumed)",
64 .type = CWIST_METRIC_COUNTER},
66 {.name = "cwist_tls_handshakes_resumed_total",
67 .help = "TLS handshakes resumed via session ID or ticket",
68 .type = CWIST_METRIC_COUNTER},
70 {.name = "cwist_tls_connections_active",
71 .help = "Currently established TLS connections",
72 .type = CWIST_METRIC_GAUGE},
74 {.name = "cwist_tls_handshakes_tls12_total",
75 .help = "TLS handshakes that negotiated TLS 1.2",
76 .type = CWIST_METRIC_COUNTER},
78 {.name = "cwist_tls_handshakes_tls13_total",
79 .help = "TLS handshakes that negotiated TLS 1.3",
80 .type = CWIST_METRIC_COUNTER},
82 {.name = "cwist_tls_ciphers_aes128_gcm_total",
83 .help = "TLS handshakes negotiated with TLS_AES_128_GCM_SHA256",
84 .type = CWIST_METRIC_COUNTER},
86 {.name = "cwist_tls_ciphers_aes256_gcm_total",
87 .help = "TLS handshakes negotiated with TLS_AES_256_GCM_SHA384",
88 .type = CWIST_METRIC_COUNTER},
90 {.name = "cwist_tls_ciphers_chacha20_total",
91 .help = "TLS handshakes negotiated with TLS_CHACHA20_POLY1305_SHA256",
92 .type = CWIST_METRIC_COUNTER},
94 {.name = "cwist_tls_ciphers_other_total",
95 .help = "TLS handshakes negotiated with any other cipher",
96 .type = CWIST_METRIC_COUNTER},
97};
98
99/* -------------------------------------------------------------------------
100 * Singleton registry
101 * ---------------------------------------------------------------------- */
102
114 if (__builtin_expect(g_reg != NULL, 1)) return g_reg;
115
117 if (!alloc) return NULL;
118 memcpy(alloc->metrics, metric_defaults, sizeof(metric_defaults));
119 for (int i = 0; i < CWIST_METRIC_COUNT; ++i) {
120 atomic_init(&alloc->metrics[i].value.raw, 0);
121 atomic_init(&alloc->metrics[i].count, 0);
122 atomic_init(&alloc->metrics[i].sum, 0);
123 }
124 g_reg = alloc;
125 return g_reg;
126}
127
134 if (!reg) return;
135 for (int i = 0; i < CWIST_METRIC_COUNT; ++i) {
136 atomic_store_explicit(&reg->metrics[i].value.raw, 0, memory_order_relaxed);
137 atomic_store_explicit(&reg->metrics[i].count, 0, memory_order_relaxed);
139 }
140}
141
142/* -------------------------------------------------------------------------
143 * Lock-free updates
144 * ---------------------------------------------------------------------- */
145
146/* Mirror the TLS counters owned by src/net/http/https.c into the registry,
147 * same synchronization contract as the shed counter below. */
148static void cwist_metrics_sync_tls(cwist_metrics_registry_t *reg) {
149 static const struct {
151 long (*read)(void);
152 } tls_counters[] = {
162 };
163 for (size_t i = 0; i < sizeof(tls_counters) / sizeof(tls_counters[0]); ++i) {
165 atomic_store_explicit(&reg->metrics[tls_counters[i].id].value.raw, v * 1000,
168 }
169}
170
184
199
211 if (!reg || id < 0 || id >= CWIST_METRIC_COUNT) return;
212 uintmax_t scaled = (uintmax_t)roundl(value * 1000.0L);
213 atomic_store_explicit(&reg->metrics[id].value.raw, scaled, memory_order_relaxed);
214}
215
229 if (!reg || id < 0 || id >= CWIST_METRIC_COUNT) return;
230 uintmax_t scaled = (uintmax_t)roundl(value * 1000.0L);
231 atomic_fetch_add_explicit(&reg->metrics[id].value.raw, scaled, memory_order_relaxed);
232 atomic_fetch_add_explicit(&reg->metrics[id].count, 1, memory_order_relaxed);
234}
235
255
256/* -------------------------------------------------------------------------
257 * Prometheus exposition
258 * ---------------------------------------------------------------------- */
259
265static const char *type_str(cwist_metric_type_t t) {
266 switch (t) {
267 case CWIST_METRIC_COUNTER: return "counter";
268 case CWIST_METRIC_GAUGE: return "gauge";
269 case CWIST_METRIC_HISTOGRAM: return "histogram";
270 default: return "unknown";
271 }
272}
273
287 if (!reg) return NULL;
288
289 /* Synchronize read-only shed counter from net/http */
297
298 /* Synchronize TLS counters from the HTTPS layer */
299 cwist_metrics_sync_tls((cwist_metrics_registry_t *)reg);
300
301 size_t cap = 4096;
302 char *buf = malloc(cap);
303 if (!buf) return NULL;
304 size_t len = 0;
305
306 for (int i = 0; i < CWIST_METRIC_COUNT; ++i) {
307 const cwist_metric_t *m = &reg->metrics[i];
310
311 int need = snprintf(NULL, 0,
312 "# HELP %s %s\n"
313 "# TYPE %s %s\n"
314 "%s %lu.%03lu\n"
315 "%s_count %lu\n\n",
316 m->name, m->help, m->name, type_str(m->type), m->name,
317 (unsigned long)(raw / 1000), (unsigned long)(raw % 1000), m->name,
318 (unsigned long)cnt);
319 if (need < 0) {
320 free(buf);
321 return NULL;
322 }
323
324 size_t required = (size_t)need + 1;
325 if (required > cap - len) {
326 size_t new_cap = cap;
327 while (required > new_cap - len) new_cap *= 2;
328 char *nb = realloc(buf, new_cap);
329 if (!nb) {
330 free(buf);
331 return NULL;
332 }
333 buf = nb;
334 cap = new_cap;
335 }
336
337 int written = snprintf(buf + len, cap - len,
338 "# HELP %s %s\n"
339 "# TYPE %s %s\n"
340 "%s %lu.%03lu\n"
341 "%s_count %lu\n\n",
342 m->name, m->help, m->name, type_str(m->type), m->name,
343 (unsigned long)(raw / 1000), (unsigned long)(raw % 1000), m->name,
344 (unsigned long)cnt);
345 if (written < 0 || (size_t)written >= cap - len) {
346 free(buf);
347 return NULL;
348 }
349 len += (size_t)written;
350 }
351
352 buf[len] = '\0';
353 return buf;
354}
355
367 if (!res) return;
370 if (!text) {
372 cwist_sstring_assign(res->body, "metrics render failed");
373 return;
374 }
376 cwist_sstring_assign(res->body, text);
377 cwist_http_header_add(&res->headers, "Content-Type",
378 "text/plain; version=0.0.4; charset=utf-8");
379 free(text);
380}
Memory allocation wrappers backed by libttak.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
Definition alloc.c:17
Safe string type with helper methods.
cwist_error_t cwist_sstring_assign(cwist_sstring *str, const char *data)
Assign a C string.
Definition sstring.c:504
@ CWIST_HTTP_INTERNAL_ERROR
Definition http.h:35
@ CWIST_HTTP_OK
Definition http.h:28
cwist_error_t cwist_http_header_add(cwist_http_header_node **head, const char *key, const char *value)
Add a header to the list.
Definition http.c:1861
uintmax_t cwist_metric_load(const cwist_metrics_registry_t *reg, cwist_metric_id_t id)
Read the current raw value of a metric.
Definition metrics.c:247
void cwist_metrics_serve_http(cwist_http_response *res)
HTTP handler that serves the metrics registry as Prometheus text.
Definition metrics.c:366
void cwist_metrics_reset(cwist_metrics_registry_t *reg)
Reset all metrics in a registry to zero.
Definition metrics.c:133
void cwist_metric_set(cwist_metrics_registry_t *reg, cwist_metric_id_t id, long double value)
Set a gauge metric to an absolute value.
Definition metrics.c:210
char * cwist_metrics_render_prometheus(const cwist_metrics_registry_t *reg)
Render the full registry in Prometheus text exposition format.
Definition metrics.c:286
void cwist_metric_inc(cwist_metrics_registry_t *reg, cwist_metric_id_t id)
Increment a metric's value by one and bump its observation count.
Definition metrics.c:179
void cwist_metric_observe(cwist_metrics_registry_t *reg, cwist_metric_id_t id, long double value)
Observe a sample for a metric: accumulate sum and bump the count.
Definition metrics.c:228
void cwist_metric_add(cwist_metrics_registry_t *reg, cwist_metric_id_t id, uintmax_t delta)
Add a delta to a metric's value and bump its observation count.
Definition metrics.c:194
Production-grade metrics engine for CWIST.
cwist_metrics_registry_t * cwist_metrics_registry(void)
Obtain the global registry, creating it on first call.
Definition metrics.c:112
@ CWIST_METRIC_REQUESTS_TOTAL
Definition metrics.h:58
@ CWIST_METRIC_HTTP_HEADER_OVERFLOW
Definition metrics.h:60
@ CWIST_METRIC_TLS_HANDSHAKES_TLS13
Definition metrics.h:68
@ CWIST_METRIC_TLS_CIPHERS_CHACHA20
Definition metrics.h:71
@ CWIST_METRIC_TLS_CIPHERS_AES128_GCM
Definition metrics.h:69
@ CWIST_METRIC_TLS_HANDSHAKES_TLS12
Definition metrics.h:67
@ CWIST_METRIC_STREAMS_HTTP3
Definition metrics.h:55
@ CWIST_METRIC_URING_BUFFERS_ACTIVE
Definition metrics.h:53
@ CWIST_METRIC_VALIDATION_ERRORS
Definition metrics.h:57
@ CWIST_METRIC_TLS_CONNECTIONS_ACTIVE
Definition metrics.h:66
@ CWIST_METRIC_REQUEST_DURATION_NS
Definition metrics.h:59
@ CWIST_METRIC_H2_HEADERS_DROPPED
Definition metrics.h:61
@ CWIST_METRIC_FLOW_WINDOW_HEALTH
Definition metrics.h:56
@ CWIST_METRIC_STREAMS_HTTP2
Definition metrics.h:54
@ CWIST_METRIC_TLS_CIPHERS_OTHER
Definition metrics.h:72
@ CWIST_METRIC_TLS_CIPHERS_AES256_GCM
Definition metrics.h:70
@ CWIST_METRIC_TLS_HANDSHAKES_TOTAL
Definition metrics.h:64
@ CWIST_METRIC_HTTP_CONTINUATION_SHED
Definition metrics.h:62
@ CWIST_METRIC_URING_SQES_PENDING
Definition metrics.h:52
@ CWIST_METRIC_TLS_HANDSHAKES_RESUMED
Definition metrics.h:65
@ CWIST_METRIC_COUNT
Definition metrics.h:73
enum cwist_metric_id cwist_metric_id_t
Pre-defined framework metrics.
struct cwist_metrics_registry cwist_metrics_registry_t
Global metrics registry (singleton, lazy-initialised).
Definition metrics.h:87
@ CWIST_METRIC_GAUGE
Definition metrics.h:31
@ CWIST_METRIC_HISTOGRAM
Definition metrics.h:32
@ CWIST_METRIC_COUNTER
Definition metrics.h:30
enum cwist_metric_type cwist_metric_type_t
Supported Prometheus metric kinds.
HTTP Protocol Definitions and Helpers.
long cwist_http_continuation_shed_count(void)
Definition http.c:293
HTTPS/TLS wrapper interface.
long cwist_https_tls_ciphers_aes128_gcm_total(void)
Handshakes negotiated with TLS_AES_128_GCM_SHA256.
Definition https.c:1613
long cwist_https_tls_handshakes_total(void)
Total TLS handshakes that completed (full + resumed).
Definition https.c:1597
long cwist_https_tls_ciphers_other_total(void)
Handshakes negotiated with any other cipher.
Definition https.c:1625
long cwist_https_tls_handshakes_tls12_total(void)
Handshakes that negotiated TLS 1.2.
Definition https.c:1605
long cwist_https_tls_ciphers_chacha20_total(void)
Handshakes negotiated with TLS_CHACHA20_POLY1305_SHA256.
Definition https.c:1621
long cwist_https_tls_handshakes_resumed_total(void)
TLS handshakes completed via session resumption/ticket.
Definition https.c:1601
long cwist_https_tls_handshakes_tls13_total(void)
Handshakes that negotiated TLS 1.3.
Definition https.c:1609
long cwist_https_tls_connections_active(void)
Currently established TLS connections (wrap minus teardown).
Definition https.c:1629
long cwist_https_tls_ciphers_aes256_gcm_total(void)
Handshakes negotiated with TLS_AES_256_GCM_SHA384.
Definition https.c:1617
size_t count
Definition otel.c:58
size_t cap
Definition otel.c:56
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
Definition http.h:82
cwist_sstring * body
Definition http.h:87
cwist_http_status_t status_code
Definition http.h:84
cwist_http_header_node * headers
Definition http.h:86
Single metric definition.
Definition metrics.h:41
const char * name
Definition metrics.h:42