]> source.dussan.org Git - rspamd.git/commitdiff
Implement an endpoint for OpenMetrics 3754/head
authorManuel Rüger <manuel@rueg.eu>
Thu, 13 May 2021 23:37:00 +0000 (01:37 +0200)
committerManuel Rüger <manuel@rueg.eu>
Mon, 17 May 2021 16:34:28 +0000 (18:34 +0200)
Now /metrics provides an endpoint that is openmetrics compatible.

src/controller.c
src/libserver/worker_util.c
src/libserver/worker_util.h

index 0ecaf860d6e45b68fefa228eccaf892a059b85e3..ce4d9b21b3be309bb930bfbfe1c0fbe2969271a2 100644 (file)
@@ -50,6 +50,7 @@
 #define PATH_HISTORY_RESET "/historyreset"
 #define PATH_LEARN_SPAM "/learnspam"
 #define PATH_LEARN_HAM "/learnham"
+#define PATH_METRICS "/metrics"
 #define PATH_SAVE_ACTIONS "/saveactions"
 #define PATH_SAVE_SYMBOLS "/savesymbols"
 #define PATH_SAVE_MAP "/savemap"
@@ -2717,6 +2718,278 @@ rspamd_controller_handle_statreset (
        return rspamd_controller_handle_stat_common (conn_ent, msg, TRUE);
 }
 
+/*
+ * Metrics command handler:
+ * request: /metrics
+ * headers: Password
+ * reply: OpenMetrics
+ */
+
+static gboolean
+rspamd_controller_metrics_fin_task (void *ud) {
+       struct rspamd_stat_cbdata *cbdata = ud;
+       struct rspamd_http_connection_entry *conn_ent;
+       ucl_object_t *top;
+       GList *fuzzy_elts, *cur;
+       struct rspamd_fuzzy_stat_entry *entry;
+       gint i;
+
+       conn_ent = cbdata->conn_ent;
+       top = cbdata->top;
+
+       ucl_object_insert_key (top,
+                       ucl_object_fromint (cbdata->learned), "total_learns", 0, false);
+
+       GString* output = g_string_new ("");
+       g_string_append_printf (output, "build_info{version=\"%s\"} 1\n",
+               ucl_object_tostring (ucl_object_lookup (top, "version")));
+       g_string_append_printf (output, "config{id=\"%s\"} 1\n",
+               ucl_object_tostring (ucl_object_lookup (top, "config_id")));
+       g_string_append_printf (output, "process_start_time_seconds %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "start_time")));
+       g_string_append_printf (output, "read_only %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "read_only")));
+       g_string_append_printf (output, "scanned %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "scanned")));
+       g_string_append_printf (output, "learned %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "learned")));
+       g_string_append_printf (output, "spam_count %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "spam_count")));
+       g_string_append_printf (output, "ham_count %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "ham_count")));
+       g_string_append_printf (output, "connections %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "connections")));
+       g_string_append_printf (output, "control_connections %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "control_connections")));
+       g_string_append_printf (output, "pools_allocated %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "pools_allocated")));
+       g_string_append_printf (output, "pools_freed %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "pools_freed")));
+       g_string_append_printf (output, "bytes_allocated %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "bytes_allocated")));
+       g_string_append_printf (output, "chunks_allocated %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "chunks_allocated")));
+       g_string_append_printf (output, "shared_chunks_allocated %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "shared_chunks_allocated")));
+       g_string_append_printf (output, "chunks_freed %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "chunks_freed")));
+       g_string_append_printf (output, "chunks_oversized %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "chunks_oversized")));
+       g_string_append_printf (output, "fragmented %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "fragmented")));
+       g_string_append_printf (output, "total_learns %" PRId64 "\n",
+               ucl_object_toint (ucl_object_lookup (top, "total_learns")));
+       for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
+               gchar* path = malloc (strlen (rspamd_action_to_str(i)));
+               g_string_append_printf (output, "actions{type=\"%s\"} %" PRId64 "\n",
+               rspamd_action_to_str (i),
+               ucl_object_toint (ucl_object_lookup_path (top, path)));
+               g_free (path);
+       }
+
+       if (cbdata->stat) {
+               const ucl_object_t *cur_elt;
+        ucl_object_iter_t it = NULL;
+        while ((cur_elt = ucl_object_iterate (cbdata->stat, &it, true))) {
+                       if (ucl_object_lookup_path (cur_elt, "symbol") && ucl_object_lookup_path (cur_elt, "type")) {
+                               g_string_append_printf (output, "statfiles_revision{symbol=\"%s\", type=\"%s\"} %" PRId64 "\n",
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "symbol")),
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "type")),
+                                       ucl_object_toint (ucl_object_lookup_path (cur_elt, "revision")));
+                               g_string_append_printf (output, "statfiles_used{symbol=\"%s\", type=\"%s\"} %" PRId64 "\n",
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "symbol")),
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "type")),
+                                       ucl_object_toint (ucl_object_lookup_path (cur_elt, "used")));
+                               g_string_append_printf (output, "statfiles_total{symbol=\"%s\", type=\"%s\"} %" PRId64 "\n",
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "symbol")),
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "type")),
+                                       ucl_object_toint (ucl_object_lookup_path (cur_elt, "total")));
+                               g_string_append_printf (output, "statfiles_size{symbol=\"%s\", type=\"%s\"} %" PRId64 "\n",
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "symbol")),
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "type")),
+                                       ucl_object_toint (ucl_object_lookup_path (cur_elt, "size")));
+                               g_string_append_printf (output, "statfiles_languages{symbol=\"%s\", type=\"%s\"} %" PRId64 "\n",
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "symbol")),
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "type")),
+                                       ucl_object_toint (ucl_object_lookup_path (cur_elt, "languages")));
+                               g_string_append_printf (output, "statfiles_users{symbol=\"%s\", type=\"%s\"} %" PRId64 "\n",
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "symbol")),
+                                       ucl_object_tostring (ucl_object_lookup_path (cur_elt, "type")),
+                                       ucl_object_toint (ucl_object_lookup_path (cur_elt, "users")));
+                       }
+               }
+       }
+
+
+       fuzzy_elts = rspamd_mempool_get_variable (cbdata->task->task_pool, "fuzzy_stat");
+
+       if (fuzzy_elts) {
+               for (cur = fuzzy_elts; cur != NULL; cur = g_list_next (cur)) {
+                       entry = cur->data;
+
+                       if (entry->name) {
+                               g_string_append_printf (output, "fuzzy_stat{storage=\"%s\"} %" PRIu32 "\n",
+                               entry->name, entry->fuzzy_cnt);
+                       }
+               }
+       }
+
+       g_string_append (output, "# EOF\n");
+       rspamd_controller_send_openmetrics (conn_ent, g_string_free (output, FALSE));
+
+       // TODO implement statfile metrics
+
+
+       return TRUE;
+}
+
+static int
+rspamd_controller_handle_metrics_common (
+       struct rspamd_http_connection_entry *conn_ent,
+       struct rspamd_http_message *msg,
+       gboolean do_reset)
+{
+       struct rspamd_controller_session *session = conn_ent->ud;
+       ucl_object_t *top, *sub;
+       gint i;
+       int64_t uptime;
+       guint64 spam = 0, ham = 0;
+       rspamd_mempool_stat_t mem_st;
+       struct rspamd_stat *stat, stat_copy;
+       struct rspamd_controller_worker_ctx *ctx;
+       struct rspamd_task *task;
+       struct rspamd_stat_cbdata *cbdata;
+
+       memset (&mem_st, 0, sizeof (mem_st));
+       rspamd_mempool_stat (&mem_st);
+       memcpy (&stat_copy, session->ctx->worker->srv->stat, sizeof (stat_copy));
+       stat = &stat_copy;
+       ctx = session->ctx;
+
+       task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
+                       ctx->lang_det, ctx->event_loop, FALSE);
+       task->resolver = ctx->resolver;
+       cbdata = rspamd_mempool_alloc0 (session->pool, sizeof (*cbdata));
+       cbdata->conn_ent = conn_ent;
+       cbdata->task = task;
+       top = ucl_object_typed_new (UCL_OBJECT);
+       cbdata->top = top;
+
+       task->s = rspamd_session_create (session->pool,
+                       rspamd_controller_metrics_fin_task,
+                       NULL,
+                       rspamd_controller_stat_cleanup_task,
+                       cbdata);
+       task->fin_arg = cbdata;
+       task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
+       task->sock = conn_ent->conn->fd;
+
+       ucl_object_insert_key (top, ucl_object_fromstring (
+                       RVERSION), "version",  0, false);
+       ucl_object_insert_key (top, ucl_object_fromstring (
+                       session->ctx->cfg->checksum), "config_id", 0, false);
+       uptime = ev_time () - session->ctx->start_time;
+       ucl_object_insert_key (top, ucl_object_fromint (
+                       uptime), "uptime", 0, false);
+       ucl_object_insert_key (top, ucl_object_fromint (
+                       session->ctx->start_time), "start_time", 0, false);
+       ucl_object_insert_key (top, ucl_object_frombool (!session->is_enable),
+                       "read_only", 0, false);
+       ucl_object_insert_key (top, ucl_object_fromint (
+                       stat->messages_scanned), "scanned", 0, false);
+       ucl_object_insert_key (top, ucl_object_fromint (
+                       stat->messages_learned), "learned", 0, false);
+
+       if (stat->messages_scanned > 0) {
+               sub = ucl_object_typed_new (UCL_OBJECT);
+               for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
+                       ucl_object_insert_key (sub,
+                               ucl_object_fromint (stat->actions_stat[i]),
+                               rspamd_action_to_str (i), 0, false);
+                       if (i < METRIC_ACTION_GREYLIST) {
+                               spam += stat->actions_stat[i];
+                       }
+                       else {
+                               ham += stat->actions_stat[i];
+                       }
+                       if (do_reset) {
+#ifndef HAVE_ATOMIC_BUILTINS
+                               session->ctx->worker->srv->stat->actions_stat[i] = 0;
+#else
+                               __atomic_store_n(&session->ctx->worker->srv->stat->actions_stat[i],
+                                               0, __ATOMIC_RELEASE);
+#endif
+                       }
+               }
+               ucl_object_insert_key (top, sub, "actions", 0, false);
+       }
+
+       ucl_object_insert_key (top, ucl_object_fromint (
+                       spam), "spam_count", 0, false);
+       ucl_object_insert_key (top, ucl_object_fromint (
+                       ham),  "ham_count",      0, false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (stat->connections_count), "connections", 0, false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (stat->control_connections_count),
+               "control_connections", 0, false);
+
+       ucl_object_insert_key (top,
+               ucl_object_fromint (mem_st.pools_allocated), "pools_allocated", 0,
+               false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (mem_st.pools_freed), "pools_freed", 0, false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (mem_st.bytes_allocated), "bytes_allocated", 0,
+               false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (
+                       mem_st.chunks_allocated), "chunks_allocated", 0, false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (mem_st.shared_chunks_allocated),
+               "shared_chunks_allocated", 0, false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (mem_st.chunks_freed), "chunks_freed", 0, false);
+       ucl_object_insert_key (top,
+               ucl_object_fromint (
+                       mem_st.oversized_chunks), "chunks_oversized", 0, false);
+       ucl_object_insert_key (top,
+                       ucl_object_fromint (mem_st.fragmented_size), "fragmented", 0, false);
+
+       if (do_reset) {
+               session->ctx->srv->stat->messages_scanned = 0;
+               session->ctx->srv->stat->messages_learned = 0;
+               session->ctx->srv->stat->connections_count = 0;
+               session->ctx->srv->stat->control_connections_count = 0;
+               rspamd_mempool_stat_reset ();
+       }
+
+       fuzzy_stat_command (task);
+
+       /* Now write statistics for each statfile */
+       rspamd_stat_statistics (task, session->ctx->cfg, &cbdata->learned,
+                       &cbdata->stat);
+       session->task = task;
+
+       rspamd_session_pending (task->s);
+
+
+       return 0;
+}
+
+
+static int
+rspamd_controller_handle_metrics (struct rspamd_http_connection_entry *conn_ent,
+       struct rspamd_http_message *msg)
+{
+       struct rspamd_controller_session *session = conn_ent->ud;
+
+       if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
+               return 0;
+       }
+       return rspamd_controller_handle_metrics_common (conn_ent, msg, FALSE);
+}
+
 
 /*
  * Counters command handler:
@@ -3618,6 +3891,9 @@ start_controller_worker (struct rspamd_worker *worker)
        rspamd_http_router_add_path (ctx->http,
                        PATH_LEARN_HAM,
                        rspamd_controller_handle_learnham);
+       rspamd_http_router_add_path (ctx->http,
+                       PATH_METRICS,
+                       rspamd_controller_handle_metrics);
        rspamd_http_router_add_path (ctx->http,
                        PATH_SAVE_ACTIONS,
                        rspamd_controller_handle_saveactions);
index 7d7a50d35f6176aa0fd9a7c71535cc7c9e52327f..c1840c76d3ad690fade608a694ac8c8eacac8ba9 100644 (file)
@@ -599,6 +599,32 @@ rspamd_controller_send_error (struct rspamd_http_connection_entry *entry,
        entry->is_reply = TRUE;
 }
 
+void
+rspamd_controller_send_openmetrics (struct rspamd_http_connection_entry *entry,
+       const gchar *str)
+{
+       struct rspamd_http_message *msg;
+       rspamd_fstring_t *reply;
+
+       msg = rspamd_http_new_message (HTTP_RESPONSE);
+       msg->date = time (NULL);
+       msg->code = 200;
+       msg->status = rspamd_fstring_new_init ("OK", 2);
+       reply = rspamd_fstring_new_init (str, strlen (str));
+
+       rspamd_http_message_set_body_from_fstring_steal (msg,
+                       rspamd_controller_maybe_compress (entry, reply, msg));
+       rspamd_http_connection_reset (entry->conn);
+       rspamd_http_router_insert_headers (entry->rt, msg);
+       rspamd_http_connection_write_message (entry->conn,
+               msg,
+               NULL,
+               "application/openmetrics-text; version=1.0.0; charset=utf-8",
+               entry,
+               entry->rt->timeout);
+       entry->is_reply = TRUE;
+}
+
 void
 rspamd_controller_send_string (struct rspamd_http_connection_entry *entry,
        const gchar *str)
@@ -2188,4 +2214,4 @@ rspamd_worker_init_controller (struct rspamd_worker *worker,
                rspamd_map_watch (worker->srv->cfg, ctx->event_loop,
                                ctx->resolver, worker, RSPAMD_MAP_WATCH_SCANNER);
        }
-}
\ No newline at end of file
+}
index 1c1c63dd8528efdf160d584d09dcedaffb5469fe..973990974ea91932710f78b0a1e9aeacb52c0ed1 100644 (file)
@@ -118,6 +118,15 @@ struct rspamd_controller_session {
 void rspamd_controller_send_error (struct rspamd_http_connection_entry *entry,
                                                                   gint code, const gchar *error_msg, ...);
 
+/**
+ * Send openmetrics-formatted strings using HTTP
+ * @param entry router entry
+ * @param str string to send
+ */
+void
+rspamd_controller_send_openmetrics (struct rspamd_http_connection_entry *entry,
+       const gchar *str);
+
 /**
  * Send a custom string using HTTP
  * @param entry router entry