diff --git a/doc/configuration.txt b/doc/configuration.txt index e434a4750..6e86a0a79 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -614,6 +614,7 @@ The following keywords are supported in the "global" section : - pidfile - presetenv - resetenv + - ring - uid - ulimit-n - user @@ -1193,6 +1194,51 @@ resetenv [ ...] next line in the configuration file sees the new environment. See also "setenv", "presetenv", and "unsetenv". +ring [desc ] [format ] [size ] [maxlen ] + Creates a named ring buffer which could be used on log line for instance. + + is an optionnal description string of the ring. It will appear on + CLI. By default, is reused to fill this field. + + 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. + + is 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. + + is the ring identifier, which follows the same naming convention as + proxies and servers. + + is the optionnal size in bytes. Default value is set to BUFSIZE. + stats bind-process [ all | odd | even | [-[process_num>]] ] ... Limits the stats socket to a certain set of processes numbers. By default the stats socket is bound to all processes, causing a warning to be emitted when diff --git a/include/common/cfgparse.h b/include/common/cfgparse.h index 1a288166a..286099b37 100644 --- a/include/common/cfgparse.h +++ b/include/common/cfgparse.h @@ -28,6 +28,7 @@ #include #include +#include #include /* configuration sections */ diff --git a/include/proto/sink.h b/include/proto/sink.h index d30f30a74..c3121855e 100644 --- a/include/proto/sink.h +++ b/include/proto/sink.h @@ -78,6 +78,9 @@ static inline ssize_t sink_write(struct sink *sink, const struct ist msg[], size return sent; } + +int parse_sinkbuf(char **args, char **err); + #endif /* _PROTO_SINK_H */ /* 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/cfgparse-global.c b/src/cfgparse-global.c index 2b3249758..d971c11ab 100644 --- a/src/cfgparse-global.c +++ b/src/cfgparse-global.c @@ -905,6 +905,13 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm) free(global.log_send_hostname); global.log_send_hostname = strdup(name); } + else if (!strcmp(args[0], "ring")) { + if (!parse_sinkbuf(args, &errmsg)) { + ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } else if (!strcmp(args[0], "server-state-base")) { /* path base where HAProxy can find server state files */ if (global.server_state_base != NULL) { ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); diff --git a/src/sink.c b/src/sink.c index 15595cd27..34bc97cdd 100644 --- a/src/sink.c +++ b/src/sink.c @@ -58,8 +58,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 +123,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; } @@ -326,10 +328,130 @@ 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); } } +/* + * Parse "ring" keyword and create corresponding sink buffer. + * + * The function returns 1 in success case, otherwise, it returns 0 and err is + * filled. + */ + +int parse_sinkbuf(char **args, char **err) +{ + int myidx = 2; + size_t size = BUFSIZE; + size_t maxlen = size; + int format = SINK_FMT_RAW; + struct sink *sink = NULL; + char *desc, *name = args[1]; + const char *inv; + + if (!*name) { + memprintf(err, "missing sink name"); + goto error; + } + + inv = invalid_char(name); + if (inv) { + memprintf(err, "invalid sink name '%s' (character '%c' is not permitted)", name, *inv); + goto error; + } + + desc = name; + + while (*args[myidx]) { + if (!strcmp(args[myidx], "size")) { + myidx++; + size = atol(args[myidx]); + if (!size) { + memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name); + goto error; + } + } + else if (!strcmp(args[myidx], "maxlen")) { + myidx++; + maxlen = atol(args[myidx]); + if (!maxlen) { + memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name); + goto error; + } + } + else if (!strcmp(args[myidx], "maxlen")) { + myidx++; + maxlen = atol(args[myidx]); + if (!maxlen) { + memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name); + goto error; + } + } + else if (!strcmp(args[myidx], "desc")) { + myidx++; + desc = args[myidx]; + } + else if (!strcmp(args[myidx],"format")) { + myidx++; + if (!strcmp(args[myidx], "raw")) { + format = SINK_FMT_RAW; + } + else if (!strcmp(args[myidx], "short")) { + format = SINK_FMT_SHORT; + } + else if (!strcmp(args[myidx], "iso")) { + format = SINK_FMT_ISO; + } + else if (!strcmp(args[myidx], "timed")) { + format = SINK_FMT_TIMED; + } + else if (!strcmp(args[myidx], "rfc3164")) { + format = SINK_FMT_RFC3164; + } + else if (!strcmp(args[myidx], "rfc5424")) { + format = SINK_FMT_RFC5424; + } + else { + memprintf(err, "unknown format '%s' for new sink buffer for ring '%s'", args[myidx], name); + goto error; + } + } + else { + memprintf(err, "unknown parameter '%s' for new sink buffer for ring '%s'", args[myidx], name); + goto error; + } + myidx++; + } + + if (maxlen > size) { + memprintf(err, "maxlen '%lu' exceeds size of the new sink buffer for ring '%s'", maxlen, name); + goto error; + } + + sink = sink_new_buf(name, desc, format, size); + if (!sink || sink->type != SINK_TYPE_BUFFER) { + memprintf(err, "failed to create a new sink buffer for ring '%s'", name); + goto error; + } + + sink->maxlen = maxlen; + + return 1; +error: + if (sink) { + if (sink->type == SINK_TYPE_BUFFER) + ring_free(sink->ctx.ring); + LIST_DEL(&sink->sink_list); + free(sink->name); + free(sink->desc); + free(sink); + } + return 0; + +} + INITCALL0(STG_REGISTER, sink_init); REGISTER_POST_DEINIT(sink_deinit);