1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Modified by djm@va.pubnix.com:
19 * If no TransferLog is given explicitly, decline to log.
21 * This is module implements the TransferLog directive (same as the
22 * common log module), and additional directives, LogFormat and CustomLog.
27 * TransferLog fn Logs transfers to fn in standard log format, unless
28 * a custom format is set with LogFormat
29 * LogFormat format Set a log format from TransferLog files
31 * Log to file fn with format given by the format
34 * There can be any number of TransferLog and CustomLog
35 * commands. Each request will be logged to _ALL_ the
36 * named files, in the appropriate format.
38 * If no TransferLog or CustomLog directive appears in a VirtualHost,
39 * the request will be logged to the log file(s) defined outside
40 * the virtual host section. If a TransferLog or CustomLog directive
41 * appears in the VirtualHost section, the log files defined outside
42 * the VirtualHost will _not_ be used. This makes this module compatible
43 * with the CLF and config log modules, where the use of TransferLog
44 * inside the VirtualHost section overrides its use outside.
48 * TransferLog logs/access_log
50 * LogFormat "... custom format ..."
51 * TransferLog log/virtual_only
52 * CustomLog log/virtual_useragents "%t %{user-agent}i"
55 * This will log using CLF to access_log any requests handled by the
56 * main server, while any requests to the virtual host will be logged
57 * with the "... custom format..." to virtual_only _AND_ using
58 * the custom user-agent log to virtual_useragents.
60 * Note that the NCSA referer and user-agent logs are easily added with
62 * CustomLog logs/referer "%{referer}i -> %U"
63 * CustomLog logs/agent "%{user-agent}i"
65 * RefererIgnore functionality can be obtained with conditional
66 * logging (SetEnvIf and CustomLog ... env=!VAR).
68 * But using this method allows much easier modification of the
69 * log format, e.g. to log hosts along with UA:
70 * CustomLog logs/referer "%{referer}i %U %h"
72 * The argument to LogFormat and CustomLog is a string, which can include
73 * literal characters copied into the log files, and '%' directives as
76 * %...B: bytes sent, excluding HTTP headers.
77 * %...b: bytes sent, excluding HTTP headers in CLF format, i.e. a '-'
78 * when no bytes where sent (rather than a '0'.
79 * %...{FOOBAR}C: The contents of the HTTP cookie FOOBAR
80 * %...{FOOBAR}e: The contents of the environment variable FOOBAR
83 * %...a: remote IP-address
84 * %...A: local IP-address
85 * %...{Foobar}i: The contents of Foobar: header line(s) in the request
87 * %...k: number of keepalive requests served over this connection
88 * %...l: remote logname (from identd, if supplied)
89 * %...{Foobar}n: The contents of note "Foobar" from another module.
90 * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
91 * %...p: the canonical port for the server
92 * %...{format}p: the canonical port for the server, or the actual local
94 * %...P: the process ID of the child that serviced the request.
95 * %...{format}P: the process ID or thread ID of the child/thread that
96 * serviced the request
97 * %...r: first line of request
98 * %...s: status. For requests that got internally redirected, this
99 * is status of the *original* request --- %...>s for the last.
100 * %...t: time, in common log format time format
101 * %...{format}t: The time, in the form given by format, which should
102 * be in strftime(3) format.
103 * %...T: the time taken to serve the request, in seconds.
104 * %...{s}T: the time taken to serve the request, in seconds, same as %T.
105 * %...{us}T: the time taken to serve the request, in micro seconds, same as %D.
106 * %...{ms}T: the time taken to serve the request, in milliseconds.
107 * %...D: the time taken to serve the request, in micro seconds.
108 * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
109 * %...U: the URL path requested.
110 * %...v: the configured name of the server (i.e. which virtual host?)
111 * %...V: the server name according to the UseCanonicalName setting
112 * %...m: the request method
113 * %...H: the request protocol
114 * %...q: the query string prepended by "?", or empty if no query string
115 * %...X: Status of the connection.
116 * 'X' = connection aborted before the response completed.
117 * '+' = connection may be kept alive after the response is sent.
118 * '-' = connection will be closed after the response is sent.
119 * (This directive was %...c in late versions of Apache 1.3, but
120 * this conflicted with the historical ssl %...{var}c syntax.)
121 * %...L: Log-Id of the Request (or '-' if none)
122 * %...{c}L: Log-Id of the Connection (or '-' if none)
124 * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
125 * indicate conditions for inclusion of the item (which will cause it
126 * to be replaced with '-' if the condition is not met). Note that
127 * there is no escaping performed on the strings from %r, %...i and
128 * %...o; some with long memories may remember that I thought this was
129 * a bad idea, once upon a time, and I'm still not comfortable with
130 * it, but it is difficult to see how to "do the right thing" with all
131 * of '%..i', unless we URL-escape everything and break with CLF.
133 * The forms of condition are a list of HTTP status codes, which may
134 * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
135 * User-agent: on 400 errors and 501 errors (Bad Request, Not
136 * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
137 * requests which did *not* return some sort of normal status.
139 * The default LogFormat reproduces CLF; see below.
141 * The way this is supposed to work with virtual hosts is as follows:
142 * a virtual host can have its own LogFormat, or its own TransferLog.
143 * If it doesn't have its own LogFormat, it inherits from the main
144 * server. If it doesn't have its own TransferLog, it writes to the
145 * same descriptor (meaning the same process for "| ...").
149 #include "apr_strings.h"
151 #include "apr_hash.h"
152 #include "apr_optional.h"
153 #include "apr_anylock.h"
155 #define APR_WANT_STRFUNC
156 #include "apr_want.h"
158 #include "ap_config.h"
159 #include "mod_log_config.h"
161 #include "http_config.h"
162 #include "http_core.h" /* For REMOTE_NAME */
163 #include "http_log.h"
164 #include "http_protocol.h"
165 #include "util_time.h"
167 #include "ap_provider.h"
169 #if APR_HAVE_UNISTD_H
176 #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
178 module AP_MODULE_DECLARE_DATA log_config_module;
181 static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE | APR_LARGEFILE);
182 static apr_fileperms_t xfer_perms = APR_OS_DEFAULT;
183 static apr_hash_t *log_hash;
184 static apr_status_t ap_default_log_writer(request_rec *r,
190 static apr_status_t ap_buffered_log_writer(request_rec *r,
196 static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,
198 static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,
201 static ap_log_writer_init *ap_log_set_writer_init(ap_log_writer_init *handle);
202 static ap_log_writer *ap_log_set_writer(ap_log_writer *handle);
203 static ap_log_writer *log_writer = ap_default_log_writer;
204 static ap_log_writer_init *log_writer_init = ap_default_log_writer_init;
205 static int buffered_logs = 0; /* default unbuffered */
206 static apr_array_header_t *all_buffered_logs = NULL;
208 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
209 * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512
210 * is guaranteed. So we'll just guess 512 in the event the system
211 * doesn't have this. Now, for file writes there is actually no limit,
212 * the entire write is atomic. Whether all systems implement this
213 * correctly is another question entirely ... so we'll just use PIPE_BUF
214 * because it's probably a good guess as to what is implemented correctly
218 #define LOG_BUFSIZE PIPE_BUF
220 #define LOG_BUFSIZE (512)
224 * multi_log_state is our per-(virtual)-server configuration. We store
225 * an array of the logs we are going to use, each of type config_log_state.
226 * If a default log format is given by LogFormat, store in default_format
227 * (backward compat. with mod_log_config). We also store for each virtual
228 * server a pointer to the logs specified for the main server, so that if this
229 * vhost has no logs defined, we can use the main server's logs instead.
231 * So, for the main server, config_logs contains a list of the log files
232 * and server_config_logs is empty. For a vhost, server_config_logs
233 * points to the same array as config_logs in the main server, and
234 * config_logs points to the array of logs defined inside this vhost,
235 * which might be empty.
239 const char *default_format_string;
240 apr_array_header_t *default_format;
241 apr_array_header_t *config_logs;
242 apr_array_header_t *server_config_logs;
243 apr_table_t *formats;
247 * config_log_state holds the status of a single log file. fname might
248 * be NULL, which means this module does no logging for this
249 * request. format might be NULL, in which case the default_format
250 * from the multi_log_state should be used, or if that is NULL as
252 * log_writer is NULL before the log file is opened and is
253 * set to a opaque structure (usually a fd) after it is opened.
259 char outbuf[LOG_BUFSIZE];
265 const char *format_string;
266 apr_array_header_t *format;
270 ap_expr_info_t *condition_expr;
271 /** place of definition or NULL if already checked */
272 const ap_directive_t *directive;
276 * log_request_state holds request specific log data that is not
277 * part of the request_rec.
280 apr_time_t request_end_time;
285 * Note that many of these could have ap_sprintfs replaced with static buffers.
289 ap_log_handler_fn_t *func;
293 apr_array_header_t *conditions;
297 * errorlog_provider_data holds pointer to provider and its handle
298 * generated by provider initialization. It is used when logging using
299 * ap_errorlog_provider.
302 struct ap_errorlog_provider *provider;
304 } errorlog_provider_data;
307 * Type of the log_writer created by ap_default_log_writer_init.
308 * It is used by ap_default_log_writer to determine the type of
311 enum default_log_writer_type {
317 * Abstract struct to allow multiple types of log writers to be created
318 * by ap_default_log_writer_init function.
321 enum default_log_writer_type type;
323 } default_log_writer;
325 static char *pfmt(apr_pool_t *p, int i)
331 return apr_itoa(p, i);
335 static const char *constant_item(request_rec *dummy, char *stuff)
340 static const char *log_remote_host(request_rec *r, char *a)
342 const char *remote_host;
343 if (a && !strcmp(a, "c")) {
344 remote_host = ap_get_remote_host(r->connection, r->per_dir_config,
348 remote_host = ap_get_useragent_host(r, REMOTE_NAME, NULL);
350 return ap_escape_logitem(r->pool, remote_host);
353 static const char *log_remote_address(request_rec *r, char *a)
355 if (a && !strcmp(a, "c")) {
356 return r->connection->client_ip;
359 return r->useragent_ip;
363 static const char *log_local_address(request_rec *r, char *a)
365 return r->connection->local_ip;
368 static const char *log_remote_logname(request_rec *r, char *a)
370 return ap_escape_logitem(r->pool, ap_get_remote_logname(r));
373 static const char *log_remote_user(request_rec *r, char *a)
375 char *rvalue = r->user;
377 if (rvalue == NULL) {
380 else if (strlen(rvalue) == 0) {
384 rvalue = ap_escape_logitem(r->pool, rvalue);
390 static const char *log_request_line(request_rec *r, char *a)
392 /* NOTE: If the original request contained a password, we
393 * re-write the request line here to contain XXXXXX instead:
394 * (note the truncation before the protocol string for HTTP/0.9 requests)
395 * (note also that r->the_request contains the unmodified request)
397 return ap_escape_logitem(r->pool,
398 (r->parsed_uri.password)
399 ? apr_pstrcat(r->pool, r->method, " ",
400 apr_uri_unparse(r->pool,
402 r->assbackwards ? NULL : " ",
407 static const char *log_request_file(request_rec *r, char *a)
409 return ap_escape_logitem(r->pool, r->filename);
411 static const char *log_request_uri(request_rec *r, char *a)
413 return ap_escape_logitem(r->pool, r->uri);
415 static const char *log_request_method(request_rec *r, char *a)
417 return ap_escape_logitem(r->pool, r->method);
419 static const char *log_log_id(request_rec *r, char *a)
421 if (a && !strcmp(a, "c")) {
422 return r->connection->log_id ? r->connection->log_id : "-";
425 return r->log_id ? r->log_id : "-";
428 static const char *log_request_protocol(request_rec *r, char *a)
430 return ap_escape_logitem(r->pool, r->protocol);
432 static const char *log_request_query(request_rec *r, char *a)
434 return (r->args) ? apr_pstrcat(r->pool, "?",
435 ap_escape_logitem(r->pool, r->args), NULL)
438 static const char *log_status(request_rec *r, char *a)
440 return pfmt(r->pool, r->status);
443 static const char *log_handler(request_rec *r, char *a)
445 return ap_escape_logitem(r->pool, r->handler);
448 static const char *clf_log_bytes_sent(request_rec *r, char *a)
450 if (!r->sent_bodyct || !r->bytes_sent) {
454 return apr_off_t_toa(r->pool, r->bytes_sent);
458 static const char *log_bytes_sent(request_rec *r, char *a)
460 if (!r->sent_bodyct || !r->bytes_sent) {
464 return apr_off_t_toa(r->pool, r->bytes_sent);
469 static const char *log_header_in(request_rec *r, char *a)
471 return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a));
474 static const char *log_trailer_in(request_rec *r, char *a)
476 return ap_escape_logitem(r->pool, apr_table_get(r->trailers_in, a));
480 static APR_INLINE char *find_multiple_headers(apr_pool_t *pool,
481 const apr_table_t *table,
484 const apr_array_header_t *elts;
485 const apr_table_entry_t *t_elt;
486 const apr_table_entry_t *t_end;
494 elts = apr_table_elts(table);
500 t_elt = (const apr_table_entry_t *)elts->elts;
501 t_end = t_elt + elts->nelts;
503 result_list = rp = NULL;
506 if (!ap_cstr_casecmp(t_elt->key, key)) {
508 result_list = rp = apr_palloc(pool, sizeof(*rp));
511 rp = rp->next = apr_palloc(pool, sizeof(*rp));
516 rp->value = t_elt->val;
517 rp->len = strlen(rp->value);
522 } while (t_elt < t_end);
525 char *result = apr_palloc(pool, len);
530 if (rp != result_list) {
534 memcpy(cp, rp->value, rp->len);
546 static const char *log_header_out(request_rec *r, char *a)
548 const char *cp = NULL;
550 if (!ap_cstr_casecmp(a, "Content-type") && r->content_type) {
551 cp = ap_field_noparam(r->pool, r->content_type);
553 else if (!ap_cstr_casecmp(a, "Set-Cookie")) {
554 cp = find_multiple_headers(r->pool, r->headers_out, a);
557 cp = apr_table_get(r->headers_out, a);
560 return ap_escape_logitem(r->pool, cp);
563 static const char *log_trailer_out(request_rec *r, char *a)
565 return ap_escape_logitem(r->pool, apr_table_get(r->trailers_out, a));
568 static const char *log_note(request_rec *r, char *a)
570 return ap_escape_logitem(r->pool, apr_table_get(r->notes, a));
572 static const char *log_env_var(request_rec *r, char *a)
574 return ap_escape_logitem(r->pool, apr_table_get(r->subprocess_env, a));
577 static const char *log_cookie(request_rec *r, char *a)
579 const char *cookies_entry;
582 * This supports Netscape version 0 cookies while being tolerant to
583 * some properties of RFC2109/2965 version 1 cookies:
584 * - case-insensitive match of cookie names
585 * - white space between the tokens
586 * It does not support the following version 1 features:
587 * - quoted strings as cookie values
588 * - commas to separate cookies
591 if ((cookies_entry = apr_table_get(r->headers_in, "Cookie"))) {
592 char *cookie, *last1, *last2;
593 char *cookies = apr_pstrdup(r->pool, cookies_entry);
595 while ((cookie = apr_strtok(cookies, ";", &last1))) {
596 char *name = apr_strtok(cookie, "=", &last2);
597 /* last2 points to the next char following an '=' delim,
598 or the trailing NUL char of the string */
600 if (name && *name && value && *value) {
601 char *last = value - 2;
602 /* Move past leading WS */
603 name += strspn(name, " \t");
604 while (last >= name && apr_isspace(*last)) {
609 if (!ap_cstr_casecmp(name, a)) {
610 /* last1 points to the next char following the ';' delim,
611 or the trailing NUL char of the string */
612 last = last1 - (*last1 ? 2 : 1);
613 /* Move past leading WS */
614 value += strspn(value, " \t");
615 while (last >= value && apr_isspace(*last)) {
620 return ap_escape_logitem(r->pool, value);
623 /* Iterate the remaining tokens using apr_strtok(NULL, ...) */
630 static const char *log_request_time_custom(request_rec *r, char *a,
634 char tstr[MAX_STRING_LEN];
635 apr_strftime(tstr, &retcode, sizeof(tstr), a, xt);
636 return apr_pstrdup(r->pool, tstr);
639 #define DEFAULT_REQUEST_TIME_SIZE 32
642 char timestr[DEFAULT_REQUEST_TIME_SIZE];
644 } cached_request_time;
646 #define TIME_FMT_CUSTOM 0
647 #define TIME_FMT_CLF 1
648 #define TIME_FMT_ABS_SEC 2
649 #define TIME_FMT_ABS_MSEC 3
650 #define TIME_FMT_ABS_USEC 4
651 #define TIME_FMT_ABS_MSEC_FRAC 5
652 #define TIME_FMT_ABS_USEC_FRAC 6
654 #define TIME_CACHE_SIZE 4
655 #define TIME_CACHE_MASK 3
656 static cached_request_time request_time_cache[TIME_CACHE_SIZE];
658 static apr_time_t get_request_end_time(request_rec *r)
660 log_request_state *state = (log_request_state *)ap_get_module_config(r->request_config,
663 state = apr_pcalloc(r->pool, sizeof(log_request_state));
664 ap_set_module_config(r->request_config, &log_config_module, state);
666 if (state->request_end_time == 0) {
667 state->request_end_time = apr_time_now();
669 return state->request_end_time;
673 static const char *log_request_time(request_rec *r, char *a)
676 apr_time_t request_time = r->request_time;
677 int fmt_type = TIME_FMT_CUSTOM;
681 if (!strncmp(fmt, "begin", 5)) {
684 fmt_type = TIME_FMT_CLF;
686 else if (*fmt == ':') {
691 else if (!strncmp(fmt, "end", 3)) {
694 request_time = get_request_end_time(r);
695 fmt_type = TIME_FMT_CLF;
697 else if (*fmt == ':') {
700 request_time = get_request_end_time(r);
703 if (!strncmp(fmt, "msec", 4)) {
706 fmt_type = TIME_FMT_ABS_MSEC;
708 else if (!strcmp(fmt, "_frac")) {
709 fmt_type = TIME_FMT_ABS_MSEC_FRAC;
712 else if (!strncmp(fmt, "usec", 4)) {
715 fmt_type = TIME_FMT_ABS_USEC;
717 else if (!strcmp(fmt, "_frac")) {
718 fmt_type = TIME_FMT_ABS_USEC_FRAC;
721 else if (!strcmp(fmt, "sec")) {
722 fmt_type = TIME_FMT_ABS_SEC;
725 fmt_type = TIME_FMT_CLF;
729 fmt_type = TIME_FMT_CLF;
732 if (fmt_type >= TIME_FMT_ABS_SEC) { /* Absolute (micro-/milli-)second time
733 * or msec/usec fraction
735 char* buf = apr_palloc(r->pool, 20);
737 case TIME_FMT_ABS_SEC:
738 apr_snprintf(buf, 20, "%" APR_TIME_T_FMT, apr_time_sec(request_time));
740 case TIME_FMT_ABS_MSEC:
741 apr_snprintf(buf, 20, "%" APR_TIME_T_FMT, apr_time_as_msec(request_time));
743 case TIME_FMT_ABS_USEC:
744 apr_snprintf(buf, 20, "%" APR_TIME_T_FMT, request_time);
746 case TIME_FMT_ABS_MSEC_FRAC:
747 apr_snprintf(buf, 20, "%03" APR_TIME_T_FMT, apr_time_msec(request_time));
749 case TIME_FMT_ABS_USEC_FRAC:
750 apr_snprintf(buf, 20, "%06" APR_TIME_T_FMT, apr_time_usec(request_time));
757 else if (fmt_type == TIME_FMT_CUSTOM) { /* Custom format */
758 /* The custom time formatting uses a very large temp buffer
759 * on the stack. To avoid using so much stack space in the
760 * common case where we're not using a custom format, the code
761 * for the custom format in a separate function. (That's why
762 * log_request_time_custom is not inlined right here.)
764 ap_explode_recent_localtime(&xt, request_time);
765 return log_request_time_custom(r, a, &xt);
767 else { /* CLF format */
768 /* This code uses the same technique as ap_explode_recent_localtime():
769 * optimistic caching with logic to detect and correct race conditions.
770 * See the comments in server/util_time.c for more information.
772 cached_request_time* cached_time = apr_palloc(r->pool,
773 sizeof(*cached_time));
774 unsigned t_seconds = (unsigned)apr_time_sec(request_time);
775 unsigned i = t_seconds & TIME_CACHE_MASK;
776 *cached_time = request_time_cache[i];
777 if ((t_seconds != cached_time->t) ||
778 (t_seconds != cached_time->t_validate)) {
780 /* Invalid or old snapshot, so compute the proper time string
781 * and store it in the cache
786 ap_explode_recent_localtime(&xt, request_time);
795 cached_time->t = t_seconds;
796 apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE,
797 "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
798 xt.tm_mday, apr_month_snames[xt.tm_mon],
799 xt.tm_year+1900, xt.tm_hour, xt.tm_min, xt.tm_sec,
800 sign, timz / (60*60), (timz % (60*60)) / 60);
801 cached_time->t_validate = t_seconds;
802 request_time_cache[i] = *cached_time;
804 return cached_time->timestr;
808 static const char *log_request_duration_microseconds(request_rec *r, char *a)
810 return apr_psprintf(r->pool, "%" APR_TIME_T_FMT,
811 (get_request_end_time(r) - r->request_time));
814 static const char *log_request_duration_scaled(request_rec *r, char *a)
816 apr_time_t duration = get_request_end_time(r) - r->request_time;
817 if (*a == '\0' || !strcasecmp(a, "s")) {
818 duration = apr_time_sec(duration);
820 else if (!strcasecmp(a, "ms")) {
821 duration = apr_time_as_msec(duration);
823 else if (!strcasecmp(a, "us")) {
829 return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, duration);
832 /* These next two routines use the canonical name:port so that log
833 * parsers don't need to duplicate all the vhost parsing crud.
835 static const char *log_virtual_host(request_rec *r, char *a)
837 return ap_escape_logitem(r->pool, r->server->server_hostname);
840 static const char *log_server_port(request_rec *r, char *a)
844 if (*a == '\0' || !strcasecmp(a, "canonical")) {
845 port = r->server->port ? r->server->port : ap_default_port(r);
847 else if (!strcasecmp(a, "remote")) {
848 port = r->useragent_addr->port;
850 else if (!strcasecmp(a, "local")) {
851 port = r->connection->local_addr->port;
857 return apr_itoa(r->pool, (int)port);
860 /* This respects the setting of UseCanonicalName so that
861 * the dynamic mass virtual hosting trick works better.
863 static const char *log_server_name(request_rec *r, char *a)
865 return ap_escape_logitem(r->pool, ap_get_server_name(r));
868 static const char *log_pid_tid(request_rec *r, char *a)
870 if (*a == '\0' || !strcasecmp(a, "pid")) {
871 return ap_append_pid(r->pool, "", "");
873 else if (!strcasecmp(a, "tid") || !strcasecmp(a, "hextid")) {
875 apr_os_thread_t tid = apr_os_thread_current();
877 int tid = 0; /* APR will format "0" anyway but an arg is needed */
879 return apr_psprintf(r->pool,
880 #if APR_MAJOR_VERSION > 1 || (APR_MAJOR_VERSION == 1 && APR_MINOR_VERSION >= 2)
881 /* APR can format a thread id in hex */
882 *a == 'h' ? "%pt" : "%pT",
884 /* APR is missing the feature, so always use decimal */
893 static const char *log_connection_status(request_rec *r, char *a)
895 if (r->connection->aborted)
898 if (r->connection->keepalive == AP_CONN_KEEPALIVE &&
899 (!r->server->keep_alive_max ||
900 (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
906 static const char *log_requests_on_connection(request_rec *r, char *a)
908 int num = r->connection->keepalives ? r->connection->keepalives - 1 : 0;
909 return apr_itoa(r->pool, num);
912 /*****************************************************************
914 * Parsing the log format string
917 static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
923 it->func = constant_item;
924 it->conditions = NULL;
927 while (*s && *s != '%') {
931 * This might allocate a few chars extra if there's a backslash
932 * escape in the format string.
934 it->arg = apr_palloc(p, s - *sa + 1);
938 while (*s && *s != '%') {
965 * Allow the loop to deal with this *s in the normal
966 * fashion so that it handles end of string etc.
979 static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
982 ap_log_handler *handler = NULL;
985 return parse_log_misc_string(p, it, sa);
989 it->condition_sense = 0;
990 it->conditions = NULL;
994 it->func = constant_item;
1001 it->arg = ""; /* For safety's sake... */
1009 it->condition_sense = !it->condition_sense;
1028 it->arg = ap_getword(p, &s, '}');
1042 while (apr_isdigit(*++s)) {
1043 i = i * 10 + (*s) - '0';
1045 if (!it->conditions) {
1046 it->conditions = apr_array_make(p, 4, sizeof(int));
1048 *(int *) apr_array_push(it->conditions) = i;
1052 /* check for '^' + two character format first */
1053 if (*s == '^' && *(s+1) && *(s+2)) {
1054 handler = (ap_log_handler *)apr_hash_get(log_hash, s, 3);
1060 handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
1067 return apr_pstrcat(p, "Unrecognized LogFormat directive %",
1070 it->func = handler->func;
1071 if (it->want_orig == -1) {
1072 it->want_orig = handler->want_orig_default;
1079 return "Ran off end of LogFormat parsing args to some directive";
1082 static apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
1084 apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
1088 if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
1095 parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
1099 /*****************************************************************
1104 static const char *process_item(request_rec *r, request_rec *orig,
1105 log_format_item *item)
1109 /* First, see if we need to process this thing at all... */
1111 if (item->conditions && item->conditions->nelts != 0) {
1113 int *conds = (int *) item->conditions->elts;
1116 for (i = 0; i < item->conditions->nelts; ++i) {
1117 if (r->status == conds[i]) {
1123 if ((item->condition_sense && in_list)
1124 || (!item->condition_sense && !in_list)) {
1129 /* We do. Do it... */
1131 cp = (*item->func) (item->want_orig ? orig : r, item->arg);
1132 return cp ? cp : "-";
1135 static void flush_log(buffered_log *buf)
1137 if (buf->outcnt && buf->handle != NULL) {
1138 /* XXX: error handling */
1139 apr_file_write_full(buf->handle, buf->outbuf, buf->outcnt, NULL);
1145 static int config_log_transaction(request_rec *r, config_log_state *cls,
1146 apr_array_header_t *default_format)
1148 log_format_item *items;
1154 apr_array_header_t *format;
1158 if (cls->fname == NULL) {
1163 * See if we've got any conditional envariable-controlled logging decisions
1166 if (cls->condition_var != NULL) {
1167 envar = cls->condition_var;
1168 if (*envar != '!') {
1169 if (apr_table_get(r->subprocess_env, envar) == NULL) {
1174 if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
1179 else if (cls->condition_expr != NULL) {
1181 int rc = ap_expr_exec(r, cls->condition_expr, &err);
1183 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00644)
1184 "Error evaluating log condition: %s", err);
1189 format = cls->format ? cls->format : default_format;
1191 strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
1192 strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
1193 items = (log_format_item *) format->elts;
1196 while (orig->prev) {
1203 for (i = 0; i < format->nelts; ++i) {
1204 strs[i] = process_item(r, orig, &items[i]);
1207 for (i = 0; i < format->nelts; ++i) {
1208 len += strl[i] = strlen(strs[i]);
1211 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00645)
1212 "log writer isn't correctly setup");
1213 return HTTP_INTERNAL_SERVER_ERROR;
1215 rv = log_writer(r, cls->log_writer, strs, strl, format->nelts, len);
1216 if (rv != APR_SUCCESS) {
1217 ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, APLOGNO(00646)
1218 "Error writing to %s", cls->fname);
1223 static int multi_log_transaction(request_rec *r)
1225 multi_log_state *mls = ap_get_module_config(r->server->module_config,
1226 &log_config_module);
1227 config_log_state *clsarray;
1231 * Initialize per request state
1233 log_request_state *state = apr_pcalloc(r->pool, sizeof(log_request_state));
1234 ap_set_module_config(r->request_config, &log_config_module, state);
1237 * Log this transaction..
1239 if (mls->config_logs->nelts) {
1240 clsarray = (config_log_state *) mls->config_logs->elts;
1241 for (i = 0; i < mls->config_logs->nelts; ++i) {
1242 config_log_state *cls = &clsarray[i];
1244 config_log_transaction(r, cls, mls->default_format);
1248 if (mls->server_config_logs) {
1249 clsarray = (config_log_state *) mls->server_config_logs->elts;
1250 for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1251 config_log_state *cls = &clsarray[i];
1253 if (cls->inherit || !mls->config_logs->nelts) {
1254 config_log_transaction(r, cls, mls->default_format);
1262 /*****************************************************************
1267 static void *make_config_log_state(apr_pool_t *p, server_rec *s)
1269 multi_log_state *mls;
1271 mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
1272 mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
1273 mls->default_format_string = NULL;
1274 mls->default_format = NULL;
1275 mls->server_config_logs = NULL;
1276 mls->formats = apr_table_make(p, 4);
1277 apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
1283 * Use the merger to simply add a pointer from the vhost log state
1284 * to the log of logs specified for the non-vhost configuration. Make sure
1285 * vhosts inherit any globally-defined format names.
1288 static void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
1290 multi_log_state *base = (multi_log_state *) basev;
1291 multi_log_state *add = (multi_log_state *) addv;
1293 add->server_config_logs = base->config_logs;
1294 if (!add->default_format) {
1295 add->default_format_string = base->default_format_string;
1296 add->default_format = base->default_format;
1298 add->formats = apr_table_overlay(p, base->formats, add->formats);
1304 * Set the default logfile format, or define a nickname for a format string.
1306 static const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
1309 const char *err_string = NULL;
1310 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1311 &log_config_module);
1314 * If we were given two arguments, the second is a name to be given to the
1315 * format. This syntax just defines the nickname - it doesn't actually
1316 * make the format the default.
1319 parse_log_string(cmd->pool, fmt, &err_string);
1320 if (err_string == NULL) {
1321 apr_table_setn(mls->formats, name, fmt);
1325 mls->default_format_string = fmt;
1326 mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
1332 static const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
1333 const char *fmt, const char *envclause)
1335 const char *err_string = NULL;
1336 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1337 &log_config_module);
1338 config_log_state *cls;
1340 cls = (config_log_state *) apr_array_push(mls->config_logs);
1341 cls->condition_var = NULL;
1342 cls->condition_expr = NULL;
1343 if (envclause != NULL) {
1344 if (strncasecmp(envclause, "env=", 4) == 0) {
1345 if ((envclause[4] == '\0')
1346 || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
1347 return "missing environment variable name";
1349 cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
1351 else if (strncasecmp(envclause, "expr=", 5) == 0) {
1353 if ((envclause[5] == '\0'))
1354 return "missing condition";
1355 cls->condition_expr = ap_expr_parse_cmd(cmd, &envclause[5],
1356 AP_EXPR_FLAG_DONT_VARY,
1362 return "error in condition clause";
1367 cls->format_string = fmt;
1368 cls->directive = cmd->directive;
1373 cls->format = parse_log_string(cmd->pool, fmt, &err_string);
1375 cls->log_writer = NULL;
1380 static const char *add_global_log(cmd_parms *cmd, void *dummy, const char *fn,
1381 const char *fmt, const char *envclause) {
1382 multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
1383 &log_config_module);
1384 config_log_state *clsarray;
1385 config_log_state *cls;
1388 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1394 /* Add a custom log through the normal channel */
1395 ret = add_custom_log(cmd, dummy, fn, fmt, envclause);
1397 /* Set the inherit flag unless there was some error */
1399 clsarray = (config_log_state*)mls->config_logs->elts;
1400 cls = &clsarray[mls->config_logs->nelts-1];
1407 static const char *set_transfer_log(cmd_parms *cmd, void *dummy,
1410 return add_custom_log(cmd, dummy, fn, NULL, NULL);
1413 static const char *set_buffered_logs_on(cmd_parms *parms, void *dummy, int flag)
1415 buffered_logs = flag;
1416 if (buffered_logs) {
1417 ap_log_set_writer_init(ap_buffered_log_writer_init);
1418 ap_log_set_writer(ap_buffered_log_writer);
1421 ap_log_set_writer_init(ap_default_log_writer_init);
1422 ap_log_set_writer(ap_default_log_writer);
1426 static const command_rec config_log_cmds[] =
1428 AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
1429 "a file name, a custom log format string or format name, "
1430 "and an optional \"env=\" or \"expr=\" clause (see docs)"),
1431 AP_INIT_TAKE23("GlobalLog", add_global_log, NULL, RSRC_CONF,
1432 "Same as CustomLog, but forces virtualhosts to inherit the log"),
1433 AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
1434 "the filename of the access log"),
1435 AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
1436 "a log format string (see docs) and an optional format name"),
1437 AP_INIT_FLAG("BufferedLogs", set_buffered_logs_on, NULL, RSRC_CONF,
1438 "Enable Buffered Logging (experimental)"),
1442 static config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
1443 config_log_state *cls,
1444 apr_array_header_t *default_format)
1446 if (cls->log_writer != NULL) {
1447 return cls; /* virtual config shared w/main server */
1450 if (cls->fname == NULL) {
1451 return cls; /* Leave it NULL to decline. */
1454 cls->log_writer = log_writer_init(p, s, cls->fname);
1455 if (cls->log_writer == NULL)
1461 static int open_multi_logs(server_rec *s, apr_pool_t *p)
1464 multi_log_state *mls = ap_get_module_config(s->module_config,
1465 &log_config_module);
1466 config_log_state *clsarray;
1470 if (mls->default_format_string) {
1471 format = apr_table_get(mls->formats, mls->default_format_string);
1473 mls->default_format = parse_log_string(p, format, &dummy);
1477 if (!mls->default_format) {
1478 mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
1481 if (mls->config_logs->nelts) {
1482 clsarray = (config_log_state *) mls->config_logs->elts;
1483 for (i = 0; i < mls->config_logs->nelts; ++i) {
1484 config_log_state *cls = &clsarray[i];
1486 if (cls->format_string) {
1487 format = apr_table_get(mls->formats, cls->format_string);
1489 cls->format = parse_log_string(p, format, &dummy);
1493 if (!open_config_log(s, p, cls, mls->default_format)) {
1494 /* Failure already logged by open_config_log */
1499 else if (mls->server_config_logs) {
1500 clsarray = (config_log_state *) mls->server_config_logs->elts;
1501 for (i = 0; i < mls->server_config_logs->nelts; ++i) {
1502 config_log_state *cls = &clsarray[i];
1504 if (cls->format_string) {
1505 format = apr_table_get(mls->formats, cls->format_string);
1507 cls->format = parse_log_string(p, format, &dummy);
1511 if (!open_config_log(s, p, cls, mls->default_format)) {
1512 /* Failure already logged by open_config_log */
1522 static apr_status_t flush_all_logs(void *data)
1524 server_rec *s = data;
1525 multi_log_state *mls;
1526 apr_array_header_t *log_list;
1527 config_log_state *clsarray;
1534 for (; s; s = s->next) {
1535 mls = ap_get_module_config(s->module_config, &log_config_module);
1537 if (mls->config_logs->nelts) {
1538 log_list = mls->config_logs;
1540 else if (mls->server_config_logs) {
1541 log_list = mls->server_config_logs;
1544 clsarray = (config_log_state *) log_list->elts;
1545 for (i = 0; i < log_list->nelts; ++i) {
1546 buf = clsarray[i].log_writer;
1555 static int init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, server_rec *s)
1559 /* First init the buffered logs array, which is needed when opening the logs. */
1560 if (buffered_logs) {
1561 all_buffered_logs = apr_array_make(p, 5, sizeof(buffered_log *));
1564 /* Next, do "physical" server, which gets default log fd and format
1565 * for the virtual servers, if they don't override...
1567 res = open_multi_logs(s, p);
1569 /* Then, virtual servers */
1571 for (s = s->next; (res == OK) && s; s = s->next) {
1572 res = open_multi_logs(s, p);
1578 static void init_child(apr_pool_t *p, server_rec *s)
1582 ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
1584 /* Now register the last buffer flush with the cleanup engine */
1585 if (buffered_logs) {
1587 buffered_log **array = (buffered_log **)all_buffered_logs->elts;
1589 apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
1591 for (i = 0; i < all_buffered_logs->nelts; i++) {
1592 buffered_log *this = array[i];
1595 if (mpm_threads > 1) {
1598 this->mutex.type = apr_anylock_threadmutex;
1599 rv = apr_thread_mutex_create(&this->mutex.lock.tm,
1600 APR_THREAD_MUTEX_DEFAULT,
1602 if (rv != APR_SUCCESS) {
1603 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(00647)
1604 "could not initialize buffered log mutex, "
1605 "transfer log may become corrupted");
1606 this->mutex.type = apr_anylock_none;
1612 this->mutex.type = apr_anylock_none;
1618 static void ap_register_log_handler(apr_pool_t *p, char *tag,
1619 ap_log_handler_fn_t *handler, int def)
1621 ap_log_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
1622 log_struct->func = handler;
1623 log_struct->want_orig_default = def;
1625 apr_hash_set(log_hash, tag, strlen(tag), (const void *)log_struct);
1627 static ap_log_writer_init *ap_log_set_writer_init(ap_log_writer_init *handle)
1629 ap_log_writer_init *old = log_writer_init;
1630 log_writer_init = handle;
1635 static ap_log_writer *ap_log_set_writer(ap_log_writer *handle)
1637 ap_log_writer *old = log_writer;
1638 log_writer = handle;
1643 static apr_status_t ap_default_log_writer( request_rec *r,
1651 default_log_writer *log_writer = handle;
1658 * We do this memcpy dance because write() is atomic for len < PIPE_BUF,
1659 * while writev() need not be.
1661 str = apr_palloc(r->pool, len + 1);
1663 for (i = 0, s = str; i < nelts; ++i) {
1664 memcpy(s, strs[i], strl[i]);
1668 if (log_writer->type == LOG_WRITER_FD) {
1669 rv = apr_file_write_full((apr_file_t*)log_writer->log_writer, str,
1673 errorlog_provider_data *data = log_writer->log_writer;
1674 ap_errorlog_info info;
1677 info.c = r->connection;
1678 info.pool = r->pool;
1682 info.using_provider = 1;
1685 rv = data->provider->writer(&info, data->handle,
1691 static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,
1694 default_log_writer *log_writer;
1695 const char *provider_name = name;
1696 ap_errorlog_provider *provider = NULL;
1699 /* We support *Log "errorlog_provider:arg" syntax now, so get the provider
1700 * name from the name. */
1701 if ((sep = ap_strchr_c(name, ':')) != NULL) {
1702 provider_name = apr_pstrmemdup(p, name, sep - name);
1709 pl = ap_open_piped_log(p, name + 1);
1714 log_writer = apr_pcalloc(p, sizeof(default_log_writer));
1715 log_writer->type = LOG_WRITER_FD;
1716 log_writer->log_writer = ap_piped_log_write_fd(pl);
1717 if (!log_writer->log_writer) {
1722 else if ((provider = ap_lookup_provider(AP_ERRORLOG_PROVIDER_GROUP,
1723 provider_name, AP_ERRORLOG_PROVIDER_VERSION)) != NULL) {
1724 void *provider_handle;
1725 errorlog_provider_data *provider_data;
1727 provider_handle = provider->init(p, s);
1728 if (!provider_handle) {
1729 /* provider must log something to the console */
1733 provider_data = apr_pcalloc(p, sizeof(errorlog_provider_data));
1734 provider_data->provider = provider;
1735 provider_data->handle = provider_handle;
1737 log_writer = apr_pcalloc(p, sizeof(default_log_writer));
1738 log_writer->type = LOG_WRITER_PROVIDER;
1739 log_writer->log_writer = provider_data;
1743 const char *fname = ap_server_root_relative(p, name);
1748 ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s, APLOGNO(00648)
1749 "invalid transfer log path %s.", name);
1752 rv = apr_file_open(&fd, fname, xfer_flags, xfer_perms, p);
1753 if (rv != APR_SUCCESS) {
1754 ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO(00649)
1755 "could not open transfer log file %s.", fname);
1759 log_writer = apr_pcalloc(p, sizeof(default_log_writer));
1760 log_writer->type = LOG_WRITER_FD;
1761 log_writer->log_writer = fd;
1765 static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,
1769 b = apr_pcalloc(p, sizeof(buffered_log));
1770 b->handle = ap_default_log_writer_init(p, s, name);
1773 *(buffered_log **)apr_array_push(all_buffered_logs) = b;
1779 static apr_status_t ap_buffered_log_writer(request_rec *r,
1791 buffered_log *buf = (buffered_log*)handle;
1793 if ((rv = APR_ANYLOCK_LOCK(&buf->mutex)) != APR_SUCCESS) {
1797 if (len + buf->outcnt > LOG_BUFSIZE) {
1800 if (len >= LOG_BUFSIZE) {
1804 * We do this memcpy dance because write() is atomic for
1805 * len < PIPE_BUF, while writev() need not be.
1807 str = apr_palloc(r->pool, len + 1);
1808 for (i = 0, s = str; i < nelts; ++i) {
1809 memcpy(s, strs[i], strl[i]);
1813 rv = apr_file_write_full(buf->handle, str, w, NULL);
1817 for (i = 0, s = &buf->outbuf[buf->outcnt]; i < nelts; ++i) {
1818 memcpy(s, strs[i], strl[i]);
1825 APR_ANYLOCK_UNLOCK(&buf->mutex);
1829 static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
1831 static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
1833 log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
1835 if (log_pfn_register) {
1836 log_pfn_register(p, "h", log_remote_host, 0);
1837 log_pfn_register(p, "a", log_remote_address, 0 );
1838 log_pfn_register(p, "A", log_local_address, 0 );
1839 log_pfn_register(p, "l", log_remote_logname, 0);
1840 log_pfn_register(p, "u", log_remote_user, 0);
1841 log_pfn_register(p, "t", log_request_time, 0);
1842 log_pfn_register(p, "f", log_request_file, 0);
1843 log_pfn_register(p, "b", clf_log_bytes_sent, 0);
1844 log_pfn_register(p, "B", log_bytes_sent, 0);
1845 log_pfn_register(p, "i", log_header_in, 0);
1846 log_pfn_register(p, "o", log_header_out, 0);
1847 log_pfn_register(p, "n", log_note, 0);
1848 log_pfn_register(p, "L", log_log_id, 1);
1849 log_pfn_register(p, "e", log_env_var, 0);
1850 log_pfn_register(p, "V", log_server_name, 0);
1851 log_pfn_register(p, "v", log_virtual_host, 0);
1852 log_pfn_register(p, "p", log_server_port, 0);
1853 log_pfn_register(p, "P", log_pid_tid, 0);
1854 log_pfn_register(p, "H", log_request_protocol, 0);
1855 log_pfn_register(p, "m", log_request_method, 0);
1856 log_pfn_register(p, "q", log_request_query, 0);
1857 log_pfn_register(p, "X", log_connection_status, 0);
1858 log_pfn_register(p, "C", log_cookie, 0);
1859 log_pfn_register(p, "k", log_requests_on_connection, 0);
1860 log_pfn_register(p, "r", log_request_line, 1);
1861 log_pfn_register(p, "D", log_request_duration_microseconds, 1);
1862 log_pfn_register(p, "T", log_request_duration_scaled, 1);
1863 log_pfn_register(p, "U", log_request_uri, 1);
1864 log_pfn_register(p, "s", log_status, 1);
1865 log_pfn_register(p, "R", log_handler, 1);
1867 log_pfn_register(p, "^ti", log_trailer_in, 0);
1868 log_pfn_register(p, "^to", log_trailer_out, 0);
1871 /* reset to default conditions */
1872 ap_log_set_writer_init(ap_default_log_writer_init);
1873 ap_log_set_writer(ap_default_log_writer);
1879 static int check_log_dir(apr_pool_t *p, server_rec *s, config_log_state *cls)
1881 if (!cls->fname || cls->fname[0] == '|' || !cls->directive) {
1885 char *abs = ap_server_root_relative(p, cls->fname);
1886 char *dir = ap_make_dirstr_parent(p, abs);
1888 const ap_directive_t *directive = cls->directive;
1889 apr_status_t rv = apr_stat(&finfo, dir, APR_FINFO_TYPE, p);
1890 cls->directive = NULL; /* Don't check this config_log_state again */
1891 if (rv == APR_SUCCESS && finfo.filetype != APR_DIR)
1893 if (rv != APR_SUCCESS) {
1894 ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, rv, s,
1896 "Cannot access directory '%s' for log file '%s' "
1897 "defined at %s:%d", dir, cls->fname,
1898 directive->filename, directive->line_num);
1905 static int log_check_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
1909 multi_log_state *mls = ap_get_module_config(s->module_config,
1910 &log_config_module);
1912 * We don't need to check mls->server_config_logs because it just
1913 * points to the parent server's mls->config_logs.
1915 apr_array_header_t *log_list = mls->config_logs;
1916 config_log_state *clsarray = (config_log_state *) log_list->elts;
1918 for (i = 0; i < log_list->nelts; ++i) {
1919 if (check_log_dir(ptemp, s, &clsarray[i]) != OK)
1928 static void register_hooks(apr_pool_t *p)
1930 ap_hook_pre_config(log_pre_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
1931 ap_hook_check_config(log_check_config,NULL,NULL,APR_HOOK_MIDDLE);
1932 ap_hook_child_init(init_child,NULL,NULL,APR_HOOK_MIDDLE);
1933 ap_hook_open_logs(init_config_log,NULL,NULL,APR_HOOK_MIDDLE);
1934 ap_hook_log_transaction(multi_log_transaction,NULL,NULL,APR_HOOK_MIDDLE);
1936 /* Init log_hash before we register the optional function. It is
1937 * possible for the optional function, ap_register_log_handler,
1938 * to be called before any other mod_log_config hooks are called.
1939 * As a policy, we should init everything required by an optional function
1940 * before calling APR_REGISTER_OPTIONAL_FN.
1942 log_hash = apr_hash_make(p);
1943 APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
1944 APR_REGISTER_OPTIONAL_FN(ap_log_set_writer_init);
1945 APR_REGISTER_OPTIONAL_FN(ap_log_set_writer);
1948 AP_DECLARE_MODULE(log_config) =
1950 STANDARD20_MODULE_STUFF,
1951 NULL, /* create per-dir config */
1952 NULL, /* merge per-dir config */
1953 make_config_log_state, /* server config */
1954 merge_config_log_state, /* merge server config */
1955 config_log_cmds, /* command apr_table_t */
1956 register_hooks /* register hooks */