From f82f6244254b262889749bf27b3368a3555621a9 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Mon, 16 Apr 2018 15:00:27 +0300 Subject: [PATCH] lib-master: Add improved log filter parsing It now supports parenthesis to perform ANDs within a query. For example: "(ruleA1 ruleA2) ruleB (ruleC1 ruleC2 ruleC3)" has 3 ORed rules. Rules can contain: - event: - source:[:] - field:= can be used multiple times - cat[egory]: can be used multiple times For example: event:http_request_finished (cat:error cat:storage) This matches either the "http_request_finished" named event, or alternatively any error event that belongs to "storage" category. --- src/lib-master/master-service-settings.c | 109 +++++++++++++++++++++-- 1 file changed, 100 insertions(+), 9 deletions(-) diff --git a/src/lib-master/master-service-settings.c b/src/lib-master/master-service-settings.c index 14ee16197d..5fee08a096 100644 --- a/src/lib-master/master-service-settings.c +++ b/src/lib-master/master-service-settings.c @@ -106,20 +106,111 @@ const struct setting_parser_info master_service_setting_parser_info = { }; /* */ +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 (strncmp(str, "cat:", 4) == 0 || + strncmp(str, "category:", 9) == 0) { + if (!array_is_created(&categories)) + t_array_init(&categories, 4); + str = strchr(str, ':'); + i_assert(str != NULL); + str++; + array_append(&categories, &str, 1); + } 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_idx(&categories, 0); + } + if (array_is_created(&fields)) { + array_append_zero(&fields); + query_r->fields = array_idx(&fields, 0); + } + return 0; +} + int master_service_log_filter_parse(struct event_filter *filter, const char *str, const char **error_r) { - const char *categories[2] = { NULL, NULL }; - struct event_filter_query query = { - .categories = categories - }; - - /* FIXME: we should support more complicated filters */ - const char *const *args = t_strsplit_spaces(str, " "); - for (unsigned int i = 0; args[i] != NULL; i++) { - categories[0] = args[i]; + 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; }