From 99c453df9d45f6dba7c63e60326f408f48216e57 Mon Sep 17 00:00:00 2001 From: Emeric Brun Date: Mon, 25 May 2020 15:01:04 +0200 Subject: [PATCH] MEDIUM: ring: new section ring to declare custom ring buffers. It is possible to globally declare ring-buffers, to be used as target for log servers or traces. ring Creates a new ring-buffer with name . description The descritpition is an optional description string of the ring. It will appear on CLI. By default, is reused to fill this field. format Format used to store events into the ring buffer. Arguments: is the log format used when generating syslog messages. It may be one of the following : iso A message containing only the ISO date, followed by the text. The PID, process name and system name are omitted. This is designed to be used with a local log server. raw A message containing only the text. The level, PID, date, time, process name and system name are omitted. This is designed to be used in containers or during development, where the severity only depends on the file descriptor used (stdout/stderr). This is the default. rfc3164 The RFC3164 syslog message format. This is the default. (https://tools.ietf.org/html/rfc3164) rfc5424 The RFC5424 syslog message format. (https://tools.ietf.org/html/rfc5424) short A message containing only a level between angle brackets such as '<3>', followed by the text. The PID, date, time, process name and system name are omitted. This is designed to be used with a local log server. This format is compatible with what the systemd logger consumes. timed A message containing only a level between angle brackets such as '<3>', followed by ISO date and by the text. The PID, process name and system name are omitted. This is designed to be used with a local log server. maxlen The maximum length of an event message stored into the ring, including formatted header. If an event message is longer than , it will be truncated to this length. size This is the optional size in bytes for the ring-buffer. Default value is set to BUFSIZE. Example: global log ring@myring local7 ring myring description "My local buffer" format rfc3164 maxlen 1200 Note: ring names are resolved during post configuration processing. --- doc/configuration.txt | 67 ++++++++++++++ include/types/log.h | 1 + include/types/sink.h | 4 +- src/log.c | 10 +-- src/sink.c | 202 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 272 insertions(+), 12 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index dcf9fe5ff..674acd2fd 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -53,6 +53,7 @@ Summary 3.6. Mailers 3.7. Programs 3.8. HTTP-errors +3.9. Rings 4. Proxies 4.1. Proxy keywords matrix @@ -2543,6 +2544,72 @@ errorfile errorfile 404 /etc/haproxy/errorfiles/site2/404.http errorfile 408 /dev/null # work around Chrome pre-connect bug +3.9. Rings +---------- + +It is possible to globally declare ring-buffers, to be used as target for log +servers or traces. + +ring + Creates a new ring-buffer with name . + +description + The descritpition is an optional description string of the ring. It will + appear on CLI. By default, is reused to fill this field. + +format + Format used to store events into the ring buffer. + + Arguments: + is the log format used when generating syslog messages. It may be + one of the following : + + iso A message containing only the ISO date, followed by the text. + The PID, process name and system name are omitted. This is + designed to be used with a local log server. + + raw A message containing only the text. The level, PID, date, time, + process name and system name are omitted. This is designed to be + used in containers or during development, where the severity + only depends on the file descriptor used (stdout/stderr). This + is the default. + + rfc3164 The RFC3164 syslog message format. This is the default. + (https://tools.ietf.org/html/rfc3164) + + rfc5424 The RFC5424 syslog message format. + (https://tools.ietf.org/html/rfc5424) + + short A message containing only a level between angle brackets such as + '<3>', followed by the text. The PID, date, time, process name + and system name are omitted. This is designed to be used with a + local log server. This format is compatible with what the systemd + logger consumes. + + timed A message containing only a level between angle brackets such as + '<3>', followed by ISO date and by the text. The PID, process + name and system name are omitted. This is designed to be + used with a local log server. + +maxlen + The maximum length of an event message stored into the ring, + including formatted header. If an event message is longer than + , it will be truncated to this length. + +size + This is the optional size in bytes for the ring-buffer. Default value is + set to BUFSIZE. + + Example: + global + log ring@myring local7 + + ring myring + description "My local buffer" + format rfc3164 + maxlen 1200 + size 32764 + 4. Proxies ---------- diff --git a/include/types/log.h b/include/types/log.h index 88c3a8c46..fb9aa586e 100644 --- a/include/types/log.h +++ b/include/types/log.h @@ -212,6 +212,7 @@ struct logsrv { struct sockaddr_storage addr; struct smp_info lb; struct sink *sink; + char *ring_name; enum log_tgt type; int format; int facility; diff --git a/include/types/sink.h b/include/types/sink.h index 28b037920..ef1109679 100644 --- a/include/types/sink.h +++ b/include/types/sink.h @@ -52,8 +52,8 @@ enum sink_fmt { /* describes the configuration and current state of an event sink */ struct sink { struct list sink_list; // position in the sink list - const char *name; // sink name - const char *desc; // sink description + char *name; // sink name + char *desc; // sink description enum sink_fmt fmt; // format expected by the sink enum sink_type type; // type of storage uint32_t maxlen; // max message length (truncated above) diff --git a/src/log.c b/src/log.c index 78f1a9e0b..d69c94e04 100644 --- a/src/log.c +++ b/src/log.c @@ -1041,16 +1041,10 @@ int parse_logsrv(char **args, struct list *logsrvs, int do_del, char **err) /* now, back to the address */ logsrv->type = LOG_TARGET_DGRAM; if (strncmp(args[1], "ring@", 5) == 0) { - struct sink *sink = sink_find(args[1] + 5); - - if (!sink || sink->type != SINK_TYPE_BUFFER) { - memprintf(err, "cannot find ring buffer '%s'", args[1] + 5); - goto error; - } - logsrv->addr.ss_family = AF_UNSPEC; logsrv->type = LOG_TARGET_BUFFER; - logsrv->sink = sink; + logsrv->sink = NULL; + logsrv->ring_name = strdup(args[1] + 5); goto done; } diff --git a/src/sink.c b/src/sink.c index 15595cd27..c65513a06 100644 --- a/src/sink.c +++ b/src/sink.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include @@ -31,6 +32,8 @@ struct list sink_list = LIST_HEAD_INIT(sink_list); +struct sink *cfg_sink; + struct sink *sink_find(const char *name) { struct sink *sink; @@ -58,8 +61,8 @@ static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt if (!sink) goto end; - sink->name = name; - sink->desc = desc; + sink->name = strdup(name); + sink->desc = strdup(desc); sink->fmt = fmt; sink->type = SINK_TYPE_NEW; sink->maxlen = BUFSIZE; @@ -123,6 +126,8 @@ struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt, sink->ctx.ring = ring_new(size); if (!sink->ctx.ring) { LIST_DEL(&sink->sink_list); + free(sink->name); + free(sink->desc); free(sink); goto fail; } @@ -311,6 +316,193 @@ static int cli_parse_show_events(char **args, char *payload, struct appctx *appc return ring_attach_cli(sink->ctx.ring, appctx); } +/* + * Parse "ring" section and create corresponding sink buffer. + * + * The function returns 0 in success case, otherwise, it returns error + * flags. + */ +int cfg_parse_ring(const char *file, int linenum, char **args, int kwm) +{ + int err_code = 0; + const char *inv; + size_t size = BUFSIZE; + + if (strcmp(args[0], "ring") == 0) { /* new peers section */ + if (!*args[1]) { + ha_alert("parsing [%s:%d] : missing ring name.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + inv = invalid_char(args[1]); + if (inv) { + ha_alert("parsing [%s:%d] : invalid ring name '%s' (character '%c' is not permitted).\n", file, linenum, args[1], *inv); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + if (sink_find(args[1])) { + ha_alert("parsing [%s:%d] : sink named '%s' already exists.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + cfg_sink = sink_new_buf(args[1], args[1] , SINK_FMT_RAW, size); + if (!cfg_sink || cfg_sink->type != SINK_TYPE_BUFFER) { + ha_alert("parsing [%s:%d] : unable to create a new sink buffer for ring '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + } + else if (strcmp(args[0], "size") == 0) { + size = atol(args[1]); + if (!size) { + ha_alert("parsing [%s:%d] : invalid size '%s' for new sink buffer.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + if (!cfg_sink || (cfg_sink->type != SINK_TYPE_BUFFER) + || !ring_resize(cfg_sink->ctx.ring, size)) { + ha_alert("parsing [%s:%d] : fail to set sink buffer size '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + } + else if (strcmp(args[0],"format") == 0) { + if (!cfg_sink) { + ha_alert("parsing [%s:%d] : unable to set format '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + if (strcmp(args[1], "raw") == 0) { + cfg_sink->fmt = SINK_FMT_RAW; + } + else if (strcmp(args[1], "short") == 0) { + cfg_sink->fmt = SINK_FMT_SHORT; + } + else if (strcmp(args[1], "iso") == 0) { + cfg_sink->fmt = SINK_FMT_ISO; + } + else if (strcmp(args[1], "timed") == 0) { + cfg_sink->fmt = SINK_FMT_TIMED; + } + else if (strcmp(args[1], "rfc3164") == 0) { + cfg_sink->fmt = SINK_FMT_RFC3164; + } + else if (strcmp(args[1], "rfc5424") == 0) { + cfg_sink->fmt = SINK_FMT_RFC5424; + } + else { + ha_alert("parsing [%s:%d] : unknown format '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + } + else if (strcmp(args[0],"maxlen") == 0) { + if (!cfg_sink) { + ha_alert("parsing [%s:%d] : unable to set event max length '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + cfg_sink->maxlen = atol(args[1]); + if (!cfg_sink->maxlen) { + ha_alert("parsing [%s:%d] : invalid size '%s' for new sink buffer.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + } + else if (strcmp(args[0],"description") == 0) { + if (!cfg_sink) { + ha_alert("parsing [%s:%d] : unable to set description '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + if (!*args[1]) { + ha_alert("parsing [%s:%d] : missing ring description text.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + + free(cfg_sink->desc); + + cfg_sink->desc = strdup(args[1]); + if (!cfg_sink->desc) { + ha_alert("parsing [%s:%d] : fail to set description '%s'.\n", file, linenum, args[1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto err; + } + } + +err: + return err_code; +} + +/* + * Post parsing "ring" section. + * + * The function returns 0 in success case, otherwise, it returns error + * flags. + */ +int cfg_post_parse_ring() +{ + int err_code = 0; + + if (cfg_sink && (cfg_sink->type == SINK_TYPE_BUFFER)) { + if (cfg_sink->maxlen > b_size(&cfg_sink->ctx.ring->buf)) { + ha_warning("ring '%s' event max length '%u' exceeds size, forced to size '%lu'.\n", + cfg_sink->name, cfg_sink->maxlen, b_size(&cfg_sink->ctx.ring->buf)); + cfg_sink->maxlen = b_size(&cfg_sink->ctx.ring->buf); + err_code |= ERR_ALERT; + } + } + + cfg_sink = NULL; + + return err_code; +} + +/* resolve sink names at end of config. Returns 0 on success otherwise error + * flags. +*/ +int post_sink_resolve() +{ + int err_code = 0; + struct logsrv *logsrv, *logb; + struct sink *sink; + struct proxy *px; + + list_for_each_entry_safe(logsrv, logb, &global.logsrvs, list) { + if (logsrv->type == LOG_TARGET_BUFFER) { + sink = sink_find(logsrv->ring_name); + if (!sink || sink->type != SINK_TYPE_BUFFER) { + ha_alert("global log server uses unkown ring named '%s'.\n", logsrv->ring_name); + err_code |= ERR_ALERT | ERR_FATAL; + } + logsrv->sink = sink; + } + } + + for (px = proxies_list; px; px = px->next) { + list_for_each_entry_safe(logsrv, logb, &px->logsrvs, list) { + if (logsrv->type == LOG_TARGET_BUFFER) { + sink = sink_find(logsrv->ring_name); + if (!sink || sink->type != SINK_TYPE_BUFFER) { + ha_alert("proxy '%s' log server uses unkown ring named '%s'.\n", px->id, logsrv->ring_name); + err_code |= ERR_ALERT | ERR_FATAL; + } + logsrv->sink = sink; + } + } + } + return err_code; +} + + static void sink_init() { sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1); @@ -326,6 +518,8 @@ static void sink_deinit() if (sink->type == SINK_TYPE_BUFFER) ring_free(sink->ctx.ring); LIST_DEL(&sink->sink_list); + free(sink->name); + free(sink->desc); free(sink); } } @@ -340,6 +534,10 @@ static struct cli_kw_list cli_kws = {{ },{ INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); +/* config parsers for this section */ +REGISTER_CONFIG_SECTION("ring", cfg_parse_ring, cfg_post_parse_ring); +REGISTER_POST_CHECK(post_sink_resolve); + /* * Local variables: * c-indent-level: 8