MEDIUM: log: split of log_format generation

* logformat functions now take a format linked list as argument
* build_logline() build a logline using a format linked list
* rename LOG_* by LOG_FMT_* in enum
* improve error management in build_logline()
diff --git a/include/proto/log.h b/include/proto/log.h
index 0ee60a0..2efb539 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -40,6 +40,8 @@
 extern char clf_http_log_format[];
 
 
+int build_logline(struct session *s, char *dst, size_t maxsize, struct list *list_format);
+
 /*
  * send a log for the session when we have enough info about it.
  * Will not log if the frontend has no log defined.
@@ -55,20 +57,19 @@
  * Parse a variable '%varname' or '%{args}varname' in logformat
  *
  */
-int parse_logformat_var(char *str, size_t len, struct proxy *curproxy, int *options);
+int parse_logformat_var(char *str, size_t len, struct proxy *curproxy, struct list *list_format, int *defoptions);
 
 /*
  * add to the logformat linked list
  */
-void add_to_logformat_list(char *start, char *end, int type, struct proxy *curproxy);
+void add_to_logformat_list(char *start, char *end, int type, struct list *list_format);
 
 /*
  * Parse the log_format string and fill a linked list.
  * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
  * You can set arguments using { } : %{many arguments}varname
  */
-void parse_logformat_string(char *str, struct proxy *curproxy);
-
+void parse_logformat_string(char *str, struct proxy *curproxy, struct list *list_format, int capabilities);
 /*
  * Displays the message on stderr with the date and pid. Overrides the quiet
  * mode during startup.
diff --git a/include/types/log.h b/include/types/log.h
index 741fba9..efb1d91 100644
--- a/include/types/log.h
+++ b/include/types/log.h
@@ -35,49 +35,49 @@
 /* lists of fields that can be logged */
 enum {
 
-	LOG_TEXT = 0, /* raw text */
+	LOG_FMT_TEXT = 0, /* raw text */
 
-	LOG_SEPARATOR, /* separator replaced by one space */
-	LOG_VARIABLE,
+	LOG_FMT_SEPARATOR, /* separator replaced by one space */
+	LOG_FMT_VARIABLE,
 
 	/* information fields */
-	LOG_GLOBAL,
-	LOG_CLIENTIP,
-	LOG_CLIENTPORT,
-	LOG_SOURCEPORT,
-	LOG_SOURCEIP,
-	LOG_DATE,
-	LOG_DATEGMT,
-	LOG_MS,
-	LOG_FRONTEND,
-	LOG_BACKEND,
-	LOG_SERVER,
-	LOG_BYTES,
-	LOG_T,
-	LOG_TQ,
-	LOG_TW,
-	LOG_TC,
-	LOG_TR,
-	LOG_TT,
-	LOG_STATUS,
-	LOG_CCLIENT,
-	LOG_CSERVER,
-	LOG_TERMSTATE,
-	LOG_TERMSTATE_CK,
-	LOG_CONN,
-	LOG_ACTCONN,
-	LOG_FECONN,
-	LOG_BECONN,
-	LOG_SRVCONN,
-	LOG_RETRIES,
-	LOG_QUEUES,
-	LOG_SRVQUEUE,
-	LOG_BCKQUEUE,
-	LOG_HDRREQUEST,
-	LOG_HDRRESPONS,
-	LOG_HDRREQUESTLIST,
-	LOG_HDRRESPONSLIST,
-	LOG_REQ,
+	LOG_FMT_GLOBAL,
+	LOG_FMT_CLIENTIP,
+	LOG_FMT_CLIENTPORT,
+	LOG_FMT_BACKENDIP,
+	LOG_FMT_BACKENDPORT,
+	LOG_FMT_DATE,
+	LOG_FMT_DATEGMT,
+	LOG_FMT_MS,
+	LOG_FMT_FRONTEND,
+	LOG_FMT_BACKEND,
+	LOG_FMT_SERVER,
+	LOG_FMT_BYTES,
+	LOG_FMT_T,
+	LOG_FMT_TQ,
+	LOG_FMT_TW,
+	LOG_FMT_TC,
+	LOG_FMT_TR,
+	LOG_FMT_TT,
+	LOG_FMT_STATUS,
+	LOG_FMT_CCLIENT,
+	LOG_FMT_CSERVER,
+	LOG_FMT_TERMSTATE,
+	LOG_FMT_TERMSTATE_CK,
+	LOG_FMT_CONN,
+	LOG_FMT_ACTCONN,
+	LOG_FMT_FECONN,
+	LOG_FMT_BECONN,
+	LOG_FMT_SRVCONN,
+	LOG_FMT_RETRIES,
+	LOG_FMT_QUEUES,
+	LOG_FMT_SRVQUEUE,
+	LOG_FMT_BCKQUEUE,
+	LOG_FMT_HDRREQUEST,
+	LOG_FMT_HDRRESPONS,
+	LOG_FMT_HDRREQUESTLIST,
+	LOG_FMT_HDRRESPONSLIST,
+	LOG_FMT_REQ,
 };
 
 /* enum for parse_logformat */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index d827d4c..aa57b9b 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3352,14 +3352,14 @@
 					goto out;
 				}
 			}
-			parse_logformat_string(logformat, curproxy);
+			parse_logformat_string(logformat, curproxy, &curproxy->logformat, curproxy->mode);
 		}
 		else if (!strcmp(args[1], "tcplog")) {
 			char *logformat;
 			/* generate a detailed TCP log */
 			curproxy->to_log |= LW_DATE | LW_CLIP | LW_SVID | LW_PXID | LW_BYTES;
 			logformat = default_tcp_log_format;
-			parse_logformat_string(logformat, curproxy);
+			parse_logformat_string(logformat, curproxy, &curproxy->logformat, curproxy->mode);
 		}
 		else if (!strcmp(args[1], "tcpka")) {
 			/* enable TCP keep-alives on client and server sessions */
@@ -4598,7 +4598,7 @@
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
-		parse_logformat_string(args[1], curproxy);
+		parse_logformat_string(args[1], curproxy, &curproxy->logformat, curproxy->mode);
 	}
 
 	else if (!strcmp(args[0], "log") && kwm == KWM_NO) {
diff --git a/src/log.c b/src/log.c
index 9065a5b..8a09121 100644
--- a/src/log.c
+++ b/src/log.c
@@ -64,40 +64,40 @@
 
 /* log_format variable names */
 static const struct logformat_type logformat_keywords[] = {
-	{ "o", LOG_GLOBAL, PR_MODE_TCP, NULL },  /* global option */
-	{ "Ci", LOG_CLIENTIP, PR_MODE_TCP, NULL },  /* client ip */
-	{ "Cp", LOG_CLIENTPORT, PR_MODE_TCP, NULL }, /* client port */
-	{ "Bp", LOG_SOURCEPORT, PR_MODE_TCP, prepare_addrsource }, /* backend source port */
-	{ "Bi", LOG_SOURCEIP, PR_MODE_TCP, prepare_addrsource }, /* backend source ip */
-	{ "t", LOG_DATE, PR_MODE_TCP, NULL },      /* date */
-	{ "T", LOG_DATEGMT, PR_MODE_TCP, NULL },   /* date GMT */
-	{ "ms", LOG_MS, PR_MODE_TCP, NULL },       /* accept date millisecond */
-	{ "f", LOG_FRONTEND, PR_MODE_TCP, NULL },  /* frontend */
-	{ "b", LOG_BACKEND, PR_MODE_TCP, NULL },   /* backend */
-	{ "s", LOG_SERVER, PR_MODE_TCP, NULL },    /* server */
-	{ "B", LOG_BYTES, PR_MODE_TCP, NULL },     /* bytes read */
-	{ "Tq", LOG_TQ, PR_MODE_HTTP, NULL },       /* Tq */
-	{ "Tw", LOG_TW, PR_MODE_TCP, NULL },       /* Tw */
-	{ "Tc", LOG_TC, PR_MODE_TCP, NULL },       /* Tc */
-	{ "Tr", LOG_TR, PR_MODE_HTTP, NULL },       /* Tr */
-	{ "Tt", LOG_TT, PR_MODE_TCP, NULL },       /* Tt */
-	{ "st", LOG_STATUS, PR_MODE_HTTP, NULL },   /* status code */
-	{ "cc", LOG_CCLIENT, PR_MODE_HTTP, NULL },  /* client cookie */
-	{ "cs", LOG_CSERVER, PR_MODE_HTTP, NULL },  /* server cookie */
-	{ "ts", LOG_TERMSTATE, PR_MODE_TCP, NULL },/* terminaison state */
-	{ "tsc", LOG_TERMSTATE_CK, PR_MODE_HTTP, NULL },/* terminaison state with cookie status */
-	{ "ac", LOG_ACTCONN, PR_MODE_TCP, NULL },  /* actconn */
-	{ "fc", LOG_FECONN, PR_MODE_TCP, NULL },   /* feconn */
-	{ "bc", LOG_BECONN, PR_MODE_TCP, NULL },   /* beconn */
-	{ "sc", LOG_SRVCONN, PR_MODE_TCP, NULL },  /* srv_conn */
-	{ "rc", LOG_RETRIES, PR_MODE_TCP, NULL },  /* retries */
-	{ "sq", LOG_SRVQUEUE, PR_MODE_TCP, NULL  }, /* srv_queue */
-	{ "bq", LOG_BCKQUEUE, PR_MODE_TCP, NULL }, /* backend_queue */
-	{ "hr", LOG_HDRREQUEST, PR_MODE_HTTP, NULL }, /* header request */
-	{ "hs", LOG_HDRRESPONS, PR_MODE_HTTP, NULL },  /* header response */
-	{ "hrl", LOG_HDRREQUESTLIST, PR_MODE_HTTP, NULL }, /* header request list */
-	{ "hsl", LOG_HDRRESPONSLIST, PR_MODE_HTTP, NULL },  /* header response list */
-	{ "r", LOG_REQ, PR_MODE_HTTP, NULL },  /* request */
+	{ "o", LOG_FMT_GLOBAL, PR_MODE_TCP, NULL },  /* global option */
+	{ "Ci", LOG_FMT_CLIENTIP, PR_MODE_TCP, NULL },  /* client ip */
+	{ "Cp", LOG_FMT_CLIENTPORT, PR_MODE_TCP, NULL }, /* client port */
+	{ "Bp", LOG_FMT_BACKENDPORT, PR_MODE_TCP, prepare_addrsource }, /* backend source port */
+	{ "Bi", LOG_FMT_BACKENDIP, PR_MODE_TCP, prepare_addrsource }, /* backend source ip */
+	{ "t", LOG_FMT_DATE, PR_MODE_TCP, NULL },      /* date */
+	{ "T", LOG_FMT_DATEGMT, PR_MODE_TCP, NULL },   /* date GMT */
+	{ "ms", LOG_FMT_MS, PR_MODE_TCP, NULL },       /* accept date millisecond */
+	{ "f", LOG_FMT_FRONTEND, PR_MODE_TCP, NULL },  /* frontend */
+	{ "b", LOG_FMT_BACKEND, PR_MODE_TCP, NULL },   /* backend */
+	{ "s", LOG_FMT_SERVER, PR_MODE_TCP, NULL },    /* server */
+	{ "B", LOG_FMT_BYTES, PR_MODE_TCP, NULL },     /* bytes read */
+	{ "Tq", LOG_FMT_TQ, PR_MODE_HTTP, NULL },       /* Tq */
+	{ "Tw", LOG_FMT_TW, PR_MODE_TCP, NULL },       /* Tw */
+	{ "Tc", LOG_FMT_TC, PR_MODE_TCP, NULL },       /* Tc */
+	{ "Tr", LOG_FMT_TR, PR_MODE_HTTP, NULL },       /* Tr */
+	{ "Tt", LOG_FMT_TT, PR_MODE_TCP, NULL },       /* Tt */
+	{ "st", LOG_FMT_STATUS, PR_MODE_HTTP, NULL },   /* status code */
+	{ "cc", LOG_FMT_CCLIENT, PR_MODE_HTTP, NULL },  /* client cookie */
+	{ "cs", LOG_FMT_CSERVER, PR_MODE_HTTP, NULL },  /* server cookie */
+	{ "ts", LOG_FMT_TERMSTATE, PR_MODE_TCP, NULL },/* terminaison state */
+	{ "tsc", LOG_FMT_TERMSTATE_CK, PR_MODE_TCP, NULL },/* terminaison state */
+	{ "ac", LOG_FMT_ACTCONN, PR_MODE_TCP, NULL },  /* actconn */
+	{ "fc", LOG_FMT_FECONN, PR_MODE_TCP, NULL },   /* feconn */
+	{ "bc", LOG_FMT_BECONN, PR_MODE_TCP, NULL },   /* beconn */
+	{ "sc", LOG_FMT_SRVCONN, PR_MODE_TCP, NULL },  /* srv_conn */
+	{ "rc", LOG_FMT_RETRIES, PR_MODE_TCP, NULL },  /* retries */
+	{ "sq", LOG_FMT_SRVQUEUE, PR_MODE_TCP, NULL  }, /* srv_queue */
+	{ "bq", LOG_FMT_BCKQUEUE, PR_MODE_TCP, NULL }, /* backend_queue */
+	{ "hr", LOG_FMT_HDRREQUEST, PR_MODE_HTTP, NULL }, /* header request */
+	{ "hs", LOG_FMT_HDRRESPONS, PR_MODE_HTTP, NULL },  /* header response */
+	{ "hrl", LOG_FMT_HDRREQUESTLIST, PR_MODE_HTTP, NULL }, /* header request list */
+	{ "hsl", LOG_FMT_HDRRESPONSLIST, PR_MODE_HTTP, NULL },  /* header response list */
+	{ "r", LOG_FMT_REQ, PR_MODE_HTTP, NULL },  /* request */
 	{ 0, 0, 0, NULL }
 };
 
@@ -188,7 +188,7 @@
  * Parse a variable '%varname' or '%{args}varname' in logformat
  *
  */
-int parse_logformat_var(char *str, size_t len, struct proxy *curproxy, int *defoptions)
+int parse_logformat_var(char *str, size_t len, struct proxy *curproxy, struct list *list_format, int *defoptions)
 {
 	int i, j;
 	char *arg = NULL; // arguments
@@ -218,7 +218,7 @@
 						node->options = *defoptions;
 						node->arg = arg;
 						parse_logformat_var_args(node->arg, node);
-						if (node->type == LOG_GLOBAL) {
+						if (node->type == LOG_FMT_GLOBAL) {
 							*defoptions = node->options;
 							free(node);
 						} else {
@@ -227,7 +227,7 @@
 									return -1;
 								 }
 							}
-							LIST_ADDQ(&curproxy->logformat, &node->list);
+							LIST_ADDQ(list_format, &node->list);
 						}
 						return 0;
 					} else {
@@ -253,28 +253,27 @@
  *  start: start pointer
  *  end: end text pointer
  *  type: string type
+ *  list_format: destination list
  *
  *  LOG_TEXT: copy chars from start to end excluding end.
  *
 */
-void add_to_logformat_list(char *start, char *end, int type, struct proxy *curproxy)
+void add_to_logformat_list(char *start, char *end, int type, struct list *list_format)
 {
 	char *str;
 
-	if (type == LOG_TEXT) { /* type text */
+	if (type == LOG_FMT_TEXT) { /* type text */
 		struct logformat_node *node = calloc(1, sizeof(struct logformat_node));
-
 		str = calloc(end - start + 1, 1);
 		strncpy(str, start, end - start);
-
 		str[end - start] = '\0';
 		node->arg = str;
-		node->type = LOG_TEXT; // type string
-		LIST_ADDQ(&curproxy->logformat, &node->list);
-	} else if (type == LOG_SEPARATOR) {
+		node->type = LOG_FMT_TEXT; // type string
+		LIST_ADDQ(list_format, &node->list);
+	} else if (type == LOG_FMT_SEPARATOR) {
 		struct logformat_node *node = calloc(1, sizeof(struct logformat_node));
-		node->type = LOG_SEPARATOR;
-		LIST_ADDQ(&curproxy->logformat, &node->list);
+		node->type = LOG_FMT_SEPARATOR;
+		LIST_ADDQ(list_format, &node->list);
 	}
 }
 
@@ -282,8 +281,13 @@
  * Parse the log_format string and fill a linked list.
  * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
  * You can set arguments using { } : %{many arguments}varname
+ *
+ *  str: the string to parse
+ *  curproxy: the proxy affected
+ *  list_format: the destination list
+ *  capabilities: PR_MODE_TCP_ | PR_MODE_HTTP
  */
-void parse_logformat_string(char *str, struct proxy *curproxy)
+void parse_logformat_string(char *str, struct proxy *curproxy, struct list *list_format, int capabilities)
 {
 	char *sp = str; /* start pointer */
 	int cformat = -1; /* current token format : LOG_TEXT, LOG_SEPARATOR, LOG_VARIABLE */
@@ -292,7 +296,7 @@
 	int options = 0;
 
 	/* flush the list first. */
-	list_for_each_entry_safe(tmplf, back, &curproxy->logformat, list) {
+	list_for_each_entry_safe(tmplf, back, list_format, list) {
 		LIST_DEL(&tmplf->list);
 		free(tmplf);
 	}
@@ -308,9 +312,9 @@
 				if (pformat > LF_VAR) // unfinished string
 					pformat = LF_TEXT;
 				if (pformat == LF_VAR)
-					parse_logformat_var(sp, str - sp, curproxy, &options);
+					parse_logformat_var(sp, str - sp, curproxy, list_format, &options);
 				else
-					add_to_logformat_list(sp, str, pformat, curproxy);
+					add_to_logformat_list(sp, str, pformat, list_format);
 				sp = str;
 				if (*str == '\0')
 					break;
@@ -482,7 +486,6 @@
 				*(dst++) = '"';
 				*(dst++) = '"';
 				*dst = '\0';
-				node->options |= LOG_OPT_WRITTEN;
 			} else {
 				dst = NULL;
 				return dst;
@@ -491,7 +494,6 @@
 			if (size > 1) {
 				*(dst++) = '-';
 				*dst = '\0';
-				node->options |= LOG_OPT_WRITTEN;
 			} else { // error no space available
 				dst = NULL;
 				return dst;
@@ -693,27 +695,26 @@
 						   Set-cookie Deleted, Set-Cookie Inserted, Set-cookie Rewritten,
 						   Set-cookie Updated, unknown, unknown */
 
+/*
+ * try to write a character if there is enough space, or goto out
+ */
 #define LOGCHAR(x) do { \
-			if (tmplog < logline + MAX_SYSLOG_LEN - 1) { \
+			if (tmplog < dst + maxsize - 1) { \
 				*(tmplog++) = (x);                     \
 			} else {                                       \
 				goto out;                              \
 			}                                              \
 		} while(0)
 
-/*
- * send a log for the session when we have enough info about it.
- * Will not log if the frontend has no log defined.
- */
-void sess_log(struct session *s)
+
+
+int build_logline(struct session *s, char *dst, size_t maxsize, struct list *list_format)
 {
 	char pn[INET6_ADDRSTRLEN];
-	char sn[INET6_ADDRSTRLEN];
 	struct proxy *fe = s->fe;
 	struct proxy *be = s->be;
-	struct proxy *prx_log;
 	struct http_txn *txn = &s->txn;
-	int tolog, level, err;
+	int tolog;
 	char *uri;
 	const char *svid;
 	struct tm tm;
@@ -721,27 +722,10 @@
 	int hdr;
 	int last_isspace = 1;
 	char *tmplog;
+	char *ret;
+	int iret;
 	struct logformat_node *tmp;
 
-	/* if we don't want to log normal traffic, return now */
-	err = (s->flags & (SN_ERR_MASK | SN_REDISP)) ||
-		(s->req->cons->conn_retries != be->conn_retries) ||
-		((s->fe->mode == PR_MODE_HTTP) && txn->status >= 500);
-	if (!err && (fe->options2 & PR_O2_NOLOGNORM))
-		return;
-
-	if (LIST_ISEMPTY(&fe->logsrvs))
-		return;
-	prx_log = fe;
-
-	if (addr_to_str(&s->req->prod->addr.from, pn, sizeof(pn)) == AF_UNIX)
-		snprintf(pn, sizeof(pn), "unix:%d", s->listener->luid);
-
-	if (be->options2 & PR_O2_SRC_ADDR) {
-	      if (addr_to_str(&s->req->cons->addr.from, sn, sizeof(sn)) == AF_UNIX)
-		snprintf(sn, sizeof(sn), "unix:%d", s->listener->luid);
-	}
-
 	/* FIXME: let's limit ourselves to frontend logging for now. */
 	tolog = fe->to_log;
 
@@ -763,85 +747,94 @@
 	if (tv_isge(&s->logs.tv_request, &s->logs.tv_accept))
 		t_request = tv_ms_elapsed(&s->logs.tv_accept, &s->logs.tv_request);
 
-	level = LOG_INFO;
-	if (err && (fe->options2 & PR_O2_LOGERRORS))
-		level = LOG_ERR;
+	tmplog = dst;
 
 	/* fill logbuffer */
-
-	tmplog = update_log_hdr(); /* update log header and skip it */
+	if (LIST_ISEMPTY(list_format))
+		return 0;
 
-	list_for_each_entry(tmp, &fe->logformat, list) {
+	list_for_each_entry(tmp, list_format, list) {
 		char *src = NULL;
 		switch (tmp->type) {
 
-			case LOG_SEPARATOR:
+			case LOG_FMT_SEPARATOR:
 				if (!last_isspace) {
 					LOGCHAR(' ');
 					last_isspace = 1;
-					*tmplog = '\0';
 				}
 				break;
 
-			case LOG_TEXT: // text
+			case LOG_FMT_TEXT: // text
 				src = tmp->arg;
-				tmplog += strlcpy2(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (tmplog > logline + MAX_SYSLOG_LEN - 2)
+				iret = strlcpy2(tmplog, src, maxsize - (tmplog - dst));
+				if (iret == 0)
 					goto out;
+				tmplog += iret;
 				last_isspace = 0;
 				break;
 
-			case LOG_CLIENTIP:  // %Ci
+			case LOG_FMT_CLIENTIP:  // %Ci
+				/* client addr */
+				if (addr_to_str(&s->req->prod->addr.from, pn, sizeof(pn)) == AF_UNIX)
+					snprintf(pn, sizeof(pn), "unix:%d", s->listener->luid);
 				src = (s->req->prod->addr.from.ss_family == AF_UNIX) ? "unix" : pn;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+				ret = logformat_write_string(tmplog, src, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_CLIENTPORT:  // %Cp
-				tmplog = ltoa_o((s->req->prod->addr.from.ss_family == AF_UNIX) ? s->listener->luid : get_host_port(&s->req->prod->addr.from),
-				                tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_CLIENTPORT:  // %Cp
+				ret = ltoa_o((s->req->prod->addr.from.ss_family == AF_UNIX) ?
+					     s->listener->luid : get_host_port(&s->req->prod->addr.from),
+					     tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_SOURCEIP:  // Bi
-				src = (s->req->cons->addr.from.ss_family == AF_UNIX) ? "unix" : sn;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+			case LOG_FMT_BACKENDIP:  // %Bi
+				/* backend addr  */
+				if (be->options2 & PR_O2_SRC_ADDR)
+					addr_to_str(&s->req->cons->addr.from, pn, sizeof(pn));
+				ret = logformat_write_string(tmplog, pn, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_SOURCEPORT:  // %Bp
-				tmplog = ltoa_o((s->req->cons->addr.from.ss_family == AF_UNIX) ? s->listener->luid : get_host_port(&s->req->cons->addr.from),
-				                tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_BACKENDPORT:  // %Bp
+				ret = ltoa_o(get_host_port(&s->req->cons->addr.from),
+					     tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_DATE: // %t
+			case LOG_FMT_DATE: // %t
 				get_localtime(s->logs.accept_date.tv_sec, &tm);
-				tmplog = date2str_log(tmplog, &tm, &(s->logs.accept_date), MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+				ret = date2str_log(tmplog, &tm, &(s->logs.accept_date), maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_DATEGMT: // %T
+			case LOG_FMT_DATEGMT: // %T
 				get_gmtime(s->logs.accept_date.tv_sec, &tm);
-				tmplog = gmt2str_log(tmplog, &tm, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+				ret = gmt2str_log(tmplog, &tm, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_MS: // %ms
-				if ((MAX_SYSLOG_LEN - (tmplog - logline)) < 4) {
-					tmplog = NULL;
+			case LOG_FMT_MS: // %ms
+				if ((maxsize - (tmplog - dst)) < 4) {
 					goto out;
 				}
 				tmplog = utoa_pad((unsigned int)s->logs.accept_date.tv_usec/1000,
@@ -851,167 +844,192 @@
 				last_isspace = 0;
 				break;
 
-			case LOG_FRONTEND: // %f
+			case LOG_FMT_FRONTEND: // %f
 				src = fe->id;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+				ret = logformat_write_string(tmplog, src, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_BACKEND: // %b
+			case LOG_FMT_BACKEND: // %b
 				src = be->id;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+				ret = logformat_write_string(tmplog, src, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_SERVER: // %s
+			case LOG_FMT_SERVER: // %s
 				src = (char *)svid;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+				ret = logformat_write_string(tmplog, src, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_TQ: // %Tq
-				tmplog = ltoa_o(t_request, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_TQ: // %Tq
+				ret = ltoa_o(t_request, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_TW: // %Tw
-				tmplog = ltoa_o((s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_TW: // %Tw
+				ret = ltoa_o((s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1,
+						tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_TC: // %Tc
-				tmplog = ltoa_o((s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_TC: // %Tc
+				ret = ltoa_o((s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
+						tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_TR: // %Tr
-				tmplog = ltoa_o((s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_TR: // %Tr
+				ret = ltoa_o((s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
+						tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_TT:  // %Tt
+			case LOG_FMT_TT:  // %Tt
 				if (!(tolog & LW_BYTES))
-					*(tmplog++) = '+';
-				tmplog = ltoa_o(s->logs.t_close, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+					LOGCHAR('+');
+				ret = ltoa_o(s->logs.t_close, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_STATUS: // %st
-				tmplog = ltoa_o(txn->status, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_STATUS: // %st
+				ret = ltoa_o(txn->status, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_BYTES: // %B
+			case LOG_FMT_BYTES: // %B
 				if (!(tolog & LW_BYTES))
-					*(tmplog++) = '+';
-				tmplog = lltoa(s->logs.bytes_out, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+					LOGCHAR('+');
+				ret = lltoa(s->logs.bytes_out, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_CCLIENT: // %cc
+			case LOG_FMT_CCLIENT: // %cc
 				src = txn->cli_cookie;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+				ret = logformat_write_string(tmplog, src, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_CSERVER: // %cs
+			case LOG_FMT_CSERVER: // %cs
 				src = txn->srv_cookie;
-				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
-				if (!tmplog)
+				ret = logformat_write_string(tmplog, src, maxsize - (tmplog - dst), tmp);
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_TERMSTATE: // %ts
+			case LOG_FMT_TERMSTATE: // %ts
 				LOGCHAR(sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT]);
 				LOGCHAR(sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT]);
 				*tmplog = '\0';
 				last_isspace = 0;
 				break;
 
-			case LOG_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
+			case LOG_FMT_TERMSTATE_CK: // %tsc, same as TS with cookie state (for mode HTTP)
 				LOGCHAR(sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT]);
 				LOGCHAR(sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT]);
 				LOGCHAR((be->options & PR_O_COOK_ANY) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-');
 				LOGCHAR((be->options & PR_O_COOK_ANY) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-');
-				*tmplog = '\0';
 				last_isspace = 0;
 				break;
 
-			case LOG_ACTCONN: // %ac
-				tmplog = ltoa_o(actconn, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_ACTCONN: // %ac
+				ret = ltoa_o(actconn, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_FECONN:  // %fc
-				tmplog = ltoa_o(fe->feconn, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_FECONN:  // %fc
+				ret = ltoa_o(fe->feconn, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_BECONN:  // %bc
-				tmplog = ltoa_o(be->beconn, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_BECONN:  // %bc
+				ret = ltoa_o(be->beconn, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_SRVCONN:  // %sc
-				tmplog = ultoa_o(target_srv(&s->target) ? target_srv(&s->target)->cur_sess : 0, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_SRVCONN:  // %sc
+				ret = ultoa_o(target_srv(&s->target) ?
+				                 target_srv(&s->target)->cur_sess :
+				                 0, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_RETRIES:  // %rq
+			case LOG_FMT_RETRIES:  // %rq
 				if (s->flags & SN_REDISP)
-					*(tmplog++) = '+';
-				tmplog = ltoa_o((s->req->cons->conn_retries>0)?(be->conn_retries - s->req->cons->conn_retries):be->conn_retries, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+					LOGCHAR('+');
+				ret = ltoa_o((s->req->cons->conn_retries>0) ?
+				                (be->conn_retries - s->req->cons->conn_retries) :
+				                be->conn_retries, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_SRVQUEUE: // %sq
-				tmplog = ltoa_o(s->logs.srv_queue_size, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_SRVQUEUE: // %sq
+				ret = ltoa_o(s->logs.srv_queue_size, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_BCKQUEUE:  // %bq
-				tmplog = ltoa_o(s->logs.prx_queue_size, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
-				if (!tmplog)
+			case LOG_FMT_BCKQUEUE:  // %bq
+				ret = ltoa_o(s->logs.prx_queue_size, tmplog, maxsize - (tmplog - dst));
+				if (ret == NULL)
 					goto out;
+				tmplog = ret;
 				last_isspace = 0;
 				break;
 
-			case LOG_HDRREQUEST: // %hr
+			case LOG_FMT_HDRREQUEST: // %hr
 				/* request header */
 				if (fe->to_log & LW_REQHDR && txn->req.cap) {
 					if (tmp->options & LOG_OPT_QUOTE)
@@ -1021,21 +1039,23 @@
 						if (hdr)
 							LOGCHAR('|');
 						if (txn->req.cap[hdr] != NULL) {
-							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+							ret = encode_string(tmplog, dst + maxsize,
 									       '#', hdr_encode_map, txn->req.cap[hdr]);
-							if (*tmplog != '\0')
+							if (ret == NULL || *ret != '\0')
 								goto out;
+							tmplog = ret;
 						}
 					}
 					LOGCHAR('}');
 					if (tmp->options & LOG_OPT_QUOTE)
 						LOGCHAR('"');
 					last_isspace = 0;
+					if (tmp->options & LOG_OPT_QUOTE)
+						LOGCHAR('"');
 				}
-				*tmplog = '\0';
 				break;
 
-			case LOG_HDRREQUESTLIST: // %hrl
+			case LOG_FMT_HDRREQUESTLIST: // %hrl
 				/* request header list */
 				if (fe->to_log & LW_REQHDR && txn->req.cap) {
 					for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
@@ -1044,22 +1064,22 @@
 						if (tmp->options & LOG_OPT_QUOTE)
 							LOGCHAR('"');
 						if (txn->req.cap[hdr] != NULL) {
-							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+							ret = encode_string(tmplog, dst + maxsize,
 									       '#', hdr_encode_map, txn->req.cap[hdr]);
-							if (*tmplog != '\0')
+							if (ret == NULL || *ret != '\0')
 								goto out;
-
+							tmplog = ret;
 						} else if (!(tmp->options & LOG_OPT_QUOTE))
 							LOGCHAR('-');
 						if (tmp->options & LOG_OPT_QUOTE)
 							LOGCHAR('"');
-						*tmplog = '\0';
 						last_isspace = 0;
 					}
 				}
 				break;
 
+
-			case LOG_HDRRESPONS: // %hs
+			case LOG_FMT_HDRRESPONS: // %hs
 				/* response header */
 				if (fe->to_log & LW_RSPHDR &&
 				    txn->rsp.cap) {
@@ -1070,10 +1090,11 @@
 						if (hdr)
 							LOGCHAR('|');
 						if (txn->rsp.cap[hdr] != NULL) {
-							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
-									       '#', hdr_encode_map, txn->rsp.cap[hdr]);
-							if (*tmplog != '\0')
+							ret = encode_string(tmplog, dst + maxsize,
+							                    '#', hdr_encode_map, txn->rsp.cap[hdr]);
+							if (ret == NULL || *ret != '\0')
 								goto out;
+							tmplog = ret;
 						}
 					}
 					LOGCHAR('}');
@@ -1081,10 +1102,9 @@
 					if (tmp->options & LOG_OPT_QUOTE)
 						LOGCHAR('"');
 				}
-				*tmplog = '\0';
 				break;
 
-			case LOG_HDRRESPONSLIST: // %hsl
+			case LOG_FMT_HDRRESPONSLIST: // %hsl
 				/* response header list */
 				if (fe->to_log & LW_RSPHDR && txn->rsp.cap) {
 					for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
@@ -1093,50 +1113,77 @@
 						if (tmp->options & LOG_OPT_QUOTE)
 							LOGCHAR('"');
 						if (txn->rsp.cap[hdr] != NULL) {
-							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
-									       '#', hdr_encode_map, txn->rsp.cap[hdr]);
-							if (*tmplog != '\0')
+							ret = encode_string(tmplog, dst + maxsize,
+							                    '#', hdr_encode_map, txn->rsp.cap[hdr]);
+							if (ret == NULL || *ret != '\0')
 								goto out;
+							tmplog = ret;
 						} else if (!(tmp->options & LOG_OPT_QUOTE))
 							LOGCHAR('-');
 						if (tmp->options & LOG_OPT_QUOTE)
 							LOGCHAR('"');
-						*tmplog = '\0';
 						last_isspace = 0;
 					}
 				}
 				break;
 
-			case LOG_REQ: // %r
+			case LOG_FMT_REQ: // %r
 				/* Request */
 				if (tmp->options & LOG_OPT_QUOTE)
 					LOGCHAR('"');
 				uri = txn->uri ? txn->uri : "<BADREQ>";
-				tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+				ret = encode_string(tmplog, dst + maxsize,
 						       '#', url_encode_map, uri);
-				if (*tmplog != '\0')
+				if (ret == NULL || *ret != '\0')
 					goto out;
+				tmplog = ret;
 				if (tmp->options & LOG_OPT_QUOTE)
 					LOGCHAR('"');
-				*tmplog = '\0';
 				last_isspace = 0;
 				break;
 		}
 	}
 
 out:
+	/* *tmplog is a unused character */
+	*tmplog = '\0';
 
-	if (tmplog == NULL) // if previous error
-		tmplog = logline + MAX_SYSLOG_LEN - 1;
-
-	__send_log(prx_log, level, logline, tmplog - logline + 1);
-	s->logs.logwait = 0;
+	return tmplog - dst + 1;
 
 }
 
+/*
+ * send a log for the session when we have enough info about it.
+ * Will not log if the frontend has no log defined.
+ */
+void sess_log(struct session *s)
+{
+	char *tmplog;
+	int size, err, level;
+
+	/* if we don't want to log normal traffic, return now */
+	err = (s->flags & (SN_ERR_MASK | SN_REDISP)) ||
+		(s->req->cons->conn_retries != s->be->conn_retries) ||
+			((s->fe->mode == PR_MODE_HTTP) && s->txn.status >= 500);
 
+	if (!err && (s->fe->options2 & PR_O2_NOLOGNORM))
+		return;
 
+	if (LIST_ISEMPTY(&s->fe->logsrvs))
+		return;
 
+	level = LOG_INFO;
+	if (err && (s->fe->options2 & PR_O2_LOGERRORS))
+		level = LOG_ERR;
+
+	tmplog = update_log_hdr();
+	size = tmplog - logline;
+	size += build_logline(s, tmplog, sizeof(logline) - size, &s->fe->logformat);
+	if (size > 0) {
+		__send_log(s->fe, level, logline, size);
+		s->logs.logwait = 0;
+	}
+}
 
 /*
  * Local variables: