mod_log_config.c revision 11a7b0dff22d26770b532c174d1cf2e7b56ec244
842ae4bd224140319ae7feec1872b93dfd491143fielding/* ====================================================================
842ae4bd224140319ae7feec1872b93dfd491143fielding * The Apache Software License, Version 1.1
842ae4bd224140319ae7feec1872b93dfd491143fielding * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
842ae4bd224140319ae7feec1872b93dfd491143fielding * reserved.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Redistribution and use in source and binary forms, with or without
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * modification, are permitted provided that the following conditions
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * 1. Redistributions of source code must retain the above copyright
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * notice, this list of conditions and the following disclaimer.
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * 2. Redistributions in binary form must reproduce the above copyright
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * notice, this list of conditions and the following disclaimer in
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the documentation and/or other materials provided with the
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh * distribution.
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh * 3. The end-user documentation included with the redistribution,
9d129b55f5a43abf43865c6b0eb6dd19bc22aba8ianh * if any, must include the following acknowledgment:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * "This product includes software developed by the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Apache Software Foundation (http://www.apache.org/)."
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * Alternately, this acknowledgment may appear in the software itself,
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * if and wherever such third-party acknowledgments normally appear.
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * 4. The names "Apache" and "Apache Software Foundation" must
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * not be used to endorse or promote products derived from this
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * software without prior written permission. For written
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * permission, please contact apache@apache.org.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * 5. Products derived from this software may not be called "Apache",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * nor may "Apache" appear in their name, without prior written
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * permission of the Apache Software Foundation.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
51af95bb51b5084e883bad250b2afa2838e9ceebfielding * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
d4f1d9c1ff112a8ab9bee31f196973761329b236rbb * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
36ef8f77bffe75d1aa327882be1b5bdbe2ff567asf * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36ef8f77bffe75d1aa327882be1b5bdbe2ff567asf * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * SUCH DAMAGE.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * ====================================================================
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This software consists of voluntary contributions made by many
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * individuals on behalf of the Apache Software Foundation. For more
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * information on the Apache Software Foundation, please see
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Portions of this software are based upon public domain software
785be1b6298010956622771c870ab3cd8ca57a2faaron * originally written at the National Center for Supercomputing Applications,
785be1b6298010956622771c870ab3cd8ca57a2faaron * University of Illinois, Urbana-Champaign.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Modified by djm@va.pubnix.com:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * If no TransferLog is given explicitly, decline to log.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This is module implements the TransferLog directive (same as the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * common log module), and additional directives, LogFormat and CustomLog.
785be1b6298010956622771c870ab3cd8ca57a2faaron * TransferLog fn Logs transfers to fn in standard log format, unless
785be1b6298010956622771c870ab3cd8ca57a2faaron * a custom format is set with LogFormat
785be1b6298010956622771c870ab3cd8ca57a2faaron * LogFormat format Set a log format from TransferLog files
7697b1b7376a532163c621e050b70c90dcb15d66covener * CustomLog fn format
7697b1b7376a532163c621e050b70c90dcb15d66covener * Log to file fn with format given by the format
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton * CookieLog fn For backwards compatability with old Cookie
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * logging module - now deprecated.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * There can be any number of TransferLog and CustomLog
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * commands. Each request will be logged to _ALL_ the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * named files, in the appropriate format.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * If no TransferLog or CustomLog directive appears in a VirtualHost,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the request will be logged to the log file(s) defined outside
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the virtual host section. If a TransferLog or CustomLog directive
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * appears in the VirtualHost section, the log files defined outside
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the VirtualHost will _not_ be used. This makes this module compatable
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * with the CLF and config log modules, where the use of TransferLog
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * inside the VirtualHost section overrides its use outside.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Examples:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * TransferLog logs/access_log
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * <VirtualHost>
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * LogFormat "... custom format ..."
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * TransferLog log/virtual_only
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * CustomLog log/virtual_useragents "%t %{user-agent}i"
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier * </VirtualHost>
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier * This will log using CLF to access_log any requests handled by the
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier * main server, while any requests to the virtual host will be logged
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier * with the "... custom format..." to virtual_only _AND_ using
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier * the custom user-agent log to virtual_useragents.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Note that the NCSA referer and user-agent logs are easily added with
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * CustomLog:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * CustomLog logs/referer "%{referer}i -> %U"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * CustomLog logs/agent "%{user-agent}i"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * RefererIgnore functionality can be obtained with conditional
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * logging (SetEnvIf and CustomLog ... env=!VAR).
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * But using this method allows much easier modification of the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * log format, e.g. to log hosts along with UA:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * CustomLog logs/referer "%{referer}i %U %h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * The argument to LogFormat and CustomLog is a string, which can include
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * literal characters copied into the log files, and '%' directives as
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...B: bytes sent, excluding HTTP headers.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...b: bytes sent, excluding HTTP headers in CLF format, i.e. a '-'
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * when no bytes where sent (rather than a '0'.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...c: Status of the connection.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * 'X' = connection aborted before the response completed.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * '+' = connection may be kept alive after the response is sent.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * '-' = connection will be closed after the response is sent.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...{FOOBAR}C: The contents of the HTTP cookie FOOBAR
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard * %...{FOOBAR}e: The contents of the environment variable FOOBAR
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...f: filename
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...h: remote host
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...a: remote IP-address
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier * %...A: local IP-address
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...{Foobar}i: The contents of Foobar: header line(s) in the request
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * sent to the client.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...l: remote logname (from identd, if supplied)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...{Foobar}n: The contents of note "Foobar" from another module.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...p: the port the request was served to
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...P: the process ID of the child that serviced the request.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...r: first line of request
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...s: status. For requests that got internally redirected, this
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * is status of the *original* request --- %...>s for the last.
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * %...t: time, in common log format time format
e8f95a682820a599fe41b22977010636be5c2717jim * %...{format}t: The time, in the form given by format, which should
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * be in strftime(3) format.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...T: the time taken to serve the request, in seconds.
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick * %...D: the time taken to serve the request, in micro seconds.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * %...U: the URL path requested.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * %...v: the configured name of the server (i.e. which virtual host?)
58fd79b56eb624bf011772994e9761d3c2e228c1orlikowski * %...V: the server name according to the UseCanonicalName setting
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * %...m: the request method
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick * %...H: the request protocol
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * %...q: the query string prepended by "?", or empty if no query string
785be1b6298010956622771c870ab3cd8ca57a2faaron * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * indicate conditions for inclusion of the item (which will cause it
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * to be replaced with '-' if the condition is not met). Note that
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * there is no escaping performed on the strings from %r, %...i and
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * %...o; some with long memories may remember that I thought this was
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * a bad idea, once upon a time, and I'm still not comfortable with
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * it, but it is difficult to see how to "do the right thing" with all
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * of '%..i', unless we URL-escape everything and break with CLF.
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * The forms of condition are a list of HTTP status codes, which may
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * User-agent: on 400 errors and 501 errors (Bad Request, Not
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * requests which did *not* return some sort of normal status.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * The default LogFormat reproduces CLF; see below.
560f6ac786d611b858b2bad932713d9e971f0716trawick * The way this is supposed to work with virtual hosts is as follows:
560f6ac786d611b858b2bad932713d9e971f0716trawick * a virtual host can have its own LogFormat, or its own TransferLog.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * If it doesn't have its own LogFormat, it inherits from the main
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * server. If it doesn't have its own TransferLog, it writes to the
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * same descriptor (meaning the same process for "| ...").
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp * --- rst */
97c78987224dcd037076d393aad1867c26b2c8cftrawick#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b"
97c78987224dcd037076d393aad1867c26b2c8cftrawickstatic int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE);
97c78987224dcd037076d393aad1867c26b2c8cftrawick/* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
97c78987224dcd037076d393aad1867c26b2c8cftrawick * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512
97c78987224dcd037076d393aad1867c26b2c8cftrawick * is guaranteed. So we'll just guess 512 in the event the system
97c78987224dcd037076d393aad1867c26b2c8cftrawick * doesn't have this. Now, for file writes there is actually no limit,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the entire write is atomic. Whether all systems implement this
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * correctly is another question entirely ... so we'll just use PIPE_BUF
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * because it's probably a good guess as to what is implemented correctly
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * everywhere.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * multi_log_state is our per-(virtual)-server configuration. We store
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * an array of the logs we are going to use, each of type config_log_state.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * If a default log format is given by LogFormat, store in default_format
785be1b6298010956622771c870ab3cd8ca57a2faaron * (backward compat. with mod_log_config). We also store for each virtual
785be1b6298010956622771c870ab3cd8ca57a2faaron * server a pointer to the logs specified for the main server, so that if this
785be1b6298010956622771c870ab3cd8ca57a2faaron * vhost has no logs defined, we can use the main server's logs instead.
785be1b6298010956622771c870ab3cd8ca57a2faaron * So, for the main server, config_logs contains a list of the log files
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * and server_config_logs in empty. For a vhost, server_config_logs
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * points to the same array as config_logs in the main server, and
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * config_logs points to the array of logs defined inside this vhost,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * which might be empty.
785be1b6298010956622771c870ab3cd8ca57a2faarontypedef struct {
c5d006b2861d49c61bcf79316163e30611c6fd08trawick * config_log_state holds the status of a single log file. fname might
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * be NULL, which means this module does no logging for this
7697b1b7376a532163c621e050b70c90dcb15d66covener * request. format might be NULL, in which case the default_format
7697b1b7376a532163c621e050b70c90dcb15d66covener * from the multi_log_state should be used, or if that is NULL as
7697b1b7376a532163c621e050b70c90dcb15d66covener * well, use the CLF. log_fd is NULL before the log file is opened and
7697b1b7376a532163c621e050b70c90dcb15d66covener * set to a valid fd after it is opened.
7697b1b7376a532163c621e050b70c90dcb15d66covenertypedef struct {
e08076ca56e6cb68b30846b9e9339061058aae6dpoirier const char *fname;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Format items...
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Note that many of these could have ap_sprintfs replaced with static buffers.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingtypedef struct {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return apr_itoa(p, i);
785be1b6298010956622771c870ab3cd8ca57a2faaron if (i <= 0) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return "-";
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic const char *constant_item(request_rec *dummy, char *stuff)
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic const char *log_remote_host(request_rec *r, char *a)
785be1b6298010956622771c870ab3cd8ca57a2faaron return ap_get_remote_host(r->connection, r->per_dir_config,
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic const char *log_remote_address(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_local_address(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_remote_logname(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_remote_user(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_request_line(request_rec *r, char *a)
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick /* NOTE: If the original request contained a password, we
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick * re-write the request line here to contain XXXXXX instead:
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick * (note the truncation before the protocol string for HTTP/0.9 requests)
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick * (note also that r->the_request contains the unmodified request)
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick return (r->parsed_uri.password) ? apr_pstrcat(r->pool, r->method, " ",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_uri_unparse_components(r->pool, &r->parsed_uri, 0),
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_request_file(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_request_uri(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_request_method(request_rec *r, char *a)
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic const char *log_request_protocol(request_rec *r, char *a)
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic const char *log_request_query(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return (r->args != NULL) ? apr_pstrcat(r->pool, "?", r->args, NULL)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_status(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *clf_log_bytes_sent(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return "-";
27c5ebb7d411a214f5b6b55a881086ce086d3dd3covenerstatic const char *log_bytes_sent(request_rec *r, char *a)
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick return "0";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_header_in(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_header_out(request_rec *r, char *a)
27c5ebb7d411a214f5b6b55a881086ce086d3dd3covener if (!strcasecmp(a, "Content-type") && r->content_type) {
27c5ebb7d411a214f5b6b55a881086ce086d3dd3covenerstatic const char *log_env_var(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_cookie(request_rec *r, char *a)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *cookies;
39b76a07959a0a332366c735a23894d9e8ed6872trawick if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
785be1b6298010956622771c870ab3cd8ca57a2faaron start_cookie += strlen(a) + 1; /* cookie_name + '=' */
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf /* kill everything in cookie after ';' */
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanfstatic const char *log_request_time(request_rec *r, char *a)
785be1b6298010956622771c870ab3cd8ca57a2faaron hi. i think getting the time again at the end of the request
785be1b6298010956622771c870ab3cd8ca57a2faaron just for logging is dumb. i know it's "required" for CLF.
785be1b6298010956622771c870ab3cd8ca57a2faaron folks writing log parsing tools don't realise that out of order
785be1b6298010956622771c870ab3cd8ca57a2faaron times have always been possible (consider what happens if one
785be1b6298010956622771c870ab3cd8ca57a2faaron process calculates the time to log, but then there's a context
785be1b6298010956622771c870ab3cd8ca57a2faaron switch before it writes and before that process is run again the
785be1b6298010956622771c870ab3cd8ca57a2faaron log rotation occurs) and they should just fix their tools rather
785be1b6298010956622771c870ab3cd8ca57a2faaron than force the server to pay extra cpu cycles. if you've got
b6d9e9d6421b9cebfc74f9c1a870b8b85473f1c1poirier a problem with this, you can set the define. -djg
b6d9e9d6421b9cebfc74f9c1a870b8b85473f1c1poirier if (a && *a) { /* Custom format */
b6d9e9d6421b9cebfc74f9c1a870b8b85473f1c1poirier apr_strftime(tstr, &retcode, MAX_STRING_LEN, a, &xt);
b6d9e9d6421b9cebfc74f9c1a870b8b85473f1c1poirier else { /* CLF format */
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf apr_snprintf(tstr, sizeof(tstr), "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf xt.tm_mday, apr_month_snames[xt.tm_mon], xt.tm_year+1900,
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic const char *log_request_duration(request_rec *r, char *a)
785be1b6298010956622771c870ab3cd8ca57a2faaron return apr_psprintf(r->pool, "%qd", (apr_time_now() - r->request_time)
7697b1b7376a532163c621e050b70c90dcb15d66covenerstatic const char *log_request_duration_microseconds(request_rec *r, char *a)
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return apr_psprintf(r->pool, "%qd", (apr_time_now() - r->request_time));
7697b1b7376a532163c621e050b70c90dcb15d66covener/* These next two routines use the canonical name:port so that log
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * parsers don't need to duplicate all the vhost parsing crud.
7697b1b7376a532163c621e050b70c90dcb15d66covenerstatic const char *log_virtual_host(request_rec *r, char *a)
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic const char *log_server_port(request_rec *r, char *a)
7697b1b7376a532163c621e050b70c90dcb15d66covener r->server->port ? r->server->port : ap_default_port(r));
7697b1b7376a532163c621e050b70c90dcb15d66covener/* This respects the setting of UseCanonicalName so that
7697b1b7376a532163c621e050b70c90dcb15d66covener * the dynamic mass virtual hosting trick works better.
7697b1b7376a532163c621e050b70c90dcb15d66covenerstatic const char *log_server_name(request_rec *r, char *a)
7697b1b7376a532163c621e050b70c90dcb15d66covenerstatic const char *log_child_pid(request_rec *r, char *a)
7697b1b7376a532163c621e050b70c90dcb15d66covener return apr_psprintf(r->pool, "%ld", (long) getpid());
7697b1b7376a532163c621e050b70c90dcb15d66covenerstatic const char *log_connection_status(request_rec *r, char *a)
7697b1b7376a532163c621e050b70c90dcb15d66covener return "X";
7697b1b7376a532163c621e050b70c90dcb15d66covener (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
7697b1b7376a532163c621e050b70c90dcb15d66covener return "+";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return "-";
ebc18d48bea83ee5ed7a1b4e30007e5192539829wrowe/*****************************************************************
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Parsing the log format string
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic char *parse_log_misc_string(apr_pool_t *p, log_format_item *it,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char **sa)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *s;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while (*s && *s != '%') {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This might allocate a few chars extra if there's a backslash
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton * escape in the format string.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while (*s && *s != '%') {
785be1b6298010956622771c870ab3cd8ca57a2faaron if (*s != '\\') {
785be1b6298010956622771c870ab3cd8ca57a2faaron *d++ = *s++;
7697b1b7376a532163c621e050b70c90dcb15d66covener switch (*s) {
7697b1b7376a532163c621e050b70c90dcb15d66covener *d++ = '\\';
7697b1b7376a532163c621e050b70c90dcb15d66covener *d++ = '\r';
785be1b6298010956622771c870ab3cd8ca57a2faaron *d++ = '\n';
785be1b6298010956622771c870ab3cd8ca57a2faaron *d++ = '\t';
7697b1b7376a532163c621e050b70c90dcb15d66covener /* copy verbatim */
7697b1b7376a532163c621e050b70c90dcb15d66covener *d++ = '\\';
7697b1b7376a532163c621e050b70c90dcb15d66covener * Allow the loop to deal with this *s in the normal
785be1b6298010956622771c870ab3cd8ca57a2faaron * fashion so that it handles end of string etc.
785be1b6298010956622771c870ab3cd8ca57a2faaron * properly.
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
785be1b6298010956622771c870ab3cd8ca57a2faaron const char *s = *sa;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (*s != '%') {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while (*s) {
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick switch (*s) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding i = *s - '0';
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while (apr_isdigit(*++s)) {
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return apr_pstrcat(p, "Unrecognized LogFormat directive %",
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz return "Ran off end of LogFormat parsing args to some directive";
4f133508c93204c06e1acba9774ff184e5812606niqstatic apr_array_header_t *parse_log_string(apr_pool_t *p, const char *s, const char **err)
4f133508c93204c06e1acba9774ff184e5812606niq apr_array_header_t *a = apr_array_make(p, 30, sizeof(log_format_item));
4f133508c93204c06e1acba9774ff184e5812606niq while (*s) {
4f133508c93204c06e1acba9774ff184e5812606niq if ((res = parse_log_item(p, (log_format_item *) apr_array_push(a), &s))) {
644be6f54749d2d9950d2c4d2ac448f7af016d26martin parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
43c3e6a4b559b76b750c245ee95e2782c15b4296jim/*****************************************************************
43c3e6a4b559b76b750c245ee95e2782c15b4296jim * Actually logging.
e8f95a682820a599fe41b22977010636be5c2717jimstatic const char *process_item(request_rec *r, request_rec *orig,
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick const char *cp;
3c8b3749225668f06abbb2b023a833a2cef46931brianp /* First, see if we need to process this thing at all... */
3c8b3749225668f06abbb2b023a833a2cef46931brianp if (item->conditions && item->conditions->nelts != 0) {
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick return "-";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* We do. Do it... */
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf cp = (*item->func) (item->want_orig ? orig : r, item->arg);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_file_write(cls->log_fd, cls->outbuf, &cls->outcnt);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int config_log_transaction(request_rec *r, config_log_state *cls,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char **strs;
785be1b6298010956622771c870ab3cd8ca57a2faaron * See if we've got any conditional envariable-controlled logging decisions
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (apr_table_get(r->subprocess_env, envar) == NULL) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding format = cls->format ? cls->format : default_format;
785be1b6298010956622771c870ab3cd8ca57a2faaron strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while (r->next) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding for (i = 0, s = &cls->outbuf[cls->outcnt]; i < format->nelts; ++i) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding multi_log_state *mls = ap_get_module_config(r->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Log this transaction..
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding clsarray = (config_log_state *) mls->config_logs->elts;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding config_log_transaction(r, cls, mls->default_format);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding clsarray = (config_log_state *) mls->server_config_logs->elts;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding for (i = 0; i < mls->server_config_logs->nelts; ++i) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*****************************************************************
785be1b6298010956622771c870ab3cd8ca57a2faaron * Module glue...
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic void *make_config_log_state(apr_pool_t *p, server_rec *s)
785be1b6298010956622771c870ab3cd8ca57a2faaron mls = (multi_log_state *) apr_palloc(p, sizeof(multi_log_state));
785be1b6298010956622771c870ab3cd8ca57a2faaron mls->config_logs = apr_array_make(p, 1, sizeof(config_log_state));
785be1b6298010956622771c870ab3cd8ca57a2faaron apr_table_setn(mls->formats, "CLF", DEFAULT_LOG_FORMAT);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Use the merger to simply add a pointer from the vhost log state
51af95bb51b5084e883bad250b2afa2838e9ceebfielding * to the log of logs specified for the non-vhost configuration. Make sure
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * vhosts inherit any globally-defined format names.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic void *merge_config_log_state(apr_pool_t *p, void *basev, void *addv)
c1b808d160bfb5c849263be8d4acff600853a328trawick add->default_format_string = base->default_format_string;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding add->formats = apr_table_overlay(p, base->formats, add->formats);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Set the default logfile format, or define a nickname for a format string.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *log_format(cmd_parms *cmd, void *dummy, const char *fmt,
785be1b6298010956622771c870ab3cd8ca57a2faaron const char *name)
785be1b6298010956622771c870ab3cd8ca57a2faaron multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * If we were given two arguments, the second is a name to be given to the
785be1b6298010956622771c870ab3cd8ca57a2faaron * format. This syntax just defines the nickname - it doesn't actually
785be1b6298010956622771c870ab3cd8ca57a2faaron * make the format the default.
785be1b6298010956622771c870ab3cd8ca57a2faaron mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic const char *add_custom_log(cmd_parms *cmd, void *dummy, const char *fn,
785be1b6298010956622771c870ab3cd8ca57a2faaron multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding cls = (config_log_state *) apr_array_push(mls->config_logs);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return "error in condition clause";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return "missing environment variable name";
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna cls->condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna cls->format = parse_log_string(cmd->pool, fmt, &err_string);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernastatic const char *set_transfer_log(cmd_parms *cmd, void *dummy,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna const char *fn)
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernastatic const char *set_cookie_log(cmd_parms *cmd, void *dummy, const char *fn)
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t", NULL);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernaAP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "a file name, a custom log format string or format name, "
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "and an optional \"env=\" clause (see docs)"),
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernaAP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "the filename of the access log"),
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernaAP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "a log format string (see docs) and an optional format name"),
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernaAP_INIT_TAKE1("CookieLog", set_cookie_log, NULL, RSRC_CONF,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "the filename of the cookie log"),
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquernastatic config_log_state *open_config_log(server_rec *s, apr_pool_t *p,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Skip opening the log the first time through. It's really
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * good to avoid starting the piped log process during preflight.
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard apr_pool_userdata_get(&data, userdata_key, s->process->pool);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding apr_pool_userdata_set((const void *)1, userdata_key,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return cls; /* virtual config shared w/main server */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *fname = ap_server_root_relative(p, cls->fname);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((status = apr_file_open(&cls->log_fd, fname, xfer_flags, xfer_perms, p))
#ifdef BUFFERED_LOGS
return cls;
const char *dummy;
const char *format;
if (format) {
if (format) {
if (format) {
return NULL;
#ifdef BUFFERED_LOGS
for (; s; s = s->next) {
if (log_list) {
return APR_SUCCESS;
open_multi_logs(s, p);
open_multi_logs(s, p);
#ifdef BUFFERED_LOGS
if (log_pfn_register) {