]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Switch event filtering to the new filter language
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Tue, 26 May 2020 15:35:38 +0000 (11:35 -0400)
committerJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Tue, 26 May 2020 15:35:38 +0000 (11:35 -0400)
src/lib-master/stats-client.c
src/lib-master/test-event-stats.c
src/lib/Makefile.am
src/lib/event-filter-parser.c [deleted file]
src/lib/event-filter.c
src/lib/event-filter.h
src/stats/client-writer.c
src/stats/test-client-reader.c
src/stats/test-client-writer.c
src/stats/test-stats-metrics.c

index 281b876b099b68150674ef170d172f859288c856..489b0e1c59a7099387220f955372b4bf7022e822 100644 (file)
@@ -40,7 +40,7 @@ client_handshake_filter(const char *const *args, struct event_filter **filter_r,
        }
 
        *filter_r = event_filter_create();
-       if (!event_filter_import_unescaped(*filter_r, args+1, error_r)) {
+       if (!event_filter_import(*filter_r, t_str_tabunescape(args[1]), error_r)) {
                event_filter_unref(filter_r);
                return -1;
        }
index b0b9837525e4c444fe59a0a1005c09cf2e089cf6..3699a363b9e3ccb2ab6da322e090580c36361db0 100644 (file)
@@ -158,7 +158,8 @@ static void stats_conn_input(struct connection *_conn)
        struct ostream *stats_data_out;
        struct server_connection *conn = (struct server_connection *)_conn;
        const char *handshake = "VERSION\tstats-server\t3\t0\n"
-               "FILTER\tctest1\t\tctest2\t\tctest3\t\tctest4\t\tctest5\t\n";
+               "FILTER\tcategory=test1 OR category=test2 OR category=test3 OR "
+               "category=test4 OR category=test5\n";
        const char *line = NULL;
        if (!conn->handshake_sent) {
                conn->handshake_sent = TRUE;
index be675f1a2483cbfa99f7998119cf0669927de617..150327c376f41c6f1baa01329767f381024dd181 100644 (file)
@@ -55,7 +55,6 @@ liblib_la_SOURCES = \
        eacces-error.c \
        env-util.c \
        event-filter.c \
-       event-filter-parser.c \
        event-filter-parser-lexer.l \
        event-filter-parser-parser.y \
        event-log.c \
diff --git a/src/lib/event-filter-parser.c b/src/lib/event-filter-parser.c
deleted file mode 100644 (file)
index 6c7b3aa..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "lib-event-private.h"
-#include "event-filter.h"
-
-static void add_category(ARRAY_TYPE(const_string) *categories, const char *name)
-{
-       if (!array_is_created(categories))
-               t_array_init(categories, 4);
-       array_push_back(categories, &name);
-}
-
-static int parse_query(const char *str, struct event_filter_query *query_r,
-                      const char **error_r)
-{
-       ARRAY_TYPE(const_string) categories = ARRAY_INIT;
-       ARRAY(struct event_filter_field) fields = ARRAY_INIT;
-
-       i_zero(query_r);
-       do {
-               while (*str == ' ')
-                       str++;
-               const char *p = strchr(str, ' ');
-               if (p != NULL)
-                       str = t_strdup_until(str, p++);
-
-               if (strncmp(str, "event:", 6) == 0) {
-                       query_r->name = str+6;
-               } else if (strncmp(str, "source:", 7) == 0) {
-                       const char *linep = strchr(str+7, ':');
-                       if (linep == NULL) {
-                               /* filename only - match to all line numbers */
-                               query_r->source_filename = str+7;
-                       } else {
-                               query_r->source_filename = t_strdup_until(str+7, linep);
-                               if (str_to_uint(linep+1, &query_r->source_linenum) < 0) {
-                                       *error_r = t_strdup_printf(
-                                               "Invalid line number in '%s'", str);
-                                       return -1;
-                               }
-                       }
-               } else if (strncmp(str, "field:", 6) == 0) {
-                       const char *value = strchr(str+6, '=');
-                       if (value == NULL) {
-                               *error_r = t_strdup_printf(
-                                       "Missing '=' in '%s'", str);
-                               return -1;
-                       }
-                       if (!array_is_created(&fields))
-                               t_array_init(&fields, 4);
-                       struct event_filter_field *field =
-                               array_append_space(&fields);
-                       field->key = t_strdup_until(str+6, value);
-                       field->value = value+1;
-               } else if (str_begins(str, "cat:"))
-                       add_category(&categories, str+4);
-               else if (str_begins(str, "category:"))
-                       add_category(&categories, str+9);
-               else if (str_begins(str, "service:")) {
-                       /* service:name is short for category:service:name */
-                       add_category(&categories, str);
-               } else {
-                       *error_r = t_strdup_printf("Unknown event '%s'", str);
-                       return -1;
-               }
-               str = p;
-       } while (str != NULL);
-
-       if (array_is_created(&categories)) {
-               array_append_zero(&categories);
-               query_r->categories = array_front(&categories);
-       }
-       if (array_is_created(&fields)) {
-               array_append_zero(&fields);
-               query_r->fields = array_front(&fields);
-       }
-       return 0;
-}
-
-int event_filter_parse(const char *str, struct event_filter *filter,
-                      const char **error_r)
-{
-       struct event_filter_query query;
-       const char *p;
-
-       while (*str != '\0') {
-               if (*str == ' ') {
-                       str++;
-                       continue;
-               }
-
-               if (*str == '(') {
-                       /* everything inside (...) is a single query */
-                       str++;
-                       p = strchr(str, ')');
-                       if (p == NULL) {
-                               *error_r = "Missing ')'";
-                               return -1;
-                       }
-                       if (parse_query(t_strdup_until(str, p), &query, error_r) < 0)
-                               return -1;
-                       str = p+1;
-               } else if ((p = strchr(str, ' ')) != NULL) {
-                       /* parse a single-word query in the middle */
-                       if (parse_query(t_strdup_until(str, p), &query, error_r) < 0)
-                               return -1;
-                       str = p+1;
-               } else {
-                       /* single-word last query */
-                       if (parse_query(str, &query, error_r) < 0)
-                               return -1;
-                       str = "";
-               }
-
-               event_filter_add(filter, &query);
-       }
-
-       *error_r = NULL;
-
-       return 0;
-}
index b1ba2c827b3828a6b188789ce3229d40d0805398..46882e081185802dbe02c63816543727096422f4 100644 (file)
@@ -35,7 +35,6 @@ static const struct log_type_map {
 };
 
 struct event_filter_query_internal {
-       enum event_filter_log_type log_type_mask;
        struct event_filter_node *expr;
        void *context;
 };
@@ -104,6 +103,51 @@ void event_filter_unref(struct event_filter **_filter)
        }
 }
 
+int event_filter_parse(const char *str, struct event_filter *filter,
+                      const char **error_r)
+{
+       struct event_filter_query_internal *int_query;
+       struct event_filter_parser_state state;
+       int ret;
+
+       i_zero(&state);
+       state.input = str;
+       state.len = strlen(str);
+       state.pos = 0;
+       state.pool = filter->pool;
+
+       event_filter_parser_lex_init(&state.scanner);
+       event_filter_parser_set_extra(&state, state.scanner);
+
+       ret = event_filter_parser_parse(&state);
+
+       event_filter_parser_lex_destroy(state.scanner);
+
+       if ((ret == 0) && (state.output != NULL)) {
+               /* success - non-NULL expression */
+               i_assert(state.error == NULL);
+
+               int_query = array_append_space(&filter->queries);
+               int_query->context = NULL;
+               int_query->expr = state.output;
+
+               filter->named_queries_only = filter->named_queries_only && state.has_event_name;
+       } else if (ret != 0) {
+               /* error */
+               i_assert(state.output == NULL);
+               i_assert(state.error != NULL);
+
+               *error_r = state.error;
+       }
+
+       /*
+        * Note that success with a NULL expression output is possible, but
+        * turns into a no-op.
+        */
+
+       return (ret != 0) ? -1 : 0;
+}
+
 bool event_filter_category_to_log_type(const char *name,
                                       enum event_filter_log_type *log_type_r)
 {
@@ -155,7 +199,6 @@ event_filter_add_categories(pool_t pool,
                            const char *const *categories)
 {
        unsigned int categories_count = str_array_length(categories);
-       enum event_filter_log_type log_type;
        unsigned int i;
 
        if (categories_count == 0)
@@ -164,16 +207,13 @@ event_filter_add_categories(pool_t pool,
        for (i = 0; i < categories_count; i++) {
                struct event_filter_node *node;
 
-               if (event_filter_category_to_log_type(categories[i], &log_type)) {
-                       int_query->log_type_mask |= log_type;
-                       continue;
-               }
-
                node = p_new(pool, struct event_filter_node, 1);
                node->type = EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY;
                node->op = EVENT_FILTER_OP_CMP_EQ;
-               node->category.name = p_strdup(pool, categories[i]);
-               node->category.ptr = event_category_find_registered(categories[i]);
+               if (!event_filter_category_to_log_type(categories[i], &node->category.log_type)) {
+                       node->category.name = p_strdup(pool, categories[i]);
+                       node->category.ptr = event_category_find_registered(categories[i]);
+               }
 
                add_node(pool, &int_query->expr, node);
        }
@@ -246,11 +286,6 @@ void event_filter_add(struct event_filter *filter,
 
        event_filter_add_categories(filter->pool, int_query, query->categories);
        event_filter_add_fields(filter->pool, int_query, query->fields);
-
-       if (int_query->log_type_mask == 0) {
-               /* no explicit log types given. default to all. */
-               int_query->log_type_mask = EVENT_FILTER_LOG_TYPE_ALL;
-       }
 }
 
 static struct event_filter_node *
@@ -291,7 +326,6 @@ event_filter_merge_with_context_internal(struct event_filter *dest,
                struct event_filter_query_internal *new;
 
                new = array_append_space(&dest->queries);
-               new->log_type_mask = int_query->log_type_mask;
                new->expr = clone_expr(dest->pool, int_query->expr);
                new->context = with_context ? new_context : int_query->context;
        } T_END;
@@ -310,46 +344,6 @@ void event_filter_merge_with_context(struct event_filter *dest,
        event_filter_merge_with_context_internal(dest, src, new_context, TRUE);
 }
 
-static void
-event_filter_export_query_expr(const struct event_filter_query_internal *query,
-                              struct event_filter_node *node,
-                              string_t *dest)
-{
-       if (node == NULL)
-               return;
-
-       switch (node->type) {
-       case EVENT_FILTER_NODE_TYPE_LOGIC:
-               /* currently only AND is supported */
-               i_assert(node->op == EVENT_FILTER_OP_AND);
-               event_filter_export_query_expr(query, node->children[0], dest);
-               event_filter_export_query_expr(query, node->children[1], dest);
-               break;
-       case EVENT_FILTER_NODE_TYPE_EVENT_NAME:
-               str_append_c(dest, EVENT_FILTER_CODE_NAME);
-               str_append_tabescaped(dest, node->str);
-               str_append_c(dest, '\t');
-               break;
-       case EVENT_FILTER_NODE_TYPE_EVENT_SOURCE_LOCATION:
-               str_append_c(dest, EVENT_FILTER_CODE_SOURCE);
-               str_append_tabescaped(dest, node->str);
-               str_printfa(dest, "\t%ju\t", node->intmax);
-               break;
-       case EVENT_FILTER_NODE_TYPE_EVENT_CATEGORY:
-               str_append_c(dest, EVENT_FILTER_CODE_CATEGORY);
-               str_append_tabescaped(dest, node->category.name);
-               str_append_c(dest, '\t');
-               break;
-       case EVENT_FILTER_NODE_TYPE_EVENT_FIELD:
-               str_append_c(dest, EVENT_FILTER_CODE_FIELD);
-               str_append_tabescaped(dest, node->field.key);
-               str_append_c(dest, '\t');
-               str_append_tabescaped(dest, node->field.value.str);
-               str_append_c(dest, '\t');
-               break;
-       }
-}
-
 static const char *
 event_filter_export_query_expr_op(enum event_filter_node_op op)
 {
@@ -374,27 +368,27 @@ event_filter_export_query_expr_op(enum event_filter_node_op op)
 }
 
 static void
-event_filter_export_query_expr_new(const struct event_filter_query_internal *query,
-                                  struct event_filter_node *node,
-                                  string_t *dest)
+event_filter_export_query_expr(const struct event_filter_query_internal *query,
+                              struct event_filter_node *node,
+                              string_t *dest)
 {
        switch (node->type) {
        case EVENT_FILTER_NODE_TYPE_LOGIC:
                str_append_c(dest, '(');
                switch (node->op) {
                case EVENT_FILTER_OP_AND:
-                       event_filter_export_query_expr_new(query, node->children[0], dest);
+                       event_filter_export_query_expr(query, node->children[0], dest);
                        str_append(dest, " AND ");
-                       event_filter_export_query_expr_new(query, node->children[1], dest);
+                       event_filter_export_query_expr(query, node->children[1], dest);
                        break;
                case EVENT_FILTER_OP_OR:
-                       event_filter_export_query_expr_new(query, node->children[0], dest);
+                       event_filter_export_query_expr(query, node->children[0], dest);
                        str_append(dest, " OR ");
-                       event_filter_export_query_expr_new(query, node->children[1], dest);
+                       event_filter_export_query_expr(query, node->children[1], dest);
                        break;
                case EVENT_FILTER_OP_NOT:
                        str_append(dest, "NOT ");
-                       event_filter_export_query_expr_new(query, node->children[0], dest);
+                       event_filter_export_query_expr(query, node->children[0], dest);
                        break;
                case EVENT_FILTER_OP_CMP_EQ:
                case EVENT_FILTER_OP_CMP_GT:
@@ -446,28 +440,9 @@ event_filter_export_query_expr_new(const struct event_filter_query_internal *que
 static void
 event_filter_export_query(const struct event_filter_query_internal *query,
                          string_t *dest)
-{
-       unsigned int i;
-
-       event_filter_export_query_expr(query, query->expr, dest);
-
-       if (query->log_type_mask != EVENT_FILTER_LOG_TYPE_ALL) {
-               for (i = 0; i < N_ELEMENTS(event_filter_log_type_map); i++) {
-                       if ((query->log_type_mask & event_filter_log_type_map[i].log_type) == 0)
-                               continue;
-                       str_append_c(dest, EVENT_FILTER_CODE_CATEGORY);
-                       str_append_tabescaped(dest, event_filter_log_type_map[i].name);
-                       str_append_c(dest, '\t');
-               }
-       }
-}
-
-static void
-event_filter_export_query_new(const struct event_filter_query_internal *query,
-                             string_t *dest)
 {
        str_append_c(dest, '(');
-       event_filter_export_query_expr_new(query, query->expr, dest);
+       event_filter_export_query_expr(query, query->expr, dest);
        str_append_c(dest, ')');
 }
 
@@ -476,112 +451,12 @@ void event_filter_export(struct event_filter *filter, string_t *dest)
        const struct event_filter_query_internal *query;
        bool first = TRUE;
 
-       array_foreach(&filter->queries, query) {
-               if (!first)
-                       str_append_c(dest, '\t');
-               first = FALSE;
-               event_filter_export_query(query, dest);
-       }
-}
-
-void event_filter_export_new(struct event_filter *filter, string_t *dest)
-{
-       const struct event_filter_query_internal *query;
-       bool first = TRUE;
-
        array_foreach(&filter->queries, query) {
                if (!first)
                        str_append(dest, " OR ");
                first = FALSE;
-               event_filter_export_query_new(query, dest);
-       }
-}
-
-bool event_filter_import(struct event_filter *filter, const char *str,
-                        const char **error_r)
-{
-       return event_filter_import_unescaped(filter,
-               t_strsplit_tabescaped(str), error_r);
-}
-
-bool event_filter_import_unescaped(struct event_filter *filter,
-                                  const char *const *args,
-                                  const char **error_r)
-{
-       struct event_filter_query query;
-       ARRAY_TYPE(const_string) categories;
-       ARRAY(struct event_filter_field) fields;
-       bool changed;
-
-       t_array_init(&categories, 8);
-       t_array_init(&fields, 8);
-       i_zero(&query);
-       changed = FALSE;
-       for (unsigned int i = 0; args[i] != NULL; i++) {
-               const char *arg = args[i];
-
-               if (arg[0] == '\0') {
-                       /* finish the query */
-                       if (array_count(&categories) > 0) {
-                               array_append_zero(&categories);
-                               query.categories = array_front(&categories);
-                       }
-                       if (array_count(&fields) > 0) {
-                               array_append_zero(&fields);
-                               query.fields = array_front(&fields);
-                       }
-                       event_filter_add(filter, &query);
-
-                       /* start the next query */
-                       i_zero(&query);
-                       array_clear(&categories);
-                       array_clear(&fields);
-                       changed = FALSE;
-                       continue;
-               }
-
-               enum event_filter_code code = arg[0];
-               arg++;
-               switch (code) {
-               case EVENT_FILTER_CODE_NAME:
-                       query.name = arg;
-                       break;
-               case EVENT_FILTER_CODE_SOURCE:
-                       query.source_filename = arg;
-                       i++;
-                       if (args[i] == NULL) {
-                               *error_r = "Source line number missing";
-                               return FALSE;
-                       }
-                       if (str_to_uint(args[i], &query.source_linenum) < 0) {
-                               *error_r = "Invalid Source line number";
-                               return FALSE;
-                       }
-                       break;
-               case EVENT_FILTER_CODE_CATEGORY:
-                       array_push_back(&categories, &arg);
-                       break;
-               case EVENT_FILTER_CODE_FIELD: {
-                       struct event_filter_field *field;
-
-                       field = array_append_space(&fields);
-                       field->key = arg;
-                       i++;
-                       if (args[i] == NULL) {
-                               *error_r = "Field value missing";
-                               return FALSE;
-                       }
-                       field->value = args[i];
-                       break;
-               }
-               }
-               changed = TRUE;
-       }
-       if (changed) {
-               *error_r = "Expected TAB at the end";
-               return FALSE;
+               event_filter_export_query(query, dest);
        }
-       return TRUE;
 }
 
 static bool
@@ -764,13 +639,6 @@ event_filter_query_match(const struct event_filter_query_internal *query,
 
        i_assert(ctx->type < N_ELEMENTS(event_filter_log_type_map));
        log_type = event_filter_log_type_map[ctx->type].log_type;
-       if ((query->log_type_mask & log_type) == 0)
-               return FALSE;
-
-       /* Nothing to evaluate - this can happen if the filter consists
-          solely of log types (e.g., cat:debug) */
-       if (query->expr == NULL)
-               return TRUE;
 
        return event_filter_query_match_eval(query->expr, event, source_filename,
                                             source_linenum, log_type);
index edc48a877114740d7208978bd37698c5ded1675a..b402ea21006767a5b37fd95f976cf0643a8572c1 100644 (file)
@@ -45,20 +45,13 @@ void event_filter_merge_with_context(struct event_filter *dest,
                                     const struct event_filter *src,
                                     void *new_context);
 
-/* Export the filter into a tabescaped string, so its fields are separated
-   with TABs and there are no NUL, CR or LF characters. The context pointers
-   aren't exported. */
+/* Export the filter into a string.  The context pointers aren't exported. */
 void event_filter_export(struct event_filter *filter, string_t *dest);
 /* Add queries to the filter from the given string. The string is expected to
    be generated by event_filter_export(). Returns TRUE on success, FALSE on
    invalid string. */
-bool event_filter_import(struct event_filter *filter, const char *str,
-                        const char **error_r);
-/* Same as event_filter_import(), but string is already split into an array
-   of strings via *_strsplit_tabescaped(). */
-bool event_filter_import_unescaped(struct event_filter *filter,
-                                  const char *const *args,
-                                  const char **error_r);
+#define event_filter_import(filter, str, error_r) \
+       (event_filter_parse((str), (filter), (error_r)) == 0)
 
 /* Parse a string-ified query, filling the passed in filter */
 int event_filter_parse(const char *str, struct event_filter *filter,
index c0e790237f26b1ad26df25841475f232480ccd16..0fc80c2f3a01ccf773e68886e3d8a2d8af9c7bde 100644 (file)
@@ -5,6 +5,7 @@
 #include "llist.h"
 #include "hash.h"
 #include "str.h"
+#include "strescape.h"
 #include "lib-event-private.h"
 #include "event-filter.h"
 #include "ostream.h"
@@ -33,10 +34,13 @@ static struct connection_list *writer_clients = NULL;
 
 static void client_writer_send_handshake(struct writer_client *client)
 {
+       string_t *filter = t_str_new(128);
        string_t *str = t_str_new(128);
 
+       event_filter_export(stats_metrics_get_event_filter(stats_metrics), filter);
+
        str_append(str, "FILTER\t");
-       event_filter_export(stats_metrics_get_event_filter(stats_metrics), str);
+       str_append_tabescaped(str, str_c(filter));
        str_append_c(str, '\n');
        o_stream_nsend(client->conn.output, str_data(str), str_len(str));
 }
index bf5a15471f46a94b0a49ae9c723052b6ffb63695..37cfe43b15aa65bb5566830303e355a25b3e6423 100644 (file)
@@ -47,7 +47,7 @@ bool test_stats_callback(struct event *event,
 static const char *settings_blob_1 =
 "metric=test\n"
 "metric/test/metric_name=test\n"
-"metric/test/filter=event:test\n"
+"metric/test/filter=event=test\n"
 "\n";
 
 static int test_reader_server_input_args(struct connection *conn ATTR_UNUSED,
@@ -126,7 +126,7 @@ static void test_client_reader(void)
 static const char *settings_blob_2 =
 "metric=test\n"
 "metric/test/metric_name=test\n"
-"metric/test/filter=event:test\n"
+"metric/test/filter=event=test\n"
 "metric/test/group_by=test_name\n"
 "\n";
 
index 743846a9feabdabc7cdb76737a2d02c59df85577..608febdbcaed64fe2eb11ff9b6875f6c762c2bc0 100644 (file)
@@ -20,7 +20,7 @@ static int test_writer_server_input_args(struct connection *conn,
 {
        /* check filter */
        test_assert_strcmp(args[0], "FILTER");
-       test_assert_strcmp(args[1], "ntest");
+       test_assert_strcmp(args[1], "(event=\"test\")");
        /* send commands now */
        string_t *send_buf = t_str_new(128);
        o_stream_nsend_str(conn->output, "CATEGORY\ttest\n");
@@ -100,7 +100,7 @@ bool test_stats_callback(struct event *event,
 static const char *settings_blob_1 =
 "metric=test\n"
 "metric/test/metric_name=test\n"
-"metric/test/filter=event:test\n"
+"metric/test/filter=event=test\n"
 "\n";
 
 static void test_client_writer(void)
index bb1232e823606d6f754bd6f3900b0ad657719c14..c384014b63b6036266d58c0b04e352a092475a5b 100644 (file)
@@ -20,7 +20,7 @@ bool test_stats_callback(struct event *event,
 static const char *settings_blob_1 =
 "metric=test\n"
 "metric/test/metric_name=test\n"
-"metric/test/filter=event:test\n"
+"metric/test/filter=event=test\n"
 "\n";
 
 static void test_stats_metrics(void)
@@ -47,7 +47,7 @@ static void test_stats_metrics(void)
 static const char *settings_blob_2 =
 "metric=test\n"
 "metric/test/metric_name=test\n"
-"metric/test/filter=(event:test field:test_field=value)\n"
+"metric/test/filter=(event=test AND test_field=value)\n"
 "\n";
 
 static void test_stats_metrics_filter(void)
@@ -61,7 +61,7 @@ static void test_stats_metrics_filter(void)
                stats_metrics_get_event_filter(stats_metrics);
        string_t *str_filter = t_str_new(64);
        event_filter_export(filter, str_filter);
-       test_assert_strcmp("ntest       ftest_field     value   ",
+       test_assert_strcmp("((event=\"test\" AND \"test_field\"=\"value\"))",
                           str_c(str_filter));
 
        /* send event */
@@ -167,7 +167,7 @@ static void test_stats_metrics_group_by_discrete_real(const struct discrete_test
 
        test_init(t_strdup_printf("metric=test\n"
                                  "metric/test/metric_name=test\n"
-                                 "metric/test/filter=event:test\n"
+                                 "metric/test/filter=event=test\n"
                                  "metric/test/group_by=%s\n"
                                  "\n", test->settings_blob));
 
@@ -343,7 +343,7 @@ static void test_stats_metrics_group_by_quantized_real(const struct quantized_te
 
        test_init(t_strdup_printf("metric=test\n"
                                  "metric/test/metric_name=test\n"
-                                 "metric/test/filter=event:test\n"
+                                 "metric/test/filter=event=test\n"
                                  "metric/test/group_by=test_name foobar:%s\n"
                                  "\n", test->settings_blob));