Skip to content

Instantly share code, notes, and snippets.

@markscottwright
Created October 24, 2023 13:10
Show Gist options
  • Save markscottwright/3ad5fa769d56d34d9dbef54068af9b35 to your computer and use it in GitHub Desktop.
Save markscottwright/3ad5fa769d56d34d9dbef54068af9b35 to your computer and use it in GitHub Desktop.
Apache module that exports metrics in Prometheus format
/*
* Need to install APR and HTTPD source
* https://github.com/apache/apr
* https://github.com/apache/httpd
* Build with `apxs -c -a mod_metrics.c`
* Install with:
* `sudo /PATH/TO/libtool --mode=install install mod_metrics.la /PATH/TO/modules/`
*/
#include "httpd.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
#include "ap_mpm.h"
static void register_hooks(apr_pool_t *pool);
static int metrics_handler(request_rec *r);
module AP_MODULE_DECLARE_DATA metrics_module =
{
STANDARD20_MODULE_STUFF,
NULL, // Per-directory configuration handler
NULL, // Merge handler for per-directory configurations
NULL, // Per-server configuration handler
NULL, // Merge handler for per-server configurations
NULL, // Any directives we may have for httpd
register_hooks // Our hook registering function
};
/* register_hooks: Adds a hook to the httpd process */
static void register_hooks(apr_pool_t *pool)
{
ap_hook_handler(metrics_handler, NULL, NULL, APR_HOOK_LAST);
}
/* return metrics in Prometheus-friendly format */
static int metrics_handler(request_rec *r)
{
apr_off_t total_request_count, total_bytes_served;
apr_uint32_t up_time_seconds;
apr_time_t total_duration;
int server_limit, thread_limit;
int server_index, thread_index;
int idle_worker_count, busy_worker_count, process_count;
worker_score *scoreboard_worker_info;
ap_loadavg_t load_averages;
/* request isn't for us, break out */
if (!r->handler || strcmp(r->handler, "metrics-handler") != 0) {
return DECLINED;
}
/* iterate through workers and count totals */
total_duration = total_request_count = total_bytes_served = 0;
busy_worker_count = idle_worker_count = 0;
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
scoreboard_worker_info = apr_palloc(r->pool, sizeof *scoreboard_worker_info);
for (server_index = 0; server_index < server_limit; ++server_index) {
for (thread_index = 0; thread_index < thread_limit; ++thread_index) {
ap_copy_scoreboard_worker(scoreboard_worker_info, server_index, thread_index);
total_bytes_served += scoreboard_worker_info->bytes_served;
total_request_count += scoreboard_worker_info->access_count;
total_duration += scoreboard_worker_info->duration;
switch (scoreboard_worker_info->status) {
case SERVER_READY:
case SERVER_IDLE_KILL:
idle_worker_count++;
break;
case SERVER_BUSY_READ:
case SERVER_BUSY_WRITE:
case SERVER_BUSY_KEEPALIVE:
case SERVER_BUSY_LOG:
case SERVER_BUSY_DNS:
case SERVER_CLOSING:
case SERVER_GRACEFUL:
busy_worker_count++;
break;
}
}
}
ap_get_loadavg(&load_averages);
up_time_seconds = (apr_uint32_t) apr_time_sec(apr_time_now() - ap_scoreboard_image->global->restart_time);
ap_rprintf(r, "# HELP apache_uptime_seconds Uptime\n");
ap_rprintf(r, "# TYPE apache_uptime_seconds guage\n");
ap_rprintf(r, "apache_uptime_seconds %u\n", up_time_seconds);
ap_rprintf(r, "# HELP apache_total_duration_milliseconds Total time spend servicing requests\n");
ap_rprintf(r, "# TYPE apache_total_duration_milliseconds guage\n");
ap_rprintf(r, "apache_total_duration_milliseconds %" APR_TIME_T_FMT "\n", apr_time_as_msec(total_duration));
ap_rprintf(r, "# HELP apache_total_bytes_served Total bytes sent\n");
ap_rprintf(r, "# TYPE apache_total_bytes_served guage\n");
ap_rprintf(r, "apache_total_bytes_served %" APR_OFF_T_FMT "\n", total_bytes_served);
ap_rprintf(r, "# HELP apache_total_request_count Total number of requests handled\n");
ap_rprintf(r, "# TYPE apache_total_request_count guage\n");
ap_rprintf(r, "apache_total_request_count %" APR_OFF_T_FMT "\n", total_request_count);
ap_rprintf(r, "# HELP apache_load_average Server load average currently and over last 5 and 15 minutes\n");
ap_rprintf(r, "# TYPE apache_load_average guage\n");
ap_rprintf(r, "apache_load_average{period=\"now\"} %f\n", load_averages.loadavg);
ap_rprintf(r, "apache_load_average{period=\"5 min\"} %f\n", load_averages.loadavg5);
ap_rprintf(r, "apache_load_average{period=\"15 min\"} %f\n", load_averages.loadavg15);
ap_rprintf(r, "# HELP apache_worker_count Number of worker threads busy and idle\n");
ap_rprintf(r, "# TYPE apache_worker_count guage\n");
ap_rprintf(r, "apache_worker_count{status=\"busy\"} %d\n", busy_worker_count);
ap_rprintf(r, "apache_worker_count{status=\"idle\"} %d\n", idle_worker_count);
return OK;
}
@markscottwright
Copy link
Author

Output is something like:

# HELP apache_uptime_seconds Uptime
# TYPE apache_uptime_seconds guage
apache_uptime_seconds 2995
# HELP apache_total_duration_milliseconds Total time spend servicing requests
# TYPE apache_total_duration_milliseconds guage
apache_total_duration_milliseconds 186715
# HELP apache_total_bytes_served Total bytes sent
# TYPE apache_total_bytes_served guage
apache_total_bytes_served 1041136336
# HELP apache_total_request_count Total number of requests handled
# TYPE apache_total_request_count guage
apache_total_request_count 1084314
# HELP apache_load_average Server load average currently and over last 5 and 15 minutes
# TYPE apache_load_average guage
apache_load_average{period="now"} 9.012207
apache_load_average{period="5 min"} 9.095215
apache_load_average{period="15 min"} 8.160645
# HELP apache_worker_count Number of worker threads busy and idle
# TYPE apache_worker_count guage
apache_worker_count{status="busy"} 1
apache_worker_count{status="idle"} 99

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment