about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2023-10-13 12:17:45 +0000
committerLaurent Bercot <ska@appnovation.com>2023-10-13 12:17:45 +0000
commitff9c276c144bc24c70f1d5b531a8fd412c34c3d9 (patch)
tree428e16ebf99802946e230853c76b2ae3b407c87b
parent8179edd85cd504058ec7c569ef87eebf5979646e (diff)
downloadtipidee-ff9c276c144bc24c70f1d5b531a8fd412c34c3d9.tar.gz
tipidee-ff9c276c144bc24c70f1d5b531a8fd412c34c3d9.tar.xz
tipidee-ff9c276c144bc24c70f1d5b531a8fd412c34c3d9.zip
Improved logging system
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--doc/tipidee.conf.html38
-rw-r--r--doc/tipideed.html12
-rw-r--r--examples/tipidee.conf4
-rw-r--r--package/deps.mak17
-rw-r--r--src/config/PARSING-config.txt30
-rw-r--r--src/config/PROTOCOL.txt1
-rw-r--r--src/config/defaults.c11
-rw-r--r--src/config/lexparse.c10
-rw-r--r--src/include/tipidee/log.h17
-rw-r--r--src/include/tipidee/response.h3
-rw-r--r--src/include/tipidee/tipidee.h1
-rw-r--r--src/libtipidee/tipidee_log_answer.c2
-rw-r--r--src/libtipidee/tipidee_log_request.c27
-rw-r--r--src/libtipidee/tipidee_response_error.c4
-rw-r--r--src/libtipidee/tipidee_response_status.c9
-rw-r--r--src/tipideed/cgi.c18
-rw-r--r--src/tipideed/deps-exe/tipideed1
-rw-r--r--src/tipideed/log.c65
-rw-r--r--src/tipideed/options.c4
-rw-r--r--src/tipideed/regular.c10
-rw-r--r--src/tipideed/responses.c37
-rw-r--r--src/tipideed/send_file.c6
-rw-r--r--src/tipideed/tipideed-internal.h90
-rw-r--r--src/tipideed/tipideed.c124
-rw-r--r--src/tipideed/trace.c4
25 files changed, 224 insertions, 321 deletions
diff --git a/doc/tipidee.conf.html b/doc/tipidee.conf.html
index 99b9e8c..76ffae1 100644
--- a/doc/tipidee.conf.html
+++ b/doc/tipidee.conf.html
@@ -348,14 +348,20 @@ In normal operation, if everything goes well, you should never see
 any of these. </li>
  <li> Depending on what the <tt>log</tt> directive says, it also prints
 informational messages related to what it's doing. These informational
-messages all have the prefix "<tt>tipideed: pid <em>pid</em>: info: </tt>". </li>
+messages all have the prefix "<tt>tipideed: pid <em>pid</em>: info: </tt>".
+  <ul>
+   <li> One line when <a href="tipideed.html">tipideed</a> starts and one
+when it exits, if the <tt>log</tt> directive includes the <tt>start</tt> keyword. </li>
+   <li> Up to three lines per client request, controlled by the <tt>request</tt>,
+<tt>resource</tt> and <tt>answer</tt> keywords. If you have a CGI script outputting
+local redirections (which is rare), there may be several <tt>resource</tt> lines. </li>
+  </ul> </li>
  <li> If no <tt>log</tt> directive has been provided, the default is
-<tt>log request answer size</tt>. </li>
+<tt>log request answer answer_size</tt>. </li>
 </ul>
 
 <p>
- Here are the informational log lines printed by <a href="tipideed.html">tipideed</a>,
-depending on the keywords in the <tt>log</tt> directive:
+ Here is the full list of keywords and what they do:
 </p>
 
 <dl>
@@ -375,17 +381,17 @@ TCPREMOTEIP otherwise. Make sure to invoke
 <a href="tipideed.html">tipideed</a> in order to get meaningful values for this field.
 This keyword has no effect when given without the <tt>start</tt> keyword. </dd>
  <dt> <tt>host_as_prefix</tt> </dt> <dd> Prepend all <tt>request</tt>, <tt>resource</tt> and <tt>answer</tt>
-lines with a <tt>host <em>host</em></tt> field. This field will not be repeated in the <tt>request</tt>
-line, so it changes the order of the fields. <em>host</em> is the virtual host the request is addressed
+lines with a <tt>host <em>host</em></tt> field. (This field will not be repeated in the <tt>request</tt>
+line, so it changes the order of the fields in that line.) <em>host</em> is the virtual host the request is addressed
 to. <tt>host_as_prefix</tt> is useful when you want to log entries for different virtual hosts to
-different locations. For instance, if you're using
+different locations: for instance, if you're using
 <a href="//skarnet.org/software/s6/s6-log.html">s6-log</a>, and want entries for <tt>example.com</tt> and
 <tt>example.org</tt> to be logged to different backends, you would use the <tt>host_as_prefix</tt> directive,
-and use <code>- +"^tipidee: pid [[:digit:]]*: info: host example\\.com " </code> to select <tt>example.com</tt>-related
-lines, and <code>- +"^tipidee: pid [[:digit:]]*: info: host example\\.org " </code>
+and use <code>- +"^tipidee: pid [[:digit:]]*: info: host example\\.com " </code> in your logging script
+to select <tt>example.com</tt>-related lines, and
+<code>- +"^tipidee: pid [[:digit:]]*: info: host example\\.org " </code>
 to select <tt>example.org</tt>-related lines. Note that warning and error messages would still need an additional
-backend, as well as <tt>start</tt> and <tt>exit</tt> lines if you add the <tt>start</tt> directive
-to your <tt>log</tt> configuration. </dd>
+backend, as well as potential <tt>start</tt> and <tt>exit</tt> lines. </dd>
  <dt> <tt>request</tt> </dt> <dd> Log a <tt>request</tt> line when <a href="tipideed.html">tipideed</a>
 receives a request from its client. The line looks like <tt>request <em>method</em> host <em>host</em> path "<em>path</em>"
 http <em>version</em></tt>. The path is decoded, but if there are non-printable characters in it, they are
@@ -403,11 +409,15 @@ This keyword has no effect when given without the <tt>request</tt> keyword. </dd
 has found a resource corresponding to the URI and is willing to serve it. The line looks like
 <tt>resource docroot <em>docroot</em> file <em>file</em> type <em>type</em></tt>. <em>docroot</em> is
 the document root of the virtual host; <em>file</em> is the path to the served file; <em>type</em>
-is <tt>nph</tt> for an NPH script, <tt>cgi</tt> for a CGI script, or the Content-Type for a regular file. </dd>
+is <tt>nph</tt> for an NPH script, <tt>cgi</tt> for a CGI script, or the Content-Type for a regular file.
+If a served CGI script outputs a local redirection, several <tt>resource</tt> lines may appear for
+a single request. </dd>
  <dt> <tt>answer</tt> </dt> <dd> Log an <tt>answer</tt> line when <a href="tipideed.html">tipideed</a>
 answers the currently processed request. The line looks like <tt>answer <em>status</em></tt>, where <em>status</em> is
-the 3-digit status code returned to the client. </dd>
- <dt> <tt>size</tt> </dt> <dd> Add a <tt>size <em>size</em></tt> field to the <tt>answer</tt> line,
+the 3-digit status code returned to the client. Note that there will be no <tt>answer</tt> line
+when a NPH script is being run (because <a href="tipideed.html">tipideed</a> execs into the
+NPH script). </dd>
+ <dt> <tt>answer_size</tt> </dt> <dd> Add a <tt>size <em>size</em></tt> field to the <tt>answer</tt> line,
 containing the Content-Length of the answer.
 This keyword has no effect when given without the <tt>answer</tt> keyword. </dd>
  <dt> <tt>debug</tt> </dt> <dd> Log debug information. You should not need this in regular use. </dd>
diff --git a/doc/tipideed.html b/doc/tipideed.html
index 977317c..1d764ff 100644
--- a/doc/tipideed.html
+++ b/doc/tipideed.html
@@ -91,15 +91,14 @@ of the tipidee package provides service templates to help you run tipideed under
 
 <dl>
  <dt> 0 </dt> <dd> Clean exit. There was a successful stream of HTTP exchanges,
-that the client decided to end &mdash; including by being inactive long enough
-that tipideed closes the connection on its own initiative, which is permitted
-by HTTP. </dd>
+that either tipideed or the client decided to end in a way that is
+permitted by HTTP. </dd>
  <dt> 1 </dt> <dd> Illicit client behaviour. tipideed exited because it could
 not serve the client in good faith. </dd>
  <dt> 2 </dt> <dd> Illicit CGI script behaviour. tipideed exited because the invoked
 CGI script made it impossible to continue. Before exiting, tipideed likely has
 sent a 502 (Bad Gateway) response to the client. </dd>
- <dt> 100 </dt> <dd> Bad usage. tipideed has been run in an incorrect way: bad command
+ <dt> 100 </dt> <dd> Bad usage. tipideed was run in an incorrect way: bad command
 line options, or missing environment variables, etc. </dd>
  <dt> 101 </dt> <dd> Cannot happen. This signals a bug in tipideed, and comes with an
 error message asking you to report the bug. Please do so, on the
@@ -108,9 +107,8 @@ error message asking you to report the bug. Please do so, on the
 data or in the document layout that it does not like. This can happen, for
 instance, when a document is a symbolic link pointing outside of the server's
 root. </dd>
- <dt> 111 </dt> <dd> System call failed. If this happens while serving a request,
-tipideed likely has sent a 500 (Internal Server Error) response to the
-client before exiting. </dd>
+ <dt> 111 </dt> <dd> System call failed. This usually signals a problem with the
+underlying system. </dd>
 </dl>
 
 <div id="environment">
diff --git a/examples/tipidee.conf b/examples/tipidee.conf
index 705b88c..a451906 100644
--- a/examples/tipidee.conf
+++ b/examples/tipidee.conf
@@ -31,6 +31,10 @@
 # existing file in this list. (Useful e.g. if you have an index.cgi program.)
 # global index_file index.html
 
+# tipidee, by default, will log basic information on the requests,
+# and the status and Content-Length of its answers.
+# log request answer answer_size
+
 # You can define your own Content-Type mappings by file extension.
 # The default mappings should work well for most servers.
 # content-type text/html .html .htm
diff --git a/package/deps.mak b/package/deps.mak
index bc7bee1..f389e28 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -3,10 +3,10 @@
 #
 
 src/include/tipidee/conf.h: src/include/tipidee/uri.h
-src/include/tipidee/log.h: src/include/tipidee/rql.h
+src/include/tipidee/log.h: src/include/tipidee/headers.h src/include/tipidee/rql.h
 src/include/tipidee/response.h: src/include/tipidee/rql.h
 src/include/tipidee/rql.h: src/include/tipidee/method.h src/include/tipidee/uri.h
-src/include/tipidee/tipidee.h: src/include/tipidee/conf.h src/include/tipidee/config.h src/include/tipidee/headers.h src/include/tipidee/method.h src/include/tipidee/response.h src/include/tipidee/rql.h src/include/tipidee/uri.h src/include/tipidee/util.h
+src/include/tipidee/tipidee.h: src/include/tipidee/conf.h src/include/tipidee/config.h src/include/tipidee/headers.h src/include/tipidee/log.h src/include/tipidee/method.h src/include/tipidee/response.h src/include/tipidee/rql.h src/include/tipidee/uri.h src/include/tipidee/util.h
 src/tipideed/tipideed-internal.h: src/include/tipidee/tipidee.h
 src/config/confnode.o src/config/confnode.lo: src/config/confnode.c src/config/tipidee-config-internal.h
 src/config/conftree.o src/config/conftree.lo: src/config/conftree.c src/config/tipidee-config-internal.h
@@ -28,7 +28,7 @@ src/libtipidee/tipidee_headers_parse.o src/libtipidee/tipidee_headers_parse.lo:
 src/libtipidee/tipidee_headers_search.o src/libtipidee/tipidee_headers_search.lo: src/libtipidee/tipidee_headers_search.c src/include/tipidee/headers.h
 src/libtipidee/tipidee_log_answer.o src/libtipidee/tipidee_log_answer.lo: src/libtipidee/tipidee_log_answer.c src/include/tipidee/log.h
 src/libtipidee/tipidee_log_exit.o src/libtipidee/tipidee_log_exit.lo: src/libtipidee/tipidee_log_exit.c src/include/tipidee/log.h
-src/libtipidee/tipidee_log_request.o src/libtipidee/tipidee_log_request.lo: src/libtipidee/tipidee_log_request.c src/include/tipidee/log.h src/include/tipidee/method.h
+src/libtipidee/tipidee_log_request.o src/libtipidee/tipidee_log_request.lo: src/libtipidee/tipidee_log_request.c src/include/tipidee/headers.h src/include/tipidee/log.h src/include/tipidee/method.h
 src/libtipidee/tipidee_log_resource.o src/libtipidee/tipidee_log_resource.lo: src/libtipidee/tipidee_log_resource.c src/include/tipidee/log.h
 src/libtipidee/tipidee_log_start.o src/libtipidee/tipidee_log_start.lo: src/libtipidee/tipidee_log_start.c src/include/tipidee/log.h
 src/libtipidee/tipidee_method.o src/libtipidee/tipidee_method.lo: src/libtipidee/tipidee_method.c src/include/tipidee/method.h
@@ -46,13 +46,12 @@ src/libtipidee/tipidee_util_defaulttext.o src/libtipidee/tipidee_util_defaulttex
 src/libtipidee/tipidee_util_httpdate.o src/libtipidee/tipidee_util_httpdate.lo: src/libtipidee/tipidee_util_httpdate.c src/include/tipidee/util.h
 src/tipideed/cgi.o src/tipideed/cgi.lo: src/tipideed/cgi.c src/include/tipidee/headers.h src/include/tipidee/method.h src/include/tipidee/response.h src/include/tipidee/uri.h src/tipideed/tipideed-internal.h
 src/tipideed/harden.o src/tipideed/harden.lo: src/tipideed/harden.c src/tipideed/tipideed-internal.h
-src/tipideed/log.o src/tipideed/log.lo: src/tipideed/log.c src/include/tipidee/method.h src/tipideed/tipideed-internal.h
-src/tipideed/options.o src/tipideed/options.lo: src/tipideed/options.c src/include/tipidee/response.h src/tipideed/tipideed-internal.h
-src/tipideed/regular.o src/tipideed/regular.lo: src/tipideed/regular.c src/include/tipidee/method.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
-src/tipideed/responses.o src/tipideed/responses.lo: src/tipideed/responses.c src/include/tipidee/response.h src/include/tipidee/rql.h src/tipideed/tipideed-internal.h
+src/tipideed/options.o src/tipideed/options.lo: src/tipideed/options.c src/include/tipidee/log.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
+src/tipideed/regular.o src/tipideed/regular.lo: src/tipideed/regular.c src/include/tipidee/log.h src/include/tipidee/method.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
+src/tipideed/responses.o src/tipideed/responses.lo: src/tipideed/responses.c src/include/tipidee/log.h src/include/tipidee/response.h src/include/tipidee/rql.h src/tipideed/tipideed-internal.h
 src/tipideed/send_file.o src/tipideed/send_file.lo: src/tipideed/send_file.c src/tipideed/tipideed-internal.h
 src/tipideed/tipideed.o src/tipideed/tipideed.lo: src/tipideed/tipideed.c src/include/tipidee/tipidee.h src/tipideed/tipideed-internal.h
-src/tipideed/trace.o src/tipideed/trace.lo: src/tipideed/trace.c src/include/tipidee/method.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
+src/tipideed/trace.o src/tipideed/trace.lo: src/tipideed/trace.c src/include/tipidee/log.h src/include/tipidee/method.h src/include/tipidee/response.h src/tipideed/tipideed-internal.h
 
 tipidee-config: EXTRA_LIBS := -lskarnet ${SPAWN_LIB}
 tipidee-config: src/config/tipidee-config.o src/config/confnode.o src/config/conftree.o src/config/defaults.o src/config/lexparse.o
@@ -66,5 +65,5 @@ endif
 libtipidee.so.xyzzy: EXTRA_LIBS := -lskarnet
 libtipidee.so.xyzzy: src/libtipidee/tipidee_conf_free.lo src/libtipidee/tipidee_conf_get.lo src/libtipidee/tipidee_conf_get_argv.lo src/libtipidee/tipidee_conf_get_content_type.lo src/libtipidee/tipidee_conf_get_redirection.lo src/libtipidee/tipidee_conf_get_string.lo src/libtipidee/tipidee_conf_get_uint32.lo src/libtipidee/tipidee_conf_init.lo src/libtipidee/tipidee_headers_get_content_length.lo src/libtipidee/tipidee_headers_init.lo src/libtipidee/tipidee_headers_parse.lo src/libtipidee/tipidee_headers_search.lo src/libtipidee/tipidee_log_answer.lo src/libtipidee/tipidee_log_exit.lo src/libtipidee/tipidee_log_resource.lo src/libtipidee/tipidee_log_request.lo src/libtipidee/tipidee_log_start.lo src/libtipidee/tipidee_method.lo src/libtipidee/tipidee_response_error.lo src/libtipidee/tipidee_response_header_builtin.lo src/libtipidee/tipidee_response_header_common_put.lo src/libtipidee/tipidee_response_header_date.lo src/libtipidee/tipidee_response_header_date_fmt.lo src/libtipidee/tipidee_response_header_lastmodified.lo src/libtipidee/tipidee_response_status.lo src/libtipidee/tipidee_rql_read.lo src/libtipidee/tipidee_uri_parse.lo src/libtipidee/tipidee_util_chunked_read.lo src/libtipidee/tipidee_util_defaulttext.lo src/libtipidee/tipidee_util_httpdate.lo
 tipideed: EXTRA_LIBS := -lskarnet
-tipideed: src/tipideed/tipideed.o src/tipideed/cgi.o src/tipideed/harden.o src/tipideed/log.o src/tipideed/options.o src/tipideed/regular.o src/tipideed/responses.o src/tipideed/send_file.o src/tipideed/tipideed.o src/tipideed/trace.o libtipidee.a.xyzzy
+tipideed: src/tipideed/tipideed.o src/tipideed/cgi.o src/tipideed/harden.o src/tipideed/options.o src/tipideed/regular.o src/tipideed/responses.o src/tipideed/send_file.o src/tipideed/tipideed.o src/tipideed/trace.o libtipidee.a.xyzzy
 INTERNAL_LIBS :=
diff --git a/src/config/PARSING-config.txt b/src/config/PARSING-config.txt
index 688d93c..072a1fa 100644
--- a/src/config/PARSING-config.txt
+++ b/src/config/PARSING-config.txt
@@ -1,33 +1,3 @@
-global verbosity 1
-global read_timeout 60000
-global index_file index.cgi index.html
-
-content-type application/pdf .pdf
-content-type text/plain .c .h .txt
-
-
-domain example.com
-
-nph-prefix nph-
-
-redirect rickroll.html 307 https://www.youtube.com/watch?v=dQw4w9WgXcQ
-redirect community/ 308 https://example.org/
-
-cgi /cgi-bin/
-nph /cgi-bin/nph/
-
-cgi /some/script
-nph /some/otherscript
-
-noncgi /cgi-bin/file.html
-noncgi /cgi-bin/directory
-
-file-type /source/ text/plain
-file-type /source/file.html text/html
-
-basic-auth /protected.html
-basic-auth /protected/
-
 
 class	|	0	1	2	3	4
 st\ev	|	\0	space	#	\n	other
diff --git a/src/config/PROTOCOL.txt b/src/config/PROTOCOL.txt
index ffeb72f..f6cc304 100644
--- a/src/config/PROTOCOL.txt
+++ b/src/config/PROTOCOL.txt
@@ -1,6 +1,5 @@
 * Globals
 
-G:verbosity -> 4 bytes
 G:read_timeout -> 4 bytes	exit if waiting on client input for read_timeout ms
 G:write_timeout -> 4 bytes	die if waiting on flush to client for write_timeout ms
 G:cgi_timeout -> 4 bytes	504 if CGI hasn't finished answering / die if NPH hasn't finished reading
diff --git a/src/config/defaults.c b/src/config/defaults.c
index 1017d29..389d84d 100644
--- a/src/config/defaults.c
+++ b/src/config/defaults.c
@@ -16,14 +16,13 @@ struct defaults_s
 
 #define REC(k, v, n) { .key = (k), .value = (v), .vlen = (n) }
 #define RECS(k, v) REC(k, v, sizeof(v))
-#define RECU32(k, v) REC(k, (char const *)(uint32_t)UINT32_BIG(v), 4)
+#define RECU32(k, u) { .key = (k), .value = (char const [4]){ UINT32_BIG(u) >> 24 & 0xffu, UINT32_BIG(u) >> 16 & 0xffu, UINT32_BIG(u) >> 8 & 0xffu, UINT32_BIG(u) & 0xffu }, .vlen = 4 }
 
-struct defaults_s const defaults[] =
+static struct defaults_s const defaults[] =
 {
-  RECU32("G:verbosity", 1),
-  RECU32("G:read_timeout", 0),
-  RECU32("G:write_timeout", 0),
-  RECU32("G:cgi_timeout", 0),
+  RECS("G:read_timeout", "\0\0\0"),
+  RECS("G:write_timeout", "\0\0\0"),
+  RECS("G:cgi_timeout", "\0\0\0"),
   RECU32("G:max_request_body_length", 8192),
   RECU32("G:max_cgi_body_length", 4194304),
   RECS("G:index_file", "index.html"),
diff --git a/src/config/lexparse.c b/src/config/lexparse.c
index b048031..d7d9e1b 100644
--- a/src/config/lexparse.c
+++ b/src/config/lexparse.c
@@ -17,7 +17,6 @@
 #include "tipidee-config-internal.h"
 
 #define dietoobig() strerr_diefu1sys(100, "read configuration")
-#define BSEARCH(type, key, array) bsearch(key, (array), sizeof(array)/sizeof(type), sizeof(type), (int (*)(void const *, void const *))&strcmp)
 
 typedef struct mdt_s mdt, *mdt_ref ;
 struct mdt_s
@@ -65,6 +64,11 @@ struct directive_s
   enum token_e token ;
 } ;
 
+static int keycmp (void const *a, void const *b)
+{
+  return strcmp((char const *)a, ((struct directive_s const *)b)->s) ;
+}
+#define BSEARCH(type, key, array) bsearch(key, (array), sizeof(array)/sizeof(type), sizeof(type), (int (*)(void const *, void const *))&keycmp)
 
 static void check_unique (char const *key, mdt const *md)
 {
@@ -95,7 +99,6 @@ static inline void parse_global (char const *s, size_t const *word, size_t n, md
     { .name = "max_cgi_body_length", .key = "G:max_cgi_body_length", .type = 0 },
     { .name = "max_request_body_length", .key = "G:max_request_body_length", .type = 0 },
     { .name = "read_timeout", .key = "G:read_timeout", .type = 0 },
-    { .name = "verbosity", .key = "G:verbosity", .type = 0 },
     { .name = "write_timeout", .key = "G:write_timeout", .type = 0 }
   } ;
   struct globalkey_s const *gl ;
@@ -135,6 +138,7 @@ static inline void parse_log (char const *s, size_t const *word, size_t n, mdt c
   static struct logkey_s const logkeys[] =
   {
     { .name = "answer", .value = TIPIDEE_LOG_ANSWER },
+    { .name = "answer_size", .value = TIPIDEE_LOG_SIZE },
     { .name = "debug", .value = TIPIDEE_LOG_DEBUG },
     { .name = "host_as_prefix", .value = TIPIDEE_LOG_HOSTASPREFIX },
     { .name = "hostname", .value = TIPIDEE_LOG_CLIENTHOST },
@@ -143,7 +147,6 @@ static inline void parse_log (char const *s, size_t const *word, size_t n, mdt c
     { .name = "referrer", .value = TIPIDEE_LOG_REFERRER },
     { .name = "request", .value = TIPIDEE_LOG_REQUEST },
     { .name = "resource", .value = TIPIDEE_LOG_RESOURCE },
-    { .name = "response_size", .value = TIPIDEE_LOG_SIZE },
     { .name = "start", .value = TIPIDEE_LOG_START },
     { .name = "user-agent", .value = TIPIDEE_LOG_UA }
   } ;
@@ -159,6 +162,7 @@ static inline void parse_log (char const *s, size_t const *word, size_t n, mdt c
     else if (n == 1) v = 0 ;
     else strerr_dief5x(1, "log nothing cannot be mixed with other log values", " in file ", g.storage.s + md->filepos, " line ", md->linefmt) ;
   }
+  
   uint32_pack_big(pack, v) ;
   add_unique("G:logv", pack, 4, md) ;
 }
diff --git a/src/include/tipidee/log.h b/src/include/tipidee/log.h
index 98e6d0e..7453bb7 100644
--- a/src/include/tipidee/log.h
+++ b/src/include/tipidee/log.h
@@ -3,13 +3,14 @@
 #ifndef TIPIDEE_LOG_H
 #define TIPIDEE_LOG_H
 
-#include <sys/types.h>
 #include <stdint.h>
 
+#include <skalibs/uint64.h>
 #include <skalibs/strerr.h>
 #include <skalibs/stralloc.h>
 
 #include <tipidee/rql.h>
+#include <tipidee/headers.h>
 
 #define TIPIDEE_LOG_REQUEST 0x0001
 #define TIPIDEE_LOG_REFERRER 0x0002
@@ -17,11 +18,11 @@
 #define TIPIDEE_LOG_RESOURCE 0x0008
 #define TIPIDEE_LOG_ANSWER 0x0010
 #define TIPIDEE_LOG_SIZE 0x0020
-#define TIPIDEE_LOG_START 0x100
-#define TIPIDEE_LOG_CLIENTIP 0x0200
-#define TIPIDEE_LOG_CLIENTHOST 0x0400
-#define TIPIDEE_LOG_HOSTASPREFIX 0x1000
-#define TIPIDEE_LOG_DEBUG 0x10000
+#define TIPIDEE_LOG_START 0x0040
+#define TIPIDEE_LOG_CLIENTIP 0x0080
+#define TIPIDEE_LOG_CLIENTHOST 0x0100
+#define TIPIDEE_LOG_HOSTASPREFIX 0x0200
+#define TIPIDEE_LOG_DEBUG 0x0400
 
 #define TIPIDEE_LOG_DEFAULT (TIPIDEE_LOG_REQUEST | TIPIDEE_LOG_ANSWER | TIPIDEE_LOG_SIZE)
 
@@ -37,9 +38,9 @@ struct tipidee_resattr_s
 extern void tipidee_log_start (uint32_t, char const *, char const *) ;
 extern void tipidee_log_exit (uint32_t, unsigned int) ;
 
-extern void tipidee_log_request (uint32_t, tipidee_rql const *, char const *, char const *, stralloc *) ;
+extern void tipidee_log_request (uint32_t, tipidee_rql const *, tipidee_headers const *, stralloc *) ;
 extern void tipidee_log_resource (uint32_t, tipidee_rql const *, char const *, char const *, tipidee_resattr const *) ;
-extern void tipidee_log_answer (uint32_t, tipidee_rql const *, unsigned int, off_t) ;
+extern void tipidee_log_answer (uint32_t, tipidee_rql const *, unsigned int, uint64_t) ;
 
 #define tipidee_log_debug(v, ...) do { if ((v) & TIPIDEE_LOG_DEBUG) strerr_warn(PROG, ": debug: ", __VA_ARGS__) ; } while (0)
 
diff --git a/src/include/tipidee/response.h b/src/include/tipidee/response.h
index d549532..d969d9c 100644
--- a/src/include/tipidee/response.h
+++ b/src/include/tipidee/response.h
@@ -22,7 +22,6 @@ struct tipidee_response_header_builtin_s
 } ;
 
 extern size_t tipidee_response_status (buffer *, tipidee_rql const *, unsigned int, char const *) ;
-#define tipidee_response_status_line(b, rql, line) tipidee_response_status(b, rql, 0, (line))
 
 extern size_t tipidee_response_header_date_fmt (char *, size_t, tain const *) ;
 extern size_t tipidee_response_header_date (char *, size_t, tain const *) ;
@@ -32,7 +31,7 @@ extern size_t tipidee_response_header_lastmodified (char *, size_t, struct stat
 extern size_t tipidee_response_header_common_put (buffer *, uint32_t, tain const *) ;
 #define tipidee_response_header_common_put_g(b, options) tipidee_response_header_common_put(b, (options), &STAMP)
 
-extern size_t tipidee_response_error (buffer *, tipidee_rql const *, char const *, char const *, uint32_t) ;
+extern size_t tipidee_response_error (buffer *, tipidee_rql const *, unsigned int, char const *, char const *, uint32_t) ;
 
 extern tipidee_response_header_builtin const *tipidee_response_header_builtin_table ;
 extern char const *tipidee_response_header_builtin_search (char const *) ;
diff --git a/src/include/tipidee/tipidee.h b/src/include/tipidee/tipidee.h
index 7634366..7153b7d 100644
--- a/src/include/tipidee/tipidee.h
+++ b/src/include/tipidee/tipidee.h
@@ -6,6 +6,7 @@
 #include <tipidee/config.h>
 #include <tipidee/conf.h>
 #include <tipidee/headers.h>
+#include <tipidee/log.h>
 #include <tipidee/method.h>
 #include <tipidee/response.h>
 #include <tipidee/rql.h>
diff --git a/src/libtipidee/tipidee_log_answer.c b/src/libtipidee/tipidee_log_answer.c
index ae4e880..15dbe46 100644
--- a/src/libtipidee/tipidee_log_answer.c
+++ b/src/libtipidee/tipidee_log_answer.c
@@ -8,7 +8,7 @@
 
 #include <tipidee/log.h>
 
-void tipidee_log_answer (uint32_t v, tipidee_rql const *rql, unsigned int status, off_t size)
+void tipidee_log_answer (uint32_t v, tipidee_rql const *rql, unsigned int status, uint64_t size)
 {
   char const *a[6] = { PROG, ": info:" } ;
   size_t m = 2 ;
diff --git a/src/libtipidee/tipidee_log_request.c b/src/libtipidee/tipidee_log_request.c
index a789ca5..a50169d 100644
--- a/src/libtipidee/tipidee_log_request.c
+++ b/src/libtipidee/tipidee_log_request.c
@@ -7,25 +7,34 @@
 #include <skalibs/skamisc.h>
 
 #include <tipidee/method.h>
+#include <tipidee/headers.h>
 #include <tipidee/log.h>
 
-void tipidee_log_request (uint32_t v, tipidee_rql const *rql, char const *referrer, char const *ua, stralloc *sa)
+void tipidee_log_request (uint32_t v, tipidee_rql const *rql, tipidee_headers const *hdr, stralloc *sa)
 {
   char const *a[16] = { PROG, ": info:" } ;
   size_t m = 2 ;
-  size_t start = sa->len ;
-  size_t refpos = start, uapos = start ;
+  size_t start = sa->len ;  /* assert: not zero */
+  size_t refpos = 0, uapos = 0 ;
   if (!(v & TIPIDEE_LOG_REQUEST)) return ;
   if (!string_quotes(sa, rql->uri.path)) goto eerr ;
   if (v & TIPIDEE_LOG_REFERRER)
   {
-    refpos = sa->len ;
-    if (!string_quotes(sa, referrer) || !stralloc_0(sa)) goto err ;
+    char const *x = tipidee_headers_search(hdr, "Referrer") ;
+    if (x)
+    {
+      refpos = sa->len ;
+      if (!string_quotes(sa, x) || !stralloc_0(sa)) goto err ;
+    }
   }
   if (v & TIPIDEE_LOG_UA)
   {
-    uapos = sa->len ;
-    if (!string_quotes(sa, ua) || !stralloc_0(sa)) goto err ;
+    char const *x = tipidee_headers_search(hdr, "User-Agent") ;
+    if (x)
+    {
+      uapos = sa->len ;
+      if (!string_quotes(sa, x) || !stralloc_0(sa)) goto err ;
+    }
   }
   if (v & TIPIDEE_LOG_HOSTASPREFIX)
   {
@@ -48,12 +57,12 @@ void tipidee_log_request (uint32_t v, tipidee_rql const *rql, char const *referr
   }
   a[m++] = " http 1." ;
   a[m++] = rql->http_minor ? "1" : "0" ;
-  if (v & TIPIDEE_LOG_REFERRER)
+  if (refpos)
   {
     a[m++] = " referrer " ;
     a[m++] = sa->s + refpos ;
   }
-  if (v & TIPIDEE_LOG_UA)
+  if (uapos)
   {
     a[m++] = " user-agent " ;
     a[m++] = sa->s + uapos ;
diff --git a/src/libtipidee/tipidee_response_error.c b/src/libtipidee/tipidee_response_error.c
index e2687a4..7c77e80 100644
--- a/src/libtipidee/tipidee_response_error.c
+++ b/src/libtipidee/tipidee_response_error.c
@@ -9,14 +9,14 @@
 #include <tipidee/rql.h>
 #include <tipidee/response.h>
 
-size_t tipidee_response_error (buffer *b, tipidee_rql const *rql, char const *rsl, char const *text, uint32_t options)
+size_t tipidee_response_error (buffer *b, tipidee_rql const *rql, unsigned int status, char const *rsl, char const *text, uint32_t options)
 {
   size_t n = 0 ;
   static char const txt1[] = "<html>\n<head><title>" ;
   static char const txt2[] = "</title></head>\n<body>\n<h1> " ;
   static char const txt3[] = " </h1>\n<p>\n" ;
   static char const txt4[] = "\n</p>\n</body>\n</html>\n" ;
-  n += tipidee_response_status_line(b, rql, rsl) ;
+  n += tipidee_response_status(b, rql, status, rsl) ;
   n += tipidee_response_header_common_put_g(buffer_1, options) ;
   if (!(options & 2))
   {
diff --git a/src/libtipidee/tipidee_response_status.c b/src/libtipidee/tipidee_response_status.c
index aedec39..4315f61 100644
--- a/src/libtipidee/tipidee_response_status.c
+++ b/src/libtipidee/tipidee_response_status.c
@@ -14,13 +14,8 @@ size_t tipidee_response_status (buffer *b, tipidee_rql const *rql, unsigned int
   n += buffer_putnoflush(b, ".", 1) ;
   n += buffer_putnoflush(b, fmt, uint_fmt(fmt, rql->http_major ? rql->http_minor : 1)) ;
   n += buffer_putnoflush(b, " ", 1) ;
-  if (status)
-  {
-    char fmt[UINT_FMT] ;
-    size_t m = uint_fmt(fmt, status) ;
-    n += buffer_putnoflush(b, fmt, m) ;
-    n += buffer_putnoflush(b, " ", 1) ;
-  }
+  n += buffer_putnoflush(b, fmt, uint_fmt(fmt, status)) ;
+  n += buffer_putnoflush(b, " ", 1) ;
   n += buffer_putsnoflush(b, line) ;
   n += buffer_putnoflush(b, "\r\n", 2) ;
   return n ;
diff --git a/src/tipideed/cgi.c b/src/tipideed/cgi.c
index 9cd395d..8dde023 100644
--- a/src/tipideed/cgi.c
+++ b/src/tipideed/cgi.c
@@ -139,7 +139,6 @@ static inline int do_nph (tipidee_rql const *rql, char const *const *argv, char
   }
   close(p[1]) ;
   if (fd_move(0, p[0]) == -1) die500sys(rql, 111, "fd_move") ;
-  log_nph(argv, envp) ;
   exec_e(argv, envp) ;
   die500sys(rql, errno == ENOENT ? 127 : 126, "exec nph ", argv[0]) ;
 }
@@ -155,7 +154,6 @@ static inline int run_cgi (tipidee_rql const *rql, char const *const *argv, char
   uint32_t parserstate = 0 ;
   buffer b ;
   char buf[4096] ;
-  log_cgi(argv, envp) ;
   {
     int fd[2] = { 0, 1 } ;
     pid = child_spawn2(argv[0], argv, envp, fd) ;
@@ -178,7 +176,7 @@ static inline int run_cgi (tipidee_rql const *rql, char const *const *argv, char
     {
       kill(pid, SIGTERM) ;
       respond_504(rql) ;
-      if (g.verbosity >= 2) strerr_warnw3x("cgi ", argv[0], " timed out") ;
+      strerr_warnw3x("cgi ", argv[0], " timed out") ;
       break ;
     }
     if (x[1].fd >= 0 && x[1].revents & (IOPAUSE_WRITE | IOPAUSE_EXCEPT))
@@ -186,7 +184,7 @@ static inline int run_cgi (tipidee_rql const *rql, char const *const *argv, char
       size_t len = allwrite(x[1].fd, body + bodyw, bodylen - bodyw) ;
       if (!len)
       {
-        if (g.verbosity) strerr_warnwu2sys("write request body to cgi ", argv[0]) ;
+        strerr_warnwu2sys("write request body to cgi ", argv[0]) ;
         bodyw = bodylen ;
       }
       else bodyw += len ;
@@ -260,7 +258,7 @@ static inline int local_redirect (tipidee_rql *rql, char const *loc, char *uribu
   rql->uri.host = uribuf + n ;
   rql->uri.port = port ;
   rql->uri.https = ishttps ;
-  if (g.verbosity >= 4) strerr_warni4x("cgi ", cginame, ": local redirect to ", rql->uri.path) ;
+  tipidee_log_debug(g.logv, "cgi ", cginame, ": local redirect to ", rql->uri.path) ;
   return 1 ;
 }
 
@@ -361,6 +359,7 @@ static inline int process_cgi_output (tipidee_rql *rql, tipidee_headers const *h
   print_cgi_headers(hdr, rbodylen) ;
   if (buffer_timed_put_g(buffer_1, "\r\n", 2, &deadline) < 2)
     strerr_diefu1sys(111, "write to stdout") ;
+  tipidee_log_answer(g.logv, rql, status, rbodylen) ;
   if (rbodylen)
   {
     if (buffer_timed_put_g(buffer_1, rbody, rbodylen, &deadline) < rbodylen)
@@ -368,15 +367,6 @@ static inline int process_cgi_output (tipidee_rql *rql, tipidee_headers const *h
   }
   if (!buffer_timed_flush_g(buffer_1, &deadline))
     strerr_diefu1sys(111, "write to stdout") ;
-  if (g.verbosity >= 2)
-  {
-    char fmt[UINT_FMT] ;
-    fmt[uint_fmt(fmt, status)] = 0 ;
-    if (status >= 300 && status < 399)
-      strerr_warni6x("cgi ", cginame, ": ", fmt, " client redirect to ", location) ;
-    else
-      strerr_warni6x("cgi ", cginame, ": ", fmt, " ", reason_phrase) ;
-  }
   return 0 ;
 }
 
diff --git a/src/tipideed/deps-exe/tipideed b/src/tipideed/deps-exe/tipideed
index aad1417..a64a8a9 100644
--- a/src/tipideed/deps-exe/tipideed
+++ b/src/tipideed/deps-exe/tipideed
@@ -1,6 +1,5 @@
 cgi.o
 harden.o
-log.o
 options.o
 regular.o
 responses.o
diff --git a/src/tipideed/log.c b/src/tipideed/log.c
deleted file mode 100644
index b08e16f..0000000
--- a/src/tipideed/log.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/* ISC license. */
-
-#include <unistd.h>
-
-#include <skalibs/uint16.h>
-#include <skalibs/types.h>
-#include <skalibs/strerr.h>
-
-#include <tipidee/method.h>
-#include "tipideed-internal.h"
-
-void log_start (void)
-{
-  if (g.verbosity >= 4)
-    strerr_warni7x("new connection", " from ip ", g.sa.s + g.remoteip, " (", g.sa.s + g.remotehost, ") port ", g.sa.s + g.remoteport) ;
-  else if (g.verbosity >= 3)
-    strerr_warni1x("new connection") ;
-}
-
-void log_and_exit (int e)
-{
-  if (g.verbosity >= 3)
-  {
-    char fmt[INT_FMT] ;
-    fmt[int_fmt(fmt, e)] = 0 ;
-    strerr_warni2x("exiting ", fmt) ;
-  }
-  _exit(e) ;
-}
-
-void log_request (tipidee_rql const *rql)
-{
-  if (g.verbosity >= 2)
-  {
-    char fmt[UINT16_FMT] ;
-    if (rql->uri.port) fmt[uint16_fmt(fmt, rql->uri.port)] = 0 ;
-    strerr_warnin(11, "request ", tipidee_method_tostr(rql->m), " for", rql->uri.host ? " host " : "", rql->uri.host ? rql->uri.host : "", rql->uri.port ? " port " : "", rql->uri.port ? fmt : "", " path ", rql->uri.path, rql->uri.query ? " query " : "", rql->uri.query ? rql->uri.query : "") ;
-  }
-}
-
-void log_regular (char const *fn, char const *sizefmt, int ishead, char const *ct)
-{
-  if (g.verbosity >= 2)
-    strerr_warni8x("sending ", ishead ? "headers for " : "", "regular file ", fn, " (", sizefmt, " bytes) with type ", ct) ;
-}
-
-void log_response (char const *ans, char const *fn)
-{
-  if (g.verbosity >= 2)
-    strerr_warni4x("answering ", ans, fn ? " for " : 0, fn) ;
-}
-
-void log_nph (char const *const *argv, char const *const *envp)
-{
-  if (g.verbosity >= 2)
-    strerr_warni3x("running ", "nph ", argv[0]) ;
-  (void)envp ;
-}
-
-void log_cgi (char const *const *argv, char const *const *envp)
-{
-  if (g.verbosity >= 2)
-    strerr_warni3x("running ", "cgi ", argv[0]) ;
-  (void)envp ;
-}
diff --git a/src/tipideed/options.c b/src/tipideed/options.c
index d425943..a023401 100644
--- a/src/tipideed/options.c
+++ b/src/tipideed/options.c
@@ -7,17 +7,19 @@
 #include <skalibs/tai.h>
 #include <skalibs/unix-timed.h>
 
+#include <tipidee/log.h>
 #include <tipidee/response.h>
 #include "tipideed-internal.h"
 
 int respond_options (tipidee_rql const *rql, uint32_t flags)
 {
   tain deadline ;
-  tipidee_response_status_line(buffer_1, rql, "200 OK") ;
+  tipidee_response_status(buffer_1, rql, 200, "OK") ;
   tipidee_response_header_common_put_g(buffer_1, 0) ;
   buffer_putsnoflush(buffer_1, "Content-Length: 0\r\nAllow: GET, HEAD") ;
   if (flags & 1) buffer_putsnoflush(buffer_1, ", POST") ;
   buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ;
+  if (flags & 2) tipidee_log_answer(g.logv, rql, 200, 0) ;
   tain_add_g(&deadline, &g.writetto) ;
   if (!buffer_timed_flush_g(buffer_1, &deadline))
     strerr_diefu1sys(111, "write to stdout") ;
diff --git a/src/tipideed/regular.c b/src/tipideed/regular.c
index 24dc392..845f12b 100644
--- a/src/tipideed/regular.c
+++ b/src/tipideed/regular.c
@@ -15,13 +15,14 @@
 
 #include <tipidee/method.h>
 #include <tipidee/response.h>
+#include <tipidee/log.h>
 #include "tipideed-internal.h"
 
 int respond_regular (tipidee_rql const *rql, char const *fn, struct stat const *st, tipidee_resattr const *ra)
 {
   tain deadline ;
   char fmt[128] ;
-  size_t n = tipidee_response_status_line(buffer_1, rql, "200 OK") ;
+  size_t n = tipidee_response_status(buffer_1, rql, 200, "OK") ;
   n += tipidee_response_header_common_put_g(buffer_1, !g.cont) ;
   {
     size_t l = tipidee_response_header_lastmodified(fmt, 128, st) ;
@@ -33,9 +34,9 @@ int respond_regular (tipidee_rql const *rql, char const *fn, struct stat const *
   fmt[uint64_fmt(fmt, st->st_size)] = 0 ;
   n += buffer_putsnoflush(buffer_1, fmt) ;
   n += buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ;
-  log_regular(fn, fmt, rql->m == TIPIDEE_METHOD_HEAD, ra->content_type) ;
   if (rql->m == TIPIDEE_METHOD_HEAD)
   {
+    tipidee_log_answer(g.logv, rql, 200, st->st_size) ;
     tain_add_g(&deadline, &g.writetto) ;
     if (!buffer_timed_flush_g(buffer_1, &deadline))
       strerr_diefu1sys(111, "write to stdout") ;
@@ -53,6 +54,7 @@ int respond_regular (tipidee_rql const *rql, char const *fn, struct stat const *
       }
       else die500sys(rql, 111, "open ", fn) ;
     }
+    tipidee_log_answer(g.logv, rql, 200, st->st_size) ;
     send_file(fd, st->st_size, fn) ;
     fd_close(fd) ;
   }
@@ -63,14 +65,14 @@ int respond_304 (tipidee_rql const *rql, char const *fn, struct stat const *st)
 {
   tain deadline ;
   char fmt[128] ;
-  size_t n = tipidee_response_status_line(buffer_1, rql, "304 Not Modified") ;
+  size_t n = tipidee_response_status(buffer_1, rql, 304, "Not Modified") ;
   n += tipidee_response_header_common_put_g(buffer_1, !g.cont) ;
   {
     size_t l = tipidee_response_header_lastmodified(fmt, 128, st) ;
     if (l) n += buffer_putnoflush(buffer_1, fmt, l) ;
   }
   n += buffer_putnoflush(buffer_1, "\r\n", 2) ;
-  log_response("304", fn) ;
+  tipidee_log_answer(g.logv, rql, 304, 0) ;
   tain_add_g(&deadline, &g.writetto) ;
   if (!buffer_timed_flush_g(buffer_1, &deadline))
     strerr_diefu1sys(111, "write to stdout") ;
diff --git a/src/tipideed/responses.c b/src/tipideed/responses.c
index 8c6f1f5..e5706e2 100644
--- a/src/tipideed/responses.c
+++ b/src/tipideed/responses.c
@@ -9,63 +9,64 @@
 #include <skalibs/unix-timed.h>
 
 #include <tipidee/rql.h>
+#include <tipidee/log.h>
 #include <tipidee/response.h>
 
 #include "tipideed-internal.h"
 
-void response_error (tipidee_rql const *rql, char const *rsl, char const *text, int doclose)
+void response_error (tipidee_rql const *rql, unsigned int status, char const *rsl, char const *text, uint32_t options)
 {
   tain deadline ;
-  char ans[4] = "???" ;
-  memcpy(ans, rsl, 3) ;
-  tipidee_response_error(buffer_1, rql, rsl, text, doclose || !g.cont) ;
+  tipidee_response_error(buffer_1, rql, status, rsl, text, options & 1 || !g.cont) ;
   tain_add_g(&deadline, &g.writetto) ;
-  log_response(ans, 0) ;
+  if (!(options & 2)) tipidee_log_answer(g.logv, rql, status, 0) ;
   if (!buffer_timed_flush_g(buffer_1, &deadline))
     strerr_diefu1sys(111, "write to stdout") ;
 }
 
-void response_error_and_exit (tipidee_rql const *rql, char const *rsl, char const *text)
+void response_error_and_exit (tipidee_rql const *rql, unsigned int status, char const *rsl, char const *text, uint32_t options)
 {
-  response_error(rql, rsl, text, 1) ;
-  log_and_exit(0) ;
+  response_error(rql, status, rsl, text, options | 1) ;
+  tipidee_log_exit(g.logv, 0) ;
+  _exit(0) ;
 }
 
-void response_error_and_die (tipidee_rql const *rql, int e, char const *rsl, char const *text, char const *const *v, unsigned int n, int dosys)
+void response_error_and_die (tipidee_rql const *rql, int e, unsigned int status, char const *rsl, char const *text, char const *const *v, unsigned int n, uint32_t options)
 {
-  response_error(rql, rsl, text, 1) ;
-  if (dosys) strerr_dievsys(e, v, n) ;
+  response_error(rql, status, rsl, text, options | 1) ;
+  if (options & 1) strerr_dievsys(e, v, n) ;
   else strerr_diev(e, v, n) ;
 }
 
 void exit_405 (tipidee_rql const *rql, uint32_t options)
 {
   tain deadline ;
-  tipidee_response_status_line(buffer_1, rql, "405 Method Not Allowed") ;
+  tipidee_response_status(buffer_1, rql, 405, "Method Not Allowed") ;
   tipidee_response_header_common_put_g(buffer_1, 1) ;
   buffer_putsnoflush(buffer_1, "Allow: GET, HEAD") ;
   if (options & 1) buffer_putsnoflush(buffer_1, ", POST") ;
   buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ;
+  if (!(options & 2)) tipidee_log_answer(g.logv, rql, 405, 0) ;
   tain_add_g(&deadline, &g.writetto) ;
   if (!buffer_timed_flush_g(buffer_1, &deadline))
     strerr_diefu1sys(111, "write to stdout") ;
-  log_and_exit(0) ;
+  tipidee_log_exit(g.logv, 0) ;
+  _exit(0) ;
 }
 
 void respond_30x (tipidee_rql const *rql, tipidee_redirection const *rd)
 {
-  static char const *rsl[4] = { "307 Temporary Redirect", "308 Permanent Redirect", "302 Found", "301 Moved Permanently" } ;
+  static unsigned int const status[4] = { 307, 308, 302, 301 } ;
+  static char const *const reason[4] = { "Temporary Redirect", "Permanent Redirect", "Found", "Moved Permanently" } ;
   tain deadline ;
-  char ans[4] = "30x" ;
-  memcpy(ans, rsl[rd->type], 3) ;
-  tipidee_response_status_line(buffer_1, rql, rsl[rd->type]) ;
+  tipidee_response_status(buffer_1, rql, status[rd->type], reason[rd->type]) ;
   tipidee_response_header_common_put_g(buffer_1, 0) ;
   buffer_putsnoflush(buffer_1, "Content-Length: 0\r\nLocation: ") ;
   buffer_putsnoflush(buffer_1, rd->location) ;
   if (rd->sub) buffer_putsnoflush(buffer_1, rd->sub) ;
   buffer_putnoflush(buffer_1, "\r\n\r\n", 4) ;
+  tipidee_log_answer(g.logv, rql, status[rd->type], 0) ;
   tain_add_g(&deadline, &g.writetto) ;
-  log_response(ans, 0) ;
   if (!buffer_timed_flush_g(buffer_1, &deadline))
     strerr_diefu1sys(111, "write to stdout") ;
 }
diff --git a/src/tipideed/send_file.c b/src/tipideed/send_file.c
index 2aeec65..6d97167 100644
--- a/src/tipideed/send_file.c
+++ b/src/tipideed/send_file.c
@@ -68,8 +68,7 @@ void send_file (int fd, uint64_t n, char const *fn)
     else if (si.n > n)
     {
       si.n = n ;
-      if (g.verbosity >= 2)
-        strerr_warnw2x("serving elongated file: ", fn) ;
+      strerr_warnw2x("serving elongated file: ", fn) ;
     }
     n -= si.n ;
     if (!n) si.last = 1 ;
@@ -109,8 +108,7 @@ void send_file (int fd, uint64_t n, char const *fn)
   if (r > n)
   {
     r = n ;
-    if (g.verbosity >= 2)
-      strerr_warnw2x("serving elongated file: ", fn) ;
+    strerr_warnw2x("serving elongated file: ", fn) ;
   }
   buffer_wseek(buffer_1, r) ;
   n -= r ;
diff --git a/src/tipideed/tipideed-internal.h b/src/tipideed/tipideed-internal.h
index 770596b..fe01152 100644
--- a/src/tipideed/tipideed-internal.h
+++ b/src/tipideed/tipideed-internal.h
@@ -18,26 +18,11 @@
 #define URI_BUFSIZE 4096
 #define HDR_BUFSIZE 8192
 
-typedef struct tipidee_resattr_s tipidee_resattr, *tipidee_resattr_ref ;
-struct tipidee_resattr_s
-{
-  char const *content_type ;
-  uint32_t iscgi : 1 ;
-  uint32_t isnph : 1 ;
-} ;
-#define TIPIDEE_RESATTR_ZERO { .content_type = 0, .iscgi = 0, .isnph = 0 }
-
 struct global_s
 {
   tipidee_conf conf ;
   stralloc sa ;
   size_t envlen ;
-  size_t localip ;
-  size_t localport ;
-  size_t localhost ;
-  size_t remoteip ;
-  size_t remoteport ;
-  size_t remotehost ;
   size_t cwdlen ;
   size_t indexlen ;
   tain readtto ;
@@ -46,11 +31,11 @@ struct global_s
   char const *defaulthost ;
   char const *indexnames[16] ;
   int p[2] ;
+  uint32_t logv ;
   uint32_t maxrqbody ;
   uint32_t maxcgibody ;
   uint16_t defaultport ;
   uint16_t indexn : 4 ;
-  uint16_t verbosity : 3 ;
   uint16_t cont : 2 ;
   uint16_t ssl : 1 ;
 } ;
@@ -59,12 +44,6 @@ struct global_s
   .conf = TIPIDEE_CONF_ZERO, \
   .sa = STRALLOC_ZERO, \
   .envlen = 0, \
-  .localip = 0, \
-  .localport = 0, \
-  .localhost = 0, \
-  .remoteip = 0, \
-  .remoteport = 0, \
-  .remotehost = 0, \
   .cwdlen = 1, \
   .indexlen = 0, \
   .readtto = TAIN_ZERO, \
@@ -73,11 +52,11 @@ struct global_s
   .defaulthost = "@", \
   .indexnames = { 0 }, \
   .p = { -1, -1 }, \
+  .logv = TIPIDEE_LOG_DEFAULT, \
   .maxrqbody = 0, \
   .maxcgibody = 0, \
   .defaultport = 0, \
   .indexn = 0, \
-  .verbosity = 1, \
   .cont = 1, \
   .ssl = 0 \
 }
@@ -92,27 +71,43 @@ extern void tipideed_harden (unsigned int) ;
 
  /* Responses */
 
-extern void response_error (tipidee_rql const *, char const *, char const *, int) ;
-extern void response_error_and_exit (tipidee_rql const *, char const *, char const *) gccattr_noreturn ;
-extern void response_error_and_die (tipidee_rql const *, int e, char const *, char const *, char const *const *, unsigned int, int) gccattr_noreturn ;
-
-#define exit_400(r, s) response_error_and_exit(r, "400 Bad Request", s)
-extern void exit_405 (tipidee_rql const *, uint32_t) gccattr_noreturn ;
-#define exit_408(r) response_error_and_exit(r, "408 Request Timeout", "")
-#define exit_413(r, s) response_error_and_exit(r, "413 Request Entity Too Large", s)
-#define exit_501(r, s) response_error_and_exit(r, "501 Not Implemented", s)
-
-#define respond_403(r) response_error(r, "403 Forbidden", "Missing credentials to access the URI.", 0)
-#define respond_404(r) response_error(r, "404 Not Found", "The request URI was not found.", 0)
-#define respond_414(r) response_error(r, "414 URI Too Long", "The request URI had an oversized component.", 0)
+extern void response_error (tipidee_rql const *, unsigned int, char const *, char const *, uint32_t) ;  /* set bit 0 for Connection: close, bit 1 for preexit */
+extern void response_error_and_exit (tipidee_rql const *, unsigned int, char const *, char const *, uint32_t) gccattr_noreturn ;
+extern void response_error_and_die (tipidee_rql const *, int e, unsigned int, char const *, char const *, char const *const *, unsigned int, uint32_t) gccattr_noreturn ;
+
+ /*
+    preexit is meant to be called before tipidee_log_request(), it won't log an answer line.
+    Use for early parsing, when the request isn't even validated yet.
+    exit is meant to be called after tipidee_log_request(), it will log an answer line.
+    respond can only happen after tipidee_log_request(), it will log an answer line.
+
+    exit will log an informational exit line.
+    die will not; there will be a fatal line instead.
+    die will log an answer line. There is no "predie", we just use strerr_die instead.
+ */
+
+#define preexit_400(r, s) response_error_and_exit(r, 400, "Bad Request", s, 2)
+#define exit_400(r, s) response_error_and_exit(r, 400, "Bad Request", s, 0)
+extern void exit_405 (tipidee_rql const *, uint32_t) gccattr_noreturn ;  /* set bit 0 for Allow: POST, bit 1 for preexit */
+#define preexit_408(r) response_error_and_exit(r, 408, "Request Timeout", "", 2)
+#define exit_408(r) response_error_and_exit(r, 408, "Request Timeout", "", 0)
+#define preexit_413(r, s) response_error_and_exit(r, 413, "Request Entity Too Large", s, 2)
+#define exit_413(r, s) response_error_and_exit(r, 413, "Request Entity Too Large", s, 0)
+#define preexit_501(r, s) response_error_and_exit(r, 501, "Not Implemented", s, 2)
+#define exit_501(r, s) response_error_and_exit(r, 501, "Not Implemented", s, 0)
+
+#define respond_403(r) response_error(r, 403, "Forbidden", "Missing credentials to access the URI.", 0)
+#define respond_404(r) response_error(r, 404, "Not Found", "The request URI was not found.", 0)
+#define respond_414(r) response_error(r, 414, "URI Too Long", "The request URI had an oversized component.", 0)
 extern void respond_30x (tipidee_rql const *, tipidee_redirection const *) ;
-#define respond_504(r) response_error(r, "504 Gateway Timeout", "The CGI script took too long to answer.", 0)
+#define respond_504(r) response_error(r, 504, "Gateway Timeout", "The CGI script took too long to answer.", 0)
+
+#define diefx(r, e, status, rsl, text, ...) response_error_and_die(r, e, status, rsl, text, strerr_array(PROG, ": fatal: ", __VA_ARGS__), sizeof(strerr_array(__VA_ARGS__))/sizeof(char const *)+2, 0)
+#define diefusys(r, e, status, rsl, text, ...) response_error_and_die(r, e, status, rsl, text, strerr_array(PROG, ": fatal: ", "unable to ", __VA_ARGS__), sizeof(strerr_array(__VA_ARGS__))/sizeof(char const *)+3, 1)
+#define die500x(r, e, ...) diefx(r, e, 500, "Internal Server Error", "Bad server configuration.", __VA_ARGS__)
+#define die500sys(r, e, ...) diefusys(r, e, 500, "Internal Server Error", "System error.", __VA_ARGS__)
+#define die502x(r, e, ...) diefx(r, e, 502, "Bad Gateway", "Bad CGI script.", __VA_ARGS__)
 
-#define diefx(r, e, rsl, text, ...) response_error_and_die(r, e, rsl, text, strerr_array(PROG, ": fatal: ", __VA_ARGS__), sizeof(strerr_array(__VA_ARGS__))/sizeof(char const *)+2, 0)
-#define diefusys(r, e, rsl, text, ...) response_error_and_die(r, e, rsl, text, strerr_array(PROG, ": fatal: ", "unable to ", __VA_ARGS__), sizeof(strerr_array(__VA_ARGS__))/sizeof(char const *)+3, 1)
-#define die500x(r, e, ...) diefx(r, e, "500 Internal Server Error", "Bad server configuration.", __VA_ARGS__)
-#define die500sys(r, e, ...) diefusys(r, e, "500 Internal Server Error", "System error.", __VA_ARGS__)
-#define die502x(r, e, ...) diefx(r, e, "502 Bad Gateway", "Bad CGI script.", __VA_ARGS__)
 
  /* Trace */
 
@@ -140,15 +135,4 @@ extern int respond_304 (tipidee_rql const *, char const *, struct stat const *)
 
 extern int respond_cgi (tipidee_rql *, char const *, size_t, char const *, char *, tipidee_headers const *, tipidee_resattr const *, char const *, size_t) ;
 
-
- /* log */
-
-extern void log_start (void) ;
-extern void log_and_exit (int) gccattr_noreturn ;
-extern void log_request (tipidee_rql const *) ;
-extern void log_regular (char const *, char const *, int, char const *) ;
-extern void log_response (char const *, char const *) ;
-extern void log_nph (char const *const *, char const *const *) ;
-extern void log_cgi (char const *const *, char const *const *) ;
-
 #endif
diff --git a/src/tipideed/tipideed.c b/src/tipideed/tipideed.c
index b85f509..fb5ede4 100644
--- a/src/tipideed/tipideed.c
+++ b/src/tipideed/tipideed.c
@@ -30,7 +30,7 @@
 #include <tipidee/tipidee.h>
 #include "tipideed-internal.h"
 
-#define USAGE "tipideed [ -v verbosity ] [ -f cdbfile ] [ -d basedir ] [ -R ] [ -U ]"
+#define USAGE "tipideed [ -f cdbfile ] [ -d basedir ] [ -R ] [ -U ]"
 #define dieusage() strerr_dieusage(100, USAGE)
 #define dienomem() strerr_diefu1sys(111, "stralloc_catb")
 
@@ -45,7 +45,13 @@ static void sigchld_handler (int sig)
   wait_reap() ;
 }
 
-static inline void prep_env (void)
+static inline void log_and_exit (int e)
+{
+  tipidee_log_exit(g.logv, e) ;
+  _exit(e) ;
+}
+
+static inline void prep_env (size_t *remoteip, size_t *remotehost)
 {
   static char const basevars[] = "PROTO\0TCPCONNNUM\0GATEWAY_INTERFACE=CGI/1.1\0SERVER_SOFTWARE=tipidee/" TIPIDEE_VERSION ;
   static char const sslvars[] = "SSL_PROTOCOL\0SSL_CIPHER\0SSL_TLS_SNI_SERVERNAME\0SSL_PEER_CERT_HASH\0SSL_PEER_CERT_SUBJECT\0HTTPS=on" ;
@@ -77,7 +83,6 @@ static inline void prep_env (void)
     if (!uint160_scan(x, &g.defaultport)) strerr_dieinvalid(100, var) ;
     if (!stralloc_catb(&g.sa, var, protolen + 10)
      || !stralloc_catb(&g.sa, "SERVER_PORT=", 12)) dienomem() ;
-    g.localport = g.sa.len ;
     m = uint16_fmt(fmt, g.defaultport) ; fmt[m++] = 0 ;
     if (!stralloc_catb(&g.sa, fmt, m)) dienomem() ;
 
@@ -87,7 +92,6 @@ static inline void prep_env (void)
     if (!ip46_scan(x, &ip)) strerr_dieinvalid(100, var) ;
     if (!stralloc_catb(&g.sa, var, protolen + 8)
      || !stralloc_catb(&g.sa, "SERVER_ADDR=", 12)) dienomem() ;
-    g.localip = g.sa.len ;
     m = ip46_fmt(fmt, &ip) ; fmt[m++] = 0 ;
     if (!stralloc_catb(&g.sa, fmt, m)) dienomem() ;
 
@@ -105,7 +109,6 @@ static inline void prep_env (void)
     if (!uint160_scan(x, &port)) strerr_dieinvalid(100, var) ;
     if (!stralloc_catb(&g.sa, var, protolen + 11)
      || !stralloc_catb(&g.sa, "REMOTE_PORT=", 12)) dienomem() ;
-    g.remoteport = g.sa.len ;
     m = uint16_fmt(fmt, port) ; fmt[m++] = 0 ;
     if (!stralloc_catb(&g.sa, fmt, m)) dienomem() ;
 
@@ -115,7 +118,7 @@ static inline void prep_env (void)
     if (!ip46_scan(x, &ip)) strerr_dieinvalid(100, var) ;
     if (!stralloc_catb(&g.sa, var, protolen + 9)
      || !stralloc_catb(&g.sa, "REMOTE_ADDR=", 12)) dienomem() ;
-    g.remoteip = g.sa.len ;
+    *remoteip = g.sa.len ;
     m = ip46_fmt(fmt, &ip) ; fmt[m++] = 0 ;
     if (!stralloc_catb(&g.sa, fmt, m)) dienomem() ;
 
@@ -123,7 +126,7 @@ static inline void prep_env (void)
     x = getenv(var) ;
     if ((x && !stralloc_catb(&g.sa, var, protolen + 11))
      || !stralloc_catb(&g.sa, "REMOTE_HOST=", 12)) dienomem() ;
-    g.remotehost = g.sa.len ;
+    *remotehost = g.sa.len ;
     if (x)
     {
       if (!stralloc_cats(&g.sa, x)) dienomem() ;
@@ -132,7 +135,7 @@ static inline void prep_env (void)
     {
       if (!stralloc_readyplus(&g.sa, m + 2)) dienomem() ;
       if (ip46_is6(&ip)) stralloc_catb(&g.sa, "[", 1) ;
-      stralloc_catb(&g.sa, g.sa.s + g.remoteip, m) ;
+      stralloc_catb(&g.sa, g.sa.s + *remoteip, m) ;
       if (ip46_is6(&ip)) stralloc_catb(&g.sa, "]", 1) ;
     }
     if (!stralloc_0(&g.sa)) dienomem() ;
@@ -326,8 +329,11 @@ static inline int serve (tipidee_rql *rql, char const *docroot, char *uribuf, ti
   }
 
   if (rql->m == TIPIDEE_METHOD_OPTIONS)
-    return respond_options(rql, ra.iscgi) ;
-  else if (ra.iscgi)
+    return respond_options(rql, 2 | ra.iscgi) ;
+
+  tipidee_log_resource(g.logv, rql, docroot, fn, &ra) ;
+
+  if (ra.iscgi)
     return respond_cgi(rql, fn, docrootlen, infopath, uribuf, hdr, &ra, body, bodylen) ;
 
   infopath = tipidee_headers_search(hdr, "If-Modified-Since") ;
@@ -353,24 +359,14 @@ int main (int argc, char const *const *argv, char const *const *envp)
     char const *conffile = TIPIDEE_SYSCONFPREFIX "tipidee.conf.cdb" ;
     char const *newroot = 0 ;
     unsigned int h = 0 ;
-    int gotv = 0 ;
     subgetopt l = SUBGETOPT_ZERO ;
 
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "v:f:d:RU", &l) ;
+      int opt = subgetopt_r(argc, argv, "f:d:RU", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
-        case 'v' :
-        {
-          unsigned int n ;
-          if (!uint0_scan(l.arg, &n)) dieusage() ;
-          if (n > 7) n = 7 ;
-          g.verbosity = n ;
-          gotv = 1 ;
-          break ;
-        }
         case 'f' : conffile = l.arg ; break ;
         case 'd' : newroot = l.arg ; break ;
         case 'R' : h |= 3 ; break ;
@@ -386,30 +382,31 @@ int main (int argc, char const *const *argv, char const *const *envp)
     if (newroot && chdir(newroot) == -1)
       strerr_diefu2sys(111, "chdir to ", newroot) ;
     tipideed_harden(h) ;
-    if (!gotv) g.verbosity = get_uint32("G:verbosity") ;
   }
 
-  prep_env() ;
-  inittto(&g.readtto, "G:read_timeout") ;
-  inittto(&g.writetto, "G:write_timeout") ;
-  inittto(&g.cgitto, "G:cgi_timeout") ;
-  g.maxrqbody = get_uint32("G:max_request_body_length") ;
-  g.maxcgibody = get_uint32("G:max_cgi_body_length") ;
   {
-    unsigned int n = tipidee_conf_get_argv(&g.conf, "G:index_file", g.indexnames, 16, &g.indexlen) ;
+    size_t remoteip, remotehost ;
+    unsigned int n ;
+    prep_env(&remoteip, &remotehost) ;
+    inittto(&g.readtto, "G:read_timeout") ;
+    inittto(&g.writetto, "G:write_timeout") ;
+    inittto(&g.cgitto, "G:cgi_timeout") ;
+    g.maxrqbody = get_uint32("G:max_request_body_length") ;
+    g.maxcgibody = get_uint32("G:max_cgi_body_length") ;
+    g.logv = get_uint32("G:logv") ;
+    n = tipidee_conf_get_argv(&g.conf, "G:index_file", g.indexnames, 16, &g.indexlen) ;
     if (!n) strerr_dief3x(102, "bad", " config value for ", "G:index_file") ;
     g.indexn = n-1 ;
-  }
-
-  if (ndelay_on(0) == -1 || ndelay_on(1) == -1)
-    strerr_diefu1sys(111, "set I/O nonblocking") ;
-  init_splice_pipe() ;
-  if (!sig_catch(SIGCHLD, &sigchld_handler))
-    strerr_diefu1sys(111, "set SIGCHLD handler") ;
-  if (!tain_now_set_stopwatch_g())
-    strerr_diefu1sys(111, "initialize clock") ;
 
-  log_start() ;
+    if (ndelay_on(0) == -1 || ndelay_on(1) == -1)
+      strerr_diefu1sys(111, "set I/O nonblocking") ;
+    init_splice_pipe() ;
+    if (!sig_catch(SIGCHLD, &sigchld_handler))
+      strerr_diefu1sys(111, "set SIGCHLD handler") ;
+    if (!tain_now_set_stopwatch_g())
+      strerr_diefu1sys(111, "initialize clock") ;
+    tipidee_log_start(g.logv, g.sa.s + remoteip, g.sa.s + remotehost) ;
+  }
 
 
  /* Main loop */
@@ -436,11 +433,11 @@ int main (int argc, char const *const *argv, char const *const *envp)
       case -1 : log_and_exit(1) ;  /* Malicious or shitty client */
       case 0 : break ;
       case 99 : g.cont = 0 ; continue ;  /* timeout, it's ok */
-      case 400 : exit_400(&rql, "Syntax error in request line") ;
+      case 400 : preexit_400(&rql, "Syntax error in request line") ;
       default : strerr_dief2x(101, "can't happen: ", "unknown tipidee_rql_read return code") ;
     }
     if (rql.http_major != 1) log_and_exit(1) ;
-    if (rql.http_minor > 1) exit_400(&rql, "Bad HTTP version") ;
+    if (rql.http_minor > 1) preexit_400(&rql, "Bad HTTP version") ;
 
     content_length = 0 ;
     tipidee_headers_init(&hdr, hdrbuf, HDR_BUFSIZE) ;
@@ -449,11 +446,11 @@ int main (int argc, char const *const *argv, char const *const *envp)
     {
       case -1 : log_and_exit(1) ;  /* connection issue, client timeout, etc. */
       case 0 : break ;
-      case 400 : exit_400(&rql, "Syntax error in headers") ;
-      case 408 : exit_408(&rql) ;  /* timeout */
-      case 413 : exit_413(&rql, hdr.n >= TIPIDEE_HEADERS_MAX ? "Too many headers" : "Too much header data") ;
-      case 500 : die500x(&rql, 101, "can't happen: ", "avltreen_insert failed") ;
-      default : die500x(&rql, 101, "can't happen: ", "unknown tipidee_headers_parse return code") ;
+      case 400 : preexit_400(&rql, "Syntax error in headers") ;
+      case 408 : preexit_408(&rql) ;  /* timeout */
+      case 413 : preexit_413(&rql, hdr.n >= TIPIDEE_HEADERS_MAX ? "Too many headers" : "Too much header data") ;
+      case 500 : strerr_dief2x(101, "can't happen: ", "avltreen_insert failed") ;
+      default : strerr_dief2x(101, "can't happen: ", "unknown tipidee_headers_parse return code") ;
     }
 
     if (!rql.http_minor) g.cont = 0 ;
@@ -470,7 +467,7 @@ int main (int argc, char const *const *argv, char const *const *envp)
     x = tipidee_headers_search(&hdr, "Transfer-Encoding") ;
     if (x)
     {
-      if (strcasecmp(x, "chunked")) exit_400(&rql, "unsupported Transfer-Encoding") ;
+      if (strcasecmp(x, "chunked")) preexit_400(&rql, "unsupported Transfer-Encoding") ;
       else tcoding = TIPIDEE_TRANSFERCODING_CHUNKED ;
     }
     else
@@ -478,7 +475,7 @@ int main (int argc, char const *const *argv, char const *const *envp)
       x = tipidee_headers_search(&hdr, "Content-Length") ;
       if (x)
       {
-        if (!size_scan(x, &content_length)) exit_400(&rql, "Invalid Content-Length") ;
+        if (!size_scan(x, &content_length)) preexit_400(&rql, "Invalid Content-Length") ;
         else if (content_length) tcoding = TIPIDEE_TRANSFERCODING_FIXED ;
         else tcoding = TIPIDEE_TRANSFERCODING_NONE ;
       }
@@ -486,22 +483,22 @@ int main (int argc, char const *const *argv, char const *const *envp)
     }
 
     if (tcoding != TIPIDEE_TRANSFERCODING_NONE && rql.m != TIPIDEE_METHOD_POST)
-      exit_400(&rql, "only POST requests can have an entity body") ;
+      preexit_400(&rql, "only POST requests can have an entity body") ;
 
     switch (rql.m)
     {
       case TIPIDEE_METHOD_GET :
       case TIPIDEE_METHOD_HEAD :
-      case TIPIDEE_METHOD_POST : break ;
+      case TIPIDEE_METHOD_POST :
+      case TIPIDEE_METHOD_TRACE : break ;
       case TIPIDEE_METHOD_OPTIONS :
         if (!rql.uri.path) { respond_options(&rql, 1) ; continue ; }
         break ;
       case TIPIDEE_METHOD_PUT :
-      case TIPIDEE_METHOD_DELETE : exit_405(&rql, 1) ;
-      case TIPIDEE_METHOD_TRACE : respond_trace(hdrbuf, &rql, &hdr) ; continue ;
-      case TIPIDEE_METHOD_CONNECT : exit_501(&rql, "CONNECT method unsupported") ;
-      case TIPIDEE_METHOD_PRI : exit_501(&rql, "PRI method attempted with HTTP version 1") ;
-      default : die500x(&rql, 101, "can't happen: unknown HTTP method") ;
+      case TIPIDEE_METHOD_DELETE : exit_405(&rql, 3) ;
+      case TIPIDEE_METHOD_CONNECT : preexit_501(&rql, "CONNECT method unsupported") ;
+      case TIPIDEE_METHOD_PRI : preexit_501(&rql, "PRI method attempted with HTTP version 1") ;
+      default : strerr_dief2x(101, "can't happen: ", "unknown HTTP method") ;
     }
 
     if (rql.http_minor)
@@ -512,16 +509,23 @@ int main (int argc, char const *const *argv, char const *const *envp)
         char *p = strchr(x, ':') ;
         if (p)
         {
-          if (!uint160_scan(p+1, &rql.uri.port)) exit_400(&rql, "Invalid Host header") ;
+          if (!uint160_scan(p+1, &rql.uri.port)) preexit_400(&rql, "Invalid Host header") ;
           *p = 0 ;
         }
-        if (!*x || *x == '.') exit_400(&rql, "Invalid Host header") ;
+        if (!*x || *x == '.') preexit_400(&rql, "Invalid Host header") ;
         rql.uri.host = x ;
       }
-      else if (!rql.uri.host) exit_400(&rql, "Missing Host header") ;
+      else if (!rql.uri.host) preexit_400(&rql, "Missing Host header") ;
     }
     else if (!rql.uri.host) rql.uri.host = g.defaulthost ;
     if (!rql.uri.port) rql.uri.port = g.defaultport ;
+    tipidee_log_request(g.logv, &rql, &hdr, &g.sa) ;
+
+    if (rql.m == TIPIDEE_METHOD_TRACE)
+    {
+      respond_trace(hdrbuf, &rql, &hdr) ;
+      continue ;
+    }
 
     {
       size_t hostlen = strlen(rql.uri.host) ;
@@ -560,10 +564,8 @@ int main (int argc, char const *const *argv, char const *const *envp)
         default : break ;
       }
 
-      log_request(&rql) ;
-
 
-     /* And serve the resource. The loop is in case of CGI local-redirection. */
+     /* Find and serve the resource. The loop is in case of CGI local-redirection. */
 
       while (serve(&rql, docroot, uribuf, &hdr, bodysa.s, bodysa.len))
         if (localredirs++ >= MAX_LOCALREDIRS)
diff --git a/src/tipideed/trace.c b/src/tipideed/trace.c
index 4761ea5..122d269 100644
--- a/src/tipideed/trace.c
+++ b/src/tipideed/trace.c
@@ -10,6 +10,7 @@
 
 #include <tipidee/method.h>
 #include <tipidee/response.h>
+#include <tipidee/log.h>
 #include "tipideed-internal.h"
 
 int respond_trace (char const *buf, tipidee_rql const *rql, tipidee_headers const *hdr)
@@ -17,7 +18,7 @@ int respond_trace (char const *buf, tipidee_rql const *rql, tipidee_headers cons
   tain deadline ;
   size_t cl = 0 ;
   char fmt[SIZE_FMT] ;
-  tipidee_response_status_line(buffer_1, rql, "200 OK") ;
+  tipidee_response_status(buffer_1, rql, 200, "OK") ;
   tipidee_response_header_common_put_g(buffer_1, 0) ;
   buffer_putsnoflush(buffer_1, "Content-Type: message/http\r\nContent-Length: ") ;
   cl += strlen(tipidee_method_tostr(rql->m)) + 1;
@@ -48,6 +49,7 @@ int respond_trace (char const *buf, tipidee_rql const *rql, tipidee_headers cons
   buffer_putnoflush(buffer_1, ".", 1) ;
   buffer_putnoflush(buffer_1, fmt, uint_fmt(fmt, rql->http_minor)) ;
   buffer_putsnoflush(buffer_1, "\r\n") ;
+  tipidee_log_answer(g.logv, rql, 200, cl) ;
   for (size_t i = 0 ; i < hdr->n ; i++)
   {
     size_t len = strlen(buf + hdr->list[i].left) ;