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