/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * Modified by djm@va.pubnix.com: * If no TransferLog is given explicitly, decline to log. * * This is module implements the TransferLog directive (same as the * common log module), and additional directives, LogFormat and CustomLog. * * * Syntax: * * TransferLog fn Logs transfers to fn in standard log format, unless * a custom format is set with LogFormat * LogFormat format Set a log format from TransferLog files * CustomLog fn format * Log to file fn with format given by the format * argument * * There can be any number of TransferLog and CustomLog * commands. Each request will be logged to _ALL_ the * named files, in the appropriate format. * * If no TransferLog or CustomLog directive appears in a VirtualHost, * the request will be logged to the log file(s) defined outside * the virtual host section. If a TransferLog or CustomLog directive * appears in the VirtualHost section, the log files defined outside * the VirtualHost will _not_ be used. This makes this module compatible * with the CLF and config log modules, where the use of TransferLog * inside the VirtualHost section overrides its use outside. * * Examples: * * TransferLog logs/access_log * <VirtualHost> * LogFormat "... custom format ..." * TransferLog log/virtual_only * CustomLog log/virtual_useragents "%t %{user-agent}i" * </VirtualHost> * * This will log using CLF to access_log any requests handled by the * main server, while any requests to the virtual host will be logged * with the "... custom format..." to virtual_only _AND_ using * the custom user-agent log to virtual_useragents. * * Note that the NCSA referer and user-agent logs are easily added with * CustomLog: * CustomLog logs/referer "%{referer}i -> %U" * CustomLog logs/agent "%{user-agent}i" * * RefererIgnore functionality can be obtained with conditional * logging (SetEnvIf and CustomLog ... env=!VAR). * * But using this method allows much easier modification of the * log format, e.g. to log hosts along with UA: * CustomLog logs/referer "%{referer}i %U %h" * * The argument to LogFormat and CustomLog is a string, which can include * literal characters copied into the log files, and '%' directives as * follows: * * %...B: bytes sent, excluding HTTP headers. * %...b: bytes sent, excluding HTTP headers in CLF format, i.e. a '-' * when no bytes where sent (rather than a '0'. * %...{FOOBAR}C: The contents of the HTTP cookie FOOBAR * %...{FOOBAR}e: The contents of the environment variable FOOBAR * %...f: filename * %...h: remote host * %...a: remote IP-address * %...A: local IP-address * %...{Foobar}i: The contents of Foobar: header line(s) in the request * sent to the client. * %...k: number of keepalive requests served over this connection * %...l: remote logname (from identd, if supplied) * %...{Foobar}n: The contents of note "Foobar" from another module. * %...{Foobar}o: The contents of Foobar: header line(s) in the reply. * %...p: the canonical port for the server * %...{format}p: the canonical port for the server, or the actual local * or remote port * %...P: the process ID of the child that serviced the request. * %...{format}P: the process ID or thread ID of the child/thread that * serviced the request * %...r: first line of request * %...s: status. For requests that got internally redirected, this * is status of the *original* request --- %...>s for the last. * %...t: time, in common log format time format * %...{format}t: The time, in the form given by format, which should * be in strftime(3) format. * %...T: the time taken to serve the request, in seconds. * %...{s}T: the time taken to serve the request, in seconds, same as %T. * %...{us}T: the time taken to serve the request, in micro seconds, same as %D. * %...{ms}T: the time taken to serve the request, in milliseconds. * %...D: the time taken to serve the request, in micro seconds. * %...u: remote user (from auth; may be bogus if return status (%s) is 401) * %...U: the URL path requested. * %...v: the configured name of the server (i.e. which virtual host?) * %...V: the server name according to the UseCanonicalName setting * %...m: the request method * %...H: the request protocol * %...q: the query string prepended by "?", or empty if no query string * %...X: Status of the connection. * 'X' = connection aborted before the response completed. * '+' = connection may be kept alive after the response is sent. * '-' = connection will be closed after the response is sent. * (This directive was %...c in late versions of Apache 1.3, but * this conflicted with the historical ssl %...{var}c syntax.) * %...L: Log-Id of the Request (or '-' if none) * %...{c}L: Log-Id of the Connection (or '-' if none) * * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can * indicate conditions for inclusion of the item (which will cause it * to be replaced with '-' if the condition is not met). Note that * there is no escaping performed on the strings from %r, %...i and * %...o; some with long memories may remember that I thought this was * a bad idea, once upon a time, and I'm still not comfortable with * it, but it is difficult to see how to "do the right thing" with all * of '%..i', unless we URL-escape everything and break with CLF. * * The forms of condition are a list of HTTP status codes, which may * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs * User-agent: on 400 errors and 501 errors (Bad Request, Not * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all * requests which did *not* return some sort of normal status. * * The default LogFormat reproduces CLF; see below. * * The way this is supposed to work with virtual hosts is as follows: * a virtual host can have its own LogFormat, or its own TransferLog. * If it doesn't have its own LogFormat, it inherits from the main * server. If it doesn't have its own TransferLog, it writes to the * same descriptor (meaning the same process for "| ..."). *
* --- rst */
/* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is * guaranteed to be atomic when writing a pipe. And PIPE_BUF >= 512 * is guaranteed. So we'll just guess 512 in the event the system * doesn't have this. Now, for file writes there is actually no limit, * the entire write is atomic. Whether all systems implement this * correctly is another question entirely ... so we'll just use PIPE_BUF * because it's probably a good guess as to what is implemented correctly * everywhere.
*/ #ifdef PIPE_BUF #define LOG_BUFSIZE PIPE_BUF #else #define LOG_BUFSIZE (512) #endif
/* * multi_log_state is our per-(virtual)-server configuration. We store * an array of the logs we are going to use, each of type config_log_state. * If a default log format is given by LogFormat, store in default_format * (backward compat. with mod_log_config). We also store for each virtual * server a pointer to the logs specified for the main server, so that if this * vhost has no logs defined, we can use the main server's logs instead. * * So, for the main server, config_logs contains a list of the log files * and server_config_logs is empty. For a vhost, server_config_logs * points to the same array as config_logs in the main server, and * config_logs points to the array of logs defined inside this vhost, * which might be empty.
*/
/* * config_log_state holds the status of a single log file. fname might * be NULL, which means this module does no logging for this * request. format might be NULL, in which case the default_format * from the multi_log_state should be used, or if that is NULL as * well, use the CLF. * log_writer is NULL before the log file is opened and is * set to a opaque structure (usually a fd) after it is opened.
typedefstruct { constchar *fname; constchar *format_string;
apr_array_header_t *format; void *log_writer; char *condition_var; int inherit;
ap_expr_info_t *condition_expr; /** place of definition or NULL if already checked */ const ap_directive_t *directive;
} config_log_state;
/* * log_request_state holds request specific log data that is not * part of the request_rec.
*/ typedefstruct {
apr_time_t request_end_time;
} log_request_state;
/* * Format items... * Note that many of these could have ap_sprintfs replaced with static buffers.
*/
typedefstruct {
ap_log_handler_fn_t *func; char *arg; int condition_sense; int want_orig;
apr_array_header_t *conditions;
} log_format_item;
staticchar *pfmt(apr_pool_t *p, int i)
{ if (i <= 0) { return"-";
} else { return apr_itoa(p, i);
}
}
staticconstchar *log_request_line(request_rec *r, char *a)
{ /* NOTE: If the original request contained a password, we * re-write the request line here to contain XXXXXX instead: * (note the truncation before the protocol string for HTTP/0.9 requests) * (note also that r->the_request contains the unmodified request)
*/ return ap_escape_logitem(r->pool,
(r->parsed_uri.password)
? apr_pstrcat(r->pool, r->method, " ",
apr_uri_unparse(r->pool,
&r->parsed_uri, 0),
r->assbackwards ? NULL : " ",
r->protocol, NULL)
: r->the_request);
}
/* * This supports Netscape version 0 cookies while being tolerant to * some properties of RFC2109/2965 version 1 cookies: * - case-insensitive match of cookie names * - white space between the tokens * It does not support the following version 1 features: * - quoted strings as cookie values * - commas to separate cookies
*/
while ((cookie = apr_strtok(cookies, ";", &last1))) { char *name = apr_strtok(cookie, "=", &last2); /* last2 points to the next char following an '=' delim,
or the trailing NUL char of the string */ char *value = last2; if (name && *name && value && *value) { char *last = value - 2; /* Move past leading WS */
name += strspn(name, " \t"); while (last >= name && apr_isspace(*last)) {
*last = '\0';
--last;
}
if (!ap_cstr_casecmp(name, a)) { /* last1 points to the next char following the ';' delim,
or the trailing NUL char of the string */
last = last1 - (*last1 ? 2 : 1); /* Move past leading WS */
value += strspn(value, " \t"); while (last >= value && apr_isspace(*last)) {
*last = '\0';
--last;
}
return ap_escape_logitem(r->pool, value);
}
} /* Iterate the remaining tokens using apr_strtok(NULL, ...) */
cookies = NULL;
}
} return NULL;
}
if (fmt_type >= TIME_FMT_ABS_SEC) { /* Absolute (micro-/milli-)second time * or msec/usec fraction
*/ char* buf = apr_palloc(r->pool, 20); switch (fmt_type) { case TIME_FMT_ABS_SEC:
apr_snprintf(buf, 20, "%" APR_TIME_T_FMT, apr_time_sec(request_time)); break; case TIME_FMT_ABS_MSEC:
apr_snprintf(buf, 20, "%" APR_TIME_T_FMT, apr_time_as_msec(request_time)); break; case TIME_FMT_ABS_USEC:
apr_snprintf(buf, 20, "%" APR_TIME_T_FMT, request_time); break; case TIME_FMT_ABS_MSEC_FRAC:
apr_snprintf(buf, 20, "%03" APR_TIME_T_FMT, apr_time_msec(request_time)); break; case TIME_FMT_ABS_USEC_FRAC:
apr_snprintf(buf, 20, "%06" APR_TIME_T_FMT, apr_time_usec(request_time)); break; default: return"-";
} return buf;
} elseif (fmt_type == TIME_FMT_CUSTOM) { /* Custom format */ /* The custom time formatting uses a very large temp buffer * on the stack. To avoid using so much stack space in the * common case where we're not using a custom format, the code * for the custom format in a separate function. (That's why * log_request_time_custom is not inlined right here.)
*/
ap_explode_recent_localtime(&xt, request_time); return log_request_time_custom(r, a, &xt);
} else { /* CLF format */ /* This code uses the same technique as ap_explode_recent_localtime(): * optimistic caching with logic to detect and correct race conditions. * See the comments in server/util_time.c for more information.
*/
cached_request_time* cached_time = apr_palloc(r->pool, sizeof(*cached_time)); unsigned t_seconds = (unsigned)apr_time_sec(request_time); unsigned i = t_seconds & TIME_CACHE_MASK;
*cached_time = request_time_cache[i]; if ((t_seconds != cached_time->t) ||
(t_seconds != cached_time->t_validate)) {
/* Invalid or old snapshot, so compute the proper time string * and store it in the cache
*/ char sign; int timz;
/* These next two routines use the canonical name:port so that log * parsers don't need to duplicate all the vhost parsing crud.
*/ staticconstchar *log_virtual_host(request_rec *r, char *a)
{ return ap_escape_logitem(r->pool, r->server->server_hostname);
}
if (*a == '\0' || !strcasecmp(a, "canonical")) {
port = r->server->port ? r->server->port : ap_default_port(r);
} elseif (!strcasecmp(a, "remote")) {
port = r->useragent_addr->port;
} elseif (!strcasecmp(a, "local")) {
port = r->connection->local_addr->port;
} else { /* bogus format */ return a;
} return apr_itoa(r->pool, (int)port);
}
/* This respects the setting of UseCanonicalName so that * the dynamic mass virtual hosting trick works better.
*/ staticconstchar *log_server_name(request_rec *r, char *a)
{ return ap_escape_logitem(r->pool, ap_get_server_name(r));
}
staticconstchar *log_pid_tid(request_rec *r, char *a)
{ if (*a == '\0' || !strcasecmp(a, "pid")) { return ap_append_pid(r->pool, "", "");
} elseif (!strcasecmp(a, "tid") || !strcasecmp(a, "hextid")) { #if APR_HAS_THREADS
apr_os_thread_t tid = apr_os_thread_current(); #else int tid = 0; /* APR will format "0" anyway but an arg is needed */ #endif return apr_psprintf(r->pool, /* APR can format a thread id in hex */
*a == 'h' ? "%pt" : "%pT", &tid);
} /* bogus format */ return a;
}
staticconstchar *log_connection_status(request_rec *r, char *a)
{ if (r->connection->aborted) return"X";
s = *sa; while (*s && *s != '%') {
s++;
} /* * This might allocate a few chars extra if there's a backslash * escape in the format string.
*/
it->arg = apr_palloc(p, s - *sa + 1);
d = it->arg;
s = *sa; while (*s && *s != '%') { if (*s != '\\') {
*d++ = *s++;
} else {
s++; switch (*s) { case'\\':
*d++ = '\\';
s++; break; case'r':
*d++ = '\r';
s++; break; case'n':
*d++ = '\n';
s++; break; case't':
*d++ = '\t';
s++; break; default: /* copy verbatim */
*d++ = '\\'; /* * Allow the loop to deal with this *s in the normal * fashion so that it handles end of string etc. * properly.
*/ break;
}
}
}
*d = '\0';
case'0': case'1': case'2': case'3': case'4': case'5': case'6': case'7': case'8': case'9':
i = *s - '0'; while (apr_isdigit(*++s)) {
i = i * 10 + (*s) - '0';
} if (!it->conditions) {
it->conditions = apr_array_make(p, 4, sizeof(int));
}
*(int *) apr_array_push(it->conditions) = i; break;
default: /* check for '^' + two character format first */ if (*s == '^' && *(s+1) && *(s+2)) {
handler = (ap_log_handler *)apr_hash_get(log_hash, s, 3); if (handler) {
s += 3;
}
} if (!handler) {
handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
} if (!handler) { char dummy[2];
/* * Use the merger to simply add a pointer from the vhost log state * to the log of logs specified for the non-vhost configuration. Make sure * vhosts inherit any globally-defined format names.
*/
/* * Set the default logfile format, or define a nickname for a format string.
*/ staticconstchar *log_format(cmd_parms *cmd, void *dummy, constchar *fmt, constchar *name)
{ constchar *err_string = NULL;
multi_log_state *mls = ap_get_module_config(cmd->server->module_config,
&log_config_module);
/* * If we were given two arguments, the second is a name to be given to the * format. This syntax just defines the nickname - it doesn't actually * make the format the default.
*/ if (name != NULL) {
parse_log_string(cmd->pool, fmt, &err_string); if (err_string == NULL) {
apr_table_setn(mls->formats, name, fmt);
}
} else {
mls->default_format_string = fmt;
mls->default_format = parse_log_string(cmd->pool, fmt, &err_string);
} return err_string;
}
/* Add a custom log through the normal channel */
ret = add_custom_log(cmd, dummy, fn, fmt, envclause);
/* Set the inherit flag unless there was some error */ if (ret == NULL) {
clsarray = (config_log_state*)mls->config_logs->elts;
cls = &clsarray[mls->config_logs->nelts-1];
cls->inherit = 1;
}
staticconstchar *set_buffered_logs_on(cmd_parms *parms, void *dummy, int flag)
{
buffered_logs = flag; if (buffered_logs) {
ap_log_set_writer_init(ap_buffered_log_writer_init);
ap_log_set_writer(ap_buffered_log_writer);
} else {
ap_log_set_writer_init(ap_default_log_writer_init);
ap_log_set_writer(ap_default_log_writer);
} return NULL;
} staticconst command_rec config_log_cmds[] =
{
AP_INIT_TAKE23("CustomLog", add_custom_log, NULL, RSRC_CONF, "a file name, a custom log format string or format name, " "and an optional \"env=\" or \"expr=\" clause (see docs)"),
AP_INIT_TAKE23("GlobalLog", add_global_log, NULL, RSRC_CONF, "Same as CustomLog, but forces virtualhosts to inherit the log"),
AP_INIT_TAKE1("TransferLog", set_transfer_log, NULL, RSRC_CONF, "the filename of the access log"),
AP_INIT_TAKE12("LogFormat", log_format, NULL, RSRC_CONF, "a log format string (see docs) and an optional format name"),
AP_INIT_FLAG("BufferedLogs", set_buffered_logs_on, NULL, RSRC_CONF, "Enable Buffered Logging (experimental)"),
{NULL}
};
if (mls->default_format_string) {
format = apr_table_get(mls->formats, mls->default_format_string); if (format) {
mls->default_format = parse_log_string(p, format, &dummy);
}
}
if (!mls->default_format) {
mls->default_format = parse_log_string(p, DEFAULT_LOG_FORMAT, &dummy);
}
if (mls->config_logs->nelts) {
clsarray = (config_log_state *) mls->config_logs->elts; for (i = 0; i < mls->config_logs->nelts; ++i) {
config_log_state *cls = &clsarray[i];
if (cls->format_string) {
format = apr_table_get(mls->formats, cls->format_string); if (format) {
cls->format = parse_log_string(p, format, &dummy);
}
}
if (!open_config_log(s, p, cls, mls->default_format)) { /* Failure already logged by open_config_log */ return DONE;
}
}
} elseif (mls->server_config_logs) {
clsarray = (config_log_state *) mls->server_config_logs->elts; for (i = 0; i < mls->server_config_logs->nelts; ++i) {
config_log_state *cls = &clsarray[i];
if (cls->format_string) {
format = apr_table_get(mls->formats, cls->format_string); if (format) {
cls->format = parse_log_string(p, format, &dummy);
}
}
if (!open_config_log(s, p, cls, mls->default_format)) { /* Failure already logged by open_config_log */ return DONE;
}
}
}
/* First init the buffered logs array, which is needed when opening the logs. */ if (buffered_logs) {
all_buffered_logs = apr_array_make(p, 5, sizeof(buffered_log *));
}
/* Next, do "physical" server, which gets default log fd and format * for the virtual servers, if they don't override...
*/
res = open_multi_logs(s, p);
/* Then, virtual servers */
for (s = s->next; (res == OK) && s; s = s->next) {
res = open_multi_logs(s, p);
}
return res;
}
staticvoid init_child(apr_pool_t *p, server_rec *s)
{ int mpm_threads;
ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
/* Now register the last buffer flush with the cleanup engine */ if (buffered_logs) { int i;
buffered_log **array = (buffered_log **)all_buffered_logs->elts;
if ((rv = APR_ANYLOCK_LOCK(&buf->mutex)) != APR_SUCCESS) { return rv;
}
if (len + buf->outcnt > LOG_BUFSIZE) {
flush_log(buf);
} if (len >= LOG_BUFSIZE) {
apr_size_t w;
/* * We do this memcpy dance because write() is atomic for * len < PIPE_BUF, while writev() need not be.
*/
str = apr_palloc(r->pool, len + 1); for (i = 0, s = str; i < nelts; ++i) {
memcpy(s, strs[i], strl[i]);
s += strl[i];
}
w = len;
rv = apr_file_write_full(buf->handle, str, w, NULL);
} else { for (i = 0, s = &buf->outbuf[buf->outcnt]; i < nelts; ++i) {
memcpy(s, strs[i], strl[i]);
s += strl[i];
}
buf->outcnt += len;
rv = APR_SUCCESS;
}
staticint log_check_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{ int rv = OK; while (s) {
multi_log_state *mls = ap_get_module_config(s->module_config,
&log_config_module); /* * We don't need to check mls->server_config_logs because it just * points to the parent server's mls->config_logs.
*/
apr_array_header_t *log_list = mls->config_logs;
config_log_state *clsarray = (config_log_state *) log_list->elts; int i; for (i = 0; i < log_list->nelts; ++i) { if (check_log_dir(ptemp, s, &clsarray[i]) != OK)
rv = !OK;
}
/* Init log_hash before we register the optional function. It is * possible for the optional function, ap_register_log_handler, * to be called before any other mod_log_config hooks are called. * As a policy, we should init everything required by an optional function * before calling APR_REGISTER_OPTIONAL_FN.
*/
log_hash = apr_hash_make(p);
APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
APR_REGISTER_OPTIONAL_FN(ap_log_set_writer_init);
APR_REGISTER_OPTIONAL_FN(ap_log_set_writer);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.