bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / loggers / mod_log_config.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * Modified by djm@va.pubnix.com:
19  * If no TransferLog is given explicitly, decline to log.
20  *
21  * This is module implements the TransferLog directive (same as the
22  * common log module), and additional directives, LogFormat and CustomLog.
23  *
24  *
25  * Syntax:
26  *
27  *    TransferLog fn      Logs transfers to fn in standard log format, unless
28  *                        a custom format is set with LogFormat
29  *    LogFormat format    Set a log format from TransferLog files
30  *    CustomLog fn format
31  *                        Log to file fn with format given by the format
32  *                        argument
33  *
34  *    CookieLog fn        For backwards compatability with old Cookie
35  *                        logging module - now deprecated.
36  *
37  * There can be any number of TransferLog and CustomLog
38  * commands. Each request will be logged to _ALL_ the
39  * named files, in the appropriate format.
40  *
41  * If no TransferLog or CustomLog directive appears in a VirtualHost,
42  * the request will be logged to the log file(s) defined outside
43  * the virtual host section. If a TransferLog or CustomLog directive
44  * appears in the VirtualHost section, the log files defined outside
45  * the VirtualHost will _not_ be used. This makes this module compatable
46  * with the CLF and config log modules, where the use of TransferLog
47  * inside the VirtualHost section overrides its use outside.
48  * 
49  * Examples:
50  *
51  *    TransferLog    logs/access_log
52  *    <VirtualHost>
53  *    LogFormat      "... custom format ..."
54  *    TransferLog    log/virtual_only
55  *    CustomLog      log/virtual_useragents "%t %{user-agent}i"
56  *    </VirtualHost>
57  *
58  * This will log using CLF to access_log any requests handled by the
59  * main server, while any requests to the virtual host will be logged
60  * with the "... custom format..." to virtual_only _AND_ using
61  * the custom user-agent log to virtual_useragents.
62  *
63  * Note that the NCSA referer and user-agent logs are easily added with
64  * CustomLog:
65  *   CustomLog   logs/referer  "%{referer}i -> %U"
66  *   CustomLog   logs/agent    "%{user-agent}i"
67  *
68  * RefererIgnore functionality can be obtained with conditional
69  * logging (SetEnvIf and CustomLog ... env=!VAR).
70  *
71  * But using this method allows much easier modification of the
72  * log format, e.g. to log hosts along with UA:
73  *   CustomLog   logs/referer "%{referer}i %U %h"
74  *
75  * The argument to LogFormat and CustomLog is a string, which can include
76  * literal characters copied into the log files, and '%' directives as
77  * follows:
78  *
79  * %...B:  bytes sent, excluding HTTP headers.
80  * %...b:  bytes sent, excluding HTTP headers in CLF format, i.e. a '-'
81  *         when no bytes where sent (rather than a '0'.
82  * %...{FOOBAR}C:  The contents of the HTTP cookie FOOBAR
83  * %...{FOOBAR}e:  The contents of the environment variable FOOBAR
84  * %...f:  filename
85  * %...h:  remote host
86  * %...a:  remote IP-address
87  * %...A:  local IP-address
88  * %...{Foobar}i:  The contents of Foobar: header line(s) in the request
89  *                 sent to the client.
90  * %...l:  remote logname (from identd, if supplied)
91  * %...{Foobar}n:  The contents of note "Foobar" from another module.
92  * %...{Foobar}o:  The contents of Foobar: header line(s) in the reply.
93  * %...p:  the port the request was served to
94  * %...P:  the process ID of the child that serviced the request.
95  * %...{format}P: the process ID or thread ID of the child/thread that
96  *                serviced the request
97  * %...r:  first line of request
98  * %...s:  status.  For requests that got internally redirected, this
99  *         is status of the *original* request --- %...>s for the last.
100  * %...t:  time, in common log format time format
101  * %...{format}t:  The time, in the form given by format, which should
102  *                 be in strftime(3) format.
103  * %...T:  the time taken to serve the request, in seconds.
104  * %...D:  the time taken to serve the request, in micro seconds.
105  * %...u:  remote user (from auth; may be bogus if return status (%s) is 401)
106  * %...U:  the URL path requested.
107  * %...v:  the configured name of the server (i.e. which virtual host?)
108  * %...V:  the server name according to the UseCanonicalName setting
109  * %...m:  the request method
110  * %...H:  the request protocol
111  * %...q:  the query string prepended by "?", or empty if no query string
112  * %...X:  Status of the connection.
113  *         'X' = connection aborted before the response completed.
114  *         '+' = connection may be kept alive after the response is sent.
115  *         '-' = connection will be closed after the response is sent.
116            (This directive was %...c in late versions of Apache 1.3, but
117             this conflicted with the historical ssl %...{var}c syntax.)
118 *
119  * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
120  * indicate conditions for inclusion of the item (which will cause it
121  * to be replaced with '-' if the condition is not met).  Note that
122  * there is no escaping performed on the strings from %r, %...i and
123  * %...o; some with long memories may remember that I thought this was
124  * a bad idea, once upon a time, and I'm still not comfortable with
125  * it, but it is difficult to see how to "do the right thing" with all
126  * of '%..i', unless we URL-escape everything and break with CLF.
127  *
128  * The forms of condition are a list of HTTP status codes, which may
129  * or may not be preceded by '!'.  Thus, '%400,501{User-agent}i' logs
130  * User-agent: on 400 errors and 501 errors (Bad Request, Not
131  * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
132  * requests which did *not* return some sort of normal status.
133  *
134  * The default LogFormat reproduces CLF; see below.
135  *
136  * The way this is supposed to work with virtual hosts is as follows:
137  * a virtual host can have its own LogFormat, or its own TransferLog.
138  * If it doesn't have its own LogFormat, it inherits from the main
139  * server.  If it doesn't have its own TransferLog, it writes to the
140  * same descriptor (meaning the same process for "| ...").
141  *
142  * --- rst */
143
144 #include "apr_strings.h"
145 #include "apr_lib.h"
146 #include "apr_hash.h"
147 #include "apr_optional.h"
148 #include "apr_anylock.h"
149
150 #define APR_WANT_STRFUNC
151 #include "apr_want.h"
152
153 #include "ap_config.h"
154 #include "mod_log_config.h"
155 #include "httpd.h"
156 #include "http_config.h"
157 #include "http_core.h"          /* For REMOTE_NAME */
158 #include "http_log.h"
159 #include "http_protocol.h"
160 #include "util_time.h"
161 #include "ap_mpm.h"
162
163 #if APR_HAVE_UNISTD_H
164 #include <unistd.h>
165 #endif
166 #ifdef HAVE_LIMITS_H
167 #include <limits.h>
168 #endif
169
170 #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
171
172 module AP_MODULE_DECLARE_DATA log_config_module;
173
174 #ifndef APR_LARGEFILE
175 #define APR_LARGEFILE 0
176 #endif
177
178 static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE | APR_LARGEFILE);
179 static apr_fileperms_t xfer_perms = APR_OS_DEFAULT;
180 static apr_hash_t *log_hash;
181 static apr_status_t ap_default_log_writer(request_rec *r,
182                            void *handle, 
183                            const char **strs,
184                            int *strl,
185                            int nelts,
186                            apr_size_t len);
187 static apr_status_t ap_buffered_log_writer(request_rec *r,
188                            void *handle, 
189                            const char **strs,
190                            int *strl,
191                            int nelts,
192                            apr_size_t len);
193 static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s, 
194                                         const char* name);
195 static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s, 
196                                         const char* name);
197
198 static void ap_log_set_writer_init(ap_log_writer_init *handle);
199 static void ap_log_set_writer(ap_log_writer *handle);
200 static ap_log_writer *log_writer = ap_default_log_writer;
201 static ap_log_writer_init *log_writer_init = ap_default_log_writer_init;
202 static int buffered_logs = 0; /* default unbuffered */
203 static apr_array_header_t *all_buffered_logs = NULL;
204
205 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
206  * guaranteed to be atomic when writing a pipe.  And PIPE_BUF >= 512
207  * is guaranteed.  So we'll just guess 512 in the event the system
208  * doesn't have this.  Now, for file writes there is actually no limit,
209  * the entire write is atomic.  Whether all systems implement this
210  * correctly is another question entirely ... so we'll just use PIPE_BUF
211  * because it's probably a good guess as to what is implemented correctly
212  * everywhere.
213  */
214 #ifdef PIPE_BUF
215 #define LOG_BUFSIZE     PIPE_BUF
216 #else
217 #define LOG_BUFSIZE     (512)
218 #endif
219
220 /*
221  * multi_log_state is our per-(virtual)-server configuration. We store
222  * an array of the logs we are going to use, each of type config_log_state.
223  * If a default log format is given by LogFormat, store in default_format
224  * (backward compat. with mod_log_config).  We also store for each virtual
225  * server a pointer to the logs specified for the main server, so that if this
226  * vhost has no logs defined, we can use the main server's logs instead.
227  *
228  * So, for the main server, config_logs contains a list of the log files
229  * and server_config_logs is empty. For a vhost, server_config_logs
230  * points to the same array as config_logs in the main server, and
231  * config_logs points to the array of logs defined inside this vhost,
232  * which might be empty.
233  */
234
235 typedef struct {
236     const char *default_format_string;
237     apr_array_header_t *default_format;
238     apr_array_header_t *config_logs;
239     apr_array_header_t *server_config_logs;
240     apr_table_t *formats;
241 } multi_log_state;
242
243 /*
244  * config_log_state holds the status of a single log file. fname might
245  * be NULL, which means this module does no logging for this
246  * request. format might be NULL, in which case the default_format
247  * from the multi_log_state should be used, or if that is NULL as
248  * well, use the CLF. 
249  * log_writer is NULL before the log file is opened and is
250  * set to a opaque structure (usually a fd) after it is opened.
251  
252  */
253 typedef struct {
254     apr_file_t *handle;
255     apr_size_t outcnt;
256     char outbuf[LOG_BUFSIZE];
257     apr_anylock_t mutex;
258 } buffered_log;
259
260 typedef struct {
261     const char *fname;
262     const char *format_string;
263     apr_array_header_t *format;
264     void *log_writer;
265     char *condition_var;
266 } config_log_state;
267
268 /*
269  * Format items...
270  * Note that many of these could have ap_sprintfs replaced with static buffers.
271  */
272
273 typedef struct {
274     ap_log_handler_fn_t *func;
275     char *arg;
276     int condition_sense;
277     int want_orig;
278     apr_array_header_t *conditions;
279 } log_format_item;
280
281 static char *format_integer(apr_pool_t *p, int i)
282 {
283     return apr_itoa(p, i);
284 }
285
286 static char *pfmt(apr_pool_t *p, int i)
287 {
288     if (i <= 0) {
289         return "-";
290     }
291     else {
292         return format_integer(p, i);
293     }
294 }
295
296 static const char *constant_item(request_rec *dummy, char *stuff)
297 {
298     return stuff;
299 }
300
301 static const char *log_remote_host(request_rec *r, char *a)
302 {
303     return ap_escape_logitem(r->pool, ap_get_remote_host(r->connection,
304                                                          r->per_dir_config,
305                                                          REMOTE_NAME, NULL));
306 }
307
308 static const char *log_remote_address(request_rec *r, char *a)
309 {
310     return r->connection->remote_ip;
311 }
312
313 static const char *log_local_address(request_rec *r, char *a)
314 {
315     return r->connection->local_ip;
316 }
317
318 static const char *log_remote_logname(request_rec *r, char *a)
319 {
320     return ap_escape_logitem(r->pool, ap_get_remote_logname(r));
321 }
322
323 static const char *log_remote_user(request_rec *r, char *a)
324 {
325     char *rvalue = r->user;
326
327     if (rvalue == NULL) {
328         rvalue = "-";
329     }
330     else if (strlen(rvalue) == 0) {
331         rvalue = "\"\"";
332     }
333     else {
334         rvalue = ap_escape_logitem(r->pool, rvalue);
335     }
336
337     return rvalue;
338 }
339
340 static const char *log_request_line(request_rec *r, char *a)
341 {
342     /* NOTE: If the original request contained a password, we
343      * re-write the request line here to contain XXXXXX instead:
344      * (note the truncation before the protocol string for HTTP/0.9 requests)
345      * (note also that r->the_request contains the unmodified request)
346      */
347     return ap_escape_logitem(r->pool,
348                              (r->parsed_uri.password)
349                                ? apr_pstrcat(r->pool, r->method, " ",
350                                              apr_uri_unparse(r->pool,
351                                                              &r->parsed_uri, 0),
352                                              r->assbackwards ? NULL : " ",
353                                              r->protocol, NULL)
354                                : r->the_request);
355 }
356
357 static const char *log_request_file(request_rec *r, char *a)
358 {
359     return ap_escape_logitem(r->pool, r->filename);
360 }
361 static const char *log_request_uri(request_rec *r, char *a)
362 {
363     return ap_escape_logitem(r->pool, r->uri);
364 }
365 static const char *log_request_method(request_rec *r, char *a)
366 {
367     return ap_escape_logitem(r->pool, r->method);
368 }
369 static const char *log_request_protocol(request_rec *r, char *a)
370 {
371     return ap_escape_logitem(r->pool, r->protocol);
372 }
373 static const char *log_request_query(request_rec *r, char *a)
374 {
375     return (r->args) ? apr_pstrcat(r->pool, "?",
376                                    ap_escape_logitem(r->pool, r->args), NULL)
377                      : "";
378 }
379 static const char *log_status(request_rec *r, char *a)
380 {
381     return pfmt(r->pool, r->status);
382 }
383
384 static const char *clf_log_bytes_sent(request_rec *r, char *a)
385 {
386     if (!r->sent_bodyct || !r->bytes_sent) {
387         return "-";
388     }
389     else {
390         return apr_off_t_toa(r->pool, r->bytes_sent);
391     }
392 }
393
394 static const char *log_bytes_sent(request_rec *r, char *a)
395 {
396     if (!r->sent_bodyct || !r->bytes_sent) {
397         return "0";
398     }
399     else {
400         return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent);
401     }
402 }
403
404
405 static const char *log_header_in(request_rec *r, char *a)
406 {
407     return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a));
408 }
409
410 static APR_INLINE char *find_multiple_headers(apr_pool_t *pool,
411                                               const apr_table_t *table,
412                                               const char *key)
413 {
414     const apr_array_header_t *elts;
415     const apr_table_entry_t *t_elt;
416     const apr_table_entry_t *t_end;
417     apr_size_t len;
418     struct sle {
419         struct sle *next;
420         const char *value;
421         apr_size_t len;
422     } *result_list, *rp;
423
424     elts = apr_table_elts(table);
425
426     if (!elts->nelts) {
427         return NULL;
428     }
429
430     t_elt = (const apr_table_entry_t *)elts->elts;
431     t_end = t_elt + elts->nelts;
432     len = 1; /* \0 */
433     result_list = rp = NULL;
434
435     do {
436         if (!strcasecmp(t_elt->key, key)) {
437             if (!result_list) {
438                 result_list = rp = apr_palloc(pool, sizeof(*rp));
439             }
440             else {
441                 rp = rp->next = apr_palloc(pool, sizeof(*rp));
442                 len += 2; /* ", " */
443             }
444
445             rp->next = NULL;
446             rp->value = t_elt->val;
447             rp->len = strlen(rp->value);
448
449             len += rp->len;
450         }
451         ++t_elt;
452     } while (t_elt < t_end);
453
454     if (result_list) {
455         char *result = apr_palloc(pool, len);
456         char *cp = result;
457
458         rp = result_list;
459         while (rp) {
460             if (rp != result_list) {
461                 *cp++ = ',';
462                 *cp++ = ' ';
463             }
464             memcpy(cp, rp->value, rp->len);
465             cp += rp->len;
466             rp = rp->next;
467         }
468         *cp = '\0';
469
470         return result;
471     }
472
473     return NULL;
474 }
475
476 static const char *log_header_out(request_rec *r, char *a)
477 {
478     const char *cp = NULL;
479
480     if (!strcasecmp(a, "Content-type") && r->content_type) {
481         cp = ap_field_noparam(r->pool, r->content_type);
482     }
483     else if (!strcasecmp(a, "Set-Cookie")) {
484         cp = find_multiple_headers(r->pool, r->headers_out, a);
485     }
486     else {
487         cp = apr_table_get(r->headers_out, a);
488     }
489
490     return ap_escape_logitem(r->pool, cp);
491 }
492
493 static const char *log_note(request_rec *r, char *a)
494 {
495     return ap_escape_logitem(r->pool, apr_table_get(r->notes, a));
496 }
497 static const char *log_env_var(request_rec *r, char *a)
498 {
499     return ap_escape_logitem(r->pool, apr_table_get(r->subprocess_env, a));
500 }
501
502 static const char *log_cookie(request_rec *r, char *a)
503 {
504     const char *cookies;
505     const char *start_cookie;
506
507     if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
508         if ((start_cookie = ap_strstr_c(cookies,a))) {
509             char *cookie, *end_cookie;
510             start_cookie += strlen(a) + 1; /* cookie_name + '=' */
511             cookie = apr_pstrdup(r->pool, start_cookie);
512             /* kill everything in cookie after ';' */
513             end_cookie = strchr(cookie, ';'); 
514             if (end_cookie) {
515                 *end_cookie = '\0';
516             }
517             return ap_escape_logitem(r->pool, cookie);
518         }
519     }
520     return NULL;
521 }
522
523 static const char *log_request_time_custom(request_rec *r, char *a,
524                                            apr_time_exp_t *xt)
525 {
526     apr_size_t retcode;
527     char tstr[MAX_STRING_LEN];
528     apr_strftime(tstr, &retcode, sizeof(tstr), a, xt);
529     return apr_pstrdup(r->pool, tstr);
530 }
531
532 #define DEFAULT_REQUEST_TIME_SIZE 32
533 typedef struct {
534     unsigned t;
535     char timestr[DEFAULT_REQUEST_TIME_SIZE];
536     unsigned t_validate;
537 } cached_request_time;
538
539 #define TIME_CACHE_SIZE 4
540 #define TIME_CACHE_MASK 3
541 static cached_request_time request_time_cache[TIME_CACHE_SIZE];
542
543 static const char *log_request_time(request_rec *r, char *a)
544 {
545     apr_time_exp_t xt;
546
547     /* ###  I think getting the time again at the end of the request
548      * just for logging is dumb.  i know it's "required" for CLF.
549      * folks writing log parsing tools don't realise that out of order
550      * times have always been possible (consider what happens if one
551      * process calculates the time to log, but then there's a context
552      * switch before it writes and before that process is run again the
553      * log rotation occurs) and they should just fix their tools rather
554      * than force the server to pay extra cpu cycles.  if you've got
555      * a problem with this, you can set the define.  -djg
556      */
557     if (a && *a) {              /* Custom format */
558         /* The custom time formatting uses a very large temp buffer
559          * on the stack.  To avoid using so much stack space in the
560          * common case where we're not using a custom format, the code
561          * for the custom format in a separate function.  (That's why
562          * log_request_time_custom is not inlined right here.)
563          */
564 #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
565         ap_explode_recent_localtime(&xt, apr_time_now());
566 #else
567         ap_explode_recent_localtime(&xt, r->request_time);
568 #endif
569         return log_request_time_custom(r, a, &xt);
570     }
571     else {                      /* CLF format */
572         /* This code uses the same technique as ap_explode_recent_localtime():
573          * optimistic caching with logic to detect and correct race conditions.
574          * See the comments in server/util_time.c for more information.
575          */
576         cached_request_time* cached_time = apr_palloc(r->pool,
577                                                       sizeof(*cached_time));
578 #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
579         apr_time_t request_time = apr_time_now();
580 #else
581         apr_time_t request_time = r->request_time;
582 #endif
583         unsigned t_seconds = (unsigned)apr_time_sec(request_time);
584         unsigned i = t_seconds & TIME_CACHE_MASK;
585         memcpy(cached_time, &(request_time_cache[i]), sizeof(*cached_time));
586         if ((t_seconds != cached_time->t) ||
587             (t_seconds != cached_time->t_validate)) {
588
589             /* Invalid or old snapshot, so compute the proper time string
590              * and store it in the cache
591              */
592             char sign;
593             int timz;
594
595             ap_explode_recent_localtime(&xt, request_time);
596             timz = xt.tm_gmtoff;
597             if (timz < 0) {
598                 timz = -timz;
599                 sign = '-';
600             }
601             else {
602                 sign = '+';
603             }
604             cached_time->t = t_seconds;
605             apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE,
606                          "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
607                          xt.tm_mday, apr_month_snames[xt.tm_mon],
608                          xt.tm_year+1900, xt.tm_hour, xt.tm_min, xt.tm_sec,
609                          sign, timz / (60*60), (timz % (60*60)) / 60);
610             cached_time->t_validate = t_seconds;
611             memcpy(&(request_time_cache[i]), cached_time,
612                    sizeof(*cached_time));
613         }
614         return cached_time->timestr;
615     }
616 }
617
618 static const char *log_request_duration(request_rec *r, char *a)
619 {
620     apr_time_t duration = apr_time_now() - r->request_time;
621     return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(duration));
622 }
623
624 static const char *log_request_duration_microseconds(request_rec *r, char *a)
625 {
626     return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, 
627                         (apr_time_now() - r->request_time));
628 }
629
630 /* These next two routines use the canonical name:port so that log
631  * parsers don't need to duplicate all the vhost parsing crud.
632  */
633 static const char *log_virtual_host(request_rec *r, char *a)
634 {
635     return ap_escape_logitem(r->pool, r->server->server_hostname);
636 }
637
638 static const char *log_server_port(request_rec *r, char *a)
639 {
640     return apr_psprintf(r->pool, "%u",
641                         r->server->port ? r->server->port : ap_default_port(r));
642 }
643
644 /* This respects the setting of UseCanonicalName so that
645  * the dynamic mass virtual hosting trick works better.
646  */
647 static const char *log_server_name(request_rec *r, char *a)
648 {
649     return ap_escape_logitem(r->pool, ap_get_server_name(r));
650 }
651
652 static const char *log_pid_tid(request_rec *r, char *a)
653 {
654     if (*a == '\0' || !strcmp(a, "pid")) {
655         return apr_psprintf(r->pool, "%" APR_PID_T_FMT, getpid());
656     }
657     else if (!strcmp(a, "tid")) {
658 #if APR_HAS_THREADS
659         apr_os_thread_t tid = apr_os_thread_current();
660 #else
661         int tid = 0; /* APR will format "0" anyway but an arg is needed */
662 #endif
663         return apr_psprintf(r->pool, "%pT", &tid);
664     }
665     /* bogus format */
666     return a;
667 }
668
669 static const char *log_connection_status(request_rec *r, char *a)
670 {
671     if (r->connection->aborted)
672         return "X";
673
674     if (r->connection->keepalive == AP_CONN_KEEPALIVE && 
675         (!r->server->keep_alive_max ||
676          (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
677         return "+";
678     }
679     return "-";
680 }
681
682 /*****************************************************************
683  *
684  * Parsing the log format string
685  */
686
687 static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
688                                    const char **sa)
689 {
690     const char *s;
691     char *d;
692
693     it->func = constant_item;
694     it->conditions = NULL;
695
696     s = *sa;
697     while (*s && *s != '%') {
698         s++;
699     }
700     /*
701      * This might allocate a few chars extra if there's a backslash
702      * escape in the format string.
703      */
704     it->arg = apr_palloc(p, s - *sa + 1);
705
706     d = it->arg;
707     s = *sa;
708     while (*s && *s != '%') {
709         if (*s != '\\') {
710             *d++ = *s++;
711         }
712         else {
713             s++;
714             switch (*s) {
715             case '\\':
716                 *d++ = '\\';
717                 s++;
718                 break;
719             case 'r':
720                 *d++ = '\r';
721                 s++;
722                 break;
723             case 'n':
724                 *d++ = '\n';
725                 s++;
726                 break;
727             case 't':
728                 *d++ = '\t';
729                 s++;
730                 break;
731             default:
732                 /* copy verbatim */
733                 *d++ = '\\';
734                 /*
735                  * Allow the loop to deal with this *s in the normal
736                  * fashion so that it handles end of string etc.
737                  * properly.
738                  */
739                 break;
740             }
741         }
742     }
743     *d = '\0';
744
745     *sa = s;
746     return NULL;
747 }
748
749 static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
750 {
751     const char *s = *sa;
752     ap_log_handler *handler;
753
754     if (*s != '%') {
755         return parse_log_misc_string(p, it, sa);
756     }
757
758     ++s;
759     it->condition_sense = 0;
760     it->conditions = NULL;
761
762     if (*s == '%') {
763         it->arg = "%";
764         it->func = constant_item;
765         *sa = ++s;
766     
767         return NULL;
768     }
769
770     it->want_orig = -1;
771     it->arg = "";               /* For safety's sake... */
772
773     while (*s) {
774         int i;
775
776         switch (*s) {
777         case '!':
778             ++s;
779             it->condition_sense = !it->condition_sense;
780             break;
781
782         case '<':
783             ++s;
784             it->want_orig = 1;
785             break;
786
787         case '>':
788             ++s;
789             it->want_orig = 0;
790             break;
791
792         case ',':
793             ++s;
794             break;
795
796         case '{':
797             ++s;
798             it->arg = ap_getword(p, &s, '}');
799             break;
800
801         case '0':
802         case '1':
803         case '2':
804         case '3':
805         case '4':
806         case '5':
807         case '6':
808         case '7':
809         case '8':
810         case '9':
811             i = *s - '0';
812             while (apr_isdigit(*++s)) {
813                 i = i * 10 + (*s) - '0';
814             }
815             if (!it->conditions) {
816                 it->conditions = apr_array_make(p, 4, sizeof(int));
817             }
818             *(int *) apr_array_push(it->conditions) = i;
819             break;
820
821         default:
822             handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
823             if (!handler) {
824                 char dummy[2];
825
826                 dummy[0] = s[-1];
827                 dummy[1] = '\0';
828                 return apr_pstrcat(p, "Unrecognized LogFormat directive %",
829                                dummy, NULL);
830             }
831             it->func = handler->func;
832             if (it->want_orig == -1) {
833                 it->want_orig = handler->want_orig_default;
834             }
835             *sa = s;
836             return NULL;
837         }
838     }
839
840     return "Ran off end of LogFormat parsing args to some directive";
841 }
842
843 static apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
844 {
845     apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
846     char *res;
847
848     while (*s) {
849         if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
850             *err = res;
851             return NULL;
852         }
853     }
854
855     s = APR_EOL_STR;
856     parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
857     return a;
858 }
859
860 /*****************************************************************
861  *
862  * Actually logging.
863  */
864
865 static const char *process_item(request_rec *r, request_rec *orig,
866                           log_format_item *item)
867 {
868     const char *cp;
869
870     /* First, see if we need to process this thing at all... */
871
872     if (item->conditions && item->conditions->nelts != 0) {
873         int i;
874         int *conds = (int *) item->conditions->elts;
875         int in_list = 0;
876
877         for (i = 0; i < item->conditions->nelts; ++i) {
878             if (r->status == conds[i]) {
879                 in_list = 1;
880                 break;
881             }
882         }
883
884         if ((item->condition_sense && in_list)
885             || (!item->condition_sense && !in_list)) {
886             return "-";
887         }
888     }
889
890     /* We do.  Do it... */
891
892     cp = (*item->func) (item->want_orig ? orig : r, item->arg);
893     return cp ? cp : "-";
894 }
895
896 static void flush_log(buffered_log *buf)
897 {
898     if (buf->outcnt && buf->handle != NULL) {
899         apr_file_write(buf->handle, buf->outbuf, &buf->outcnt);
900         buf->outcnt = 0;
901     }
902 }
903
904
905 static int config_log_transaction(request_rec *r, config_log_state *cls,
906                                   apr_array_header_t *default_format)
907 {
908     log_format_item *items;
909     const char **strs;
910     int *strl;
911     request_rec *orig;
912     int i;
913     apr_size_t len = 0;
914     apr_array_header_t *format;
915     char *envar;
916     apr_status_t rv;
917
918     if (cls->fname == NULL) {
919         return DECLINED;
920     }
921
922     /*
923      * See if we've got any conditional envariable-controlled logging decisions
924      * to make.
925      */
926     if (cls->condition_var != NULL) {
927         envar = cls->condition_var;
928         if (*envar != '!') {
929             if (apr_table_get(r->subprocess_env, envar) == NULL) {
930                 return DECLINED;
931             }
932         }
933         else {
934             if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
935                 return DECLINED;
936             }
937         }
938     }
939
940     format = cls->format ? cls->format : default_format;
941
942     strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
943     strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
944     items = (log_format_item *) format->elts;
945
946     orig = r;
947     while (orig->prev) {
948         orig = orig->prev;
949     }
950     while (r->next) {
951         r = r->next;
952     }
953
954     for (i = 0; i < format->nelts; ++i) {
955         strs[i] = process_item(r, orig, &items[i]);
956     }
957
958     for (i = 0; i < format->nelts; ++i) {
959         len += strl[i] = strlen(strs[i]);
960     }
961     if (!log_writer) {
962         ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
963                 "log writer isn't correctly setup");
964          return HTTP_INTERNAL_SERVER_ERROR;
965     }
966     rv = log_writer(r, cls->log_writer, strs, strl, format->nelts, len);
967     /* xxx: do we return an error on log_writer? */
968     return OK;
969 }
970
971 static int multi_log_transaction(request_rec *r)
972 {
973     multi_log_state *mls = ap_get_module_config(r->server->module_config,
974                                                 &log_config_module);
975     config_log_state *clsarray;
976     int i;
977
978     /*
979      * Log this transaction..
980      */
981     if (mls->config_logs->nelts) {
982         clsarray = (config_log_state *) mls->config_logs->elts;
983         for (i = 0; i < mls->config_logs->nelts; ++i) {
984             config_log_state *cls = &clsarray[i];
985
986             config_log_transaction(r, cls, mls->default_format);
987         }
988     }
989     else if (mls->server_config_logs) {
990         clsarray = (config_log_state *) mls->server_config_logs->elts;
991         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
992             config_log_state *cls = &clsarray[i];
993
994             config_log_transaction(r, cls, mls->default_format);
995         }
996     }
997
998     return OK;
999 }
1000
1001 /*****************************************************************
1002  *
1003  * Module glue...
1004  */
1005
1006 static void *make_config_log_state(apr_pool_t *p, server_rec *s)
1007 {
1008     multi_log_state *mls;
1009
1010     mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
1011     mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
1012     mls->default_format_string = NULL;
1013     mls->default_format = NULL;
1014     mls->server_config_logs = NULL;
1015     mls->formats = apr_table_make(p, 4);
1016     apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
1017
1018     return mls;
1019 }
1020
1021 /*
1022  * Use the merger to simply add a pointer from the vhost log state
1023  * to the log of logs specified for the non-vhost configuration.  Make sure
1024  * vhosts inherit any globally-defined format names.
1025  */
1026
1027 static void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
1028 {
1029     multi_log_state *base = (multi_log_state *) basev;
1030     multi_log_state *add = (multi_log_state *) addv;
1031
1032     add->server_config_logs = base->config_logs;
1033     if (!add->default_format) {
1034         add->default_format_string = base->default_format_string;
1035         add->default_format = base->default_format;
1036     }
1037     add->formats = apr_table_overlay(p, base->formats, add->formats);
1038
1039     return add;
1040 }
1041
1042 /*
1043  * Set the default logfile format, or define a nickname for a format string.
1044  */
1045 static const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
1046                               const char *name)
1047 {
1048     const char *err_string = NULL;
1049     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1050                                                 &log_config_module);
1051
1052     /*
1053      * If we were given two arguments, the second is a name to be given to the
1054      * format.  This syntax just defines the nickname - it doesn't actually
1055      * make the format the default.
1056      */
1057     if (name != NULL) {
1058         parse_log_string(cmd->pool, fmt, &err_string);
1059         if (err_string == NULL) {
1060             apr_table_setn(mls->formats, name, fmt);
1061         }
1062     }
1063     else {
1064         mls->default_format_string = fmt;
1065         mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
1066     }
1067     return err_string;
1068 }
1069
1070
1071 static const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
1072                                   const char *fmt, const char *envclause)
1073 {
1074     const char *err_string = NULL;
1075     multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1076                                                 &log_config_module);
1077     config_log_state *cls;
1078
1079     cls = (config_log_state *) apr_array_push(mls->config_logs);
1080     cls->condition_var = NULL;
1081     if (envclause != NULL) {
1082         if (strncasecmp(envclause, "env=", 4) != 0) {
1083             return "error in condition clause";
1084         }
1085         if ((envclause[4] == '\0')
1086             || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
1087             return "missing environment variable name";
1088         }
1089         cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
1090     }
1091
1092     cls->fname = fn;
1093     cls->format_string = fmt;
1094     if (fmt == NULL) {
1095         cls->format = NULL;
1096     }
1097     else {
1098         cls->format = parse_log_string(cmd->pool, fmt, &err_string);
1099     }
1100     cls->log_writer = NULL;
1101
1102     return err_string;
1103 }
1104
1105 static const char *set_transfer_log(cmd_parms *cmd, void *dummy,
1106                                     const char *fn)
1107 {
1108     return add_custom_log(cmd, dummy, fn, NULL, NULL);
1109 }
1110
1111 static const char *set_cookie_log(cmd_parms *cmd, void *dummy, const char *fn)
1112 {
1113     return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
1114 }
1115
1116 static const char *set_buffered_logs_on(cmd_parms *parms, void *dummy, int flag)
1117 {
1118     buffered_logs = flag;
1119     if (buffered_logs) {
1120         ap_log_set_writer_init(ap_buffered_log_writer_init);
1121         ap_log_set_writer(ap_buffered_log_writer);
1122     }
1123     return NULL;
1124 }
1125 static const command_rec config_log_cmds[] =
1126 {
1127 AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
1128      "a file name, a custom log format string or format name, "
1129      "and an optional \"env=\" clause (see docs)"),
1130 AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
1131      "the filename of the access log"),
1132 AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
1133      "a log format string (see docs) and an optional format name"),
1134 AP_INIT_TAKE1("CookieLog", set_cookie_log, NULL, RSRC_CONF,
1135      "the filename of the cookie log"),
1136 AP_INIT_FLAG("BufferedLogs", set_buffered_logs_on, NULL, RSRC_CONF,
1137                  "Enable Buffered Logging (experimental)"),
1138     {NULL}
1139 };
1140
1141 static config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
1142                                          config_log_state *cls,
1143                                          apr_array_header_t *default_format)
1144 {
1145     if (cls->log_writer != NULL) {
1146         return cls;             /* virtual config shared w/main server */
1147     }
1148
1149     if (cls->fname == NULL) {
1150         return cls;             /* Leave it NULL to decline.  */
1151     }
1152     
1153     cls->log_writer = log_writer_init(p, s, cls->fname);
1154     if (cls->log_writer == NULL)
1155         return NULL; 
1156
1157     return cls;
1158 }
1159
1160 static int open_multi_logs(server_rec *s, apr_pool_t *p)
1161 {
1162     int i;
1163     multi_log_state *mls = ap_get_module_config(s->module_config,
1164                                              &log_config_module);
1165     config_log_state *clsarray;
1166     const char *dummy;
1167     const char *format;
1168
1169     if (mls->default_format_string) {
1170         format = apr_table_get(mls->formats, mls->default_format_string);
1171         if (format) {
1172             mls->default_format = parse_log_string(p, format, &dummy);
1173         }
1174     }    
1175
1176     if (!mls->default_format) {
1177         mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
1178     }
1179
1180     if (mls->config_logs->nelts) {
1181         clsarray = (config_log_state *) mls->config_logs->elts;
1182         for (i = 0; i < mls->config_logs->nelts; ++i) {
1183             config_log_state *cls = &clsarray[i];
1184
1185             if (cls->format_string) {
1186                 format = apr_table_get(mls->formats, cls->format_string);
1187                 if (format) {
1188                     cls->format = parse_log_string(p, format, &dummy);
1189                 }
1190             }
1191
1192             if (!open_config_log(s, p, cls, mls->default_format)) {
1193                 /* Failure already logged by open_config_log */
1194                 return DONE;
1195             }
1196         }
1197     }
1198     else if (mls->server_config_logs) {
1199         clsarray = (config_log_state *) mls->server_config_logs->elts;
1200         for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1201             config_log_state *cls = &clsarray[i];
1202
1203             if (cls->format_string) {
1204                 format = apr_table_get(mls->formats, cls->format_string);
1205                 if (format) {
1206                     cls->format = parse_log_string(p, format, &dummy);
1207                 }
1208             }
1209
1210             if (!open_config_log(s, p, cls, mls->default_format)) {
1211                 /* Failure already logged by open_config_log */
1212                 return DONE;
1213             }
1214         }
1215     }
1216
1217     return OK;
1218 }
1219
1220
1221 static apr_status_t flush_all_logs(void *data)
1222 {
1223     server_rec *s = data;
1224     multi_log_state *mls;
1225     apr_array_header_t *log_list;
1226     config_log_state *clsarray;
1227     buffered_log *buf;
1228     int i;
1229
1230     if (!buffered_logs)
1231         return APR_SUCCESS;
1232     
1233     for (; s; s = s->next) {
1234         mls = ap_get_module_config(s->module_config, &log_config_module);
1235         log_list = NULL;
1236         if (mls->config_logs->nelts) {
1237             log_list = mls->config_logs;
1238         }
1239         else if (mls->server_config_logs) {
1240             log_list = mls->server_config_logs;
1241         }
1242         if (log_list) {
1243             clsarray = (config_log_state *) log_list->elts;
1244             for (i = 0; i < log_list->nelts; ++i) {
1245                 buf = clsarray[i].log_writer;
1246                 flush_log(buf);
1247             }
1248         }
1249     }
1250     return APR_SUCCESS;
1251 }
1252
1253
1254 static int init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s)
1255 {
1256     int res;
1257
1258     /* First init the buffered logs array, which is needed when opening the logs. */
1259     if (buffered_logs) {
1260         all_buffered_logs = apr_array_make(p, 5, sizeof(buffered_log *));
1261     }
1262
1263     /* Next, do "physical" server, which gets default log fd and format
1264      * for the virtual servers, if they don't override...
1265      */
1266     res = open_multi_logs(s, p);
1267
1268     /* Then, virtual servers */
1269
1270     for (s = s->next; (res == OK) && s; s = s->next) {
1271         res = open_multi_logs(s, p);
1272     }
1273
1274     return res;
1275 }
1276
1277 static void init_child(apr_pool_t *p, server_rec *s)
1278 {
1279     int mpm_threads;
1280
1281     ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1282
1283     /* Now register the last buffer flush with the cleanup engine */
1284     if (buffered_logs) {
1285         int i;
1286         buffered_log **array = (buffered_log **)all_buffered_logs->elts;
1287         
1288         apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
1289
1290         for (i = 0; i < all_buffered_logs->nelts; i++) {
1291             buffered_log *this = array[i];
1292             
1293 #if APR_HAS_THREADS
1294             if (mpm_threads > 1) {
1295                 apr_status_t rv;
1296
1297                 this->mutex.type = apr_anylock_threadmutex;
1298                 rv = apr_thread_mutex_create(&this->mutex.lock.tm,
1299                                              APR_THREAD_MUTEX_DEFAULT,
1300                                              p);
1301                 if (rv != APR_SUCCESS) {
1302                     ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
1303                                  "could not initialize buffered log mutex, "
1304                                  "transfer log may become corrupted");
1305                     this->mutex.type = apr_anylock_none;
1306                 }
1307             }
1308             else
1309 #endif
1310             {
1311                 this->mutex.type = apr_anylock_none;
1312             }
1313         }
1314     }
1315 }
1316
1317 static void ap_register_log_handler(apr_pool_t *p, char *tag, 
1318                                     ap_log_handler_fn_t *handler, int def)
1319 {
1320     ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
1321     log_struct->func = handler;
1322     log_struct->want_orig_default = def;
1323
1324     apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
1325 }
1326 static void ap_log_set_writer_init(ap_log_writer_init *handle)
1327 {
1328     log_writer_init = handle;
1329
1330 }
1331 static void ap_log_set_writer(ap_log_writer *handle)
1332 {
1333     log_writer = handle;
1334 }
1335
1336 static apr_status_t ap_default_log_writer( request_rec *r,
1337                            void *handle, 
1338                            const char **strs,
1339                            int *strl,
1340                            int nelts,
1341                            apr_size_t len)
1342
1343 {
1344     char *str;
1345     char *s;
1346     int i;
1347     apr_status_t rv;
1348
1349     str = apr_palloc(r->pool, len + 1);
1350
1351     for (i = 0, s = str; i < nelts; ++i) {
1352         memcpy(s, strs[i], strl[i]);
1353         s += strl[i];
1354     }
1355
1356     rv = apr_file_write((apr_file_t*)handle, str, &len);
1357
1358     return rv;
1359 }
1360 static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s, 
1361                                         const char* name)
1362 {
1363     if (*name == '|') {
1364         piped_log *pl;
1365
1366         pl = ap_open_piped_log(p, name + 1);
1367         if (pl == NULL) {
1368            return NULL;;
1369         }
1370         return ap_piped_log_write_fd(pl);
1371     }
1372     else {
1373         const char *fname = ap_server_root_relative(p, name);
1374         apr_file_t *fd;
1375         apr_status_t rv;
1376
1377         if (!fname) {
1378             ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
1379                             "invalid transfer log path %s.", name);
1380             return NULL;
1381         }
1382         rv = apr_file_open(&fd, fname, xfer_flags, xfer_perms, p);
1383         if (rv != APR_SUCCESS) {
1384             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
1385                             "could not open transfer log file %s.", fname);
1386             return NULL;
1387         }
1388         return fd;
1389     }
1390 }
1391 static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s, 
1392                                         const char* name)
1393 {
1394     buffered_log *b;
1395     b = apr_pcalloc(p, sizeof(buffered_log));
1396     b->handle = ap_default_log_writer_init(p, s, name);
1397     
1398     if (b->handle) {
1399         *(buffered_log **)apr_array_push(all_buffered_logs) = b;
1400         return b;
1401     }
1402     else
1403         return NULL;
1404 }
1405 static apr_status_t ap_buffered_log_writer(request_rec *r,
1406                                            void *handle, 
1407                                            const char **strs,
1408                                            int *strl,
1409                                            int nelts,
1410                                            apr_size_t len)
1411
1412 {
1413     char *str;
1414     char *s;
1415     int i;
1416     apr_status_t rv;
1417     buffered_log *buf = (buffered_log*)handle;
1418
1419     if ((rv = APR_ANYLOCK_LOCK(&buf->mutex)) != APR_SUCCESS) {
1420         return rv;
1421     }
1422
1423     if (len + buf->outcnt > LOG_BUFSIZE) {
1424         flush_log(buf);
1425     }
1426     if (len >= LOG_BUFSIZE) {
1427         apr_size_t w;
1428
1429         str = apr_palloc(r->pool, len + 1);
1430         for (i = 0, s = str; i < nelts; ++i) {
1431             memcpy(s, strs[i], strl[i]);
1432             s += strl[i];
1433         }
1434         w = len;
1435         rv = apr_file_write(buf->handle, str, &w);
1436         
1437     }
1438     else {
1439         for (i = 0, s = &buf->outbuf[buf->outcnt]; i < nelts; ++i) {
1440             memcpy(s, strs[i], strl[i]);
1441             s += strl[i];
1442         }
1443         buf->outcnt += len;
1444         rv = APR_SUCCESS;
1445     }
1446
1447     APR_ANYLOCK_UNLOCK(&buf->mutex);
1448     return rv;
1449 }
1450
1451 static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
1452 {
1453     static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1454     
1455     log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1456
1457     if (log_pfn_register) {
1458         log_pfn_register(p, "h", log_remote_host, 0);
1459         log_pfn_register(p, "a", log_remote_address, 0 );
1460         log_pfn_register(p, "A", log_local_address, 0 );
1461         log_pfn_register(p, "l", log_remote_logname, 0);
1462         log_pfn_register(p, "u", log_remote_user, 0);
1463         log_pfn_register(p, "t", log_request_time, 0);
1464         log_pfn_register(p, "f", log_request_file, 0);
1465         log_pfn_register(p, "b", clf_log_bytes_sent, 0);
1466         log_pfn_register(p, "B", log_bytes_sent, 0);
1467         log_pfn_register(p, "i", log_header_in, 0);
1468         log_pfn_register(p, "o", log_header_out, 0);
1469         log_pfn_register(p, "n", log_note, 0);
1470         log_pfn_register(p, "e", log_env_var, 0);
1471         log_pfn_register(p, "V", log_server_name, 0);
1472         log_pfn_register(p, "v", log_virtual_host, 0);
1473         log_pfn_register(p, "p", log_server_port, 0);
1474         log_pfn_register(p, "P", log_pid_tid, 0);
1475         log_pfn_register(p, "H", log_request_protocol, 0);
1476         log_pfn_register(p, "m", log_request_method, 0);
1477         log_pfn_register(p, "q", log_request_query, 0);
1478         log_pfn_register(p, "X", log_connection_status, 0);
1479         log_pfn_register(p, "C", log_cookie, 0);
1480         log_pfn_register(p, "r", log_request_line, 1);
1481         log_pfn_register(p, "D", log_request_duration_microseconds, 1);
1482         log_pfn_register(p, "T", log_request_duration, 1);
1483         log_pfn_register(p, "U", log_request_uri, 1);
1484         log_pfn_register(p, "s", log_status, 1);
1485     }
1486
1487     return OK;
1488 }
1489
1490 static void register_hooks(apr_pool_t *p)
1491 {
1492     ap_hook_pre_config(log_pre_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
1493     ap_hook_child_init(init_child,NULL,NULL,APR_HOOK_MIDDLE);
1494     ap_hook_open_logs(init_config_log,NULL,NULL,APR_HOOK_MIDDLE);
1495     ap_hook_log_transaction(multi_log_transaction,NULL,NULL,APR_HOOK_MIDDLE);
1496
1497     /* Init log_hash before we register the optional function. It is 
1498      * possible for the optional function, ap_register_log_handler,
1499      * to be called before any other mod_log_config hooks are called.
1500      * As a policy, we should init everything required by an optional function
1501      * before calling APR_REGISTER_OPTIONAL_FN.
1502      */ 
1503     log_hash = apr_hash_make(p);
1504     APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
1505     APR_REGISTER_OPTIONAL_FN(ap_log_set_writer_init);
1506     APR_REGISTER_OPTIONAL_FN(ap_log_set_writer);
1507 }
1508
1509 module AP_MODULE_DECLARE_DATA log_config_module =
1510 {
1511     STANDARD20_MODULE_STUFF,
1512     NULL,                       /* create per-dir config */
1513     NULL,                       /* merge per-dir config */
1514     make_config_log_state,      /* server config */
1515     merge_config_log_state,     /* merge server config */
1516     config_log_cmds,            /* command apr_table_t */
1517     register_hooks              /* register hooks */
1518 };
1519