]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: log: reorder send log helpers by dependency order
authorAurelien DARRAGON <adarragon@haproxy.com>
Wed, 22 May 2024 13:19:32 +0000 (15:19 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Thu, 13 Jun 2024 13:43:09 +0000 (15:43 +0200)
This commit looks messy, but all it does is reorganize send_log() helpers
by dependency order to remove the need of forward-declaring some of them.

Also, since they're all internal helpers, let's explicitly mark them as
static to prevent any misuse.

include/haproxy/log.h
src/log.c

index bc865524f777f399cb4c3ee537389b765b317700..8d7b508688b018bd2f2cf9810b13ffaf6410ae16 100644 (file)
@@ -122,15 +122,6 @@ int parse_logger(char **args, struct list *loggers, int do_del, const char *file
 void send_log(struct proxy *p, int level, const char *format, ...)
        __attribute__ ((format(printf, 3, 4)));
 
-/*
- * This function sends a syslog message to all loggers of a proxy,
- * or to global loggers if the proxy is NULL.
- * It also tries not to waste too much time computing the message header.
- * It doesn't care about errors nor does it report them.
- */
-
-void __send_log(struct list *loggers, struct buffer *tag, int level, char *message, size_t size, char *sd, size_t sd_size);
-
 /*
  * returns log format for <fmt> or LOG_FORMAT_UNSPEC if not found.
  */
index b607d793e7001cf20439a9250556947e93bb6e38..052fc07e1431f0ec9f817a827511f4b40dbc856f 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -2551,651 +2551,651 @@ static char *lf_port(char *dst, const struct sockaddr *sockaddr, size_t size, st
        return ret;
 }
 
-
-/*
- * This function sends the syslog message using a printf format string. It
- * expects an LF-terminated message.
- */
-void send_log(struct proxy *p, int level, const char *format, ...)
-{
-       va_list argp;
-       int  data_len;
-
-       if (level < 0 || format == NULL || logline == NULL)
-               return;
-
-       va_start(argp, format);
-       data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
-       if (data_len < 0 || data_len > global.max_syslog_len)
-               data_len = global.max_syslog_len;
-       va_end(argp);
-
-       __send_log((p ? &p->loggers : NULL), (p ? &p->log_tag : NULL), level,
-                  logline, data_len, default_rfc5424_sd_log_format, 2);
-}
 /*
- * This function builds a log header according to <hdr> settings.
+ * This function sends a syslog message.
+ * <target> is the actual log target where log will be sent,
  *
- * If hdr.format is set to LOG_FORMAT_UNSPEC, it tries to determine
- * format based on hdr.metadata. It is useful for log-forwarding to be
- * able to forward any format without settings.
+ * Message will be prefixed by header according to <hdr> setting.
+ * Final message will be truncated <maxlen> parameter and will be
+ * terminated with an LF character.
  *
- * This function returns a struct ist array of elements of the header
- * nbelem is set to the number of available elements.
- * This function returns currently a maximum of NB_LOG_HDR_IST_ELEMENTS
- * elements.
+ * Does not return any error
  */
-struct ist *build_log_header(struct log_header hdr, size_t *nbelem)
+static inline void __do_send_log(struct log_target *target, struct log_header hdr,
+                                 int nblogger, size_t maxlen,
+                                 char *message, size_t size)
 {
-       static THREAD_LOCAL struct {
-               struct ist ist_vector[NB_LOG_HDR_MAX_ELEMENTS];
-               char timestamp_buffer[LOG_LEGACYTIME_LEN+1+1];
-               time_t cur_legacy_time;
-               char priority_buffer[6];
-       } hdr_ctx = { .priority_buffer = "<<<<>" };
+       static THREAD_LOCAL struct iovec iovec[NB_LOG_HDR_MAX_ELEMENTS+1+1] = { }; /* header elements + message + LF */
+       static THREAD_LOCAL struct msghdr msghdr = {
+               //.msg_iov = iovec,
+               .msg_iovlen = NB_LOG_HDR_MAX_ELEMENTS+2
+       };
+       static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
+       static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
+       int *plogfd;
+       int sent;
+       size_t nbelem;
+       struct ist *msg_header = NULL;
 
-       struct tm logtime;
-       int len;
-       int fac_level = 0;
-       time_t time = date.tv_sec;
-       struct ist *metadata = hdr.metadata;
-       enum log_fmt format = hdr.format;
-       int facility = hdr.facility;
-       int level = hdr.level;
+       msghdr.msg_iov = iovec;
 
-       *nbelem = 0;
+       /* historically some messages used to already contain the trailing LF
+        * or Zero. Let's remove all trailing LF or Zero
+        */
+       while (size && (message[size-1] == '\n' || (message[size-1] == 0)))
+               size--;
 
+       if (target->type == LOG_TARGET_BUFFER) {
+               plogfd = NULL;
+               goto send;
+       }
+       else if (target->addr->ss_family == AF_CUST_EXISTING_FD) {
+               /* the socket's address is a file descriptor */
+               plogfd = (int *)&((struct sockaddr_in *)target->addr)->sin_addr.s_addr;
+       }
+       else if (target->addr->ss_family == AF_UNIX)
+               plogfd = &logfdunix;
+       else
+               plogfd = &logfdinet;
 
-       if (format == LOG_FORMAT_UNSPEC) {
-               format = LOG_FORMAT_RAW;
-               if (metadata) {
-                       /* If a hostname is set, it appears we want to perform syslog
-                        * because only rfc5427 or rfc3164 support an hostname.
-                        */
-                       if (metadata[LOG_META_HOST].len) {
-                               /* If a rfc5424 compliant timestamp is used we consider
-                                * that output format is rfc5424, else legacy format
-                                * is used as specified default for local logs
-                                * in documentation.
-                                */
-                               if ((metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-')
-                                   || (metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN))
-                                       format = LOG_FORMAT_RFC5424;
-                               else
-                                       format = LOG_FORMAT_RFC3164;
-                       }
-                       else if (metadata[LOG_META_TAG].len) {
-                               /* Tag is present but no hostname, we should
-                                * consider we try to emit a local log
-                                * in legacy format (analog to RFC3164 but
-                                * with stripped hostname).
-                                */
-                               format = LOG_FORMAT_LOCAL;
-                       }
-                       else if (metadata[LOG_META_PRIO].len) {
-                               /* the source seems a parsed message
-                                * offering a valid level/prio prefix
-                                * so we consider this format.
-                                */
-                               format = LOG_FORMAT_PRIO;
+       if (plogfd && unlikely(*plogfd < 0)) {
+               /* socket not successfully initialized yet */
+               if ((*plogfd = socket(target->addr->ss_family, SOCK_DGRAM,
+                                     (target->addr->ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
+                       static char once;
+
+                       if (!once) {
+                               once = 1; /* note: no need for atomic ops here */
+                               ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
+                                                nblogger, strerror(errno), errno);
                        }
+                       return;
+               } else {
+                       /* we don't want to receive anything on this socket */
+                       setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
+                       /* we may want to adjust the output buffer (tune.sndbuf.backend) */
+                       if (global.tune.backend_sndbuf)
+                               setsockopt(*plogfd, SOL_SOCKET, SO_SNDBUF, &global.tune.backend_sndbuf, sizeof(global.tune.backend_sndbuf));
+                       /* does nothing under Linux, maybe needed for others */
+                       shutdown(*plogfd, SHUT_RD);
+                       fd_set_cloexec(*plogfd);
                }
        }
 
-       /* prepare priority, stored into 1 single elem */
-       switch (format) {
-               case LOG_FORMAT_LOCAL:
-               case LOG_FORMAT_RFC3164:
-               case LOG_FORMAT_RFC5424:
-               case LOG_FORMAT_PRIO:
-                       fac_level = facility << 3;
-                       /* further format ignore the facility */
-                       __fallthrough;
-               case LOG_FORMAT_TIMED:
-               case LOG_FORMAT_SHORT:
-                       fac_level += level;
-                       hdr_ctx.ist_vector[*nbelem].ptr = &hdr_ctx.priority_buffer[3]; /* last digit of the log level */
-                       do {
-                               *hdr_ctx.ist_vector[*nbelem].ptr = '0' + fac_level % 10;
-                               fac_level /= 10;
-                               hdr_ctx.ist_vector[*nbelem].ptr--;
-                       } while (fac_level && hdr_ctx.ist_vector[*nbelem].ptr > &hdr_ctx.priority_buffer[0]);
-                       *hdr_ctx.ist_vector[*nbelem].ptr = '<';
-                       hdr_ctx.ist_vector[(*nbelem)++].len = &hdr_ctx.priority_buffer[5] - hdr_ctx.ist_vector[0].ptr;
-                       break;
-               case LOG_FORMAT_ISO:
-               case LOG_FORMAT_RAW:
-                       break;
-               case LOG_FORMAT_UNSPEC:
-               case LOG_FORMATS:
-                       ABORT_NOW();
+       msg_header = build_log_header(hdr, &nbelem);
+ send:
+       if (target->type == LOG_TARGET_BUFFER) {
+               struct ist msg;
+               size_t e_maxlen = maxlen;
+
+               msg = ist2(message, size);
+
+               /* make room for the final '\n' which may be forcefully inserted
+                * by tcp forwarder applet (sink_forward_io_handler)
+                */
+               e_maxlen -= 1;
+
+               sent = sink_write(target->sink, hdr, e_maxlen, &msg, 1);
        }
+       else if (target->addr->ss_family == AF_CUST_EXISTING_FD) {
+               struct ist msg;
 
+               msg = ist2(message, size);
 
-       /* prepare timestamp, stored into a max of 4 elems */
-       switch (format) {
-               case LOG_FORMAT_LOCAL:
-               case LOG_FORMAT_RFC3164:
-                       /* rfc3164 ex: 'Jan  1 00:00:00 ' */
-                       if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                               /* time is set, break immediately */
+               sent = fd_write_frag_line(*plogfd, maxlen, msg_header, nbelem, &msg, 1, 1);
+       }
+       else {
+               int i = 0;
+               int totlen = maxlen - 1; /* save space for the final '\n' */
+
+               for (i = 0 ; i < nbelem ; i++ ) {
+                       iovec[i].iov_base = msg_header[i].ptr;
+                       iovec[i].iov_len  = msg_header[i].len;
+                       if (totlen <= iovec[i].iov_len) {
+                               iovec[i].iov_len = totlen;
+                               totlen = 0;
                                break;
                        }
-                       else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
-                               int month;
-                               char *timestamp = metadata[LOG_META_TIME].ptr;
+                       totlen -= iovec[i].iov_len;
+               }
+               if (totlen) {
+                       iovec[i].iov_base = message;
+                       iovec[i].iov_len  = size;
+                       if (totlen <= iovec[i].iov_len)
+                               iovec[i].iov_len = totlen;
+                       i++;
+               }
+               iovec[i].iov_base = "\n"; /* insert a \n at the end of the message */
+               iovec[i].iov_len = 1;
+               i++;
 
-                               /* iso time always begins like this: '1970-01-01T00:00:00' */
+               msghdr.msg_iovlen = i;
+               msghdr.msg_name = (struct sockaddr *)target->addr;
+               msghdr.msg_namelen = get_addr_len(target->addr);
 
-                               /* compute month */
-                               month = 10*(timestamp[5] - '0') + (timestamp[6] - '0');
-                               if (month)
-                                       month--;
-                               if (month <= 11) {
-                                       /* builds log prefix ex: 'Jan  1 ' */
-                                       len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
-                                                      "%s %c%c ", monthname[month],
-                                                      timestamp[8] != '0' ? timestamp[8] : ' ',
-                                                      timestamp[9]);
-                                       /* we reused the timestamp_buffer, signal that it does not
-                                        * contain local time anymore
-                                        */
-                                       hdr_ctx.cur_legacy_time = 0;
-                                       if (len == 7) {
-                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
-                                               /* adds 'HH:MM:SS' from iso time */
-                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[11], 8);
-                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                                               /* we successfully reuse iso time, we can break */
-                                               break;
-                                       }
-                               }
-                               /* Failed to reuse isotime time, fallback to local legacy time */
-                       }
+               sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
+       }
 
-                       if (unlikely(time != hdr_ctx.cur_legacy_time)) {
-                               /* re-builds timestamp from the current local time */
-                               get_localtime(time, &logtime);
+       if (sent < 0) {
+               static char once;
 
-                               len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
-                                              "%s %2d %02d:%02d:%02d ",
-                                              monthname[logtime.tm_mon],
-                                              logtime.tm_mday, logtime.tm_hour, logtime.tm_min, logtime.tm_sec);
-                               if (len != LOG_LEGACYTIME_LEN+1)
-                                       hdr_ctx.cur_legacy_time = 0;
-                               else
-                                       hdr_ctx.cur_legacy_time = time;
-                       }
-                       if (likely(hdr_ctx.cur_legacy_time))
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], LOG_LEGACYTIME_LEN+1);
-                       else
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("Jan  1 00:00:00 ", LOG_LEGACYTIME_LEN+1);
-                       break;
-               case LOG_FORMAT_RFC5424:
-                       /* adds rfc5425 version prefix */
-                       hdr_ctx.ist_vector[(*nbelem)++] = ist2("1 ", 2);
-                       if (metadata && metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-') {
-                               /* submitted len is NILVALUE, it is a valid timestamp for rfc5425 */
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                               break;
-                       }
-                       /* let continue as 'timed' and 'iso' format for usual timestamp */
-                       __fallthrough;
-               case LOG_FORMAT_TIMED:
-               case LOG_FORMAT_ISO:
-                       /* ISO format ex: '1900:01:01T12:00:00.123456Z'
-                        *                '1900:01:01T14:00:00+02:00'
-                        *                '1900:01:01T10:00:00.123456-02:00'
-                        */
-                       if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                               /* time is set, break immediately */
-                               break;
-                       }
-                       else if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
-                               int month;
-                               char *timestamp = metadata[LOG_META_TIME].ptr;
-
-                               for (month = 0; month < 12; month++)
-                                       if (!memcmp(monthname[month], timestamp, 3))
-                                               break;
-
-                               if (month < 12) {
-
-                                       /* get local time to retrieve year */
-                                       get_localtime(time, &logtime);
+               if (errno == EAGAIN || errno == EWOULDBLOCK)
+                       _HA_ATOMIC_INC(&dropped_logs);
+               else if (!once) {
+                       once = 1; /* note: no need for atomic ops here */
+                       ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
+                                        nblogger, strerror(errno), errno);
+               }
+       }
+}
 
-                                       /* year seems changed since log */
-                                       if (logtime.tm_mon < month)
-                                               logtime.tm_year--;
+/* does the same as __do_send_log() does for a single target, but here the log
+ * will be sent according to the log backend's lb settings. The function will
+ * leverage __do_send_log() function to actually send the log messages.
+ */
+static inline void __do_send_log_backend(struct proxy *be, struct log_header hdr,
+                                         int nblogger, size_t maxlen,
+                                         char *message, size_t size)
+{
+       struct server *srv = NULL;
 
-                                       /* builds rfc5424 prefix ex: '1900-01-01T' */
-                                       len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
-                                                          "%4d-%02d-%c%cT",
-                                                          logtime.tm_year+1900, month+1,
-                                                          timestamp[4] != ' ' ? timestamp[4] : '0',
-                                                          timestamp[5]);
+       /* log-balancing logic: */
 
-                                       /* we reused the timestamp_buffer, signal that it does not
-                                        * contain local time anymore
-                                        */
-                                       hdr_ctx.cur_legacy_time = 0;
-                                       if (len == 11) {
-                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
-                                               /* adds HH:MM:SS from legacy timestamp */
-                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[7], 8);
-                                               /* skip secfraq because it is optional */
-                                               /* according to rfc: -00:00 means we don't know the timezone */
-                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("-00:00 ", 7);
-                                               /* we successfully reuse legacy time, we can break */
-                                               break;
-                                       }
-                               }
-                               /* Failed to reuse legacy time, fallback to local iso time */
-                       }
-                       hdr_ctx.ist_vector[(*nbelem)++] = ist2(timeofday_as_iso_us(1), LOG_ISOTIME_MAXLEN + 1);
-                       break;
-               case LOG_FORMAT_PRIO:
-               case LOG_FORMAT_SHORT:
-               case LOG_FORMAT_RAW:
-                       break;
-               case LOG_FORMAT_UNSPEC:
-               case LOG_FORMATS:
-                       ABORT_NOW();
+       if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_RR) {
+               srv = fwrr_get_next_server(be, NULL);
        }
+       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SS) {
+               /* sticky mode: use first server in the pool, which will always stay
+                * first during dequeuing and requeuing, unless it becomes unavailable
+                * and will be replaced by another one
+                */
+               srv = ss_get_server(be);
+       }
+       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_RND) {
+               unsigned int hash;
 
-       /* prepare other meta data, stored into a max of 10 elems */
-       switch (format) {
-               case LOG_FORMAT_RFC3164:
-                       if (metadata && metadata[LOG_META_HOST].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                       }
-                       else /* the caller MUST fill the hostname, this field is mandatory */
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("localhost ", 10);
-                       __fallthrough;
-               case LOG_FORMAT_LOCAL:
-                       if (!metadata || !metadata[LOG_META_TAG].len)
-                               break;
-
-                       hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
-                       if (metadata[LOG_META_PID].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("[", 1);
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("]", 1);
-                       }
-                       hdr_ctx.ist_vector[(*nbelem)++] = ist2(": ", 2);
-                       break;
-               case LOG_FORMAT_RFC5424:
-                       if (metadata && metadata[LOG_META_HOST].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                       }
-                       else
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
-
-                       if (metadata && metadata[LOG_META_TAG].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                       }
-                       else
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
+               hash = statistical_prng(); /* random */
+               srv = chash_get_server_hash(be, hash, NULL);
+       }
+       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_LH) {
+               struct sample result;
 
-                       if (metadata && metadata[LOG_META_PID].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                       }
-                       else
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
+               /* log-balance hash */
+               memset(&result, 0, sizeof(result));
+               result.data.type = SMP_T_STR;
+               result.flags = SMP_F_CONST;
+               result.data.u.str.area = message;
+               result.data.u.str.data = size;
+               result.data.u.str.size = size + 1; /* with terminating NULL byte */
+               if (sample_process_cnv(be->lbprm.expr, &result)) {
+                       /* gen_hash takes binary input, ensure that we provide such value to it */
+                       if (result.data.type == SMP_T_BIN || sample_casts[result.data.type][SMP_T_BIN]) {
+                               unsigned int hash;
 
-                       if (metadata && metadata[LOG_META_MSGID].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_MSGID];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                               sample_casts[result.data.type][SMP_T_BIN](&result);
+                               hash = gen_hash(be, result.data.u.str.area, result.data.u.str.data);
+                               srv = map_get_server_hash(be, hash);
                        }
-                       else
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
+               }
+       }
 
-                       if (metadata && metadata[LOG_META_STDATA].len) {
-                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_STDATA];
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
-                       }
-                       else
-                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
-                       break;
-               case LOG_FORMAT_PRIO:
-               case LOG_FORMAT_SHORT:
-               case LOG_FORMAT_TIMED:
-               case LOG_FORMAT_ISO:
-               case LOG_FORMAT_RAW:
-                       break;
-               case LOG_FORMAT_UNSPEC:
-               case LOG_FORMATS:
-                       ABORT_NOW();
+       if (!srv) {
+               /* no srv available, can't log */
+               goto drop;
        }
 
-       return hdr_ctx.ist_vector;
+       __do_send_log(srv->log_target, hdr, nblogger, maxlen, message, size);
+       return;
+
+ drop:
+       _HA_ATOMIC_INC(&dropped_logs);
 }
 
 /*
  * This function sends a syslog message.
- * <target> is the actual log target where log will be sent,
- *
- * Message will be prefixed by header according to <hdr> setting.
- * Final message will be truncated <maxlen> parameter and will be
- * terminated with an LF character.
- *
- * Does not return any error
+ * It doesn't care about errors nor does it report them.
+ * The argument <metadata> MUST be an array of size
+ * LOG_META_FIELDS*sizeof(struct ist)  containing
+ * data to build the header.
  */
-static inline void __do_send_log(struct log_target *target, struct log_header hdr,
-                                 int nblogger, size_t maxlen,
-                                 char *message, size_t size)
+static void process_send_log(struct list *loggers, int level, int facility,
+                             struct ist *metadata, char *message, size_t size)
 {
-       static THREAD_LOCAL struct iovec iovec[NB_LOG_HDR_MAX_ELEMENTS+1+1] = { }; /* header elements + message + LF */
-       static THREAD_LOCAL struct msghdr msghdr = {
-               //.msg_iov = iovec,
-               .msg_iovlen = NB_LOG_HDR_MAX_ELEMENTS+2
-       };
-       static THREAD_LOCAL int logfdunix = -1; /* syslog to AF_UNIX socket */
-       static THREAD_LOCAL int logfdinet = -1; /* syslog to AF_INET socket */
-       int *plogfd;
-       int sent;
-       size_t nbelem;
-       struct ist *msg_header = NULL;
+       struct logger *logger;
+       int nblogger;
 
-       msghdr.msg_iov = iovec;
+       /* Send log messages to syslog server. */
+       nblogger = 0;
+       list_for_each_entry(logger, loggers, list) {
+               int in_range = 1;
 
-       /* historically some messages used to already contain the trailing LF
-        * or Zero. Let's remove all trailing LF or Zero
-        */
-       while (size && (message[size-1] == '\n' || (message[size-1] == 0)))
-               size--;
+               /* we can filter the level of the messages that are sent to each logger */
+               if (level > logger->level)
+                       continue;
 
-       if (target->type == LOG_TARGET_BUFFER) {
-               plogfd = NULL;
-               goto send;
-       }
-       else if (target->addr->ss_family == AF_CUST_EXISTING_FD) {
-               /* the socket's address is a file descriptor */
-               plogfd = (int *)&((struct sockaddr_in *)target->addr)->sin_addr.s_addr;
-       }
-       else if (target->addr->ss_family == AF_UNIX)
-               plogfd = &logfdunix;
-       else
-               plogfd = &logfdinet;
+               if (logger->lb.smp_rgs) {
+                       struct smp_log_range *smp_rg;
+                       uint next_idx, curr_rg;
+                       ullong curr_rg_idx, next_rg_idx;
 
-       if (plogfd && unlikely(*plogfd < 0)) {
-               /* socket not successfully initialized yet */
-               if ((*plogfd = socket(target->addr->ss_family, SOCK_DGRAM,
-                                     (target->addr->ss_family == AF_UNIX) ? 0 : IPPROTO_UDP)) < 0) {
-                       static char once;
+                       curr_rg_idx = _HA_ATOMIC_LOAD(&logger->lb.curr_rg_idx);
+                       do {
+                               next_idx = (curr_rg_idx & 0xFFFFFFFFU) + 1;
+                               curr_rg  = curr_rg_idx >> 32;
+                               smp_rg = &logger->lb.smp_rgs[curr_rg];
 
-                       if (!once) {
-                               once = 1; /* note: no need for atomic ops here */
-                               ha_alert("socket() failed in logger #%d: %s (errno=%d)\n",
-                                                nblogger, strerror(errno), errno);
+                               /* check if the index we're going to take is within range  */
+                               in_range = smp_rg->low <= next_idx && next_idx <= smp_rg->high;
+                               if (in_range) {
+                                       /* Let's consume this range. */
+                                       if (next_idx == smp_rg->high) {
+                                               /* If consumed, let's select the next range. */
+                                               curr_rg = (curr_rg + 1) % logger->lb.smp_rgs_sz;
+                                       }
+                               }
+
+                               next_idx = next_idx % logger->lb.smp_sz;
+                               next_rg_idx = ((ullong)curr_rg << 32) + next_idx;
+                       } while (!_HA_ATOMIC_CAS(&logger->lb.curr_rg_idx, &curr_rg_idx, next_rg_idx) &&
+                                __ha_cpu_relax());
+               }
+               if (in_range) {
+                       struct log_header hdr;
+
+                       hdr.level = MAX(level, logger->minlvl);
+                       hdr.facility = (facility == -1) ? logger->facility : facility;
+                       hdr.format = logger->format;
+                       hdr.metadata = metadata;
+
+                       nblogger += 1;
+                       if (logger->target.type == LOG_TARGET_BACKEND) {
+                               __do_send_log_backend(logger->target.be, hdr, nblogger, logger->maxlen, message, size);
                        }
-                       return;
-               } else {
-                       /* we don't want to receive anything on this socket */
-                       setsockopt(*plogfd, SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero));
-                       /* we may want to adjust the output buffer (tune.sndbuf.backend) */
-                       if (global.tune.backend_sndbuf)
-                               setsockopt(*plogfd, SOL_SOCKET, SO_SNDBUF, &global.tune.backend_sndbuf, sizeof(global.tune.backend_sndbuf));
-                       /* does nothing under Linux, maybe needed for others */
-                       shutdown(*plogfd, SHUT_RD);
-                       fd_set_cloexec(*plogfd);
+                       else {
+                               /* normal target */
+                               __do_send_log(&logger->target, hdr, nblogger, logger->maxlen, message, size);
+                       }
+               }
+       }
+}
+
+/*
+ * This function sends a syslog message.
+ * It doesn't care about errors nor does it report them.
+ * The arguments <sd> and <sd_size> are used for the structured-data part
+ * in RFC5424 formatted syslog messages.
+ */
+static void __send_log(struct list *loggers, struct buffer *tagb, int level,
+                       char *message, size_t size, char *sd, size_t sd_size)
+{
+       static THREAD_LOCAL pid_t curr_pid;
+       static THREAD_LOCAL char pidstr[16];
+       static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
+
+       if (loggers == NULL) {
+               if (!LIST_ISEMPTY(&global.loggers)) {
+                       loggers = &global.loggers;
                }
        }
+       if (!loggers || LIST_ISEMPTY(loggers))
+               return;
+
+       if (!metadata[LOG_META_HOST].len) {
+               if (global.log_send_hostname)
+                       metadata[LOG_META_HOST] = ist(global.log_send_hostname);
+       }
 
-       msg_header = build_log_header(hdr, &nbelem);
- send:
-       if (target->type == LOG_TARGET_BUFFER) {
-               struct ist msg;
-               size_t e_maxlen = maxlen;
+       if (!tagb || !tagb->area)
+               tagb = &global.log_tag;
 
-               msg = ist2(message, size);
+       if (tagb)
+               metadata[LOG_META_TAG] = ist2(tagb->area, tagb->data);
 
-               /* make room for the final '\n' which may be forcefully inserted
-                * by tcp forwarder applet (sink_forward_io_handler)
-                */
-               e_maxlen -= 1;
+       if (unlikely(curr_pid != getpid()))
+               metadata[LOG_META_PID].len = 0;
 
-               sent = sink_write(target->sink, hdr, e_maxlen, &msg, 1);
+       if (!metadata[LOG_META_PID].len) {
+               curr_pid = getpid();
+               ltoa_o(curr_pid, pidstr, sizeof(pidstr));
+               metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
        }
-       else if (target->addr->ss_family == AF_CUST_EXISTING_FD) {
-               struct ist msg;
 
-               msg = ist2(message, size);
+       metadata[LOG_META_STDATA] = ist2(sd, sd_size);
 
-               sent = fd_write_frag_line(*plogfd, maxlen, msg_header, nbelem, &msg, 1, 1);
-       }
-       else {
-               int i = 0;
-               int totlen = maxlen - 1; /* save space for the final '\n' */
+       /* Remove trailing space of structured data */
+       while (metadata[LOG_META_STDATA].len && metadata[LOG_META_STDATA].ptr[metadata[LOG_META_STDATA].len-1] == ' ')
+               metadata[LOG_META_STDATA].len--;
 
-               for (i = 0 ; i < nbelem ; i++ ) {
-                       iovec[i].iov_base = msg_header[i].ptr;
-                       iovec[i].iov_len  = msg_header[i].len;
-                       if (totlen <= iovec[i].iov_len) {
-                               iovec[i].iov_len = totlen;
-                               totlen = 0;
-                               break;
-                       }
-                       totlen -= iovec[i].iov_len;
-               }
-               if (totlen) {
-                       iovec[i].iov_base = message;
-                       iovec[i].iov_len  = size;
-                       if (totlen <= iovec[i].iov_len)
-                               iovec[i].iov_len = totlen;
-                       i++;
-               }
-               iovec[i].iov_base = "\n"; /* insert a \n at the end of the message */
-               iovec[i].iov_len = 1;
-               i++;
+       return process_send_log(loggers, level, -1, metadata, message, size);
+}
 
-               msghdr.msg_iovlen = i;
-               msghdr.msg_name = (struct sockaddr *)target->addr;
-               msghdr.msg_namelen = get_addr_len(target->addr);
+/*
+ * This function sends the syslog message using a printf format string. It
+ * expects an LF-terminated message.
+ */
+void send_log(struct proxy *p, int level, const char *format, ...)
+{
+       va_list argp;
+       int  data_len;
 
-               sent = sendmsg(*plogfd, &msghdr, MSG_DONTWAIT | MSG_NOSIGNAL);
-       }
+       if (level < 0 || format == NULL || logline == NULL)
+               return;
 
-       if (sent < 0) {
-               static char once;
+       va_start(argp, format);
+       data_len = vsnprintf(logline, global.max_syslog_len, format, argp);
+       if (data_len < 0 || data_len > global.max_syslog_len)
+               data_len = global.max_syslog_len;
+       va_end(argp);
 
-               if (errno == EAGAIN || errno == EWOULDBLOCK)
-                       _HA_ATOMIC_INC(&dropped_logs);
-               else if (!once) {
-                       once = 1; /* note: no need for atomic ops here */
-                       ha_alert("sendmsg()/writev() failed in logger #%d: %s (errno=%d)\n",
-                                        nblogger, strerror(errno), errno);
-               }
-       }
+       __send_log((p ? &p->loggers : NULL), (p ? &p->log_tag : NULL), level,
+                  logline, data_len, default_rfc5424_sd_log_format, 2);
 }
 
-/* does the same as __do_send_log() does for a single target, but here the log
- * will be sent according to the log backend's lb settings. The function will
- * leverage __do_send_log() function to actually send the log messages.
+/*
+ * This function builds a log header according to <hdr> settings.
+ *
+ * If hdr.format is set to LOG_FORMAT_UNSPEC, it tries to determine
+ * format based on hdr.metadata. It is useful for log-forwarding to be
+ * able to forward any format without settings.
+ *
+ * This function returns a struct ist array of elements of the header
+ * nbelem is set to the number of available elements.
+ * This function returns currently a maximum of NB_LOG_HDR_IST_ELEMENTS
+ * elements.
  */
-static inline void __do_send_log_backend(struct proxy *be, struct log_header hdr,
-                                         int nblogger, size_t maxlen,
-                                         char *message, size_t size)
+struct ist *build_log_header(struct log_header hdr, size_t *nbelem)
 {
-       struct server *srv = NULL;
+       static THREAD_LOCAL struct {
+               struct ist ist_vector[NB_LOG_HDR_MAX_ELEMENTS];
+               char timestamp_buffer[LOG_LEGACYTIME_LEN+1+1];
+               time_t cur_legacy_time;
+               char priority_buffer[6];
+       } hdr_ctx = { .priority_buffer = "<<<<>" };
 
-       /* log-balancing logic: */
+       struct tm logtime;
+       int len;
+       int fac_level = 0;
+       time_t time = date.tv_sec;
+       struct ist *metadata = hdr.metadata;
+       enum log_fmt format = hdr.format;
+       int facility = hdr.facility;
+       int level = hdr.level;
 
-       if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_RR) {
-               srv = fwrr_get_next_server(be, NULL);
-       }
-       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SS) {
-               /* sticky mode: use first server in the pool, which will always stay
-                * first during dequeuing and requeuing, unless it becomes unavailable
-                * and will be replaced by another one
-                */
-               srv = ss_get_server(be);
+       *nbelem = 0;
+
+
+       if (format == LOG_FORMAT_UNSPEC) {
+               format = LOG_FORMAT_RAW;
+               if (metadata) {
+                       /* If a hostname is set, it appears we want to perform syslog
+                        * because only rfc5427 or rfc3164 support an hostname.
+                        */
+                       if (metadata[LOG_META_HOST].len) {
+                               /* If a rfc5424 compliant timestamp is used we consider
+                                * that output format is rfc5424, else legacy format
+                                * is used as specified default for local logs
+                                * in documentation.
+                                */
+                               if ((metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-')
+                                   || (metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN))
+                                       format = LOG_FORMAT_RFC5424;
+                               else
+                                       format = LOG_FORMAT_RFC3164;
+                       }
+                       else if (metadata[LOG_META_TAG].len) {
+                               /* Tag is present but no hostname, we should
+                                * consider we try to emit a local log
+                                * in legacy format (analog to RFC3164 but
+                                * with stripped hostname).
+                                */
+                               format = LOG_FORMAT_LOCAL;
+                       }
+                       else if (metadata[LOG_META_PRIO].len) {
+                               /* the source seems a parsed message
+                                * offering a valid level/prio prefix
+                                * so we consider this format.
+                                */
+                               format = LOG_FORMAT_PRIO;
+                       }
+               }
        }
-       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_RND) {
-               unsigned int hash;
 
-               hash = statistical_prng(); /* random */
-               srv = chash_get_server_hash(be, hash, NULL);
+       /* prepare priority, stored into 1 single elem */
+       switch (format) {
+               case LOG_FORMAT_LOCAL:
+               case LOG_FORMAT_RFC3164:
+               case LOG_FORMAT_RFC5424:
+               case LOG_FORMAT_PRIO:
+                       fac_level = facility << 3;
+                       /* further format ignore the facility */
+                       __fallthrough;
+               case LOG_FORMAT_TIMED:
+               case LOG_FORMAT_SHORT:
+                       fac_level += level;
+                       hdr_ctx.ist_vector[*nbelem].ptr = &hdr_ctx.priority_buffer[3]; /* last digit of the log level */
+                       do {
+                               *hdr_ctx.ist_vector[*nbelem].ptr = '0' + fac_level % 10;
+                               fac_level /= 10;
+                               hdr_ctx.ist_vector[*nbelem].ptr--;
+                       } while (fac_level && hdr_ctx.ist_vector[*nbelem].ptr > &hdr_ctx.priority_buffer[0]);
+                       *hdr_ctx.ist_vector[*nbelem].ptr = '<';
+                       hdr_ctx.ist_vector[(*nbelem)++].len = &hdr_ctx.priority_buffer[5] - hdr_ctx.ist_vector[0].ptr;
+                       break;
+               case LOG_FORMAT_ISO:
+               case LOG_FORMAT_RAW:
+                       break;
+               case LOG_FORMAT_UNSPEC:
+               case LOG_FORMATS:
+                       ABORT_NOW();
        }
-       else if ((be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_LH) {
-               struct sample result;
 
-               /* log-balance hash */
-               memset(&result, 0, sizeof(result));
-               result.data.type = SMP_T_STR;
-               result.flags = SMP_F_CONST;
-               result.data.u.str.area = message;
-               result.data.u.str.data = size;
-               result.data.u.str.size = size + 1; /* with terminating NULL byte */
-               if (sample_process_cnv(be->lbprm.expr, &result)) {
-                       /* gen_hash takes binary input, ensure that we provide such value to it */
-                       if (result.data.type == SMP_T_BIN || sample_casts[result.data.type][SMP_T_BIN]) {
-                               unsigned int hash;
 
-                               sample_casts[result.data.type][SMP_T_BIN](&result);
-                               hash = gen_hash(be, result.data.u.str.area, result.data.u.str.data);
-                               srv = map_get_server_hash(be, hash);
+       /* prepare timestamp, stored into a max of 4 elems */
+       switch (format) {
+               case LOG_FORMAT_LOCAL:
+               case LOG_FORMAT_RFC3164:
+                       /* rfc3164 ex: 'Jan  1 00:00:00 ' */
+                       if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                               /* time is set, break immediately */
+                               break;
+                       }
+                       else if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
+                               int month;
+                               char *timestamp = metadata[LOG_META_TIME].ptr;
+
+                               /* iso time always begins like this: '1970-01-01T00:00:00' */
+
+                               /* compute month */
+                               month = 10*(timestamp[5] - '0') + (timestamp[6] - '0');
+                               if (month)
+                                       month--;
+                               if (month <= 11) {
+                                       /* builds log prefix ex: 'Jan  1 ' */
+                                       len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
+                                                      "%s %c%c ", monthname[month],
+                                                      timestamp[8] != '0' ? timestamp[8] : ' ',
+                                                      timestamp[9]);
+                                       /* we reused the timestamp_buffer, signal that it does not
+                                        * contain local time anymore
+                                        */
+                                       hdr_ctx.cur_legacy_time = 0;
+                                       if (len == 7) {
+                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
+                                               /* adds 'HH:MM:SS' from iso time */
+                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[11], 8);
+                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                                               /* we successfully reuse iso time, we can break */
+                                               break;
+                                       }
+                               }
+                               /* Failed to reuse isotime time, fallback to local legacy time */
                        }
-               }
-       }
-
-       if (!srv) {
-               /* no srv available, can't log */
-               goto drop;
-       }
 
-       __do_send_log(srv->log_target, hdr, nblogger, maxlen, message, size);
-       return;
+                       if (unlikely(time != hdr_ctx.cur_legacy_time)) {
+                               /* re-builds timestamp from the current local time */
+                               get_localtime(time, &logtime);
 
- drop:
-       _HA_ATOMIC_INC(&dropped_logs);
-}
+                               len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
+                                              "%s %2d %02d:%02d:%02d ",
+                                              monthname[logtime.tm_mon],
+                                              logtime.tm_mday, logtime.tm_hour, logtime.tm_min, logtime.tm_sec);
+                               if (len != LOG_LEGACYTIME_LEN+1)
+                                       hdr_ctx.cur_legacy_time = 0;
+                               else
+                                       hdr_ctx.cur_legacy_time = time;
+                       }
+                       if (likely(hdr_ctx.cur_legacy_time))
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], LOG_LEGACYTIME_LEN+1);
+                       else
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("Jan  1 00:00:00 ", LOG_LEGACYTIME_LEN+1);
+                       break;
+               case LOG_FORMAT_RFC5424:
+                       /* adds rfc5425 version prefix */
+                       hdr_ctx.ist_vector[(*nbelem)++] = ist2("1 ", 2);
+                       if (metadata && metadata[LOG_META_TIME].len == 1 && metadata[LOG_META_TIME].ptr[0] == '-') {
+                               /* submitted len is NILVALUE, it is a valid timestamp for rfc5425 */
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                               break;
+                       }
+                       /* let continue as 'timed' and 'iso' format for usual timestamp */
+                       __fallthrough;
+               case LOG_FORMAT_TIMED:
+               case LOG_FORMAT_ISO:
+                       /* ISO format ex: '1900:01:01T12:00:00.123456Z'
+                        *                '1900:01:01T14:00:00+02:00'
+                        *                '1900:01:01T10:00:00.123456-02:00'
+                        */
+                       if (metadata && metadata[LOG_META_TIME].len >= LOG_ISOTIME_MINLEN) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TIME];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                               /* time is set, break immediately */
+                               break;
+                       }
+                       else if (metadata && metadata[LOG_META_TIME].len == LOG_LEGACYTIME_LEN) {
+                               int month;
+                               char *timestamp = metadata[LOG_META_TIME].ptr;
 
-/*
- * This function sends a syslog message.
- * It doesn't care about errors nor does it report them.
- * The argument <metadata> MUST be an array of size
- * LOG_META_FIELDS*sizeof(struct ist)  containing
- * data to build the header.
- */
-void process_send_log(struct list *loggers, int level, int facility,
-                      struct ist *metadata, char *message, size_t size)
-{
-       struct logger *logger;
-       int nblogger;
+                               for (month = 0; month < 12; month++)
+                                       if (!memcmp(monthname[month], timestamp, 3))
+                                               break;
 
-       /* Send log messages to syslog server. */
-       nblogger = 0;
-       list_for_each_entry(logger, loggers, list) {
-               int in_range = 1;
+                               if (month < 12) {
 
-               /* we can filter the level of the messages that are sent to each logger */
-               if (level > logger->level)
-                       continue;
+                                       /* get local time to retrieve year */
+                                       get_localtime(time, &logtime);
 
-               if (logger->lb.smp_rgs) {
-                       struct smp_log_range *smp_rg;
-                       uint next_idx, curr_rg;
-                       ullong curr_rg_idx, next_rg_idx;
+                                       /* year seems changed since log */
+                                       if (logtime.tm_mon < month)
+                                               logtime.tm_year--;
 
-                       curr_rg_idx = _HA_ATOMIC_LOAD(&logger->lb.curr_rg_idx);
-                       do {
-                               next_idx = (curr_rg_idx & 0xFFFFFFFFU) + 1;
-                               curr_rg  = curr_rg_idx >> 32;
-                               smp_rg = &logger->lb.smp_rgs[curr_rg];
+                                       /* builds rfc5424 prefix ex: '1900-01-01T' */
+                                       len = snprintf(hdr_ctx.timestamp_buffer, sizeof(hdr_ctx.timestamp_buffer),
+                                                          "%4d-%02d-%c%cT",
+                                                          logtime.tm_year+1900, month+1,
+                                                          timestamp[4] != ' ' ? timestamp[4] : '0',
+                                                          timestamp[5]);
 
-                               /* check if the index we're going to take is within range  */
-                               in_range = smp_rg->low <= next_idx && next_idx <= smp_rg->high;
-                               if (in_range) {
-                                       /* Let's consume this range. */
-                                       if (next_idx == smp_rg->high) {
-                                               /* If consumed, let's select the next range. */
-                                               curr_rg = (curr_rg + 1) % logger->lb.smp_rgs_sz;
+                                       /* we reused the timestamp_buffer, signal that it does not
+                                        * contain local time anymore
+                                        */
+                                       hdr_ctx.cur_legacy_time = 0;
+                                       if (len == 11) {
+                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&hdr_ctx.timestamp_buffer[0], len);
+                                               /* adds HH:MM:SS from legacy timestamp */
+                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(&timestamp[7], 8);
+                                               /* skip secfraq because it is optional */
+                                               /* according to rfc: -00:00 means we don't know the timezone */
+                                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("-00:00 ", 7);
+                                               /* we successfully reuse legacy time, we can break */
+                                               break;
                                        }
                                }
-
-                               next_idx = next_idx % logger->lb.smp_sz;
-                               next_rg_idx = ((ullong)curr_rg << 32) + next_idx;
-                       } while (!_HA_ATOMIC_CAS(&logger->lb.curr_rg_idx, &curr_rg_idx, next_rg_idx) &&
-                                __ha_cpu_relax());
-               }
-               if (in_range) {
-                       struct log_header hdr;
-
-                       hdr.level = MAX(level, logger->minlvl);
-                       hdr.facility = (facility == -1) ? logger->facility : facility;
-                       hdr.format = logger->format;
-                       hdr.metadata = metadata;
-
-                       nblogger += 1;
-                       if (logger->target.type == LOG_TARGET_BACKEND) {
-                               __do_send_log_backend(logger->target.be, hdr, nblogger, logger->maxlen, message, size);
-                       }
-                       else {
-                               /* normal target */
-                               __do_send_log(&logger->target, hdr, nblogger, logger->maxlen, message, size);
+                               /* Failed to reuse legacy time, fallback to local iso time */
                        }
-               }
+                       hdr_ctx.ist_vector[(*nbelem)++] = ist2(timeofday_as_iso_us(1), LOG_ISOTIME_MAXLEN + 1);
+                       break;
+               case LOG_FORMAT_PRIO:
+               case LOG_FORMAT_SHORT:
+               case LOG_FORMAT_RAW:
+                       break;
+               case LOG_FORMAT_UNSPEC:
+               case LOG_FORMATS:
+                       ABORT_NOW();
        }
-}
-
-/*
- * This function sends a syslog message.
- * It doesn't care about errors nor does it report them.
- * The arguments <sd> and <sd_size> are used for the structured-data part
- * in RFC5424 formatted syslog messages.
- */
-void __send_log(struct list *loggers, struct buffer *tagb, int level,
-               char *message, size_t size, char *sd, size_t sd_size)
-{
-       static THREAD_LOCAL pid_t curr_pid;
-       static THREAD_LOCAL char pidstr[16];
-       static THREAD_LOCAL struct ist metadata[LOG_META_FIELDS];
 
-       if (loggers == NULL) {
-               if (!LIST_ISEMPTY(&global.loggers)) {
-                       loggers = &global.loggers;
-               }
-       }
-       if (!loggers || LIST_ISEMPTY(loggers))
-               return;
+       /* prepare other meta data, stored into a max of 10 elems */
+       switch (format) {
+               case LOG_FORMAT_RFC3164:
+                       if (metadata && metadata[LOG_META_HOST].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                       }
+                       else /* the caller MUST fill the hostname, this field is mandatory */
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("localhost ", 10);
+                       __fallthrough;
+               case LOG_FORMAT_LOCAL:
+                       if (!metadata || !metadata[LOG_META_TAG].len)
+                               break;
 
-       if (!metadata[LOG_META_HOST].len) {
-               if (global.log_send_hostname)
-                       metadata[LOG_META_HOST] = ist(global.log_send_hostname);
-       }
+                       hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
+                       if (metadata[LOG_META_PID].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("[", 1);
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("]", 1);
+                       }
+                       hdr_ctx.ist_vector[(*nbelem)++] = ist2(": ", 2);
+                       break;
+               case LOG_FORMAT_RFC5424:
+                       if (metadata && metadata[LOG_META_HOST].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_HOST];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                       }
+                       else
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
 
-       if (!tagb || !tagb->area)
-               tagb = &global.log_tag;
+                       if (metadata && metadata[LOG_META_TAG].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_TAG];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                       }
+                       else
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
 
-       if (tagb)
-               metadata[LOG_META_TAG] = ist2(tagb->area, tagb->data);
+                       if (metadata && metadata[LOG_META_PID].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_PID];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                       }
+                       else
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
 
-       if (unlikely(curr_pid != getpid()))
-               metadata[LOG_META_PID].len = 0;
+                       if (metadata && metadata[LOG_META_MSGID].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_MSGID];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                       }
+                       else
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
 
-       if (!metadata[LOG_META_PID].len) {
-               curr_pid = getpid();
-               ltoa_o(curr_pid, pidstr, sizeof(pidstr));
-               metadata[LOG_META_PID] = ist2(pidstr, strlen(pidstr));
+                       if (metadata && metadata[LOG_META_STDATA].len) {
+                               hdr_ctx.ist_vector[(*nbelem)++] = metadata[LOG_META_STDATA];
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2(" ", 1);
+                       }
+                       else
+                               hdr_ctx.ist_vector[(*nbelem)++] = ist2("- ", 2);
+                       break;
+               case LOG_FORMAT_PRIO:
+               case LOG_FORMAT_SHORT:
+               case LOG_FORMAT_TIMED:
+               case LOG_FORMAT_ISO:
+               case LOG_FORMAT_RAW:
+                       break;
+               case LOG_FORMAT_UNSPEC:
+               case LOG_FORMATS:
+                       ABORT_NOW();
        }
 
-       metadata[LOG_META_STDATA] = ist2(sd, sd_size);
-
-       /* Remove trailing space of structured data */
-       while (metadata[LOG_META_STDATA].len && metadata[LOG_META_STDATA].ptr[metadata[LOG_META_STDATA].len-1] == ' ')
-               metadata[LOG_META_STDATA].len--;
-
-       return process_send_log(loggers, level, -1, metadata, message, size);
+       return hdr_ctx.ist_vector;
 }
 
 const char sess_cookie[8]     = "NIDVEOU7";    /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie, Expired cookie, Old cookie, Unused, unknown */