diff --git a/include/types/log.h b/include/types/log.h index d4d5976a8..9f9912aa2 100644 --- a/include/types/log.h +++ b/include/types/log.h @@ -169,9 +169,33 @@ struct logformat_node { #define LW_FRTIP 8192 /* frontend IP */ #define LW_XPRT 16384 /* transport layer information (eg: SSL) */ +/* Range of indexes for log sampling. */ +struct smp_log_range { + unsigned int low; /* Low limit of the indexes of this range. */ + unsigned int high; /* High limit of the indexes of this range. */ + size_t sz; /* The size of this range, or number of indexes in + * this range. + */ + unsigned int curr_idx; /* The current index used to sample this range of + *indexes. + */ +}; + +/* Log sampling information. */ +struct smp_info { + struct smp_log_range *smp_rgs; /* Array of ranges for log sampling. */ + size_t smp_rgs_sz; /* The size of array. */ + size_t smp_sz; /* The total number of logs to be sampled. */ + unsigned int curr_rg; /* The current range to be sampled. */ + unsigned int curr_idx; /* A counter to store the current index of the log + * already sampled. + */ +}; + struct logsrv { struct list list; struct sockaddr_storage addr; + struct smp_info lb; int format; int facility; int level; diff --git a/src/log.c b/src/log.c index e9d711c72..cffd8f148 100644 --- a/src/log.c +++ b/src/log.c @@ -691,6 +691,79 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list return 1; } +/* + * Parse the first range of indexes from a string made of a list of comma seperated + * ranges of indexes. Note that an index may be considered as a particular range + * with a high limit to the low limit. + */ +int get_logsrv_smp_range(unsigned int *low, unsigned int *high, char **arg, char **err) +{ + char *end, *p; + + *low = *high = 0; + + p = *arg; + end = strchr(p, ','); + if (!end) + end = p + strlen(p); + + *high = *low = read_uint((const char **)&p, end); + if (!*low || (p != end && *p != '-')) + goto err; + + if (p == end) + goto done; + + p++; + *high = read_uint((const char **)&p, end); + if (!*high || *high <= *low || p != end) + goto err; + + done: + if (*end == ',') + end++; + *arg = end; + return 1; + + err: + memprintf(err, "wrong sample range '%s'", *arg); + return 0; +} + +/* + * Returns 1 if the range defined by and overlaps + * one of them in array of ranges with the size of this + * array, 0 if not. + */ +int smp_log_ranges_overlap(struct smp_log_range *rgs, size_t sz, + unsigned int low, unsigned int high, char **err) +{ + size_t i; + + for (i = 0; i < sz; i++) { + if ((low >= rgs[i].low && low <= rgs[i].high) || + (high >= rgs[i].low && high <= rgs[i].high)) { + memprintf(err, "ranges are overlapping"); + return 1; + } + } + + return 0; +} + +int smp_log_range_cmp(const void *a, const void *b) +{ + const struct smp_log_range *rg_a = a; + const struct smp_log_range *rg_b = b; + + if (rg_a->high < rg_b->low) + return -1; + else if (rg_a->low > rg_b->high) + return 1; + + return 0; +} + /* * Parse "log" keyword and update list accordingly. * @@ -808,6 +881,71 @@ int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err) cur_arg += 2; } + if (strcmp(args[cur_arg], "sample") == 0) { + unsigned low, high; + char *p, *beg, *end, *smp_sz_str; + struct smp_log_range *smp_rgs = NULL; + size_t smp_rgs_sz = 0, smp_sz = 0, new_smp_sz; + + p = args[cur_arg+1]; + smp_sz_str = strchr(p, ':'); + if (!smp_sz_str) { + memprintf(err, "Missing sample size"); + goto error; + } + + *smp_sz_str++ = '\0'; + + end = p + strlen(p); + + while (p != end) { + if (!get_logsrv_smp_range(&low, &high, &p, err)) + goto error; + + if (smp_rgs && smp_log_ranges_overlap(smp_rgs, smp_rgs_sz, low, high, err)) + goto error; + + smp_rgs = my_realloc2(smp_rgs, (smp_rgs_sz + 1) * sizeof *smp_rgs); + if (!smp_rgs) { + memprintf(err, "out of memory error"); + goto error; + } + + smp_rgs[smp_rgs_sz].low = low; + smp_rgs[smp_rgs_sz].high = high; + smp_rgs[smp_rgs_sz].sz = high - low + 1; + smp_rgs[smp_rgs_sz].curr_idx = 0; + if (smp_rgs[smp_rgs_sz].high > smp_sz) + smp_sz = smp_rgs[smp_rgs_sz].high; + smp_rgs_sz++; + } + + beg = smp_sz_str; + end = beg + strlen(beg); + new_smp_sz = read_uint((const char **)&beg, end); + if (!new_smp_sz || beg != end) { + memprintf(err, "wrong sample size '%s' for sample range '%s'", + smp_sz_str, args[cur_arg+1]); + goto error; + } + + if (new_smp_sz < smp_sz) { + memprintf(err, "sample size %zu should be greater or equal to " + "%zu the maximum of the high ranges limits", + new_smp_sz, smp_sz); + goto error; + } + smp_sz = new_smp_sz; + + /* Let's order array. */ + qsort(smp_rgs, smp_rgs_sz, sizeof(struct smp_log_range), smp_log_range_cmp); + + logsrv->lb.smp_rgs = smp_rgs; + logsrv->lb.smp_rgs_sz = smp_rgs_sz; + logsrv->lb.smp_sz = smp_sz; + + cur_arg += 2; + } /* parse the facility */ logsrv->facility = get_log_facility(args[cur_arg]); if (logsrv->facility < 0) {