commit
92ac9b056f
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum epoll_tag
|
||||
{
|
||||
EPOLL_TAG_INVALID = 0,
|
||||
EPOLL_TAG_TENANT_SERVER_SOCKET = 1,
|
||||
EPOLL_TAG_METRICS_SERVER_SOCKET,
|
||||
EPOLL_TAG_HTTP_SESSION_CLIENT_SOCKET,
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
|
||||
struct http_route_total {
|
||||
atomic_ulong total_requests;
|
||||
atomic_ulong total_2XX;
|
||||
atomic_ulong total_4XX;
|
||||
atomic_ulong total_5XX;
|
||||
};
|
||||
#else
|
||||
struct http_route_total {
|
||||
};
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
http_route_total_init(struct http_route_total *rm)
|
||||
{
|
||||
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
|
||||
atomic_init(&rm->total_requests, 0);
|
||||
atomic_init(&rm->total_2XX, 0);
|
||||
atomic_init(&rm->total_4XX, 0);
|
||||
atomic_init(&rm->total_5XX, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
http_route_total_increment_request(struct http_route_total *rm)
|
||||
{
|
||||
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
|
||||
atomic_fetch_add(&rm->total_requests, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
http_route_total_increment(struct http_route_total *rm, int status_code)
|
||||
{
|
||||
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
|
||||
if (status_code >= 200 && status_code <= 299) {
|
||||
atomic_fetch_add(&rm->total_2XX, 1);
|
||||
} else if (status_code >= 400 && status_code <= 499) {
|
||||
atomic_fetch_add(&rm->total_4XX, 1);
|
||||
} else if (status_code >= 500 && status_code <= 599) {
|
||||
atomic_fetch_add(&rm->total_5XX, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "epoll_tag.h"
|
||||
#include "tcp_server.h"
|
||||
|
||||
struct metrics_server {
|
||||
enum epoll_tag tag;
|
||||
struct tcp_server tcp;
|
||||
pthread_attr_t thread_settings;
|
||||
};
|
||||
|
||||
|
||||
extern struct metrics_server metrics_server;
|
||||
|
||||
void metrics_server_init();
|
||||
void metrics_server_thread_spawn(int client_socket);
|
||||
int metrics_server_close();
|
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "runtime.h" /* For runtime_pid */
|
||||
|
||||
/* Used to read process-level metrics associated with sledgert from procfs
|
||||
* The parsing behavior is based on prtstat -r
|
||||
*/
|
||||
|
||||
|
||||
enum PROC_STAT
|
||||
{
|
||||
PROC_STAT_PID = 0, /* Process ID */
|
||||
PROC_STAT_COMM = 1, /* Process Name */
|
||||
PROC_STAT_STATE = 2, /* State */
|
||||
PROC_STAT_PPID, /* Parent Process ID */
|
||||
PROC_STAT_PGRP, /* Group ID */
|
||||
PROC_STAT_SESSION, /* Session ID */
|
||||
PROC_STAT_TTY_NR, /* ??? */
|
||||
PROC_STAT_TPGID, /* ??? */
|
||||
PROC_STAT_FLAGS, /* ??? */
|
||||
PROC_STAT_MINFLT, /* Minor Page Faults */
|
||||
PROC_STAT_CMINFLT, /* Minor Page Faults of children */
|
||||
PROC_STAT_MAJFLT, /* Major Page Faults */
|
||||
PROC_STAT_CMAJFLT, /* Major Page Faults of children */
|
||||
PROC_STAT_UTIME, /* User Time */
|
||||
PROC_STAT_STIME, /* System Time */
|
||||
PROC_STAT_CUTIME, /* Child User Time */
|
||||
PROC_STAT_CSTIME, /* Child System Time */
|
||||
PROC_STAT_PRIORITY,
|
||||
PROC_STAT_NICE,
|
||||
PROC_STAT_NUM_THREADS,
|
||||
PROC_STAT_ITREALVALUE,
|
||||
PROC_STAT_STARTTIME, /* Start Time */
|
||||
PROC_STAT_VSIZE, /* Virtual Memory */
|
||||
PROC_STAT_RSS,
|
||||
PROC_STAT_RSSLIM,
|
||||
PROC_STAT_STARTCODE,
|
||||
PROC_STAT_ENDCODE,
|
||||
PROC_STAT_STARTSTACK,
|
||||
PROC_STAT_KSTKESP,
|
||||
PROC_STAT_KSTKEIP,
|
||||
PROC_STAT_WCHAN,
|
||||
PROC_STAT_NSWAP,
|
||||
PROC_STAT_CNSWAP,
|
||||
PROC_STAT_EXIT_SIGNAL,
|
||||
PROC_STAT_PROCESSOR,
|
||||
PROC_STAT_RT_PRIORITY,
|
||||
PROC_STAT_POLICY,
|
||||
PROC_STAT_DELAYACCR_BLKIO_TICKS,
|
||||
PROC_STAT_GUEST_TIME,
|
||||
PROC_STAT_CGUEST_TIME,
|
||||
PROC_STAT_COUNT
|
||||
};
|
||||
|
||||
struct proc_stat_metrics {
|
||||
uint64_t minor_page_faults;
|
||||
uint64_t major_page_faults;
|
||||
uint64_t child_minor_page_faults;
|
||||
uint64_t child_major_page_faults;
|
||||
uint64_t user_time;
|
||||
uint64_t system_time;
|
||||
uint64_t guest_time;
|
||||
};
|
||||
|
||||
static inline void
|
||||
proc_stat_metrics_init(struct proc_stat_metrics *stat)
|
||||
{
|
||||
assert(runtime_pid > 0);
|
||||
|
||||
// Open sledgert's stat file in procfs
|
||||
char path[256];
|
||||
snprintf(path, 256, "/proc/%d/stat", runtime_pid);
|
||||
FILE *proc_stat = fopen(path, "r");
|
||||
|
||||
/* Read stat file into in-memory buffer */
|
||||
char buf[BUFSIZ];
|
||||
fgets(buf, BUFSIZ, proc_stat);
|
||||
fclose(proc_stat);
|
||||
|
||||
/* Parse into an array of tokens with indices aligning to the PROC_STAT enum */
|
||||
char *pos = NULL;
|
||||
char *proc_stat_values[PROC_STAT_COUNT];
|
||||
for (int i = 0; i < PROC_STAT_COUNT; i++) {
|
||||
char *tok = i == 0 ? strtok_r(buf, " ", &pos) : strtok_r(NULL, " ", &pos);
|
||||
proc_stat_values[i] = tok;
|
||||
}
|
||||
|
||||
/* Fill the proc_state_metrics struct with metrics of interest */
|
||||
/* Minor Page Faults, Major Page Faults, Vsize, User, System, Guest, Uptime */
|
||||
stat->minor_page_faults = strtoul(proc_stat_values[PROC_STAT_MINFLT], NULL, 10);
|
||||
stat->major_page_faults = strtoul(proc_stat_values[PROC_STAT_MAJFLT], NULL, 10);
|
||||
stat->child_minor_page_faults = strtoul(proc_stat_values[PROC_STAT_CMINFLT], NULL, 10);
|
||||
stat->child_major_page_faults = strtoul(proc_stat_values[PROC_STAT_CMAJFLT], NULL, 10);
|
||||
stat->user_time = strtoul(proc_stat_values[PROC_STAT_UTIME], NULL, 10);
|
||||
stat->system_time = strtoul(proc_stat_values[PROC_STAT_STIME], NULL, 10);
|
||||
stat->guest_time = strtoul(proc_stat_values[PROC_STAT_GUEST_TIME], NULL, 10);
|
||||
}
|
@ -0,0 +1,228 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "admissions_control.h"
|
||||
#include "debuglog.h"
|
||||
#include "http.h"
|
||||
#include "http_total.h"
|
||||
#include "metrics_server.h"
|
||||
#include "proc_stat.h"
|
||||
#include "runtime.h"
|
||||
#include "sandbox_total.h"
|
||||
#include "sandbox_state.h"
|
||||
#include "tcp_server.h"
|
||||
|
||||
/* We run threads on the "reserved OS core" using blocking semantics */
|
||||
#define METRICS_SERVER_CORE_ID 0
|
||||
#define METRICS_SERVER_PORT 1776
|
||||
|
||||
struct metrics_server metrics_server;
|
||||
static void *metrics_server_handler(void *arg);
|
||||
|
||||
extern void metrics_server_route_level_metrics_render(FILE *ostream);
|
||||
|
||||
void
|
||||
metrics_server_init()
|
||||
{
|
||||
metrics_server.tag = EPOLL_TAG_METRICS_SERVER_SOCKET;
|
||||
tcp_server_init(&metrics_server.tcp, METRICS_SERVER_PORT);
|
||||
int rc = tcp_server_listen(&metrics_server.tcp);
|
||||
assert(rc == 0);
|
||||
|
||||
/* Configure pthread attributes to pin metrics server threads to CPU 0 */
|
||||
pthread_attr_init(&metrics_server.thread_settings);
|
||||
cpu_set_t cs;
|
||||
CPU_ZERO(&cs);
|
||||
CPU_SET(METRICS_SERVER_CORE_ID, &cs);
|
||||
pthread_attr_setaffinity_np(&metrics_server.thread_settings, sizeof(cpu_set_t), &cs);
|
||||
}
|
||||
|
||||
int
|
||||
metrics_server_close()
|
||||
{
|
||||
return tcp_server_close(&metrics_server.tcp);
|
||||
}
|
||||
|
||||
void
|
||||
metrics_server_thread_spawn(int client_socket)
|
||||
{
|
||||
/* Duplicate fd so fclose doesn't close the actual client_socket */
|
||||
int temp_fd = dup(client_socket);
|
||||
FILE *req_body = fdopen(temp_fd, "r");
|
||||
|
||||
/* Basic L7 routing to filter out favicon requests */
|
||||
char http_status_code_buf[256];
|
||||
fgets(http_status_code_buf, 256, req_body);
|
||||
fclose(req_body);
|
||||
|
||||
if (strncmp(http_status_code_buf, "GET /metrics HTTP", 10) != 0) {
|
||||
write(client_socket, http_header_build(404), http_header_len(404));
|
||||
close(client_socket);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fire and forget, so we don't save the thread handles */
|
||||
pthread_t metrics_server_thread;
|
||||
int rc = pthread_create(&metrics_server_thread, &metrics_server.thread_settings, metrics_server_handler,
|
||||
(void *)(long)client_socket);
|
||||
|
||||
if (rc != 0) {
|
||||
debuglog("Metrics Server failed to spawn pthread with %s\n", strerror(rc));
|
||||
close(client_socket);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
metrics_server_handler(void *arg)
|
||||
{
|
||||
/* Intermediate cast to integral value of 64-bit width to silence compiler nits */
|
||||
int client_socket = (int)(long)arg;
|
||||
|
||||
int rc = 0;
|
||||
|
||||
char *ostream_base = NULL;
|
||||
size_t ostream_size = 0;
|
||||
FILE *ostream = open_memstream(&ostream_base, &ostream_size);
|
||||
assert(ostream != NULL);
|
||||
|
||||
#ifdef HTTP_TOTAL_COUNTERS
|
||||
uint32_t total_reqs = atomic_load(&http_total_requests);
|
||||
uint32_t total_5XX = atomic_load(&http_total_5XX);
|
||||
uint32_t total_2XX = atomic_load(&http_total_2XX);
|
||||
uint32_t total_4XX = atomic_load(&http_total_4XX);
|
||||
#endif
|
||||
|
||||
uint32_t total_sandboxes = atomic_load(&sandbox_total);
|
||||
|
||||
#ifdef SANDBOX_STATE_TOTALS
|
||||
uint32_t total_sandboxes_uninitialized = atomic_load(&sandbox_state_totals[SANDBOX_UNINITIALIZED]);
|
||||
uint32_t total_sandboxes_allocated = atomic_load(&sandbox_state_totals[SANDBOX_ALLOCATED]);
|
||||
uint32_t total_sandboxes_initialized = atomic_load(&sandbox_state_totals[SANDBOX_INITIALIZED]);
|
||||
uint32_t total_sandboxes_runnable = atomic_load(&sandbox_state_totals[SANDBOX_RUNNABLE]);
|
||||
uint32_t total_sandboxes_preempted = atomic_load(&sandbox_state_totals[SANDBOX_PREEMPTED]);
|
||||
uint32_t total_sandboxes_running_sys = atomic_load(&sandbox_state_totals[SANDBOX_RUNNING_SYS]);
|
||||
uint32_t total_sandboxes_running_user = atomic_load(&sandbox_state_totals[SANDBOX_RUNNING_USER]);
|
||||
uint32_t total_sandboxes_interrupted = atomic_load(&sandbox_state_totals[SANDBOX_INTERRUPTED]);
|
||||
uint32_t total_sandboxes_asleep = atomic_load(&sandbox_state_totals[SANDBOX_ASLEEP]);
|
||||
uint32_t total_sandboxes_returned = atomic_load(&sandbox_state_totals[SANDBOX_RETURNED]);
|
||||
uint32_t total_sandboxes_complete = atomic_load(&sandbox_state_totals[SANDBOX_COMPLETE]);
|
||||
uint32_t total_sandboxes_error = atomic_load(&sandbox_state_totals[SANDBOX_ERROR]);
|
||||
#endif
|
||||
|
||||
#ifdef ADMISSIONS_CONTROL
|
||||
uint32_t work_admitted = atomic_load(&admissions_control_admitted);
|
||||
double work_admitted_percentile = (double)work_admitted / admissions_control_capacity * 100;
|
||||
#endif
|
||||
|
||||
#ifdef PROC_STAT_METRICS
|
||||
struct proc_stat_metrics stat;
|
||||
proc_stat_metrics_init(&stat);
|
||||
#endif
|
||||
|
||||
fprintf(ostream, "HTTP/1.1 200 OK\r\n\r\n");
|
||||
|
||||
#ifdef PROC_STAT_METRICS
|
||||
fprintf(ostream, "# TYPE os_proc_major_page_faults counter\n");
|
||||
fprintf(ostream, "os_proc_major_page_faults: %lu\n", stat.major_page_faults);
|
||||
|
||||
fprintf(ostream, "# TYPE os_proc_minor_page_faults counter\n");
|
||||
fprintf(ostream, "os_proc_minor_page_faults: %lu\n", stat.minor_page_faults);
|
||||
|
||||
fprintf(ostream, "# TYPE os_proc_child_major_page_faults counter\n");
|
||||
fprintf(ostream, "os_proc_child_major_page_faults: %lu\n", stat.child_major_page_faults);
|
||||
|
||||
fprintf(ostream, "# TYPE os_proc_child_minor_page_faults counter\n");
|
||||
fprintf(ostream, "os_proc_child_minor_page_faults: %lu\n", stat.child_minor_page_faults);
|
||||
|
||||
fprintf(ostream, "# TYPE os_proc_user_time counter\n");
|
||||
fprintf(ostream, "os_proc_user_time: %lu\n", stat.user_time);
|
||||
|
||||
fprintf(ostream, "# TYPE os_proc_sys_time counter\n");
|
||||
fprintf(ostream, "os_proc_sys_time: %lu\n", stat.system_time);
|
||||
|
||||
fprintf(ostream, "# TYPE os_proc_guest_time counter\n");
|
||||
fprintf(ostream, "os_proc_guest_time: %lu\n", stat.guest_time);
|
||||
#endif /* PROC_STAT_METRICS */
|
||||
|
||||
#ifdef ADMISSIONS_CONTROL
|
||||
fprintf(ostream, "# TYPE work_admitted_percentile gauge\n");
|
||||
fprintf(ostream, "work_admitted_percentile: %f\n", work_admitted_percentile);
|
||||
#endif
|
||||
|
||||
#ifdef HTTP_TOTAL_COUNTERS
|
||||
fprintf(ostream, "# TYPE total_requests counter\n");
|
||||
fprintf(ostream, "total_requests: %d\n", total_reqs);
|
||||
|
||||
fprintf(ostream, "# TYPE total_2XX counter\n");
|
||||
fprintf(ostream, "total_2XX: %d\n", total_2XX);
|
||||
|
||||
fprintf(ostream, "# TYPE total_4XX counter\n");
|
||||
fprintf(ostream, "total_4XX: %d\n", total_4XX);
|
||||
|
||||
fprintf(ostream, "# TYPE total_5XX counter\n");
|
||||
fprintf(ostream, "total_5XX: %d\n", total_5XX);
|
||||
#endif
|
||||
|
||||
metrics_server_route_level_metrics_render(ostream);
|
||||
|
||||
// This global is padded by 1 for error handling, so decrement here for true value
|
||||
fprintf(ostream, "# TYPE total_sandboxes counter\n");
|
||||
fprintf(ostream, "total_sandboxes: %d\n", total_sandboxes - 1);
|
||||
|
||||
#ifdef SANDBOX_STATE_TOTALS
|
||||
fprintf(ostream, "# TYPE total_sandboxes_uninitialized gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_uninitialized: %d\n", total_sandboxes_uninitialized);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_allocated gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_allocated: %d\n", total_sandboxes_allocated);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_initialized gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_initialized: %d\n", total_sandboxes_initialized);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_runnable gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_runnable: %d\n", total_sandboxes_runnable);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_preempted gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_preempted: %d\n", total_sandboxes_preempted);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_running_sys gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_running_sys: %d\n", total_sandboxes_running_sys);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_running_user gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_running_user: %d\n", total_sandboxes_running_user);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_interrupted gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_interrupted: %d\n", total_sandboxes_interrupted);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_asleep gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_asleep: %d\n", total_sandboxes_asleep);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_returned gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_returned: %d\n", total_sandboxes_returned);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_complete gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_complete: %d\n", total_sandboxes_complete);
|
||||
|
||||
fprintf(ostream, "# TYPE total_sandboxes_error gauge\n");
|
||||
fprintf(ostream, "total_sandboxes_error: %d\n", total_sandboxes_error);
|
||||
#endif
|
||||
|
||||
fflush(ostream);
|
||||
assert(ostream_size > 0);
|
||||
rc = fclose(ostream);
|
||||
assert(rc == 0);
|
||||
|
||||
/* Closing the memstream does not close the generated buffer */
|
||||
ssize_t nwritten = write(client_socket, ostream_base, ostream_size);
|
||||
assert(nwritten == ostream_size);
|
||||
|
||||
free(ostream_base);
|
||||
ostream_size = 0;
|
||||
|
||||
close(client_socket);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "perf_window.h"
|
||||
#include "tenant_functions.h"
|
||||
|
||||
static const int p50_idx = PERF_WINDOW_CAPACITY * 50 / 100;
|
||||
static const int p90_idx = PERF_WINDOW_CAPACITY * 90 / 100;
|
||||
|
||||
void
|
||||
render_routes(struct route *route, void *arg_one, void *arg_two)
|
||||
{
|
||||
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
|
||||
FILE *ostream = (FILE *)arg_one;
|
||||
struct tenant *tenant = (struct tenant *)arg_two;
|
||||
|
||||
#ifdef ADMISSIONS_CONTROL
|
||||
uint64_t latency_p50 = perf_window_get_percentile(&route->admissions_info.perf_window, 50, p50_idx);
|
||||
uint64_t latency_p90 = perf_window_get_percentile(&route->admissions_info.perf_window, 90, p90_idx);
|
||||
#endif
|
||||
|
||||
uint64_t total_requests = atomic_load(&route->metrics.total_requests);
|
||||
uint64_t total_2XX = atomic_load(&route->metrics.total_2XX);
|
||||
uint64_t total_4XX = atomic_load(&route->metrics.total_4XX);
|
||||
uint64_t total_5XX = atomic_load(&route->metrics.total_5XX);
|
||||
|
||||
// Strip leading /
|
||||
const char *route_label = &route->route[1];
|
||||
|
||||
fprintf(ostream, "# TYPE %s_%s_total_requests counter\n", tenant->name, route_label);
|
||||
fprintf(ostream, "%s_%s_total_requests: %lu\n", tenant->name, route_label, total_requests);
|
||||
|
||||
fprintf(ostream, "# TYPE %s_%s_total_2XX counter\n", tenant->name, route_label);
|
||||
fprintf(ostream, "%s_%s_total_2XX: %lu\n", tenant->name, route_label, total_2XX);
|
||||
|
||||
fprintf(ostream, "# TYPE %s_%s_total_4XX counter\n", tenant->name, route_label);
|
||||
fprintf(ostream, "%s_%s_total_4XX: %lu\n", tenant->name, route_label, total_4XX);
|
||||
|
||||
fprintf(ostream, "# TYPE %s_%s_total_5XX counter\n", tenant->name, route_label);
|
||||
fprintf(ostream, "%s_%s_total_5XX: %lu\n", tenant->name, route_label, total_5XX);
|
||||
|
||||
#ifdef ADMISSIONS_CONTROL
|
||||
fprintf(ostream, "# TYPE %s_%s_latency_p50 gauge\n", tenant->name, route_label);
|
||||
fprintf(ostream, "%s_%s_latency_p50: %lu\n", tenant->name, route_label, latency_p50);
|
||||
|
||||
fprintf(ostream, "# TYPE %s_%s_latency_p90 gauge\n", tenant->name, route_label);
|
||||
fprintf(ostream, "%s_%s_latency_p90: %lu\n", tenant->name, route_label, latency_p90);
|
||||
#endif
|
||||
|
||||
#endif /* HTTP_ROUTE_TOTAL_COUNTERS */
|
||||
}
|
||||
|
||||
void
|
||||
render_tenant_routers(struct tenant *tenant, void *arg_one, void *arg_two)
|
||||
{
|
||||
FILE *ostream = (FILE *)arg_one;
|
||||
char *name = tenant->name;
|
||||
|
||||
http_router_foreach(&tenant->router, render_routes, ostream, tenant);
|
||||
}
|
||||
|
||||
void
|
||||
metrics_server_route_level_metrics_render(FILE *ostream)
|
||||
{
|
||||
#ifdef HTTP_ROUTE_TOTAL_COUNTERS
|
||||
tenant_database_foreach(render_tenant_routers, ostream, NULL);
|
||||
#endif
|
||||
}
|
Loading…
Reference in new issue