aboutsummaryrefslogtreecommitdiffstats
path: root/src/libserver/logger/logger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libserver/logger/logger.c')
-rw-r--r--src/libserver/logger/logger.c885
1 files changed, 885 insertions, 0 deletions
diff --git a/src/libserver/logger/logger.c b/src/libserver/logger/logger.c
new file mode 100644
index 000000000..568c0b8e9
--- /dev/null
+++ b/src/libserver/logger/logger.c
@@ -0,0 +1,885 @@
+/*-
+ * Copyright 2016 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include "logger.h"
+#include "rspamd.h"
+#include "map.h"
+#include "map_helpers.h"
+#include "ottery.h"
+#include "unix-std.h"
+#include "logger_private.h"
+
+
+static rspamd_logger_t *default_logger = NULL;
+static rspamd_logger_t *emergency_logger = NULL;
+static struct rspamd_log_modules *log_modules = NULL;
+
+guint rspamd_task_log_id = (guint)-1;
+RSPAMD_CONSTRUCTOR(rspamd_task_log_init)
+{
+ rspamd_task_log_id = rspamd_logger_add_debug_module("task");
+}
+
+rspamd_logger_t *
+rspamd_log_default_logger (void)
+{
+ return default_logger;
+}
+
+rspamd_logger_t *
+rspamd_log_emergency_logger (void)
+{
+ return emergency_logger;
+}
+
+void
+rspamd_log_set_log_level (rspamd_logger_t *logger, gint level)
+{
+ g_assert (logger != NULL);
+
+ logger->log_level = level;
+}
+
+void
+rspamd_log_set_log_flags (rspamd_logger_t *logger, gint flags)
+{
+ g_assert (logger != NULL);
+
+ logger->flags = flags;
+}
+
+void
+rspamd_log_close (rspamd_logger_t *logger)
+{
+ g_assert (logger != NULL);
+
+ if (logger->closed) {
+ return;
+ }
+
+ logger->closed = TRUE;
+
+ if (logger->debug_ip) {
+ rspamd_map_helper_destroy_radix (logger->debug_ip);
+ }
+
+ if (logger->pk) {
+ rspamd_pubkey_unref (logger->pk);
+ }
+
+ if (logger->keypair) {
+ rspamd_keypair_unref (logger->keypair);
+ }
+
+ logger->ops.dtor (logger, logger->ops.specific);
+
+ /* TODO: Do we really need that ? */
+ if (logger == default_logger) {
+ default_logger = NULL;
+ }
+
+ if (logger == emergency_logger) {
+ emergency_logger = NULL;
+ }
+
+ if (!logger->pool) {
+ g_free (logger);
+ }
+}
+
+bool
+rspamd_log_reopen (rspamd_logger_t *rspamd_log, struct rspamd_config *cfg,
+ uid_t uid, gid_t gid)
+{
+ void *nspec;
+ GError *err = NULL;
+
+ g_assert (rspamd_log != NULL);
+
+ nspec = rspamd_log->ops.reload (rspamd_log, cfg, rspamd_log->ops.specific,
+ uid, gid, &err);
+
+ if (nspec != NULL) {
+ rspamd_log->ops.specific = nspec;
+ }
+ else {
+
+ }
+
+ return nspec != NULL;
+}
+
+static void
+rspamd_emergency_logger_dtor (gpointer d)
+{
+ rspamd_logger_t *logger = (rspamd_logger_t *)d;
+
+ rspamd_log_close (logger);
+}
+
+rspamd_logger_t *
+rspamd_log_open_emergency (rspamd_mempool_t *pool)
+{
+ rspamd_logger_t *logger;
+ GError *err = NULL;
+
+ g_assert (default_logger == NULL);
+ g_assert (emergency_logger == NULL);
+
+ if (pool) {
+ logger = rspamd_mempool_alloc0 (pool, sizeof (rspamd_logger_t));
+ logger->mtx = rspamd_mempool_get_mutex (pool);
+ }
+ else {
+ logger = g_malloc0 (sizeof (rspamd_logger_t));
+ }
+
+
+ logger->pool = pool;
+ logger->process_type = "main";
+
+ const struct rspamd_logger_funcs *funcs = &console_log_funcs;
+ memcpy (&logger->ops, funcs, sizeof (*funcs));
+
+ logger->ops.specific = logger->ops.init (logger, NULL, -1, -1, &err);
+
+ if (logger->ops.specific == NULL) {
+ rspamd_fprintf (stderr, "fatal error: cannot init console logging: %e\n",
+ err);
+ g_error_free (err);
+
+ exit (EXIT_FAILURE);
+ }
+
+ default_logger = logger;
+ emergency_logger = logger;
+
+ rspamd_mempool_add_destructor (pool, rspamd_emergency_logger_dtor,
+ emergency_logger);
+
+ return logger;
+}
+
+rspamd_logger_t *
+rspamd_log_open_specific (rspamd_mempool_t *pool,
+ struct rspamd_config *cfg,
+ const gchar *ptype,
+ uid_t uid, gid_t gid)
+{
+ rspamd_logger_t *logger;
+ GError *err = NULL;
+
+ g_assert (emergency_logger != NULL);
+
+ if (pool) {
+ logger = rspamd_mempool_alloc0 (pool, sizeof (rspamd_logger_t));
+ logger->mtx = rspamd_mempool_get_mutex (pool);
+ }
+ else {
+ logger = g_malloc0 (sizeof (rspamd_logger_t));
+ }
+
+ logger->pool = pool;
+
+ if (cfg) {
+ if (cfg->log_error_elts > 0 && pool) {
+ logger->errlog = rspamd_mempool_alloc0_shared (pool,
+ sizeof (*logger->errlog));
+ logger->errlog->pool = pool;
+ logger->errlog->max_elts = cfg->log_error_elts;
+ logger->errlog->elt_len = cfg->log_error_elt_maxlen;
+ logger->errlog->elts = rspamd_mempool_alloc0_shared (pool,
+ sizeof (struct rspamd_logger_error_elt) * cfg->log_error_elts +
+ cfg->log_error_elt_maxlen * cfg->log_error_elts);
+ }
+
+ logger->log_level = cfg->log_level;
+ logger->flags = cfg->log_flags;
+
+ if (!(logger->flags & RSPAMD_LOG_FLAG_ENFORCED)) {
+ logger->log_level = cfg->log_level;
+ }
+ }
+
+ const struct rspamd_logger_funcs *funcs = NULL;
+
+ switch (cfg->log_type) {
+ case RSPAMD_LOG_CONSOLE:
+ funcs = &console_log_funcs;
+ break;
+ case RSPAMD_LOG_SYSLOG:
+ funcs = &syslog_log_funcs;
+ break;
+ case RSPAMD_LOG_FILE:
+ funcs = &file_log_funcs;
+ break;
+ }
+
+ g_assert (funcs != NULL);
+ memcpy (&logger->ops, funcs, sizeof (*funcs));
+
+ logger->ops.specific = logger->ops.init (logger, cfg, uid, gid, &err);
+
+ if (logger->ops.specific == NULL) {
+ rspamd_common_log_function (emergency_logger, G_LOG_LEVEL_CRITICAL,
+ "logger", NULL, G_STRFUNC,
+ "cannot open specific logger: %e", err);
+ g_error_free (err);
+
+ return NULL;
+ }
+
+ logger->pid = getpid ();
+ logger->process_type = ptype;
+ logger->enabled = TRUE;
+
+ /* Set up conditional logging */
+ if (cfg) {
+ if (cfg->debug_ip_map != NULL) {
+ /* Try to add it as map first of all */
+ if (logger->debug_ip) {
+ rspamd_map_helper_destroy_radix (logger->debug_ip);
+ }
+
+ logger->debug_ip = NULL;
+ rspamd_config_radix_from_ucl (cfg,
+ cfg->debug_ip_map,
+ "IP addresses for which debug logs are enabled",
+ &logger->debug_ip,
+ NULL,
+ NULL);
+ }
+
+ if (cfg->log_encryption_key) {
+ logger->pk = rspamd_pubkey_ref (cfg->log_encryption_key);
+ logger->keypair = rspamd_keypair_new (RSPAMD_KEYPAIR_KEX,
+ RSPAMD_CRYPTOBOX_MODE_25519);
+ rspamd_pubkey_calculate_nm (logger->pk, logger->keypair);
+ }
+ }
+
+ default_logger = logger;
+
+ return logger;
+}
+
+
+/**
+ * Used after fork() for updating structure params
+ */
+void
+rspamd_log_on_fork (GQuark ptype, struct rspamd_config *cfg,
+ rspamd_logger_t *logger)
+{
+ logger->pid = getpid ();
+ logger->process_type = g_quark_to_string (ptype);
+
+ if (logger->ops.on_fork) {
+ GError *err = NULL;
+
+ bool ret = logger->ops.on_fork (logger, cfg, logger->ops.specific, &err);
+
+ if (!ret && emergency_logger) {
+ rspamd_common_log_function (emergency_logger, G_LOG_LEVEL_CRITICAL,
+ "logger", NULL, G_STRFUNC,
+ "cannot update logging on fork: %e", err);
+ g_error_free (err);
+ }
+ }
+}
+
+static inline gboolean
+rspamd_logger_need_log (rspamd_logger_t *rspamd_log, GLogLevelFlags log_level,
+ guint module_id)
+{
+ g_assert (rspamd_log != NULL);
+
+ if ((log_level & RSPAMD_LOG_FORCED) ||
+ (log_level & (RSPAMD_LOG_LEVEL_MASK & G_LOG_LEVEL_MASK)) <= rspamd_log->log_level) {
+ return TRUE;
+ }
+
+ if (module_id != (guint)-1 && isset (log_modules->bitset, module_id)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+rspamd_log_encrypt_message (const gchar *begin, const gchar *end, gsize *enc_len,
+ rspamd_logger_t *rspamd_log)
+{
+ guchar *out;
+ gchar *b64;
+ guchar *p, *nonce, *mac;
+ const guchar *comp;
+ guint len, inlen;
+
+ g_assert (end > begin);
+ /* base64 (pubkey | nonce | message) */
+ inlen = rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519) +
+ rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519) +
+ rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519) +
+ (end - begin);
+ out = g_malloc (inlen);
+
+ p = out;
+ comp = rspamd_pubkey_get_pk (rspamd_log->pk, &len);
+ memcpy (p, comp, len);
+ p += len;
+ ottery_rand_bytes (p, rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519));
+ nonce = p;
+ p += rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
+ mac = p;
+ p += rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519);
+ memcpy (p, begin, end - begin);
+ comp = rspamd_pubkey_get_nm (rspamd_log->pk, rspamd_log->keypair);
+ g_assert (comp != NULL);
+ rspamd_cryptobox_encrypt_nm_inplace (p, end - begin, nonce, comp, mac,
+ RSPAMD_CRYPTOBOX_MODE_25519);
+ b64 = rspamd_encode_base64 (out, inlen, 0, enc_len);
+ g_free (out);
+
+ return b64;
+}
+
+static void
+rspamd_log_write_ringbuffer (rspamd_logger_t *rspamd_log,
+ const gchar *module, const gchar *id,
+ const gchar *data, glong len)
+{
+ guint32 row_num;
+ struct rspamd_logger_error_log *elog;
+ struct rspamd_logger_error_elt *elt;
+
+ if (!rspamd_log->errlog) {
+ return;
+ }
+
+ elog = rspamd_log->errlog;
+
+ g_atomic_int_compare_and_exchange (&elog->cur_row, elog->max_elts, 0);
+#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30))
+ row_num = g_atomic_int_add (&elog->cur_row, 1);
+#else
+ row_num = g_atomic_int_exchange_and_add (&elog->cur_row, 1);
+#endif
+
+ if (row_num < elog->max_elts) {
+ elt = (struct rspamd_logger_error_elt *)(((guchar *)elog->elts) +
+ (sizeof (*elt) + elog->elt_len) * row_num);
+ g_atomic_int_set (&elt->completed, 0);
+ }
+ else {
+ /* Race condition */
+ elog->cur_row = 0;
+ return;
+ }
+
+ elt->pid = rspamd_log->pid;
+ elt->ptype = g_quark_from_string (rspamd_log->process_type);
+ elt->ts = rspamd_get_calendar_ticks ();
+
+ if (id) {
+ rspamd_strlcpy (elt->id, id, sizeof (elt->id));
+ }
+ else {
+ rspamd_strlcpy (elt->id, "", sizeof (elt->id));
+ }
+
+ if (module) {
+ rspamd_strlcpy (elt->module, module, sizeof (elt->module));
+ }
+ else {
+ rspamd_strlcpy (elt->module, "", sizeof (elt->module));
+ }
+
+ rspamd_strlcpy (elt->message, data, MIN (len + 1, elog->elt_len));
+ g_atomic_int_set (&elt->completed, 1);
+}
+
+bool
+rspamd_common_logv (rspamd_logger_t *rspamd_log, gint level_flags,
+ const gchar *module, const gchar *id, const gchar *function,
+ const gchar *fmt, va_list args)
+{
+ gchar logbuf[RSPAMD_LOGBUF_SIZE], *end;
+ gint level = level_flags & (RSPAMD_LOG_LEVEL_MASK & G_LOG_LEVEL_MASK), mod_id;
+ bool ret = false;
+
+ if (G_UNLIKELY (rspamd_log == NULL)) {
+ rspamd_log = default_logger;
+ }
+
+ if (G_UNLIKELY (rspamd_log == NULL)) {
+ /* Just fprintf message to stderr */
+ if (level >= G_LOG_LEVEL_INFO) {
+ end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, args);
+ rspamd_fprintf (stderr, "%*s\n", (gint)(end - logbuf), logbuf);
+ }
+ }
+ else {
+ if (level == G_LOG_LEVEL_DEBUG) {
+ mod_id = rspamd_logger_add_debug_module (module);
+ }
+ else {
+ mod_id = -1;
+ }
+
+ if (rspamd_logger_need_log (rspamd_log, level_flags, mod_id)) {
+ end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, args);
+
+ if ((level_flags & RSPAMD_LOG_ENCRYPTED) && rspamd_log->pk) {
+ gchar *encrypted;
+ gsize enc_len;
+
+ encrypted = rspamd_log_encrypt_message (logbuf, end, &enc_len,
+ rspamd_log);
+ ret = rspamd_log->ops.log (module, id,
+ function,
+ level_flags,
+ encrypted,
+ enc_len,
+ rspamd_log,
+ rspamd_log->ops.specific);
+ g_free (encrypted);
+ }
+ else {
+ ret = rspamd_log->ops.log (module, id,
+ function,
+ level_flags,
+ logbuf,
+ end - logbuf,
+ rspamd_log,
+ rspamd_log->ops.specific);
+ }
+
+ switch (level) {
+ case G_LOG_LEVEL_CRITICAL:
+ rspamd_log->log_cnt[0] ++;
+ rspamd_log_write_ringbuffer (rspamd_log, module, id, logbuf,
+ end - logbuf);
+ break;
+ case G_LOG_LEVEL_WARNING:
+ rspamd_log->log_cnt[1]++;
+ break;
+ case G_LOG_LEVEL_INFO:
+ rspamd_log->log_cnt[2]++;
+ break;
+ case G_LOG_LEVEL_DEBUG:
+ rspamd_log->log_cnt[3]++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * This log functions select real logger and write message if level is less or equal to configured log level
+ */
+bool
+rspamd_common_log_function (rspamd_logger_t *rspamd_log,
+ gint level_flags,
+ const gchar *module, const gchar *id,
+ const gchar *function,
+ const gchar *fmt,
+ ...)
+{
+ va_list vp;
+
+ va_start (vp, fmt);
+ bool ret = rspamd_common_logv (rspamd_log, level_flags, module, id, function, fmt, vp);
+ va_end (vp);
+
+ return ret;
+}
+
+bool
+rspamd_default_logv (gint level_flags, const gchar *module, const gchar *id,
+ const gchar *function,
+ const gchar *fmt, va_list args)
+{
+ return rspamd_common_logv (NULL, level_flags, module, id, function, fmt, args);
+}
+
+bool
+rspamd_default_log_function (gint level_flags,
+ const gchar *module, const gchar *id,
+ const gchar *function, const gchar *fmt, ...)
+{
+
+ va_list vp;
+
+ va_start (vp, fmt);
+ bool ret = rspamd_default_logv (level_flags, module, id, function, fmt, vp);
+ va_end (vp);
+
+ return ret;
+}
+
+
+/**
+ * Main file interface for logging
+ */
+/**
+ * Write log line depending on ip
+ */
+bool
+rspamd_conditional_debug (rspamd_logger_t *rspamd_log,
+ rspamd_inet_addr_t *addr, const gchar *module, const gchar *id,
+ const gchar *function, const gchar *fmt, ...)
+{
+ static gchar logbuf[LOGBUF_LEN];
+ va_list vp;
+ gchar *end;
+ guint mod_id;
+
+ if (rspamd_log == NULL) {
+ rspamd_log = default_logger;
+ }
+
+ mod_id = rspamd_logger_add_debug_module (module);
+
+ if (rspamd_logger_need_log (rspamd_log, G_LOG_LEVEL_DEBUG, mod_id) ||
+ rspamd_log->is_debug) {
+ if (rspamd_log->debug_ip && addr != NULL) {
+ if (rspamd_match_radix_map_addr (rspamd_log->debug_ip,
+ addr) == NULL) {
+ return false;
+ }
+ }
+
+ va_start (vp, fmt);
+ end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, vp);
+ *end = '\0';
+ va_end (vp);
+ return rspamd_log->ops.log (module, id,
+ function,
+ G_LOG_LEVEL_DEBUG | RSPAMD_LOG_FORCED,
+ logbuf,
+ end - logbuf,
+ rspamd_log,
+ rspamd_log->ops.specific);
+ }
+
+ return false;
+}
+
+bool
+rspamd_conditional_debug_fast (rspamd_logger_t *rspamd_log,
+ rspamd_inet_addr_t *addr,
+ guint mod_id, const gchar *module, const gchar *id,
+ const gchar *function, const gchar *fmt, ...)
+{
+ static gchar logbuf[LOGBUF_LEN];
+ va_list vp;
+ gchar *end;
+
+ if (rspamd_log == NULL) {
+ rspamd_log = default_logger;
+ }
+
+ if (rspamd_logger_need_log (rspamd_log, G_LOG_LEVEL_DEBUG, mod_id) ||
+ rspamd_log->is_debug) {
+ if (rspamd_log->debug_ip && addr != NULL) {
+ if (rspamd_match_radix_map_addr (rspamd_log->debug_ip, addr)
+ == NULL) {
+ return false;
+ }
+ }
+
+ va_start (vp, fmt);
+ end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, vp);
+ *end = '\0';
+ va_end (vp);
+ return rspamd_log->ops.log (module, id,
+ function,
+ G_LOG_LEVEL_DEBUG | RSPAMD_LOG_FORCED,
+ logbuf,
+ end - logbuf,
+ rspamd_log,
+ rspamd_log->ops.specific);
+ }
+
+ return false;
+}
+
+bool
+rspamd_conditional_debug_fast_num_id (rspamd_logger_t *rspamd_log,
+ rspamd_inet_addr_t *addr,
+ guint mod_id, const gchar *module, guint64 id,
+ const gchar *function, const gchar *fmt, ...)
+{
+ static gchar logbuf[LOGBUF_LEN], idbuf[64];
+ va_list vp;
+ gchar *end;
+
+ if (rspamd_log == NULL) {
+ rspamd_log = default_logger;
+ }
+
+ if (rspamd_logger_need_log (rspamd_log, G_LOG_LEVEL_DEBUG, mod_id) ||
+ rspamd_log->is_debug) {
+ if (rspamd_log->debug_ip && addr != NULL) {
+ if (rspamd_match_radix_map_addr (rspamd_log->debug_ip, addr)
+ == NULL) {
+ return false;
+ }
+ }
+
+ rspamd_snprintf (idbuf, sizeof (idbuf), "%XuL", id);
+ va_start (vp, fmt);
+ end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, vp);
+ *end = '\0';
+ va_end (vp);
+ return rspamd_log->ops.log (module, idbuf,
+ function,
+ G_LOG_LEVEL_DEBUG | RSPAMD_LOG_FORCED,
+ logbuf,
+ end - logbuf,
+ rspamd_log,
+ rspamd_log->ops.specific);
+ }
+
+ return false;
+}
+
+/**
+ * Wrapper for glib logger
+ */
+void
+rspamd_glib_log_function (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer arg)
+{
+ rspamd_logger_t *rspamd_log = (rspamd_logger_t *)arg;
+
+ if (rspamd_log->enabled &&
+ rspamd_logger_need_log (rspamd_log, log_level, -1)) {
+ rspamd_log->ops.log ("glib", NULL,
+ NULL,
+ log_level,
+ message,
+ strlen (message),
+ rspamd_log,
+ rspamd_log->ops.specific);
+ }
+}
+
+void
+rspamd_glib_printerr_function (const gchar *message)
+{
+ rspamd_common_log_function (NULL, G_LOG_LEVEL_CRITICAL, "glib",
+ NULL, G_STRFUNC,
+ "%s", message);
+}
+
+/**
+ * Temporary turn on debugging
+ */
+void
+rspamd_log_debug (rspamd_logger_t *rspamd_log)
+{
+ rspamd_log->is_debug = TRUE;
+}
+
+/**
+ * Turn off temporary debugging
+ */
+void
+rspamd_log_nodebug (rspamd_logger_t *rspamd_log)
+{
+ rspamd_log->is_debug = FALSE;
+}
+
+const guint64 *
+rspamd_log_counters (rspamd_logger_t *logger)
+{
+ if (logger) {
+ return logger->log_cnt;
+ }
+
+ return NULL;
+}
+
+static gint
+rspamd_log_errlog_cmp (const ucl_object_t **o1, const ucl_object_t **o2)
+{
+ const ucl_object_t *ts1, *ts2;
+
+ ts1 = ucl_object_lookup (*o1, "ts");
+ ts2 = ucl_object_lookup (*o2, "ts");
+
+ if (ts1 && ts2) {
+ gdouble t1 = ucl_object_todouble (ts1), t2 = ucl_object_todouble (ts2);
+
+ if (t1 > t2) {
+ return -1;
+ }
+ else if (t2 > t1) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+ucl_object_t *
+rspamd_log_errorbuf_export (const rspamd_logger_t *logger)
+{
+ struct rspamd_logger_error_elt *cpy, *cur;
+ ucl_object_t *top = ucl_object_typed_new (UCL_ARRAY);
+ guint i;
+
+ if (logger->errlog == NULL) {
+ return top;
+ }
+
+ cpy = g_malloc0_n (logger->errlog->max_elts,
+ sizeof (*cpy) + logger->errlog->elt_len);
+ memcpy (cpy, logger->errlog->elts, logger->errlog->max_elts *
+ (sizeof (*cpy) + logger->errlog->elt_len));
+
+ for (i = 0; i < logger->errlog->max_elts; i ++) {
+ cur = (struct rspamd_logger_error_elt *)((guchar *)cpy +
+ i * ((sizeof (*cpy) + logger->errlog->elt_len)));
+ if (cur->completed) {
+ ucl_object_t *obj = ucl_object_typed_new (UCL_OBJECT);
+
+ ucl_object_insert_key (obj, ucl_object_fromdouble (cur->ts),
+ "ts", 0, false);
+ ucl_object_insert_key (obj, ucl_object_fromint (cur->pid),
+ "pid", 0, false);
+ ucl_object_insert_key (obj,
+ ucl_object_fromstring (g_quark_to_string (cur->ptype)),
+ "type", 0, false);
+ ucl_object_insert_key (obj, ucl_object_fromstring (cur->id),
+ "id", 0, false);
+ ucl_object_insert_key (obj, ucl_object_fromstring (cur->module),
+ "module", 0, false);
+ ucl_object_insert_key (obj, ucl_object_fromstring (cur->message),
+ "message", 0, false);
+
+ ucl_array_append (top, obj);
+ }
+ }
+
+ ucl_object_array_sort (top, rspamd_log_errlog_cmp);
+ g_free (cpy);
+
+ return top;
+}
+
+static guint
+rspamd_logger_allocate_mod_bit (void)
+{
+ if (log_modules->bitset_allocated * NBBY > log_modules->bitset_len + 1) {
+ log_modules->bitset_len ++;
+ return log_modules->bitset_len - 1;
+ }
+ else {
+ /* Need to expand */
+ log_modules->bitset_allocated *= 2;
+ log_modules->bitset = g_realloc (log_modules->bitset,
+ log_modules->bitset_allocated);
+
+ return rspamd_logger_allocate_mod_bit ();
+ }
+}
+
+RSPAMD_DESTRUCTOR (rspamd_debug_modules_dtor)
+{
+ if (log_modules) {
+ g_hash_table_unref (log_modules->modules);
+ g_free (log_modules->bitset);
+ g_free (log_modules);
+ }
+}
+
+guint
+rspamd_logger_add_debug_module (const gchar *mname)
+{
+ struct rspamd_log_module *m;
+
+ if (mname == NULL) {
+ return (guint)-1;
+ }
+
+ if (log_modules == NULL) {
+ /*
+ * This is usually called from constructors, so we call init check
+ * each time to avoid dependency issues between ctors calls
+ */
+ log_modules = g_malloc0 (sizeof (*log_modules));
+ log_modules->modules = g_hash_table_new_full (rspamd_strcase_hash,
+ rspamd_strcase_equal, g_free, g_free);
+ log_modules->bitset_allocated = 16;
+ log_modules->bitset_len = 0;
+ log_modules->bitset = g_malloc0 (log_modules->bitset_allocated);
+ }
+
+ if ((m = g_hash_table_lookup (log_modules->modules, mname)) == NULL) {
+ m = g_malloc0 (sizeof (*m));
+ m->mname = g_strdup (mname);
+ m->id = rspamd_logger_allocate_mod_bit ();
+ clrbit (log_modules->bitset, m->id);
+ g_hash_table_insert (log_modules->modules, m->mname, m);
+ }
+
+ return m->id;
+}
+
+void
+rspamd_logger_configure_modules (GHashTable *mods_enabled)
+{
+ GHashTableIter it;
+ gpointer k, v;
+ guint id;
+
+ /* On first iteration, we go through all modules enabled and add missing ones */
+ g_hash_table_iter_init (&it, mods_enabled);
+
+ while (g_hash_table_iter_next (&it, &k, &v)) {
+ rspamd_logger_add_debug_module ((const gchar *)k);
+ }
+
+ g_hash_table_iter_init (&it, mods_enabled);
+
+ while (g_hash_table_iter_next (&it, &k, &v)) {
+ id = rspamd_logger_add_debug_module ((const gchar *)k);
+
+ if (isclr (log_modules->bitset, id)) {
+ msg_info ("enable debugging for module %s (%d)", (const gchar *) k,
+ id);
+ setbit (log_modules->bitset, id);
+ }
+ }
+}
+
+struct rspamd_logger_funcs*
+rspamd_logger_set_log_function (rspamd_logger_t *logger,
+ struct rspamd_logger_funcs *nfuncs)
+{
+ /* TODO: write this */
+
+ return NULL;
+} \ No newline at end of file