MEDIUM: log: make http_sess_log use log_format

http_sess_log now use the logformat linked list to make the log
string, snprintf is not used for speed issue.

CLF mode also uses logformat.

NOTE: as of now, empty fields in CLF now are "" not "-" anymore.
diff --git a/include/proto/log.h b/include/proto/log.h
index 2cf114b..34d3813 100644
--- a/include/proto/log.h
+++ b/include/proto/log.h
@@ -113,6 +113,14 @@
  */
 int get_log_facility(const char *fac);
 
+/*
+ * Write a string in the log string
+ * Take cares of mandatory and quote options
+ *
+ * Return the adress of the \0 character, or NULL on error
+ */
+char *logformat_write_string(char *dst, char *src, size_t size, struct logformat_node *node);
+
 #endif /* _PROTO_LOG_H */
 
 /*
diff --git a/src/log.c b/src/log.c
index 8f639e0..17dc281 100644
--- a/src/log.c
+++ b/src/log.c
@@ -430,6 +430,60 @@
 	return facility;
 }
 
+/*
+ * Write a string in the log string
+ * Take cares of mandatory and quote options
+ *
+ * Return the adress of the \0 character, or NULL on error
+ */
+char *logformat_write_string(char *dst, char *src, size_t size, struct logformat_node *node)
+{
+        char *orig = dst;
+
+        if (src == NULL || *src == '\0') {
+                        if (node->options & LOG_OPT_QUOTE) {
+                                if (size > 2) {
+                                        *(dst++) = '"';
+                                        *(dst++) = '"';
+                                        *dst = '\0';
+                                        node->options |= LOG_OPT_WRITTEN;
+                                } else {
+                                        dst = NULL;
+                                        return dst;
+                                }
+                        } else {
+                                if (size > 1) {
+                                        *(dst++) = '-';
+                                        *dst = '\0';
+                                        node->options |= LOG_OPT_WRITTEN;
+                                } else { // error no space available
+                                        dst = NULL;
+                                        return dst;
+                                }
+                        }
+        } else {
+                if (node->options & LOG_OPT_QUOTE) {
+                        if (size-- > 1 ) {
+                                *(dst++) = '"';
+                        } else {
+                                dst = NULL;
+                                return NULL;
+                        }
+                        dst += strlcpy2(dst, src, size);
+                        size -= orig - dst + 1;
+                        if (size > 1) {
+                                *(dst++) = '"';
+                                *dst = '\0';
+                        } else {
+                                dst = NULL;
+                        }
+                } else {
+                        dst += strlcpy2(dst, src, size);
+                }
+        }
+        return dst;
+}
+
 /* generate the syslog header once a second */
 char *hdr_log(char *dst)
 {
diff --git a/src/proto_http.c b/src/proto_http.c
index 52efdf8..dd40f94 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -851,7 +851,19 @@
 struct pool_head *pool2_requri;
 struct pool_head *pool2_capture;
 
-void http_sess_clflog(struct session *s)
+#define LOGCHAR(x) do { \
+			if (MAX_SYSLOG_LEN - (tmplog - logline) > 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 http_sess_log(struct session *s)
 {
 	char pn[INET6_ADDRSTRLEN];
 	struct proxy *fe = s->fe;
@@ -859,85 +871,33 @@
 	struct proxy *prx_log;
 	struct http_txn *txn = &s->txn;
 	int tolog, level, err;
-	char *uri, *h;
+	char *uri;
 	const char *svid;
 	struct tm tm;
-	static char tmpline[MAX_SYSLOG_LEN];
-	int hdr;
-	size_t w;
 	int t_request;
+	int hdr;
+	int last_isspace = 1;
+	static char logline[MAX_SYSLOG_LEN] = { 0 };
+	static char *tmplog;
+	struct logformat_node *tmp;
 
-	prx_log = fe;
+	/* 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) ||
 		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);
 
-	get_gmtime(s->logs.accept_date.tv_sec, &tm);
-
 	/* FIXME: let's limit ourselves to frontend logging for now. */
 	tolog = fe->to_log;
 
-	h = tmpline;
-
-	w = snprintf(h, sizeof(tmpline),
-	             "%s - - [%02d/%s/%04d:%02d:%02d:%02d +0000]",
-	             pn,
-	             tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
-	             tm.tm_hour, tm.tm_min, tm.tm_sec);
-	if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
-		goto trunc;
-	h += w;
-
-	if (h >= tmpline + sizeof(tmpline) - 4)
-		goto trunc;
-
-	*(h++) = ' ';
-	*(h++) = '\"';
-	uri = txn->uri ? txn->uri : "<BADREQ>";
-	h = encode_string(h, tmpline + sizeof(tmpline) - 1,
-	                  '#', url_encode_map, uri);
-	*(h++) = '\"';
-
-	w = snprintf(h, sizeof(tmpline) - (h - tmpline), " %d %lld", txn->status, s->logs.bytes_out);
-	if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
-		goto trunc;
-	h += w;
-
-	if (h >= tmpline + sizeof(tmpline) - 9)
-		goto trunc;
-	memcpy(h, " \"-\" \"-\"", 8);
-	h += 8;
-
-	w = snprintf(h, sizeof(tmpline) - (h - tmpline),
-	             " %d %03d",
-		     s->req->prod->addr.from.ss_family == AF_UNIX ? s->listener->luid :
-		         get_host_port(&s->req->prod->addr.from),
-	             (int)s->logs.accept_date.tv_usec/1000);
-	if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
-		goto trunc;
-	h += w;
-
-	w = strlen(fe->id);
-	if (h >= tmpline + sizeof(tmpline) - 4 - w)
-		goto trunc;
-	*(h++) = ' ';
-	*(h++) = '\"';
-	memcpy(h, fe->id, w);
-	h += w;
-	*(h++) = '\"';
-
-	w = strlen(be->id);
-	if (h >= tmpline + sizeof(tmpline) - 4 - w)
-		goto trunc;
-	*(h++) = ' ';
-	*(h++) = '\"';
-	memcpy(h, be->id, w);
-	h += w;
-	*(h++) = '\"';
-
 	if (!(tolog & LW_SVID))
 		svid = "-";
 	else switch (s->target.type) {
@@ -952,262 +912,339 @@
 		break;
 	}
 
-	w = strlen(svid);
-	if (h >= tmpline + sizeof(tmpline) - 4 - w)
-		goto trunc;
-	*(h++) = ' ';
-	*(h++) = '\"';
-	memcpy(h, svid, w);
-	h += w;
-	*(h++) = '\"';
-
 	t_request = -1;
 	if (tv_isge(&s->logs.tv_request, &s->logs.tv_accept))
 		t_request = tv_ms_elapsed(&s->logs.tv_accept, &s->logs.tv_request);
-	w = snprintf(h, sizeof(tmpline) - (h - tmpline),
-	             " %d %ld %ld %ld %ld",
-	             t_request,
-	             (s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1,
-	             (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
-	             (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
-	             s->logs.t_close);
-	if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
-		goto trunc;
-	h += w;
 
-	if (h >= tmpline + sizeof(tmpline) - 8)
-		goto trunc;
-	*(h++) = ' ';
-	*(h++) = '\"';
-	*(h++) = sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT];
-	*(h++) = sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT];
-	*(h++) = (be->options & PR_O_COOK_ANY) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-',
-	*(h++) = (be->options & PR_O_COOK_ANY) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-';
-	*(h++) = '\"';
+	level = LOG_INFO;
+	if (err && (fe->options2 & PR_O2_LOGERRORS))
+		level = LOG_ERR;
 
-	w = snprintf(h, sizeof(tmpline) - (h - tmpline),
-	             " %d %d %d %d %d %ld %ld",
-	             actconn, fe->feconn, be->beconn, target_srv(&s->target) ? target_srv(&s->target)->cur_sess : 0,
-	             (s->req->cons->conn_retries > 0) ? (be->conn_retries - s->req->cons->conn_retries) : be->conn_retries,
-	             s->logs.srv_queue_size, s->logs.prx_queue_size);
+	/* fill logbuffer */
 
-	if (w < 0 || w >= sizeof(tmpline) - (h - tmpline))
-		goto trunc;
-	h += w;
+	tmplog = logline;
+	tmplog = hdr_log(tmplog);
 
-	if (txn->cli_cookie) {
-		w = strlen(txn->cli_cookie);
-		if (h >= tmpline + sizeof(tmpline) - 4 - w)
-			goto trunc;
-		*(h++) = ' ';
-		*(h++) = '\"';
-		memcpy(h, txn->cli_cookie, w);
-		h += w;
-		*(h++) = '\"';
-	} else {
-		if (h >= tmpline + sizeof(tmpline) - 5)
-			goto trunc;
-		memcpy(h, " \"-\"", 4);
-		h += 4;
-	}
+	list_for_each_entry(tmp, &fe->logformat, list) {
+		char *src = NULL;
+		switch (tmp->type) {
 
-	if (txn->srv_cookie) {
-		w = strlen(txn->srv_cookie);
-		if (h >= tmpline + sizeof(tmpline) - 4 - w)
-			goto trunc;
-		*(h++) = ' ';
-		*(h++) = '\"';
-		memcpy(h, txn->srv_cookie, w);
-		h += w;
-		*(h++) = '\"';
-	} else {
-		if (h >= tmpline + sizeof(tmpline) - 5)
-			goto trunc;
-		memcpy(h, " \"-\"", 4);
-		h += 4;
-	}
+			case LOG_SEPARATOR:
+				if (!last_isspace) {
+					LOGCHAR(' ');
+					last_isspace = 1;
+					*tmplog = '\0';
+				}
+				break;
 
-	if ((fe->to_log & LW_REQHDR) && txn->req.cap) {
-		for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
-			if (h >= sizeof (tmpline) + tmpline - 4)
-				goto trunc;
-			if (txn->req.cap[hdr] != NULL) {
-				*(h++) = ' ';
-				*(h++) = '\"';
-				h = encode_string(h, tmpline + sizeof(tmpline) - 2,
-						'#', hdr_encode_map, txn->req.cap[hdr]);
-				*(h++) = '\"';
-			} else {
-				memcpy(h, " \"-\"", 4);
-				h += 4;
-			}
-		}
-	}
+			case LOG_TEXT: // text
+				src = tmp->arg;
+				tmplog += strlcpy2(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	if ((fe->to_log & LW_RSPHDR) && txn->rsp.cap) {
-		for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
-			if (h >= sizeof (tmpline) + tmpline - 4)
-				goto trunc;
-			if (txn->rsp.cap[hdr] != NULL) {
-				*(h++) = ' ';
-				*(h++) = '\"';
-				h = encode_string(h, tmpline + sizeof(tmpline) - 2,
-						'#', hdr_encode_map, txn->rsp.cap[hdr]);
-				*(h++) = '\"';
-			} else {
-				memcpy(h, " \"-\"", 4);
-				h += 4;
-			}
-		}
-	}
+			case LOG_CLIENTIP:  // %Ci
+				src = (s->req->prod->addr.from.ss_family == AF_UNIX) ? "unix" : pn;
+				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
 
-trunc:
-	*h = '\0';
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	level = LOG_INFO;
-	if (err && (fe->options2 & PR_O2_LOGERRORS))
-		level = LOG_ERR;
+			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)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	send_log(prx_log, level, "%s\n", tmpline);
+			case LOG_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)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	s->logs.logwait = 0;
-}
+			case LOG_DATEGMT: // %T
+				get_gmtime(s->logs.accept_date.tv_sec, &tm);
+				tmplog = gmt2str_log(tmplog, &tm, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-/*
- * send a log for the session when we have enough info about it.
- * Will not log if the frontend has no log defined.
- */
-void http_sess_log(struct session *s)
-{
-	char pn[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;
-	char *uri, *h;
-	const char *svid;
-	struct tm tm;
-	static char tmpline[MAX_SYSLOG_LEN];
-	int t_request;
-	int hdr;
+			case LOG_MS: // %ms
+				if ((MAX_SYSLOG_LEN - (tmplog - logline)) < 4) {
+					tmplog = NULL;
+					goto out;
+				}
+				tmplog = utoa_pad((unsigned int)s->logs.accept_date.tv_usec/1000,
+						  tmplog, 4);
+				last_isspace = 0;
 
-	/* 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) ||
-		txn->status >= 500;
-	if (!err && (fe->options2 & PR_O2_NOLOGNORM))
-		return;
+				break;
 
-	if (LIST_ISEMPTY(&fe->logsrvs))
-		return;
-	prx_log = fe;
+			case LOG_FRONTEND: // %f
+				src = fe->id;
+				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
+				if (!tmplog)
+					goto out;
+				last_isspace = 0 ;
+				break;
 
-	if (prx_log->options2 & PR_O2_CLFLOG)
-		return http_sess_clflog(s);
+			case LOG_BACKEND: // %b
+				src = be->id;
+				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
+				if (!tmplog)
+					goto out;
+				last_isspace = 0 ;
+				break;
 
-	if (addr_to_str(&s->req->prod->addr.from, pn, sizeof(pn)) == AF_UNIX)
-		snprintf(pn, sizeof(pn), "unix:%d", s->listener->luid);
+			case LOG_SERVER: // %s
+				src = (char *)svid;
+				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	get_localtime(s->logs.accept_date.tv_sec, &tm);
+			case LOG_TQ: // %Tq
+				tmplog = ltoa_o(t_request, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	/* FIXME: let's limit ourselves to frontend logging for now. */
-	tolog = fe->to_log;
+			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)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	h = tmpline;
-	if (fe->to_log & LW_REQHDR &&
-	    txn->req.cap &&
-	    (h < tmpline + sizeof(tmpline) - 10)) {
-		*(h++) = ' ';
-		*(h++) = '{';
-		for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
-			if (hdr)
-				*(h++) = '|';
-			if (txn->req.cap[hdr] != NULL)
-				h = encode_string(h, tmpline + sizeof(tmpline) - 7,
-						  '#', hdr_encode_map, txn->req.cap[hdr]);
-		}
-		*(h++) = '}';
-	}
+			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)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	if (fe->to_log & LW_RSPHDR &&
-	    txn->rsp.cap &&
-	    (h < tmpline + sizeof(tmpline) - 7)) {
-		*(h++) = ' ';
-		*(h++) = '{';
-		for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
-			if (hdr)
-				*(h++) = '|';
-			if (txn->rsp.cap[hdr] != NULL)
-				h = encode_string(h, tmpline + sizeof(tmpline) - 4,
-						  '#', hdr_encode_map, txn->rsp.cap[hdr]);
-		}
-		*(h++) = '}';
-	}
+			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)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	if (h < tmpline + sizeof(tmpline) - 4) {
-		*(h++) = ' ';
-		*(h++) = '"';
-		uri = txn->uri ? txn->uri : "<BADREQ>";
-		h = encode_string(h, tmpline + sizeof(tmpline) - 1,
-				  '#', url_encode_map, uri);
-		*(h++) = '"';
-	}
-	*h = '\0';
+			case LOG_TT:  // %Tt
+				if (!(tolog & LW_BYTES))
+					*(tmplog++) = '+';
+				tmplog = ltoa_o(s->logs.t_close, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	if (!(tolog & LW_SVID))
-		svid = "-";
-	else switch (s->target.type) {
-	case TARG_TYPE_SERVER:
-		svid = s->target.ptr.s->id;
-		break;
-	case TARG_TYPE_APPLET:
-		svid = s->target.ptr.a->name;
-		break;
-	default:
-		svid = "<NOSRV>";
-		break;
-	}
+			case LOG_STATUS: // %st
+				tmplog = ultoa_o(txn->status, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	t_request = -1;
-	if (tv_isge(&s->logs.tv_request, &s->logs.tv_accept))
-		t_request = tv_ms_elapsed(&s->logs.tv_accept, &s->logs.tv_request);
+			case LOG_BYTES: // %B
+				if (!(tolog & LW_BYTES))
+					*(tmplog++) = '+';
+				tmplog = lltoa(s->logs.bytes_out, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	level = LOG_INFO;
-	if (err && (fe->options2 & PR_O2_LOGERRORS))
-		level = LOG_ERR;
+			case LOG_CCLIENT: // %cc
+				src = txn->cli_cookie;
+				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
+				last_isspace = 0;
+				break;
+
+			case LOG_CSERVER: // %cs
+				src = txn->srv_cookie;
+				tmplog = logformat_write_string(tmplog, src, MAX_SYSLOG_LEN - (tmplog - logline), tmp);
+				last_isspace = 0;
+				break;
+
+			case LOG_TERMSTATE: // %ts
+				if (MAX_SYSLOG_LEN - (tmplog - logline) < 5)
+					goto out;
+				*(tmplog++) = sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT];
+				*(tmplog++) = sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT];
+				*(tmplog++) = (be->options & PR_O_COOK_ANY) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-';
+				*(tmplog++) = (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)
+					goto out;
+				last_isspace = 0;
+				break;
+
+			case LOG_FECONN:  // %fc
+				tmplog = ltoa_o(fe->feconn, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
+
+			case LOG_BECONN:  // %bc
+				tmplog = ltoa_o(be->beconn, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
 
-	send_log(prx_log, level,
-		 "%s:%d [%02d/%s/%04d:%02d:%02d:%02d.%03d]"
-		 " %s %s/%s %d/%ld/%ld/%ld/%s%ld %d %s%lld"
-		 " %s %s %c%c%c%c %d/%d/%d/%d/%s%u %ld/%ld%s\n",
-		 (s->req->prod->addr.from.ss_family == AF_UNIX) ? "unix" : pn,
-		 (s->req->prod->addr.from.ss_family == AF_UNIX) ? s->listener->luid :
-		     get_host_port(&s->req->prod->addr.from),
-		 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
-		 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)s->logs.accept_date.tv_usec/1000,
-		 fe->id, be->id, svid,
-		 t_request,
-		 (s->logs.t_queue >= 0) ? s->logs.t_queue - t_request : -1,
-		 (s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
-		 (s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
-		 (tolog & LW_BYTES) ? "" : "+", s->logs.t_close,
-		 txn->status,
-		 (tolog & LW_BYTES) ? "" : "+", s->logs.bytes_out,
-		 txn->cli_cookie ? txn->cli_cookie : "-",
-		 txn->srv_cookie ? txn->srv_cookie : "-",
-		 sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
-		 sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
-		 (be->options & PR_O_COOK_ANY) ? sess_cookie[(txn->flags & TX_CK_MASK) >> TX_CK_SHIFT] : '-',
-		 (be->options & PR_O_COOK_ANY) ? sess_set_cookie[(txn->flags & TX_SCK_MASK) >> TX_SCK_SHIFT] : '-',
-		 actconn, fe->feconn, be->beconn, target_srv(&s->target) ? target_srv(&s->target)->cur_sess : 0,
-		 (s->flags & SN_REDISP)?"+":"",
-		 (s->req->cons->conn_retries>0)?(be->conn_retries - s->req->cons->conn_retries):be->conn_retries,
-		 s->logs.srv_queue_size, s->logs.prx_queue_size, tmpline);
+			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)
+					goto out;
+				last_isspace = 0;
+				break;
 
+			case LOG_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));
+				last_isspace = 0;
+				break;
+
+			case LOG_SRVQUEUE: // %sq
+				tmplog = ltoa_o(s->logs.srv_queue_size, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
+
+			case LOG_BCKQUEUE:  // %bq
+				tmplog = ltoa_o(s->logs.prx_queue_size, tmplog, MAX_SYSLOG_LEN - (tmplog - logline));
+				if (!tmplog)
+					goto out;
+				last_isspace = 0;
+				break;
+
+			case LOG_HDRREQUEST: // %hr
+				/* request header */
+				if (fe->to_log & LW_REQHDR && txn->req.cap) {
+					if (tmp->options & LOG_OPT_QUOTE)
+						LOGCHAR('"');
+					LOGCHAR('{');
+					for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
+						if (hdr)
+							LOGCHAR('|');
+						if (txn->req.cap[hdr] != NULL)
+							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+									       '#', hdr_encode_map, txn->req.cap[hdr]);
+					}
+					LOGCHAR('}');
+					last_isspace = 0;
+				}
+				*tmplog = '\0';
+				break;
+
+			case LOG_HDRREQUESTLIST: // %hrl
+				/* request header list */
+				if (fe->to_log & LW_REQHDR && txn->req.cap) {
+					for (hdr = 0; hdr < fe->nb_req_cap; hdr++) {
+						if (hdr > 0)
+							LOGCHAR(' ');
+						if (tmp->options & LOG_OPT_QUOTE)
+							LOGCHAR('"');
+						if (txn->req.cap[hdr] != NULL) {
+							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+									       '#', hdr_encode_map, txn->req.cap[hdr]);
+						} 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
+				/* response header */
+				if (fe->to_log & LW_RSPHDR &&
+				    txn->rsp.cap) {
+					if (tmp->options & LOG_OPT_QUOTE)
+						LOGCHAR('"');
+					LOGCHAR('{');
+					for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
+						if (hdr)
+							LOGCHAR('|');
+						if (txn->rsp.cap[hdr] != NULL)
+							tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+									       '#', hdr_encode_map, txn->rsp.cap[hdr]);
+					}
+					LOGCHAR('}');
+					last_isspace = 0;
+					if (tmp->options & LOG_OPT_QUOTE)
+						LOGCHAR('"');
+				}
+				*tmplog = '\0';
+				break;
+
+			case LOG_HDRRESPONSLIST: // %hsl
+				/* response header list */
+				if (fe->to_log & LW_RSPHDR && txn->rsp.cap) {
+					for (hdr = 0; hdr < fe->nb_rsp_cap; hdr++) {
+						if (hdr > 0)
+							LOGCHAR(' ');
+						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]);
+						} 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
+				/* Request */
+				if (tmp->options & LOG_OPT_QUOTE)
+					LOGCHAR('"');
+				uri = txn->uri ? txn->uri : "<BADREQ>";
+				tmplog = encode_string(tmplog, logline + MAX_SYSLOG_LEN,
+						       '#', url_encode_map, uri);
+				if (tmp->options & LOG_OPT_QUOTE)
+					LOGCHAR('"');
+				*tmplog = '\0';
+				last_isspace = 0;
+				break;
+		}
+	}
+
+out:
+
+	if (tmplog == NULL) // if previous error
+		tmplog = logline + MAX_SYSLOG_LEN - 1;
+
+	__send_log(prx_log, level, logline, tmplog - logline + 2);
 	s->logs.logwait = 0;
-}
 
+}
 
 /*
  * Capture headers from message starting at <som> according to header list