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