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);
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 *
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;
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;
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();
}