Skip to content

Commit

Permalink
lib-master: Add improved log filter parsing
Browse files Browse the repository at this point in the history
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:<name>
 - source:<filename>[:<line number>]
 - field:<key>=<value> can be used multiple times
 - cat[egory]:<value> 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.
  • Loading branch information
sirainen authored and cmouse committed Aug 7, 2018
1 parent 82ab59c commit f82f624
Showing 1 changed file with 100 additions and 9 deletions.
109 changes: 100 additions & 9 deletions src/lib-master/master-service-settings.c
Expand Up @@ -106,20 +106,111 @@ const struct setting_parser_info master_service_setting_parser_info = {
};

/* <settings checks> */
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;
}
Expand Down

0 comments on commit f82f624

Please sign in to comment.