MEDIUM: log: make sess_build_logline() support being called with no stream

Till now it was impossible to emit logs from the lower layers only because
a stream was mandatory. From now on it will at least be possible to emit a
log to report a bad request or some timings for example. When the stream
is null, sess_build_logline() will use default values and will extract the
timing information from the session just like stream_new() does, so the
resulting log line is perfectly valid.

The termination state will indicate a proxy error during the request phase
since it is the only realistic use for such a call with no stream.
diff --git a/src/log.c b/src/log.c
index 52223e9..8e14530 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1566,17 +1566,18 @@
 /* Builds a log line in <dst> based on <list_format>, and stops before reaching
  * <maxsize> characters. Returns the size of the output string in characters,
  * not counting the trailing zero which is always added if the resulting size
- * is not zero. It requires a session and a stream.
+ * is not zero. It requires a valid session and optionally a stream. If the
+ * stream is NULL, default values will be assumed for the stream part.
  */
 int sess_build_logline(struct session *sess, struct stream *s, char *dst, size_t maxsize, struct list *list_format)
 {
 	struct proxy *fe = sess->fe;
-	struct proxy *be = s ? s->be : fe;
-	struct http_txn *txn = s ? s->txn : NULL;
-	const struct strm_logs *logs = s ? &s->logs : NULL;
-	const struct connection *be_conn = s ? cs_conn(objt_cs(s->si[1].end)) : NULL;
-	unsigned int s_flags = s ? s->flags : 0;
-	unsigned int uniq_id = s ? s->uniq_id : 0;
+	struct proxy *be;
+	struct http_txn *txn;
+	const struct strm_logs *logs;
+	const struct connection *be_conn;
+	unsigned int s_flags;
+	unsigned int uniq_id;
 	struct buffer chunk;
 	char *uri;
 	char *spc;
@@ -1592,9 +1593,49 @@
 	int iret;
 	struct logformat_node *tmp;
 	struct timeval tv;
+	struct strm_logs tmp_strm_log;
 
 	/* FIXME: let's limit ourselves to frontend logging for now. */
 
+	if (likely(s)) {
+		be = s->be;
+		txn = s->txn;
+		be_conn = cs_conn(objt_cs(s->si[1].end));
+		s_flags = s->flags;
+		uniq_id = s->uniq_id;
+		logs = &s->logs;
+	} else {
+		/* we have no stream so we first need to initialize a few
+		 * things that are needed later. We do increment the request
+		 * ID so that it's uniquely assigned to this request just as
+		 * if the request had reached the point of being processed.
+		 * A request error is reported as it's the only element we have
+		 * here and which justifies emitting such a log.
+		 */
+		be = fe;
+		txn = NULL;
+		be_conn = NULL;
+		s_flags = SF_ERR_PRXCOND | SF_FINST_R;
+		uniq_id = HA_ATOMIC_XADD(&global.req_count, 1);
+
+		/* prepare a valid log structure */
+		tmp_strm_log.tv_accept = sess->tv_accept;
+		tmp_strm_log.accept_date = sess->accept_date;
+		tmp_strm_log.t_handshake = sess->t_handshake;
+		tmp_strm_log.t_idle = tv_ms_elapsed(&sess->tv_accept, &now) - sess->t_handshake;
+		tv_zero(&tmp_strm_log.tv_request);
+		tmp_strm_log.t_queue = -1;
+		tmp_strm_log.t_connect = -1;
+		tmp_strm_log.t_data = -1;
+		tmp_strm_log.t_close = tv_ms_elapsed(&sess->tv_accept, &now);
+		tmp_strm_log.bytes_in = 0;
+		tmp_strm_log.bytes_out = 0;
+		tmp_strm_log.prx_queue_pos = 0;
+		tmp_strm_log.srv_queue_pos = 0;
+
+		logs = &tmp_strm_log;
+	}
+
 	t_request = -1;
 	if (tv_isge(&logs->tv_request, &logs->tv_accept))
 		t_request = tv_ms_elapsed(&logs->tv_accept, &logs->tv_request);