]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Fix global events to actually work
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 9 Mar 2021 14:33:15 +0000 (16:33 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Wed, 29 Sep 2021 10:09:58 +0000 (10:09 +0000)
Also add comments to clarify how exactly it works.

src/lib/event-filter.c
src/lib/lib-event.c
src/lib/lib-event.h
src/lib/test-event-filter.c

index 5574f2bd2efba11c8531afeb6ad3e68788f99ff0..b4f8ce566e142d3c34b4da0ecfbfad523d441386 100644 (file)
@@ -469,6 +469,13 @@ event_has_category(struct event *event, struct event_filter_node *node,
                /* try also the parent events */
                event = event_get_parent(event);
        }
+       /* check also the global event and its parents */
+       event = event_get_global();
+       while (event != NULL) {
+               if (event_has_category_nonrecursive(event, wanted_category))
+                       return TRUE;
+               event = event_get_parent(event);
+       }
        return FALSE;
 }
 
index 9afa48968a95f22f8635fd500723ffd28f1b6dc5..f1c45cbe5c24f9431582716f0b9cd74fc604251f 100644 (file)
@@ -241,8 +241,9 @@ struct event *event_flatten(struct event *src)
 {
        struct event *dst;
 
-       /* If we don't have a parent, we have nothing to flatten. */
-       if (src->parent == NULL)
+       /* If we don't have a parent or a global event,
+          we have nothing to flatten. */
+       if (src->parent == NULL && current_global_event == NULL)
                return event_ref(src);
 
        /* We have to flatten the event. */
@@ -251,6 +252,8 @@ struct event *event_flatten(struct event *src)
                                    src->source_linenum);
        dst = event_set_name(dst, src->sending_name);
 
+       if (current_global_event != NULL)
+               event_flatten_recurse(dst, current_global_event, NULL);
        event_flatten_recurse(dst, src, NULL);
 
        dst->tv_created_ioloop = src->tv_created_ioloop;
@@ -837,6 +840,14 @@ event_find_field_recursive(const struct event *event, const char *key)
                        return field;
                event = event->parent;
        } while (event != NULL);
+
+       /* check also the global event and its parents */
+       event = event_get_global();
+       while (event != NULL) {
+               if ((field = event_find_field_nonrecursive(event, key)) != NULL)
+                       return field;
+               event = event->parent;
+       }
        return NULL;
 }
 
@@ -889,8 +900,11 @@ event_find_field_recursive_str(const struct event *event, const char *key)
                ARRAY_TYPE(const_string) list;
                t_array_init(&list, 8);
                /* This is a bit different, because it needs to be merging
-                  all of the parent events' lists together. */
+                  all of the parent events' and global events' lists
+                  together. */
                event_get_recursive_strlist(event, NULL, key, &list);
+               event_get_recursive_strlist(event_get_global(), NULL,
+                                           key, &list);
                return t_array_const_string_join(&list, ",");
        }
        }
index dc27a79d8e555ec3a8bdbce98a9b6ae4f5006dfe..7d1b6da0306f1dbe22a057ce23101d646bdf626e 100644 (file)
@@ -166,21 +166,32 @@ struct event *event_ref(struct event *event);
    freed. The current global event's refcount must not drop to 0. */
 void event_unref(struct event **event);
 
-/* Set the event to be the global default event used by i_error(), etc.
-   Returns the event parameter. The event must be explicitly popped before
-   it's freed.
-
-   The global event stack is also an alternative nonpermanent hierarchy for
-   events. For example the global event can be "IMAP command SELECT", which
-   can be used for filtering events that happen while the SELECT command is
-   being executed. However, for the created struct mailbox the parent event
-   should be the mail_user, not the SELECT command. Otherwise everything else
-   that happens afterwards to the selected mailbox would also count towards
-   SELECT. This means that events shouldn't be using the current global event
-   as their parent event. */
+/* Set the event to be the global event and push it at the top of the global
+   event stack. Returns the event parameter. The event must be explicitly
+   popped before it's freed.
+
+   The global event acts as the root event for all the events while they are
+   being emitted. The global events don't permanently affect the event
+   hierarchy. The global events are typically used to add extra fields to all
+   emitted events while some specific work is running.
+
+   For example the global event can be "IMAP command SELECT", which can be used
+   for filtering events that happen while the SELECT command is being executed.
+   However, for the created struct mailbox the parent event should be the
+   mail_user, not the SELECT command. (If the mailbox used SELECT command as
+   the parent event, then any future event emitted via the mailbox event would
+   show SELECT command as the parent, even after SELECT had already finished.)
+
+   The global event works the same as if all the events' roots were instead
+   pointing to the global event. Global events don't affect log prefixes.
+
+   The created global events should use event_get_global() as their parent
+   event. Only the last pushed global event is used. */
 struct event *event_push_global(struct event *event);
-/* Pop the global event. Assert-crash if the current global event isn't the
-   given event parameter. Returns the new global event. */
+/* Pop the current global event and set the global event to the next one at
+   the top of the stack. Assert-crash if the current global event isn't the
+   given event parameter. Returns the next (now activated) global event in the
+   stack, or NULL if the stack is now empty. */
 struct event *event_pop_global(struct event *event);
 /* Returns the current global event. */
 struct event *event_get_global(void);
@@ -332,10 +343,11 @@ void event_get_last_duration(const struct event *event,
 struct event_field *
 event_find_field_nonrecursive(const struct event *event, const char *key);
 /* Returns field for a given key, or NULL if it doesn't exist. If the key
-   isn't found from the event itself, find it from parent events. */
+   isn't found from the event itself, find it from parent events, including
+   from the global event. */
 const struct event_field *
 event_find_field_recursive(const struct event *event, const char *key);
-/* Returns the given key's value as string, or NULL if it doesn't exist.
+/* Same as event_find_field(), but return the value converted to a string.
    If the field isn't stored as a string, the result is allocated from
    data stack. */
 const char *
index ddb284f87ae11e9bd340405aac00ea3836635f34..d2cf9e0c891edc9b732992702ce98eb87e27baea 100644 (file)
@@ -61,6 +61,65 @@ static void test_event_filter_override_parent_fields(void)
        test_end();
 }
 
+static void test_event_filter_override_global_fields(void)
+{
+       struct event_filter *filter;
+       const char *error;
+       const struct failure_context failure_ctx = {
+               .type = LOG_TYPE_DEBUG
+       };
+
+       test_begin("event filter: override global fields");
+
+       struct event *global = event_create(NULL);
+       event_add_str(global, "str", "global_str");
+       event_add_str(global, "global_str", "global_str");
+       event_add_int(global, "int1", 0);
+       event_add_int(global, "int2", 5);
+       event_add_int(global, "global_int", 6);
+       event_push_global(global);
+
+       struct event *local = event_create(NULL);
+       event_add_str(local, "str", "local_str");
+       event_add_str(local, "local_str", "local_str");
+       event_add_int(local, "int1", 6);
+       event_add_int(local, "int2", 0);
+       event_add_int(local, "local_int", 8);
+
+       /* global matches: test a mix of global/local fields */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("str=global_str AND int1=0 AND int2=5", filter, &error) == 0);
+       test_assert(event_filter_match(filter, global, &failure_ctx));
+       test_assert(!event_filter_match(filter, local, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* global matches: test fields that exist only in global */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("global_str=global_str AND global_int=6", filter, &error) == 0);
+       test_assert(event_filter_match(filter, global, &failure_ctx));
+       test_assert(event_filter_match(filter, local, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* local matches: test a mix of global/local fields */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("str=local_str AND int1=6 AND int2=0", filter, &error) == 0);
+       test_assert(event_filter_match(filter, local, &failure_ctx));
+       test_assert(!event_filter_match(filter, global, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* local matches: test fields that exist only in local */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("local_str=local_str AND local_int=8", filter, &error) == 0);
+       test_assert(event_filter_match(filter, local, &failure_ctx));
+       test_assert(!event_filter_match(filter, global, &failure_ctx));
+       event_filter_unref(&filter);
+
+       event_pop_global(global);
+       event_unref(&global);
+       event_unref(&local);
+       test_end();
+}
+
 static void test_event_filter_clear_parent_fields(void)
 {
        struct event_filter *filter;
@@ -110,6 +169,57 @@ static void test_event_filter_clear_parent_fields(void)
        test_end();
 }
 
+static void test_event_filter_clear_global_fields(void)
+{
+       struct event_filter *filter;
+       const char *error;
+       const struct failure_context failure_ctx = {
+               .type = LOG_TYPE_DEBUG
+       };
+       const char *keys[] = { "str", "int" };
+
+       test_begin("event filter: clear global fields");
+
+       struct event *global = event_create(NULL);
+       event_add_str(global, "str", "global_str");
+       event_add_int(global, "int", 0);
+       event_push_global(global);
+
+       struct event *local = event_create(NULL);
+       event_field_clear(local, "str");
+       event_field_clear(local, "int");
+
+       for (unsigned int i = 0; i < N_ELEMENTS(keys); i++) {
+               /* match any value */
+               const char *query = t_strdup_printf("%s=*", keys[i]);
+               filter = event_filter_create();
+               test_assert(event_filter_parse(query, filter, &error) == 0);
+
+               test_assert_idx(event_filter_match(filter, global, &failure_ctx), i);
+               test_assert_idx(!event_filter_match(filter, local, &failure_ctx), i);
+               event_filter_unref(&filter);
+       }
+
+       /* match empty field */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("str=\"\"", filter, &error) == 0);
+       test_assert(!event_filter_match(filter, global, &failure_ctx));
+       test_assert(event_filter_match(filter, local, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* match nonexistent field */
+       filter = event_filter_create();
+       test_assert(event_filter_parse("nonexistent=\"\"", filter, &error) == 0);
+       test_assert(event_filter_match(filter, global, &failure_ctx));
+       test_assert(event_filter_match(filter, local, &failure_ctx));
+       event_filter_unref(&filter);
+
+       event_pop_global(global);
+       event_unref(&global);
+       event_unref(&local);
+       test_end();
+}
+
 static void test_event_filter_inc_int(void)
 {
        struct event_filter *filter;
@@ -272,12 +382,69 @@ static void test_event_filter_strlist_recursive(void)
        test_end();
 }
 
+static void test_event_filter_strlist_global_events(void)
+{
+       struct event_filter *filter;
+       const struct failure_context failure_ctx = {
+               .type = LOG_TYPE_DEBUG
+       };
+
+       test_begin("event filter: match string list - global events");
+
+       struct event *global = event_create(NULL);
+       event_push_global(global);
+
+       struct event *e = event_create(NULL);
+
+       /* empty filter: global is non-empty */
+       filter = event_filter_create();
+       event_filter_parse("list1=\"\"", filter, NULL);
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       event_strlist_append(global, "list1", "foo");
+       test_assert(!event_filter_match(filter, e, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* matching filter: matches global */
+       filter = event_filter_create();
+       event_filter_parse("list2=global", filter, NULL);
+       /* empty: */
+       test_assert(!event_filter_match(filter, e, &failure_ctx));
+       /* set global but no local: */
+       event_strlist_append(global, "list2", "global");
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       /* set local to non-matching: */
+       event_strlist_append(e, "list2", "local");
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       event_filter_unref(&filter);
+
+       /* matching filter: matches local */
+       filter = event_filter_create();
+       event_filter_parse("list3=local", filter, NULL);
+       /* empty: */
+       test_assert(!event_filter_match(filter, e, &failure_ctx));
+       /* set local but no global: */
+       event_strlist_append(e, "list3", "local");
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       /* set global to non-matching: */
+       event_strlist_append(e, "list3", "global");
+       test_assert(event_filter_match(filter, e, &failure_ctx));
+       event_filter_unref(&filter);
+
+       event_unref(&e);
+       event_pop_global(global);
+       event_unref(&global);
+       test_end();
+}
+
 void test_event_filter(void)
 {
        test_event_filter_override_parent_fields();
+       test_event_filter_override_global_fields();
        test_event_filter_clear_parent_fields();
+       test_event_filter_clear_global_fields();
        test_event_filter_inc_int();
        test_event_filter_parent_category_match();
        test_event_filter_strlist();
        test_event_filter_strlist_recursive();
+       test_event_filter_strlist_global_events();
 }