MEDIUM: logs: remove the hostname, tag and pid part from the logheader

At the moment we have to call snprintf() for every log line just to
rebuild a constant. Thanks to sendmsg(), we send the message in 3 parts:
time-based header, proxy-specific hostname+log-tag+pid, session-specific
message.
diff --git a/include/proto/log.h b/include/proto/log.h
index 14dfc5b..e8b57d8 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -138,6 +138,11 @@
  */
 char *lf_port(char *dst, struct sockaddr *sockaddr, size_t size, struct logformat_node *node);
 
+/*
+ * Write hostname, log_tag and pid to the log string
+ */
+char *lf_host_tag_pid(char *dst, const char *hostname, const char *log_tag, int pid, size_t size);
+
 
 #endif /* _PROTO_LOG_H */
 
diff --git a/include/types/log.h b/include/types/log.h
index 6707aa6..0214ae6 100644
--- a/include/types/log.h
+++ b/include/types/log.h
@@ -30,7 +30,7 @@
 
 #define NB_LOG_FACILITIES       24
 #define NB_LOG_LEVELS           8
-#define NB_MSG_IOVEC_ELEMENTS   2
+#define NB_MSG_IOVEC_ELEMENTS   3
 #define SYSLOG_PORT             514
 #define UNIQUEID_LEN            128
 
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 170e6f7..1400126 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -346,6 +346,7 @@
 	struct list logsrvs;
 	struct list logformat; 			/* log_format linked list */
 	char *log_tag;                          /* override default syslog tag */
+	struct chunk log_htp;			/* a syslog header part that contains hostname, log_tag and pid */
 	char *header_unique_id; 		/* unique-id header */
 	struct list format_unique_id;		/* unique-id format */
 	int to_log;				/* things to be logged (LW_*) */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 39ee359..fdbb462 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -7818,6 +7818,27 @@
 		}
 out_uri_auth_compat:
 
+		/* write a syslog header string that contains hostname, log_tag and pid */
+		curproxy->log_htp.str = lf_host_tag_pid(logheader,
+		                                        global.log_send_hostname ? global.log_send_hostname : "",
+		                                        curproxy->log_tag ? curproxy->log_tag : global.log_tag, pid,
+		                                        global.max_syslog_len);
+
+		if ((curproxy->log_htp.str == NULL) ||
+		    (curproxy->log_htp.len = curproxy->log_htp.str - logheader) >= global.max_syslog_len) {
+			Alert("Proxy '%s': cannot write a syslog header string that contains "
+			      "hostname, log_tag and pid.\n",
+			      curproxy->id);
+			cfgerr++;
+		}
+		else {
+			curproxy->log_htp.str = (char *)malloc(curproxy->log_htp.len);
+			memcpy(curproxy->log_htp.str, logheader, curproxy->log_htp.len);
+			curproxy->log_htp.size = 0;
+		}
+
+		logheader[0] = 0;
+
 		/* compile the log format */
 		if (!(curproxy->cap & PR_CAP_FE)) {
 			if (curproxy->conf.logformat_string != default_http_log_format &&
diff --git a/src/haproxy.c b/src/haproxy.c
index f6ba96b..1edb823 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1328,6 +1328,7 @@
 			LIST_DEL(&log->list);
 			free(log);
 		}
+		chunk_destroy(&p->log_htp);
 
 		list_for_each_entry_safe(lf, lfb, &p->logformat, list) {
 			LIST_DEL(&lf->list);
diff --git a/src/log.c b/src/log.c
index 3724dd4..ad25174 100644
--- a/src/log.c
+++ b/src/log.c
@@ -152,8 +152,7 @@
 char *log_format = NULL;
 
 /* This is a global syslog header, common to all outgoing messages. It
- * begins with the syslog tag and the date that are updated by
- * update_log_hdr().
+ * begins with time-based part and is updated by update_log_hdr().
  */
 char *logheader = NULL;
 
@@ -740,14 +739,26 @@
 	return ret;
 }
 
+char *lf_host_tag_pid(char *dst, const char *hostname, const char *log_tag, int pid, size_t size)
+{
+	char *ret = dst;
+	int iret;
+
+	iret = snprintf(dst, size, "%s%s[%d]: ", hostname, log_tag, pid);
+	if (iret < 0 || iret > size)
+		return NULL;
+	ret += iret;
+
+	return ret;
+}
+
 /* Re-generate the syslog header at the beginning of logheader once a second and
  * return the pointer to the first character after the header.
  */
-static char *update_log_hdr(const char *log_tag)
+char *update_log_hdr()
 {
 	static long tvsec;
 	static char *dataptr = NULL; /* backup of last end of header, NULL first time */
-	int tag_len;
 
 	if (unlikely(date.tv_sec != tvsec || dataptr == NULL)) {
 		/* this string is rebuild only once a second */
@@ -758,10 +769,9 @@
 		get_localtime(tvsec, &tm);
 
 		hdr_len = snprintf(logheader, global.max_syslog_len,
-				   "<<<<>%s %2d %02d:%02d:%02d %s",
+				   "<<<<>%s %2d %02d:%02d:%02d ",
 				   monthname[tm.tm_mon],
-				   tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
-				   global.log_send_hostname ? global.log_send_hostname : "");
+				   tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
 		/* WARNING: depending upon implementations, snprintf may return
 		 * either -1 or the number of bytes that would be needed to store
 		 * the total message. In both cases, we must adjust it.
@@ -774,11 +784,7 @@
 
 	dataptr[0] = 0; // ensure we get rid of any previous attempt
 
-	tag_len = snprintf(dataptr, logheader + global.max_syslog_len - dataptr, "%s[%d]: ", log_tag, pid);
-	if (tag_len < 0 || tag_len > logheader + global.max_syslog_len - dataptr)
-		tag_len = logheader + global.max_syslog_len - dataptr;
-
-	return dataptr + tag_len;
+	return dataptr;
 }
 
 /*
@@ -821,7 +827,6 @@
 	struct list *logsrvs = NULL;
 	struct logsrv *tmp = NULL;
 	int nblogger;
-	char *log_tag = global.log_tag;
 	char *log_ptr;
 	char *hdr_ptr;
 	size_t hdr_size;
@@ -836,15 +841,12 @@
 		if (!LIST_ISEMPTY(&p->logsrvs)) {
 			logsrvs = &p->logsrvs;
 		}
-		if (p->log_tag) {
-			log_tag = p->log_tag;
-		}
 	}
 
 	if (!logsrvs)
 		return;
 
-	hdr_ptr = update_log_hdr(log_tag);
+	hdr_ptr = update_log_hdr();
 	hdr_size = hdr_ptr - logheader;
 
 	/* Send log messages to syslog server. */
@@ -854,7 +856,9 @@
 		int *plogfd = logsrv->addr.ss_family == AF_UNIX ?
 			&logfdunix : &logfdinet;
 		int sent;
+		int maxlen;
 		int hdr_max = 0;
+		int htp_max = 0;
 		int max = 1;
 		char backup;
 
@@ -904,7 +908,15 @@
 			goto send;
 		}
 
+		maxlen = logsrv->maxlen - hdr_max;
+		htp_max = p->log_htp.len;
+
+		if (unlikely(htp_max >= maxlen)) {
+			htp_max = maxlen - 1;
+			goto send;
+		}
+
-		max = MIN(size, logsrv->maxlen - hdr_max);
+		max = MIN(size, maxlen - htp_max);
 
 		log_ptr += max - 1;
 
@@ -918,8 +930,10 @@
 
 		iovec[0].iov_base = hdr_ptr;
 		iovec[0].iov_len = hdr_max;
-		iovec[1].iov_base = dataptr;
-		iovec[1].iov_len = max;
+		iovec[1].iov_base = p->log_htp.str;
+		iovec[1].iov_len = htp_max;
+		iovec[2].iov_base = dataptr;
+		iovec[2].iov_len = max;
 
 		msghdr.msg_name = (struct sockaddr *)&logsrv->addr;
 		msghdr.msg_namelen = get_addr_len(&logsrv->addr);