1 /* ====================================================================
2 * Copyright (c) 1995-2000 The Apache Software Foundation. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
16 * 3. All advertising materials mentioning features or use of this
17 * software must display the following acknowledgment:
18 * "This product includes software developed by the Apache Software Foundation
19 * for use in the Apache HTTP server project (http://www.apache.org/)."
21 * 4. The names "Apache Server" and "Apache Software Foundation" must not be used to
22 * endorse or promote products derived from this software without
23 * prior written permission. For written permission, please contact
26 * 5. Products derived from this software may not be called "Apache"
27 * nor may "Apache" appear in their names without prior written
28 * permission of the Apache Software Foundation.
30 * 6. Redistributions of any form whatsoever must retain the following
32 * "This product includes software developed by the Apache Software Foundation
33 * for use in the Apache HTTP server project (http://www.apache.org/)."
35 * THIS SOFTWARE IS PROVIDED BY THE Apache Software Foundation ``AS IS'' AND ANY
36 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38 * PURPOSE ARE 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
41 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
42 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
44 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
46 * OF THE POSSIBILITY OF SUCH DAMAGE.
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation and was originally based
51 * on public domain software written at the National Center for
52 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
53 * For more information on the Apache Software Foundation and the Apache HTTP server
54 * project, please see <http://www.apache.org/>.
59 * Modified by djm@va.pubnix.com:
60 * If no TransferLog is given explicitly, decline to log.
62 * This is module implements the TransferLog directive (same as the
63 * common log module), and additional directives, LogFormat and CustomLog.
68 * TransferLog fn Logs transfers to fn in standard log format, unless
69 * a custom format is set with LogFormat
70 * LogFormat format Set a log format from TransferLog files
72 * Log to file fn with format given by the format
75 * CookieLog fn For backwards compatability with old Cookie
76 * logging module - now deprecated.
78 * There can be any number of TransferLog and CustomLog
79 * commands. Each request will be logged to _ALL_ the
80 * named files, in the appropriate format.
82 * If no TransferLog or CustomLog directive appears in a VirtualHost,
83 * the request will be logged to the log file(s) defined outside
84 * the virtual host section. If a TransferLog or CustomLog directive
85 * appears in the VirtualHost section, the log files defined outside
86 * the VirtualHost will _not_ be used. This makes this module compatable
87 * with the CLF and config log modules, where the use of TransferLog
88 * inside the VirtualHost section overrides its use outside.
92 * TransferLog logs/access_log
94 * LogFormat "... custom format ..."
95 * TransferLog log/virtual_only
96 * CustomLog log/virtual_useragents "%t %{user-agent}i"
99 * This will log using CLF to access_log any requests handled by the
100 * main server, while any requests to the virtual host will be logged
101 * with the "... custom format..." to virtual_only _AND_ using
102 * the custom user-agent log to virtual_useragents.
104 * Note that the NCSA referer and user-agent logs are easily added with
106 * CustomLog logs/referer "%{referer}i -> %U"
107 * CustomLog logs/agent "%{user-agent}i"
109 * RefererIgnore functionality can be obtained with conditional
110 * logging (SetEnvIf and CustomLog ... env=!VAR).
112 * But using this method allows much easier modification of the
113 * log format, e.g. to log hosts along with UA:
114 * CustomLog logs/referer "%{referer}i %U %h"
116 * The argument to LogFormat and CustomLog is a string, which can include
117 * literal characters copied into the log files, and '%' directives as
120 * %...b: bytes sent, excluding HTTP headers.
121 * %...{FOOBAR}e: The contents of the environment variable FOOBAR
124 * %...a: remote IP-address
125 * %...A: local IP-address
126 * %...{Foobar}i: The contents of Foobar: header line(s) in the request
127 * sent to the client.
128 * %...l: remote logname (from identd, if supplied)
129 * %...{Foobar}n: The contents of note "Foobar" from another module.
130 * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
131 * %...p: the port the request was served to
132 * %...P: the process ID of the child that serviced the request.
133 * %...r: first line of request
134 * %...s: status. For requests that got internally redirected, this
135 * is status of the *original* request --- %...>s for the last.
136 * %...t: time, in common log format time format
137 * %...{format}t: The time, in the form given by format, which should
138 * be in strftime(3) format.
139 * %...T: the time taken to serve the request, in seconds.
140 * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
141 * %...U: the URL path requested.
142 * %...v: the configured name of the server (i.e. which virtual host?)
143 * %...V: the server name according to the UseCanonicalName setting
145 * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
146 * indicate conditions for inclusion of the item (which will cause it
147 * to be replaced with '-' if the condition is not met). Note that
148 * there is no escaping performed on the strings from %r, %...i and
149 * %...o; some with long memories may remember that I thought this was
150 * a bad idea, once upon a time, and I'm still not comfortable with
151 * it, but it is difficult to see how to "do the right thing" with all
152 * of '%..i', unless we URL-escape everything and break with CLF.
154 * The forms of condition are a list of HTTP status codes, which may
155 * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
156 * User-agent: on 400 errors and 501 errors (Bad Request, Not
157 * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
158 * requests which did *not* return some sort of normal status.
160 * The default LogFormat reproduces CLF; see below.
162 * The way this is supposed to work with virtual hosts is as follows:
163 * a virtual host can have its own LogFormat, or its own TransferLog.
164 * If it doesn't have its own LogFormat, it inherits from the main
165 * server. If it doesn't have its own TransferLog, it writes to the
166 * same descriptor (meaning the same process for "| ...").
170 #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
172 #include "ap_config.h"
174 #include "http_config.h"
175 #include "http_core.h" /* For REMOTE_NAME */
176 #include "http_log.h"
177 #include "http_protocol.h"
182 module MODULE_VAR_EXPORT config_log_module;
184 static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE);
185 #if defined(OS2) || defined(WIN32)
186 /* OS/2 dosen't support users and groups */
187 static mode_t xfer_mode = (S_IREAD | S_IWRITE);
189 static mode_t xfer_mode = (APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD);
192 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
193 * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512
194 * is guaranteed. So we'll just guess 512 in the event the system
195 * doesn't have this. Now, for file writes there is actually no limit,
196 * the entire write is atomic. Whether all systems implement this
197 * correctly is another question entirely ... so we'll just use PIPE_BUF
198 * because it's probably a good guess as to what is implemented correctly
202 #define LOG_BUFSIZE PIPE_BUF
204 #define LOG_BUFSIZE (512)
208 * multi_log_state is our per-(virtual)-server configuration. We store
209 * an array of the logs we are going to use, each of type config_log_state.
210 * If a default log format is given by LogFormat, store in default_format
211 * (backward compat. with mod_log_config). We also store for each virtual
212 * server a pointer to the logs specified for the main server, so that if this
213 * vhost has no logs defined, we can use the main server's logs instead.
215 * So, for the main server, config_logs contains a list of the log files
216 * and server_config_logs in empty. For a vhost, server_config_logs
217 * points to the same array as config_logs in the main server, and
218 * config_logs points to the array of logs defined inside this vhost,
219 * which might be empty.
223 char *default_format_string;
224 ap_array_header_t *default_format;
225 ap_array_header_t *config_logs;
226 ap_array_header_t *server_config_logs;
231 * config_log_state holds the status of a single log file. fname might
232 * be NULL, which means this module does no logging for this
233 * request. format might be NULL, in which case the default_format
234 * from the multi_log_state should be used, or if that is NULL as
235 * well, use the CLF. log_fd is NULL before the log file is opened and
236 * set to a valid fd after it is opened.
242 ap_array_header_t *format;
247 char outbuf[LOG_BUFSIZE];
253 * Note that many of these could have ap_sprintfs replaced with static buffers.
256 typedef const char *(*item_key_func) (request_rec *, char *);
263 ap_array_header_t *conditions;
266 static char *format_integer(ap_context_t *p, int i)
268 return ap_psprintf(p, "%d", i);
271 static char *pfmt(ap_context_t *p, int i)
277 return format_integer(p, i);
281 static const char *constant_item(request_rec *dummy, char *stuff)
286 static const char *log_remote_host(request_rec *r, char *a)
288 return ap_get_remote_host(r->connection, r->per_dir_config,
292 static const char *log_remote_address(request_rec *r, char *a)
294 return r->connection->remote_ip;
297 static const char *log_local_address(request_rec *r, char *a)
299 return r->connection->local_ip;
302 static const char *log_remote_logname(request_rec *r, char *a)
304 return ap_get_remote_logname(r);
307 static const char *log_remote_user(request_rec *r, char *a)
309 char *rvalue = r->user;
311 if (rvalue == NULL) {
314 else if (strlen(rvalue) == 0) {
320 static const char *log_request_line(request_rec *r, char *a)
322 /* NOTE: If the original request contained a password, we
323 * re-write the request line here to contain XXXXXX instead:
324 * (note the truncation before the protocol string for HTTP/0.9 requests)
325 * (note also that r->the_request contains the unmodified request)
327 return (r->parsed_uri.password) ? ap_pstrcat(r->pool, r->method, " ",
328 ap_unparse_uri_components(r->pool, &r->parsed_uri, 0),
329 r->assbackwards ? NULL : " ", r->protocol, NULL)
333 static const char *log_request_file(request_rec *r, char *a)
337 static const char *log_request_uri(request_rec *r, char *a)
341 static const char *log_status(request_rec *r, char *a)
343 return pfmt(r->pool, r->status);
346 static const char *log_bytes_sent(request_rec *r, char *a)
348 if (!r->sent_bodyct) {
353 ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
354 return ap_psprintf(r->pool, "%ld", bs);
358 static const char *log_header_in(request_rec *r, char *a)
360 return ap_table_get(r->headers_in, a);
363 static const char *log_header_out(request_rec *r, char *a)
365 const char *cp = ap_table_get(r->headers_out, a);
366 if (!strcasecmp(a, "Content-type") && r->content_type) {
367 cp = r->content_type;
372 return ap_table_get(r->err_headers_out, a);
375 static const char *log_note(request_rec *r, char *a)
377 return ap_table_get(r->notes, a);
379 static const char *log_env_var(request_rec *r, char *a)
381 return ap_table_get(r->subprocess_env, a);
384 static const char *log_request_time(request_rec *r, char *a)
386 ap_exploded_time_t xt;
388 char tstr[MAX_STRING_LEN];
391 hi. i think getting the time again at the end of the request
392 just for logging is dumb. i know it's "required" for CLF.
393 folks writing log parsing tools don't realise that out of order
394 times have always been possible (consider what happens if one
395 process calculates the time to log, but then there's a context
396 switch before it writes and before that process is run again the
397 log rotation occurs) and they should just fix their tools rather
398 than force the server to pay extra cpu cycles. if you've got
399 a problem with this, you can set the define. -djg
401 #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
402 ap_explode_localtime(&xt, ap_now());
404 ap_explode_localtime(&xt, r->request_time);
406 if (a && *a) { /* Custom format */
407 ap_strftime(tstr, &retcode, MAX_STRING_LEN, a, &xt);
409 else { /* CLF format */
422 ap_snprintf(tstr, sizeof(tstr), "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
423 xt.tm_mday, ap_month_snames[xt.tm_mon], xt.tm_year+1900,
424 xt.tm_hour, xt.tm_min, xt.tm_sec,
425 sign, timz / (60*60), timz % (60*60));
428 return ap_pstrdup(r->pool, tstr);
431 static const char *log_request_duration(request_rec *r, char *a)
433 return ap_psprintf(r->pool, "%ld", (ap_now() - r->request_time) / AP_USEC_PER_SEC);
436 /* These next two routines use the canonical name:port so that log
437 * parsers don't need to duplicate all the vhost parsing crud.
439 static const char *log_virtual_host(request_rec *r, char *a)
441 return r->server->server_hostname;
444 static const char *log_server_port(request_rec *r, char *a)
446 return ap_psprintf(r->pool, "%u",
447 r->server->port ? r->server->port : ap_default_port(r));
450 /* This respects the setting of UseCanonicalName so that
451 * the dynamic mass virtual hosting trick works better.
453 static const char *log_server_name(request_rec *r, char *a)
455 return ap_get_server_name(r);
458 static const char *log_child_pid(request_rec *r, char *a)
460 return ap_psprintf(r->pool, "%ld", (long) getpid());
463 /*****************************************************************
465 * Parsing the log format string
468 static struct log_item_list {
471 int want_orig_default;
472 } log_item_keys[] = {
475 'h', log_remote_host, 0
478 'a', log_remote_address, 0
481 'A', log_local_address, 0
484 'l', log_remote_logname, 0
487 'u', log_remote_user, 0
490 't', log_request_time, 0
493 'T', log_request_duration, 1
496 'r', log_request_line, 1
499 'f', log_request_file, 0
502 'U', log_request_uri, 1
508 'b', log_bytes_sent, 0
511 'i', log_header_in, 0
514 'o', log_header_out, 0
523 'V', log_server_name, 0
526 'v', log_virtual_host, 0
529 'p', log_server_port, 0
532 'P', log_child_pid, 0
539 static struct log_item_list *find_log_func(char k)
543 for (i = 0; log_item_keys[i].ch; ++i)
544 if (k == log_item_keys[i].ch) {
545 return &log_item_keys[i];
551 static char *parse_log_misc_string(ap_context_t *p, log_format_item *it,
557 it->func = constant_item;
558 it->conditions = NULL;
561 while (*s && *s != '%') {
565 * This might allocate a few chars extra if there's a backslash
566 * escape in the format string.
568 it->arg = ap_palloc(p, s - *sa + 1);
572 while (*s && *s != '%') {
595 * Allow the loop to deal with this *s in the normal
596 * fashion so that it handles end of string etc.
609 static char *parse_log_item(ap_context_t *p, log_format_item *it, const char **sa)
614 return parse_log_misc_string(p, it, sa);
618 it->condition_sense = 0;
619 it->conditions = NULL;
621 it->arg = ""; /* For safety's sake... */
625 struct log_item_list *l;
630 it->condition_sense = !it->condition_sense;
649 it->arg = ap_getword(p, &s, '}');
663 while (ap_isdigit(*++s)) {
664 i = i * 10 + (*s) - '0';
666 if (!it->conditions) {
667 it->conditions = ap_make_array(p, 4, sizeof(int));
669 *(int *) ap_push_array(it->conditions) = i;
673 l = find_log_func(*s++);
679 return ap_pstrcat(p, "Unrecognized LogFormat directive %",
683 if (it->want_orig == -1) {
684 it->want_orig = l->want_orig_default;
691 return "Ran off end of LogFormat parsing args to some directive";
694 static ap_array_header_t *parse_log_string(ap_context_t *p, const char *s, const char **err)
696 ap_array_header_t *a = ap_make_array(p, 30, sizeof(log_format_item));
700 if ((res = parse_log_item(p, (log_format_item *) ap_push_array(a), &s))) {
707 parse_log_item(p, (log_format_item *) ap_push_array(a), &s);
711 /*****************************************************************
716 static const char *process_item(request_rec *r, request_rec *orig,
717 log_format_item *item)
721 /* First, see if we need to process this thing at all... */
723 if (item->conditions && item->conditions->nelts != 0) {
725 int *conds = (int *) item->conditions->elts;
728 for (i = 0; i < item->conditions->nelts; ++i) {
729 if (r->status == conds[i]) {
735 if ((item->condition_sense && in_list)
736 || (!item->condition_sense && !in_list)) {
741 /* We do. Do it... */
743 cp = (*item->func) (item->want_orig ? orig : r, item->arg);
744 return cp ? cp : "-";
748 static void flush_log(config_log_state *cls)
750 if (cls->outcnt && cls->log_fd != NULL) {
751 ap_write(cls->log_fd, cls->outbuf, cls->outcnt);
757 static int config_log_transaction(request_rec *r, config_log_state *cls,
758 ap_array_header_t *default_format)
760 log_format_item *items;
767 ap_array_header_t *format;
770 if (cls->fname == NULL) {
775 * See if we've got any conditional envariable-controlled logging decisions
778 if (cls->condition_var != NULL) {
779 envar = cls->condition_var;
781 if (ap_table_get(r->subprocess_env, envar) == NULL) {
786 if (ap_table_get(r->subprocess_env, &envar[1]) != NULL) {
792 format = cls->format ? cls->format : default_format;
794 strs = ap_palloc(r->pool, sizeof(char *) * (format->nelts));
795 strl = ap_palloc(r->pool, sizeof(int) * (format->nelts));
796 items = (log_format_item *) format->elts;
806 for (i = 0; i < format->nelts; ++i) {
807 strs[i] = process_item(r, orig, &items[i]);
810 for (i = 0; i < format->nelts; ++i) {
811 len += strl[i] = strlen(strs[i]);
815 if (len + cls->outcnt > LOG_BUFSIZE) {
818 if (len >= LOG_BUFSIZE) {
819 str = ap_palloc(r->pool, len + 1);
820 for (i = 0, s = str; i < format->nelts; ++i) {
821 memcpy(s, strs[i], strl[i]);
824 ap_write(cls->log_fd, str, len);
827 for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) {
828 memcpy(s, strs[i], strl[i]);
834 str = ap_palloc(r->pool, len + 1);
836 for (i = 0, s = str; i < format->nelts; ++i) {
837 memcpy(s, strs[i], strl[i]);
841 ap_write(cls->log_fd, str, &len);
847 static int multi_log_transaction(request_rec *r)
849 multi_log_state *mls = ap_get_module_config(r->server->module_config,
851 config_log_state *clsarray;
855 * Log this transaction..
857 if (mls->config_logs->nelts) {
858 clsarray = (config_log_state *) mls->config_logs->elts;
859 for (i = 0; i < mls->config_logs->nelts; ++i) {
860 config_log_state *cls = &clsarray[i];
862 config_log_transaction(r, cls, mls->default_format);
865 else if (mls->server_config_logs) {
866 clsarray = (config_log_state *) mls->server_config_logs->elts;
867 for (i = 0; i < mls->server_config_logs->nelts; ++i) {
868 config_log_state *cls = &clsarray[i];
870 config_log_transaction(r, cls, mls->default_format);
877 /*****************************************************************
882 static void *make_config_log_state(ap_context_t *p, server_rec *s)
884 multi_log_state *mls;
886 mls = (multi_log_state *) ap_palloc(p, sizeof(multi_log_state));
887 mls->config_logs = ap_make_array(p, 1, sizeof(config_log_state));
888 mls->default_format_string = NULL;
889 mls->default_format = NULL;
890 mls->server_config_logs = NULL;
891 mls->formats = ap_make_table(p, 4);
892 ap_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
898 * Use the merger to simply add a pointer from the vhost log state
899 * to the log of logs specified for the non-vhost configuration. Make sure
900 * vhosts inherit any globally-defined format names.
903 static void *merge_config_log_state(ap_context_t *p, void *basev, void *addv)
905 multi_log_state *base = (multi_log_state *) basev;
906 multi_log_state *add = (multi_log_state *) addv;
908 add->server_config_logs = base->config_logs;
909 if (!add->default_format) {
910 add->default_format_string = base->default_format_string;
911 add->default_format = base->default_format;
913 add->formats = ap_overlay_tables(p, base->formats, add->formats);
919 * Set the default logfile format, or define a nickname for a format string.
921 static const char *log_format(cmd_parms *cmd, void *dummy, char *fmt,
924 const char *err_string = NULL;
925 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
929 * If we were given two arguments, the second is a name to be given to the
930 * format. This syntax just defines the nickname - it doesn't actually
931 * make the format the default.
934 parse_log_string(cmd->pool, fmt, &err_string);
935 if (err_string == NULL) {
936 ap_table_setn(mls->formats, name, fmt);
940 mls->default_format_string = fmt;
941 mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
947 static const char *add_custom_log(cmd_parms *cmd, void *dummy, char *fn,
948 char *fmt, char *envclause)
950 const char *err_string = NULL;
951 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
953 config_log_state *cls;
955 cls = (config_log_state *) ap_push_array(mls->config_logs);
956 cls->condition_var = NULL;
957 if (envclause != NULL) {
958 if (strncasecmp(envclause, "env=", 4) != 0) {
959 return "error in condition clause";
961 if ((envclause[4] == '\0')
962 || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
963 return "missing environment variable name";
965 cls->condition_var = ap_pstrdup(cmd->pool, &envclause[4]);
969 cls->format_string = fmt;
974 cls->format = parse_log_string(cmd->pool, fmt, &err_string);
981 static const char *set_transfer_log(cmd_parms *cmd, void *dummy, char *fn)
983 return add_custom_log(cmd, dummy, fn, NULL, NULL);
986 static const char *set_cookie_log(cmd_parms *cmd, void *dummy, char *fn)
988 return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
991 static const command_rec config_log_cmds[] =
993 {"CustomLog", add_custom_log, NULL, RSRC_CONF, TAKE23,
994 "a file name, a custom log format string or format name, "
995 "and an optional \"env=\" clause (see docs)"},
996 {"TransferLog", set_transfer_log, NULL, RSRC_CONF, TAKE1,
997 "the filename of the access log"},
998 {"LogFormat", log_format, NULL, RSRC_CONF, TAKE12,
999 "a log format string (see docs) and an optional format name"},
1000 {"CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1,
1001 "the filename of the cookie log"},
1005 static config_log_state *open_config_log(server_rec *s, ap_context_t *p,
1006 config_log_state *cls,
1007 ap_array_header_t *default_format)
1011 if (cls->log_fd != NULL) {
1012 return cls; /* virtual config shared w/main server */
1015 if (cls->fname == NULL) {
1016 return cls; /* Leave it NULL to decline. */
1019 if (*cls->fname == '|') {
1022 pl = ap_open_piped_log(p, cls->fname + 1);
1026 cls->log_fd = ap_piped_log_write_fd(pl);
1029 const char *fname = ap_server_root_relative(p, cls->fname);
1030 if ((status = ap_open(&cls->log_fd, fname, xfer_flags, xfer_mode, p))
1032 ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
1033 "could not open transfer log file %s.", fname);
1037 #ifdef BUFFERED_LOGS
1044 static config_log_state *open_multi_logs(server_rec *s, ap_context_t *p)
1047 multi_log_state *mls = ap_get_module_config(s->module_config,
1048 &config_log_module);
1049 config_log_state *clsarray;
1053 if (mls->default_format_string) {
1054 format = ap_table_get(mls->formats, mls->default_format_string);
1056 mls->default_format = parse_log_string(p, format, &dummy);
1060 if (!mls->default_format) {
1061 mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
1064 if (mls->config_logs->nelts) {
1065 clsarray = (config_log_state *) mls->config_logs->elts;
1066 for (i = 0; i < mls->config_logs->nelts; ++i) {
1067 config_log_state *cls = &clsarray[i];
1069 if (cls->format_string) {
1070 format = ap_table_get(mls->formats, cls->format_string);
1072 cls->format = parse_log_string(p, format, &dummy);
1076 cls = open_config_log(s, p, cls, mls->default_format);
1079 else if (mls->server_config_logs) {
1080 clsarray = (config_log_state *) mls->server_config_logs->elts;
1081 for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1082 config_log_state *cls = &clsarray[i];
1084 if (cls->format_string) {
1085 format = ap_table_get(mls->formats, cls->format_string);
1087 cls->format = parse_log_string(p, format, &dummy);
1091 cls = open_config_log(s, p, cls, mls->default_format);
1098 static void init_config_log(ap_context_t *pc, ap_context_t *p, ap_context_t *pt, server_rec *s)
1100 /* First, do "physical" server, which gets default log fd and format
1101 * for the virtual servers, if they don't override...
1104 open_multi_logs(s, p);
1106 /* Then, virtual servers */
1108 for (s = s->next; s; s = s->next) {
1109 open_multi_logs(s, p);
1111 #ifdef BUFFERED_LOGS
1112 /* Now register the last buffer flush with the cleanup engine */
1113 ap_register_cleanup(p , s, flush_all_logs, flush_all_logs);
1117 #ifdef BUFFERED_LOGS
1118 static void flush_all_logs(server_rec *s)
1120 multi_log_state *mls;
1121 ap_array_header_t *log_list;
1122 config_log_state *clsarray;
1125 for (; s; s = s->next) {
1126 mls = ap_get_module_config(s->module_config, &config_log_module);
1128 if (mls->config_logs->nelts) {
1129 log_list = mls->config_logs;
1131 else if (mls->server_config_logs) {
1132 log_list = mls->server_config_logs;
1135 clsarray = (config_log_state *) log_list->elts;
1136 for (i = 0; i < log_list->nelts; ++i) {
1137 flush_log(&clsarray[i]);
1144 static void register_hooks(void)
1146 ap_hook_open_logs(init_config_log,NULL,NULL,HOOK_MIDDLE);
1147 ap_hook_log_transaction(multi_log_transaction,NULL,NULL,HOOK_MIDDLE);
1150 module MODULE_VAR_EXPORT config_log_module =
1152 STANDARD20_MODULE_STUFF,
1153 NULL, /* create per-dir config */
1154 NULL, /* merge per-dir config */
1155 make_config_log_state, /* server config */
1156 merge_config_log_state, /* merge server config */
1157 config_log_cmds, /* command ap_table_t */
1158 NULL, /* handlers */
1159 register_hooks /* register hooks */