1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
60 * Modified by djm@va.pubnix.com:
61 * If no TransferLog is given explicitly, decline to log.
63 * This is module implements the TransferLog directive (same as the
64 * common log module), and additional directives, LogFormat and CustomLog.
69 * TransferLog fn Logs transfers to fn in standard log format, unless
70 * a custom format is set with LogFormat
71 * LogFormat format Set a log format from TransferLog files
73 * Log to file fn with format given by the format
76 * CookieLog fn For backwards compatability with old Cookie
77 * logging module - now deprecated.
79 * There can be any number of TransferLog and CustomLog
80 * commands. Each request will be logged to _ALL_ the
81 * named files, in the appropriate format.
83 * If no TransferLog or CustomLog directive appears in a VirtualHost,
84 * the request will be logged to the log file(s) defined outside
85 * the virtual host section. If a TransferLog or CustomLog directive
86 * appears in the VirtualHost section, the log files defined outside
87 * the VirtualHost will _not_ be used. This makes this module compatable
88 * with the CLF and config log modules, where the use of TransferLog
89 * inside the VirtualHost section overrides its use outside.
93 * TransferLog logs/access_log
95 * LogFormat "... custom format ..."
96 * TransferLog log/virtual_only
97 * CustomLog log/virtual_useragents "%t %{user-agent}i"
100 * This will log using CLF to access_log any requests handled by the
101 * main server, while any requests to the virtual host will be logged
102 * with the "... custom format..." to virtual_only _AND_ using
103 * the custom user-agent log to virtual_useragents.
105 * Note that the NCSA referer and user-agent logs are easily added with
107 * CustomLog logs/referer "%{referer}i -> %U"
108 * CustomLog logs/agent "%{user-agent}i"
110 * RefererIgnore functionality can be obtained with conditional
111 * logging (SetEnvIf and CustomLog ... env=!VAR).
113 * But using this method allows much easier modification of the
114 * log format, e.g. to log hosts along with UA:
115 * CustomLog logs/referer "%{referer}i %U %h"
117 * The argument to LogFormat and CustomLog is a string, which can include
118 * literal characters copied into the log files, and '%' directives as
121 * %...B: bytes sent, excluding HTTP headers.
122 * %...b: bytes sent, excluding HTTP headers in CLF format, i.e. a '-'
123 * when no bytes where sent (rather than a '0'.
124 * %...c: Status of the connection.
125 * 'X' = connection aborted before the response completed.
126 * '+' = connection may be kept alive after the response is sent.
127 * '-' = connection will be closed after the response is sent.
128 * %...{FOOBAR}C: The contents of the HTTP cookie FOOBAR
129 * %...{FOOBAR}e: The contents of the environment variable FOOBAR
132 * %...a: remote IP-address
133 * %...A: local IP-address
134 * %...{Foobar}i: The contents of Foobar: header line(s) in the request
135 * sent to the client.
136 * %...l: remote logname (from identd, if supplied)
137 * %...{Foobar}n: The contents of note "Foobar" from another module.
138 * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
139 * %...p: the port the request was served to
140 * %...P: the process ID of the child that serviced the request.
141 * %...r: first line of request
142 * %...s: status. For requests that got internally redirected, this
143 * is status of the *original* request --- %...>s for the last.
144 * %...t: time, in common log format time format
145 * %...{format}t: The time, in the form given by format, which should
146 * be in strftime(3) format.
147 * %...T: the time taken to serve the request, in seconds.
148 * %...D: the time taken to serve the request, in micro seconds.
149 * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
150 * %...U: the URL path requested.
151 * %...v: the configured name of the server (i.e. which virtual host?)
152 * %...V: the server name according to the UseCanonicalName setting
153 * %...m: the request method
154 * %...H: the request protocol
155 * %...q: the query string prepended by "?", or empty if no query string
157 * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
158 * indicate conditions for inclusion of the item (which will cause it
159 * to be replaced with '-' if the condition is not met). Note that
160 * there is no escaping performed on the strings from %r, %...i and
161 * %...o; some with long memories may remember that I thought this was
162 * a bad idea, once upon a time, and I'm still not comfortable with
163 * it, but it is difficult to see how to "do the right thing" with all
164 * of '%..i', unless we URL-escape everything and break with CLF.
166 * The forms of condition are a list of HTTP status codes, which may
167 * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
168 * User-agent: on 400 errors and 501 errors (Bad Request, Not
169 * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
170 * requests which did *not* return some sort of normal status.
172 * The default LogFormat reproduces CLF; see below.
174 * The way this is supposed to work with virtual hosts is as follows:
175 * a virtual host can have its own LogFormat, or its own TransferLog.
176 * If it doesn't have its own LogFormat, it inherits from the main
177 * server. If it doesn't have its own TransferLog, it writes to the
178 * same descriptor (meaning the same process for "| ...").
182 #include "apr_strings.h"
185 #define APR_WANT_STRFUNC
186 #include "apr_want.h"
188 #include "ap_config.h"
190 #include "http_config.h"
191 #include "http_core.h" /* For REMOTE_NAME */
192 #include "http_log.h"
193 #include "http_protocol.h"
195 #if APR_HAVE_UNISTD_H
202 #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
204 module AP_MODULE_DECLARE_DATA config_log_module;
206 static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE);
207 static apr_fileperms_t xfer_perms = APR_OS_DEFAULT;
209 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
210 * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512
211 * is guaranteed. So we'll just guess 512 in the event the system
212 * doesn't have this. Now, for file writes there is actually no limit,
213 * the entire write is atomic. Whether all systems implement this
214 * correctly is another question entirely ... so we'll just use PIPE_BUF
215 * because it's probably a good guess as to what is implemented correctly
219 #define LOG_BUFSIZE PIPE_BUF
221 #define LOG_BUFSIZE (512)
225 * multi_log_state is our per-(virtual)-server configuration. We store
226 * an array of the logs we are going to use, each of type config_log_state.
227 * If a default log format is given by LogFormat, store in default_format
228 * (backward compat. with mod_log_config). We also store for each virtual
229 * server a pointer to the logs specified for the main server, so that if this
230 * vhost has no logs defined, we can use the main server's logs instead.
232 * So, for the main server, config_logs contains a list of the log files
233 * and server_config_logs in empty. For a vhost, server_config_logs
234 * points to the same array as config_logs in the main server, and
235 * config_logs points to the array of logs defined inside this vhost,
236 * which might be empty.
240 const char *default_format_string;
241 apr_array_header_t *default_format;
242 apr_array_header_t *config_logs;
243 apr_array_header_t *server_config_logs;
244 apr_table_t *formats;
248 * config_log_state holds the status of a single log file. fname might
249 * be NULL, which means this module does no logging for this
250 * request. format might be NULL, in which case the default_format
251 * from the multi_log_state should be used, or if that is NULL as
252 * well, use the CLF. log_fd is NULL before the log file is opened and
253 * set to a valid fd after it is opened.
258 const char *format_string;
259 apr_array_header_t *format;
264 char outbuf[LOG_BUFSIZE];
270 * Note that many of these could have ap_sprintfs replaced with static buffers.
273 typedef const char *(*item_key_func) (request_rec *, char *);
280 apr_array_header_t *conditions;
283 static char *format_integer(apr_pool_t *p, int i)
285 return apr_psprintf(p, "%d", i);
288 static char *pfmt(apr_pool_t *p, int i)
294 return format_integer(p, i);
298 static const char *constant_item(request_rec *dummy, char *stuff)
303 static const char *log_remote_host(request_rec *r, char *a)
305 return ap_get_remote_host(r->connection, r->per_dir_config,
309 static const char *log_remote_address(request_rec *r, char *a)
311 return r->connection->remote_ip;
314 static const char *log_local_address(request_rec *r, char *a)
316 return r->connection->local_ip;
319 static const char *log_remote_logname(request_rec *r, char *a)
321 return ap_get_remote_logname(r);
324 static const char *log_remote_user(request_rec *r, char *a)
326 char *rvalue = r->user;
328 if (rvalue == NULL) {
331 else if (strlen(rvalue) == 0) {
337 static const char *log_request_line(request_rec *r, char *a)
339 /* NOTE: If the original request contained a password, we
340 * re-write the request line here to contain XXXXXX instead:
341 * (note the truncation before the protocol string for HTTP/0.9 requests)
342 * (note also that r->the_request contains the unmodified request)
344 return (r->parsed_uri.password) ? apr_pstrcat(r->pool, r->method, " ",
345 ap_unparse_uri_components(r->pool, &r->parsed_uri, 0),
346 r->assbackwards ? NULL : " ", r->protocol, NULL)
350 static const char *log_request_file(request_rec *r, char *a)
354 static const char *log_request_uri(request_rec *r, char *a)
358 static const char *log_request_method(request_rec *r, char *a)
362 static const char *log_request_protocol(request_rec *r, char *a)
366 static const char *log_request_query(request_rec *r, char *a)
368 return (r->args != NULL) ? apr_pstrcat(r->pool, "?", r->args, NULL)
371 static const char *log_status(request_rec *r, char *a)
373 return pfmt(r->pool, r->status);
376 static const char *clf_log_bytes_sent(request_rec *r, char *a)
378 if (!r->sent_bodyct) {
382 return apr_psprintf(r->pool, "%ld", r->bytes_sent);
386 static const char *log_bytes_sent(request_rec *r, char *a)
388 if (!r->sent_bodyct) {
392 return apr_psprintf(r->pool, "%ld", r->bytes_sent);
397 static const char *log_header_in(request_rec *r, char *a)
399 return apr_table_get(r->headers_in, a);
402 static const char *log_header_out(request_rec *r, char *a)
404 const char *cp = apr_table_get(r->headers_out, a);
405 if (!strcasecmp(a, "Content-type") && r->content_type) {
406 cp = ap_field_noparam(r->pool, r->content_type);
411 return apr_table_get(r->err_headers_out, a);
414 static const char *log_note(request_rec *r, char *a)
416 return apr_table_get(r->notes, a);
418 static const char *log_env_var(request_rec *r, char *a)
420 return apr_table_get(r->subprocess_env, a);
423 static const char *log_cookie(request_rec *r, char *a)
426 const char *start_cookie;
428 if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
429 if ((start_cookie = ap_strstr_c(cookies,a))) {
430 char *cookie, *end_cookie;
431 start_cookie += strlen(a) + 1; /* cookie_name + '=' */
432 cookie = apr_pstrdup(r->pool, start_cookie);
433 /* kill everything in cookie after ';' */
434 end_cookie = strchr(cookie, ';');
444 static const char *log_request_time(request_rec *r, char *a)
446 apr_exploded_time_t xt;
448 char tstr[MAX_STRING_LEN];
451 hi. i think getting the time again at the end of the request
452 just for logging is dumb. i know it's "required" for CLF.
453 folks writing log parsing tools don't realise that out of order
454 times have always been possible (consider what happens if one
455 process calculates the time to log, but then there's a context
456 switch before it writes and before that process is run again the
457 log rotation occurs) and they should just fix their tools rather
458 than force the server to pay extra cpu cycles. if you've got
459 a problem with this, you can set the define. -djg
461 #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
462 apr_explode_localtime(&xt, apr_time_now());
464 apr_explode_localtime(&xt, r->request_time);
466 if (a && *a) { /* Custom format */
467 apr_strftime(tstr, &retcode, MAX_STRING_LEN, a, &xt);
469 else { /* CLF format */
482 apr_snprintf(tstr, sizeof(tstr), "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
483 xt.tm_mday, apr_month_snames[xt.tm_mon], xt.tm_year+1900,
484 xt.tm_hour, xt.tm_min, xt.tm_sec,
485 sign, timz / (60*60), timz % (60*60));
488 return apr_pstrdup(r->pool, tstr);
491 static const char *log_request_duration(request_rec *r, char *a)
493 return apr_psprintf(r->pool, "%ld", (apr_time_now() - r->request_time)
497 static const char *log_request_duration_microseconds(request_rec *r, char *a)
499 return apr_psprintf(r->pool, "%ld", (apr_time_now() - r->request_time));
502 /* These next two routines use the canonical name:port so that log
503 * parsers don't need to duplicate all the vhost parsing crud.
505 static const char *log_virtual_host(request_rec *r, char *a)
507 return r->server->server_hostname;
510 static const char *log_server_port(request_rec *r, char *a)
512 return apr_psprintf(r->pool, "%u",
513 r->server->port ? r->server->port : ap_default_port(r));
516 /* This respects the setting of UseCanonicalName so that
517 * the dynamic mass virtual hosting trick works better.
519 static const char *log_server_name(request_rec *r, char *a)
521 return ap_get_server_name(r);
524 static const char *log_child_pid(request_rec *r, char *a)
526 return apr_psprintf(r->pool, "%ld", (long) getpid());
528 static const char *log_connection_status(request_rec *r, char *a)
530 if (r->connection->aborted)
533 if ((r->connection->keepalive) &&
534 ((r->server->keep_alive_max - r->connection->keepalives) > 0)) {
540 /*****************************************************************
542 * Parsing the log format string
545 static struct log_item_list {
548 int want_orig_default;
549 } log_item_keys[] = {
552 'h', log_remote_host, 0
555 'a', log_remote_address, 0
558 'A', log_local_address, 0
561 'l', log_remote_logname, 0
564 'u', log_remote_user, 0
567 't', log_request_time, 0
570 'T', log_request_duration, 1
573 'r', log_request_line, 1
576 'f', log_request_file, 0
579 'U', log_request_uri, 1
585 'b', clf_log_bytes_sent, 0
588 'B', log_bytes_sent, 0
591 'i', log_header_in, 0
594 'o', log_header_out, 0
603 'V', log_server_name, 0
606 'v', log_virtual_host, 0
609 'p', log_server_port, 0
612 'P', log_child_pid, 0
615 'H', log_request_protocol, 0
618 'm', log_request_method, 0
621 'q', log_request_query, 0
624 'c', log_connection_status, 0
630 'D', log_request_duration_microseconds, 1
637 static struct log_item_list *find_log_func(char k)
641 for (i = 0; log_item_keys[i].ch; ++i)
642 if (k == log_item_keys[i].ch) {
643 return &log_item_keys[i];
649 static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
655 it->func = constant_item;
656 it->conditions = NULL;
659 while (*s && *s != '%') {
663 * This might allocate a few chars extra if there's a backslash
664 * escape in the format string.
666 it->arg = apr_palloc(p, s - *sa + 1);
670 while (*s && *s != '%') {
697 * Allow the loop to deal with this *s in the normal
698 * fashion so that it handles end of string etc.
711 static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
716 return parse_log_misc_string(p, it, sa);
720 it->condition_sense = 0;
721 it->conditions = NULL;
723 it->arg = ""; /* For safety's sake... */
727 struct log_item_list *l;
732 it->condition_sense = !it->condition_sense;
751 it->arg = ap_getword(p, &s, '}');
765 while (apr_isdigit(*++s)) {
766 i = i * 10 + (*s) - '0';
768 if (!it->conditions) {
769 it->conditions = apr_array_make(p, 4, sizeof(int));
771 *(int *) apr_array_push(it->conditions) = i;
775 l = find_log_func(*s++);
781 return apr_pstrcat(p, "Unrecognized LogFormat directive %",
785 if (it->want_orig == -1) {
786 it->want_orig = l->want_orig_default;
793 return "Ran off end of LogFormat parsing args to some directive";
796 static apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
798 apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
802 if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
809 parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
813 /*****************************************************************
818 static const char *process_item(request_rec *r, request_rec *orig,
819 log_format_item *item)
823 /* First, see if we need to process this thing at all... */
825 if (item->conditions && item->conditions->nelts != 0) {
827 int *conds = (int *) item->conditions->elts;
830 for (i = 0; i < item->conditions->nelts; ++i) {
831 if (r->status == conds[i]) {
837 if ((item->condition_sense && in_list)
838 || (!item->condition_sense && !in_list)) {
843 /* We do. Do it... */
845 cp = (*item->func) (item->want_orig ? orig : r, item->arg);
846 return cp ? cp : "-";
850 static void flush_log(config_log_state *cls)
852 if (cls->outcnt && cls->log_fd != NULL) {
853 apr_file_write(cls->log_fd, cls->outbuf, &cls->outcnt);
859 static int config_log_transaction(request_rec *r, config_log_state *cls,
860 apr_array_header_t *default_format)
862 log_format_item *items;
869 apr_array_header_t *format;
872 if (cls->fname == NULL) {
877 * See if we've got any conditional envariable-controlled logging decisions
880 if (cls->condition_var != NULL) {
881 envar = cls->condition_var;
883 if (apr_table_get(r->subprocess_env, envar) == NULL) {
888 if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
894 format = cls->format ? cls->format : default_format;
896 strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
897 strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
898 items = (log_format_item *) format->elts;
908 for (i = 0; i < format->nelts; ++i) {
909 strs[i] = process_item(r, orig, &items[i]);
912 for (i = 0; i < format->nelts; ++i) {
913 len += strl[i] = strlen(strs[i]);
917 if (len + cls->outcnt > LOG_BUFSIZE) {
920 if (len >= LOG_BUFSIZE) {
923 str = apr_palloc(r->pool, len + 1);
924 for (i = 0, s = str; i < format->nelts; ++i) {
925 memcpy(s, strs[i], strl[i]);
929 apr_file_write(cls->log_fd, str, &w);
932 for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) {
933 memcpy(s, strs[i], strl[i]);
939 str = apr_palloc(r->pool, len + 1);
941 for (i = 0, s = str; i < format->nelts; ++i) {
942 memcpy(s, strs[i], strl[i]);
946 apr_file_write(cls->log_fd, str, &len);
952 static int multi_log_transaction(request_rec *r)
954 multi_log_state *mls = ap_get_module_config(r->server->module_config,
956 config_log_state *clsarray;
960 * Log this transaction..
962 if (mls->config_logs->nelts) {
963 clsarray = (config_log_state *) mls->config_logs->elts;
964 for (i = 0; i < mls->config_logs->nelts; ++i) {
965 config_log_state *cls = &clsarray[i];
967 config_log_transaction(r, cls, mls->default_format);
970 else if (mls->server_config_logs) {
971 clsarray = (config_log_state *) mls->server_config_logs->elts;
972 for (i = 0; i < mls->server_config_logs->nelts; ++i) {
973 config_log_state *cls = &clsarray[i];
975 config_log_transaction(r, cls, mls->default_format);
982 /*****************************************************************
987 static void *make_config_log_state(apr_pool_t *p, server_rec *s)
989 multi_log_state *mls;
991 mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
992 mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
993 mls->default_format_string = NULL;
994 mls->default_format = NULL;
995 mls->server_config_logs = NULL;
996 mls->formats = apr_table_make(p, 4);
997 apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
1003 * Use the merger to simply add a pointer from the vhost log state
1004 * to the log of logs specified for the non-vhost configuration. Make sure
1005 * vhosts inherit any globally-defined format names.
1008 static void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
1010 multi_log_state *base = (multi_log_state *) basev;
1011 multi_log_state *add = (multi_log_state *) addv;
1013 add->server_config_logs = base->config_logs;
1014 if (!add->default_format) {
1015 add->default_format_string = base->default_format_string;
1016 add->default_format = base->default_format;
1018 add->formats = apr_table_overlay(p, base->formats, add->formats);
1024 * Set the default logfile format, or define a nickname for a format string.
1026 static const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
1029 const char *err_string = NULL;
1030 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1031 &config_log_module);
1034 * If we were given two arguments, the second is a name to be given to the
1035 * format. This syntax just defines the nickname - it doesn't actually
1036 * make the format the default.
1039 parse_log_string(cmd->pool, fmt, &err_string);
1040 if (err_string == NULL) {
1041 apr_table_setn(mls->formats, name, fmt);
1045 mls->default_format_string = fmt;
1046 mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
1052 static const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
1053 const char *fmt, const char *envclause)
1055 const char *err_string = NULL;
1056 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1057 &config_log_module);
1058 config_log_state *cls;
1060 cls = (config_log_state *) apr_array_push(mls->config_logs);
1061 cls->condition_var = NULL;
1062 if (envclause != NULL) {
1063 if (strncasecmp(envclause, "env=", 4) != 0) {
1064 return "error in condition clause";
1066 if ((envclause[4] == '\0')
1067 || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
1068 return "missing environment variable name";
1070 cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
1074 cls->format_string = fmt;
1079 cls->format = parse_log_string(cmd->pool, fmt, &err_string);
1086 static const char *set_transfer_log(cmd_parms *cmd, void *dummy,
1089 return add_custom_log(cmd, dummy, fn, NULL, NULL);
1092 static const char *set_cookie_log(cmd_parms *cmd, void *dummy, const char *fn)
1094 return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
1097 static const command_rec config_log_cmds[] =
1099 AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
1100 "a file name, a custom log format string or format name, "
1101 "and an optional \"env=\" clause (see docs)"),
1102 AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
1103 "the filename of the access log"),
1104 AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
1105 "a log format string (see docs) and an optional format name"),
1106 AP_INIT_TAKE1("CookieLog", set_cookie_log, NULL, RSRC_CONF,
1107 "the filename of the cookie log"),
1111 static config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
1112 config_log_state *cls,
1113 apr_array_header_t *default_format)
1115 apr_status_t status;
1117 if (cls->log_fd != NULL) {
1118 return cls; /* virtual config shared w/main server */
1121 if (cls->fname == NULL) {
1122 return cls; /* Leave it NULL to decline. */
1125 if (*cls->fname == '|') {
1128 pl = ap_open_piped_log(p, cls->fname + 1);
1132 cls->log_fd = ap_piped_log_write_fd(pl);
1135 const char *fname = ap_server_root_relative(p, cls->fname);
1136 if ((status = apr_file_open(&cls->log_fd, fname, xfer_flags, xfer_perms, p))
1138 ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
1139 "could not open transfer log file %s.", fname);
1143 #ifdef BUFFERED_LOGS
1150 static config_log_state *open_multi_logs(server_rec *s, apr_pool_t *p)
1153 multi_log_state *mls = ap_get_module_config(s->module_config,
1154 &config_log_module);
1155 config_log_state *clsarray;
1159 if (mls->default_format_string) {
1160 format = apr_table_get(mls->formats, mls->default_format_string);
1162 mls->default_format = parse_log_string(p, format, &dummy);
1166 if (!mls->default_format) {
1167 mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
1170 if (mls->config_logs->nelts) {
1171 clsarray = (config_log_state *) mls->config_logs->elts;
1172 for (i = 0; i < mls->config_logs->nelts; ++i) {
1173 config_log_state *cls = &clsarray[i];
1175 if (cls->format_string) {
1176 format = apr_table_get(mls->formats, cls->format_string);
1178 cls->format = parse_log_string(p, format, &dummy);
1182 cls = open_config_log(s, p, cls, mls->default_format);
1185 else if (mls->server_config_logs) {
1186 clsarray = (config_log_state *) mls->server_config_logs->elts;
1187 for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1188 config_log_state *cls = &clsarray[i];
1190 if (cls->format_string) {
1191 format = apr_table_get(mls->formats, cls->format_string);
1193 cls->format = parse_log_string(p, format, &dummy);
1197 cls = open_config_log(s, p, cls, mls->default_format);
1204 #ifdef BUFFERED_LOGS
1205 static apr_status_t flush_all_logs(void *data)
1207 server_rec *s = data;
1208 multi_log_state *mls;
1209 apr_array_header_t *log_list;
1210 config_log_state *clsarray;
1213 for (; s; s = s->next) {
1214 mls = ap_get_module_config(s->module_config, &config_log_module);
1216 if (mls->config_logs->nelts) {
1217 log_list = mls->config_logs;
1219 else if (mls->server_config_logs) {
1220 log_list = mls->server_config_logs;
1223 clsarray = (config_log_state *) log_list->elts;
1224 for (i = 0; i < log_list->nelts; ++i) {
1225 flush_log(&clsarray[i]);
1233 static void init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s)
1235 /* First, do "physical" server, which gets default log fd and format
1236 * for the virtual servers, if they don't override...
1239 open_multi_logs(s, p);
1241 /* Then, virtual servers */
1243 for (s = s->next; s; s = s->next) {
1244 open_multi_logs(s, p);
1248 static void init_child(apr_pool_t *p, server_rec *s)
1250 #ifdef BUFFERED_LOGS
1251 /* Now register the last buffer flush with the cleanup engine */
1252 apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
1256 static void register_hooks(apr_pool_t *p)
1258 ap_hook_child_init(init_child,NULL,NULL,APR_HOOK_MIDDLE);
1259 ap_hook_open_logs(init_config_log,NULL,NULL,APR_HOOK_MIDDLE);
1260 ap_hook_log_transaction(multi_log_transaction,NULL,NULL,APR_HOOK_MIDDLE);
1263 module AP_MODULE_DECLARE_DATA config_log_module =
1265 STANDARD20_MODULE_STUFF,
1266 NULL, /* create per-dir config */
1267 NULL, /* merge per-dir config */
1268 make_config_log_state, /* server config */
1269 merge_config_log_state, /* merge server config */
1270 config_log_cmds, /* command apr_table_t */
1271 register_hooks /* register hooks */