summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES5
-rw-r--r--STATUS8
-rw-r--r--docs/manual/logs.xml1
-rw-r--r--docs/manual/mod/core.xml147
-rw-r--r--docs/manual/mod/mod_log_config.xml4
-rw-r--r--include/ap_mmn.h7
-rw-r--r--include/http_config.h2
-rw-r--r--include/http_core.h65
-rw-r--r--include/httpd.h11
-rw-r--r--include/util_time.h2
-rw-r--r--modules/loggers/mod_log_config.c18
-rw-r--r--server/config.c6
-rw-r--r--server/core.c243
-rw-r--r--server/log.c711
-rw-r--r--server/util_time.c58
15 files changed, 1116 insertions, 172 deletions
diff --git a/CHANGES b/CHANGES
index 10f8e47a5f..d90f587be8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,11 @@
Changes with Apache 2.3.9
+ *) core: Add ErrorLogFormat to allow configuring error log format, including
+ additional information that is logged once per connection or request. Add
+ error log IDs for connections and request to allow correlating error log
+ lines and the corresponding access log entry.
+
*) core: Disable sendfile by default. [Stefan Fritsch]
*) mod_cache: Check the request to determine whether we are allowed
diff --git a/STATUS b/STATUS
index e989118590..43fddc7b61 100644
--- a/STATUS
+++ b/STATUS
@@ -64,11 +64,6 @@ RELEASE SHOWSTOPPERS:
- MPM simple
- mod_serf
- * Error log format should be configurable. The current format is
- too verbose: The prefix length ranges from 70 chars (server-scope,
- level <= info) to 120 chars (conn-scope, level >= debug).
- (sf is working on this).
-
FOR BETA:
* Modules without documentation need to be moved to experimental or be
@@ -141,9 +136,6 @@ CURRENT VOTES:
RELEASE NON-SHOWSTOPPERS BUT WOULD BE REAL NICE TO WRAP THESE UP:
- * Allow to log module name in error log format.
- (sf is working on this)
-
* Add mod_allow_method or some other (usable) functionality to replace
Limit/LimitExcept.
diff --git a/docs/manual/logs.xml b/docs/manual/logs.xml
index d6d8a2768b..1c00a1df95 100644
--- a/docs/manual/logs.xml
+++ b/docs/manual/logs.xml
@@ -92,6 +92,7 @@
</modulelist>
<directivelist>
<directive module="core">ErrorLog</directive>
+ <directive module="core">ErrorLogFormat</directive>
<directive module="core">LogLevel</directive>
</directivelist>
</related>
diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml
index 4811f6a0e8..6aa6333c2e 100644
--- a/docs/manual/mod/core.xml
+++ b/docs/manual/mod/core.xml
@@ -1044,6 +1044,153 @@ in case of an error</description>
</directivesynopsis>
<directivesynopsis>
+<name>ErrorLogFormat</name>
+<description>Format specification for error log entries</description>
+<syntax> ErrorLog [connection|request] <var>format</var></syntax>
+<contextlist><context>server config</context><context>virtual host</context>
+</contextlist>
+<compatibility>Available in Apache httpd 2.3.9 and later</compatibility>
+
+<usage>
+ <p><directive>ErrorLogFormat</directive> allows to specify what
+ supplementary information is logged in the error log in addition to the
+ actual log message.</p>
+
+ <example><title>Simple example</title>
+ ErrorLogFormat "[%t] [%l] [pid %P] %F: %E: [client %a] %M"
+ </example>
+
+ <p>Specifying <code>connection</code> or <code>request</code> as first
+ paramter allows to specify additional formats, causing additional
+ information to be logged when the first message is logged for a specific
+ connection or request, respectivly. This additional information is only
+ logged once per connection/request. If a connection or request is processed
+ without causing any log message, the additional information is not logged
+ either.</p>
+
+ <p>It can happen that some format string items do not produce output. For
+ example, the Referer header is only present if the log message is
+ associated to a request and the log message happens at a time when the
+ Referer header has already been read from the client. If no output is
+ produced, the default behaviour is to delete everything from the preceeding
+ space character to the next space character. This means the log line is
+ implicitly divided into fields on non-whitespace to whitespace transitions.
+ If a format string item does not produce output, the whole field is
+ ommitted. For example, if the remote address <code>%a</code> in the log
+ format <code>[%t] [%l] [%a] %M&nbsp;</code> is not available, the surrounding
+ brackets are not logged either. Space characters can be escaped with a
+ backslash to prevent them from delimiting a field. The combination '%&nbsp;'
+ (percent space) is a zero-witdh field delimiter that does not produce any
+ output.</p>
+
+ <p>The above behaviour can be changed by adding flags to the format string
+ item. A <code>-</code> (minus) flag causes a minus to be logged if the
+ respective item does not produce any output. In once-per-connection/request
+ formats, it is also possible to use the <code>+</code> (plus) flag. If an
+ item with the plus flag does not produce any output, the whole line is
+ ommitted.</p>
+
+ <p>Some format string items accept additional parameters in braces.</p>
+
+ <table border="1" style="zebra">
+ <columnspec><column width=".2"/><column width=".8"/></columnspec>
+
+ <tr><th>Format&nbsp;String</th> <th>Description</th></tr>
+
+ <tr><td><code>%%</code></td>
+ <td>The percent sign</td></tr>
+
+ <tr><td><code>%...a</code></td>
+ <td>Remote IP-address and port</td></tr>
+
+ <tr><td><code>%...A</code></td>
+ <td>Local IP-address and port</td></tr>
+
+ <tr><td><code>%...E</code></td>
+ <td>APR/OS error status code and string</td></tr>
+
+ <tr><td><code>%...F</code></td>
+ <td>Source file name and line number of the log call</td></tr>
+
+ <tr><td><code>%...{name}i</code></td>
+ <td>Request header <code>name</code></td></tr>
+
+ <tr><td><code>%...k</code></td>
+ <td>Number of keep-alive requests on this connection</td></tr>
+
+ <tr><td><code>%...l</code></td>
+ <td>Loglevel of the message</td></tr>
+
+ <tr><td><code>%...L</code></td>
+ <td>Log ID of the request</td></tr>
+
+ <tr><td><code>%...{c}L</code></td>
+ <td>Log ID of the connection</td></tr>
+
+ <tr><td><code>%...{C}L</code></td>
+ <td>Log ID of the connection if used in connection scope, empty otherwise</td></tr>
+
+ <tr><td><code>%...m</code></td>
+ <td>Name of the module logging the message</td></tr>
+
+ <tr><td><code>%M</code></td>
+ <td>The actual log message</td></tr>
+
+ <tr><td><code>%...P</code></td>
+ <td>Process ID of current process</td></tr>
+
+ <tr><td><code>%...T</code></td>
+ <td>Thread ID of current thread</td></tr>
+
+ <tr><td><code>%...t</code></td>
+ <td>The current time</td></tr>
+
+ <tr><td><code>%...{u}t</code></td>
+ <td>The current time including micro-seconds</td></tr>
+
+ <tr><td><code>%...{cu}t</code></td>
+ <td>The current time in compact ISO 8601 format, including
+ micro-seconds</td></tr>
+
+ <tr><td><code>\&nbsp;</code> (backslash space)</td>
+ <td>Non-field delimiting space</td></tr>
+
+ <tr><td><code>%&nbsp;</code> (percent space)</td>
+ <td>Field delimiter (no output)</td></tr>
+ </table>
+
+ <p>The log ID format <code>%L</code> produces a unique id for a connection
+ or request. This can be used to correlate which log lines belong to the
+ same connection or request, which request happens on which connection.
+ A <code>%L</code> format string is also available in
+ <module>mod_log_config</module>, to allow to correlate access log entries
+ with error log lines.</p>
+
+ <example><title>Example (somewhat similar to default format)</title>
+ ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P] %F: %E: [client\ %a]
+ %M%&nbsp;,\&nbsp;referer\&nbsp;%{Referer}i"
+ </example>
+
+ <example><title>Example (similar to the 2.2.x format)</title>
+ ErrorLogFormat "[%t] [%l] %F: %E: [client\ %a]
+ %M%&nbsp;,\&nbsp;referer\&nbsp;%{Referer}i"
+ </example>
+
+ <example><title>Advanced example with request/connection log IDs</title>
+ ErrorLogFormat "[%{uc}t] [%-m:%-l] [R:%L] [C:%{C}L] %M"<br/>
+ ErrorLogFormat request "[%{uc}t] [R:%L] Request %k on C:%{c}L pid:%P tid:%T"<br/>
+ ErrorLogFormat request "[%{uc}t] [R:%L] UA:'%+{User-Agent}i'"<br/>
+ ErrorLogFormat request "[%{uc}t] [R:%L] Referer:'%+{Referer}i'"<br/>
+ ErrorLogFormat connection "[%{uc}t] [C:%{c}L] local\ %a remote\ %A"<br/>
+ </example>
+
+</usage>
+<seealso><directive module="core">ErrorLog</directive></seealso>
+<seealso><directive module="core">LogLevel</directive></seealso>
+<seealso><a href="../logs.html">Apache HTTP Server Log Files</a></seealso>
+</directivesynopsis>
+
+<directivesynopsis>
<name>ExtendedStatus</name>
<description>Keep track of extended status information for each
request</description>
diff --git a/docs/manual/mod/mod_log_config.xml b/docs/manual/mod/mod_log_config.xml
index 7761b69edc..7ac233ab83 100644
--- a/docs/manual/mod/mod_log_config.xml
+++ b/docs/manual/mod/mod_log_config.xml
@@ -122,6 +122,10 @@
module="mod_ident">IdentityCheck</directive> is set
<code>On</code>.</td></tr>
+ <tr><td><code>%L</code></td>
+ <td>The request log ID from the error log (or '-' if nothing has been
+ logged to the error log for this request)</td></tr>
+
<tr><td><code>%m</code></td>
<td>The request method</td></tr>
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 010484725d..a066a83a2d 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -245,14 +245,17 @@
* proxy worker structs
* 20100723.2 (2.3.7-dev) Add ap_request_has_body()
* 20100723.3 (2.3.8-dev) Add ap_check_mpm()
+ * 20100905.0 (2.3.9-dev) Add log_id to conn and req recs. Add error log
+ * format handlers. Support AP_CTIME_OPTION_COMPACT in
+ * ap_recent_ctime_ex().
*/
#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
#ifndef MODULE_MAGIC_NUMBER_MAJOR
-#define MODULE_MAGIC_NUMBER_MAJOR 20100723
+#define MODULE_MAGIC_NUMBER_MAJOR 20100905
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 3 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
diff --git a/include/http_config.h b/include/http_config.h
index c0b2dd7b44..095880d918 100644
--- a/include/http_config.h
+++ b/include/http_config.h
@@ -724,7 +724,7 @@ AP_DECLARE(const char *) ap_find_module_name(module *m);
/**
* Find the short name of the module identified by the specified module index
* @param module_index The module index to get the name for
- * @return the name of the module
+ * @return the name of the module, NULL if not found
*/
AP_DECLARE(const char *) ap_find_module_short_name(int module_index);
/**
diff --git a/include/http_core.h b/include/http_core.h
index 7951978759..affd42a6ad 100644
--- a/include/http_core.h
+++ b/include/http_core.h
@@ -569,6 +569,15 @@ typedef struct {
const char *protocol;
apr_table_t *accf_map;
+ /* array of ap_errorlog_format_item for error log format string */
+ apr_array_header_t *error_log_format;
+ /*
+ * two arrays of arrays of ap_errorlog_format_item for additional information
+ * logged to the error log once per connection/request
+ */
+ apr_array_header_t *error_log_conn;
+ apr_array_header_t *error_log_req;
+
/* TRACE control */
#define AP_TRACE_UNSET -1
#define AP_TRACE_DISABLE 0
@@ -663,6 +672,62 @@ APR_DECLARE_OPTIONAL_FN(apr_off_t, ap_logio_get_last_bytes, (conn_rec *c));
/* ----------------------------------------------------------------------
*
+ * Error log formats
+ */
+
+/**
+ * info structure passed to callback functions of errorlog handlers
+ */
+typedef struct ap_errorlog_info {
+ const server_rec *s;
+ const conn_rec *c;
+ const request_rec *r;
+ const request_rec *rmain;
+ const char *file;
+ int line;
+ int module_index;
+ int level;
+ apr_status_t status;
+ int using_syslog;
+ int startup;
+} ap_errorlog_info;
+
+/**
+ * callback function prototype for a external errorlog handler
+ */
+typedef int ap_errorlog_handler_fn_t(const ap_errorlog_info *info,
+ const char *arg, char *buf, int buflen);
+
+/**
+ * Register external errorlog handler
+ * @param p config pool to use
+ * @param tag the new format specifier (i.e. the letter after the %)
+ * @param handler the handler function
+ * @param flags flags (reserved, set to 0)
+ */
+AP_DECLARE(void) ap_register_errorlog_handler(apr_pool_t *p, char *tag,
+ ap_errorlog_handler_fn_t *handler,
+ int flags);
+
+typedef struct ap_errorlog_handler {
+ ap_errorlog_handler_fn_t *func;
+ int flags;
+} ap_errorlog_handler;
+
+typedef struct {
+ ap_errorlog_handler_fn_t *func;
+ const char *arg;
+#define AP_ERRORLOG_FLAG_FIELD_SEP 1
+#define AP_ERRORLOG_FLAG_MESSAGE 2
+#define AP_ERRORLOG_FLAG_REQUIRED 4
+#define AP_ERRORLOG_FLAG_NULL_AS_HYPHEN 8
+ unsigned int flags;
+} ap_errorlog_format_item;
+
+AP_DECLARE(void) ap_register_builtin_errorlog_handlers(apr_pool_t *p);
+
+/* ----------------------------------------------------------------------
+ *
* ident lookups with mod_ident
*/
diff --git a/include/httpd.h b/include/httpd.h
index 0447866cf3..c275132d26 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -962,6 +962,11 @@ struct request_rec {
* modifying */
const struct ap_logconf *log;
+ /** Id to identify request in access and error log. Set when the first
+ * error log entry for this request is generated.
+ */
+ const char *log_id;
+
/**
* A linked list of the .htaccess configuration directives
* accessed by this request.
@@ -1103,6 +1108,12 @@ struct conn_rec {
* per_dir config, i.e. must be copied before modifying */
const struct ap_logconf *log;
+ /** Id to identify this connection in error log. Set when the first
+ * error log entry for this connection is generated.
+ */
+ const char *log_id;
+
+
/** This points to the current thread being used to process this request,
* over the lifetime of a request, the value may change. Users of the connection
* record should not rely upon it staying the same between calls that invole
diff --git a/include/util_time.h b/include/util_time.h
index 16706cff86..e4e02dbbfd 100644
--- a/include/util_time.h
+++ b/include/util_time.h
@@ -45,6 +45,8 @@ extern "C" {
#define AP_CTIME_OPTION_NONE 0x0
/* Add sub second timestamps with micro second resolution */
#define AP_CTIME_OPTION_USEC 0x1
+/* Use more compact ISO 8601 format */
+#define AP_CTIME_OPTION_COMPACT 0x2
/**
diff --git a/modules/loggers/mod_log_config.c b/modules/loggers/mod_log_config.c
index 7f0085f030..54acb20021 100644
--- a/modules/loggers/mod_log_config.c
+++ b/modules/loggers/mod_log_config.c
@@ -116,9 +116,11 @@
* '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.)
-*
+ * (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
@@ -369,6 +371,15 @@ static const char *log_request_method(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, r->method);
}
+static const char *log_log_id(request_rec *r, char *a)
+{
+ if (a && !strcmp(a, "c")) {
+ return r->connection->log_id ? r->connection->log_id : "-";
+ }
+ else {
+ return r->log_id ? r->log_id : "-";
+ }
+}
static const char *log_request_protocol(request_rec *r, char *a)
{
return ap_escape_logitem(r->pool, r->protocol);
@@ -1613,6 +1624,7 @@ static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
log_pfn_register(p, "i", log_header_in, 0);
log_pfn_register(p, "o", log_header_out, 0);
log_pfn_register(p, "n", log_note, 0);
+ log_pfn_register(p, "L", log_log_id, 1);
log_pfn_register(p, "e", log_env_var, 0);
log_pfn_register(p, "V", log_server_name, 0);
log_pfn_register(p, "v", log_virtual_host, 0);
diff --git a/server/config.c b/server/config.c
index ea54280ea4..dc9b3b64a4 100644
--- a/server/config.c
+++ b/server/config.c
@@ -747,7 +747,7 @@ AP_DECLARE(const char *) ap_setup_prelinked_modules(process_rec *process)
ap_loaded_modules = (module **)apr_palloc(process->pool,
sizeof(module *) * conf_vector_length);
if (!ap_module_short_names)
- ap_module_short_names = malloc(sizeof(char *) * conf_vector_length);
+ ap_module_short_names = calloc(sizeof(char *), conf_vector_length);
if (ap_loaded_modules == NULL || ap_module_short_names == NULL) {
return "Ouch! Out of memory in ap_setup_prelinked_modules()!";
@@ -780,8 +780,8 @@ AP_DECLARE(const char *) ap_find_module_name(module *m)
AP_DECLARE(const char *) ap_find_module_short_name(int module_index)
{
- if (module_index < 0)
- return "-";
+ if (module_index < 0 || module_index >= conf_vector_length)
+ return NULL;
return ap_module_short_names[module_index];
}
diff --git a/server/core.c b/server/core.c
index 47129cea43..25f307f5b0 100644
--- a/server/core.c
+++ b/server/core.c
@@ -42,6 +42,7 @@
#include "util_filter.h"
#include "util_ebcdic.h"
#include "util_mutex.h"
+#include "util_time.h"
#include "mpm_common.h"
#include "scoreboard.h"
#include "mod_core.h"
@@ -53,6 +54,9 @@
#if defined(RLIMIT_CPU) || defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS) || defined (RLIMIT_NPROC)
#include "unixd.h"
#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
/* LimitRequestBody handling */
#define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1)
@@ -460,6 +464,15 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
if (virt->gprof_dir)
conf->gprof_dir = virt->gprof_dir;
+ if (virt->error_log_format)
+ conf->error_log_format = virt->error_log_format;
+
+ if (virt->error_log_conn)
+ conf->error_log_conn = virt->error_log_conn;
+
+ if (virt->error_log_req)
+ conf->error_log_req = virt->error_log_req;
+
return conf;
}
@@ -3100,6 +3113,229 @@ static const char *set_trace_enable(cmd_parms *cmd, void *dummy,
return NULL;
}
+static apr_hash_t *errorlog_hash;
+
+static int log_constant_item(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ char *end = apr_cpystrn(buf, arg, buflen);
+ return end - buf;
+}
+
+static char *parse_errorlog_misc_string(apr_pool_t *p,
+ ap_errorlog_format_item *it,
+ const char **sa)
+{
+ const char *s;
+ char scratch[MAX_STRING_LEN];
+ char *d = scratch;
+ /*
+ * non-leading white space terminates this string to allow the next field
+ * separator to be inserted
+ */
+ int at_start = 1;
+
+ it->func = log_constant_item;
+ s = *sa;
+
+ while (*s && *s != '%' && (*s != ' ' || at_start) && d < scratch + MAX_STRING_LEN) {
+ if (*s != '\\') {
+ if (*s != ' ') {
+ at_start = 0;
+ }
+ *d++ = *s++;
+ }
+ else {
+ s++;
+ switch (*s) {
+ case 'r':
+ *d++ = '\r';
+ s++;
+ break;
+ case 'n':
+ *d++ = '\n';
+ s++;
+ break;
+ case 't':
+ *d++ = '\t';
+ s++;
+ break;
+ case '\0':
+ /* handle end of string */
+ *d++ = '\\';
+ break;
+ default:
+ /* copy next char verbatim */
+ *d++ = *s++;
+ break;
+ }
+ }
+ }
+ *d = '\0';
+ it->arg = apr_pstrdup(p, scratch);
+
+ *sa = s;
+ return NULL;
+}
+
+static char *parse_errorlog_item(apr_pool_t *p, ap_errorlog_format_item *it,
+ const char **sa)
+{
+ const char *s = *sa;
+ ap_errorlog_handler *handler;
+
+ if (*s != '%') {
+ if (*s == ' ') {
+ it->flags |= AP_ERRORLOG_FLAG_FIELD_SEP;
+ }
+ return parse_errorlog_misc_string(p, it, sa);
+ }
+
+ ++s;
+
+ if (*s == ' ') {
+ /* percent-space (% ) is a field separator */
+ it->flags |= AP_ERRORLOG_FLAG_FIELD_SEP;
+ *sa = ++s;
+ /* recurse */
+ return parse_errorlog_item(p, it, sa);
+ }
+ else if (*s == '%') {
+ it->arg = "%";
+ it->func = log_constant_item;
+ *sa = ++s;
+ return NULL;
+ }
+
+ while (*s) {
+ switch (*s) {
+ case '{':
+ ++s;
+ it->arg = ap_getword(p, &s, '}');
+ break;
+ case '+':
+ ++s;
+ it->flags |= AP_ERRORLOG_FLAG_REQUIRED;
+ break;
+ case '-':
+ ++s;
+ it->flags |= AP_ERRORLOG_FLAG_NULL_AS_HYPHEN;
+ break;
+ case 'M':
+ it->func = NULL;
+ it->flags |= AP_ERRORLOG_FLAG_MESSAGE;
+ *sa = ++s;
+ return NULL;
+ default:
+ handler = (ap_errorlog_handler *)apr_hash_get(errorlog_hash, s, 1);
+ if (!handler) {
+ char dummy[2];
+
+ dummy[0] = *s;
+ dummy[1] = '\0';
+ return apr_pstrcat(p, "Unrecognized error log format directive %",
+ dummy, NULL);
+ }
+ it->func = handler->func;
+ *sa = ++s;
+ return NULL;
+ }
+ }
+
+ return "Ran off end of error log format parsing args to some directive";
+}
+
+static apr_array_header_t *parse_errorlog_string(apr_pool_t *p,
+ const char *s,
+ const char **err,
+ int want_msg_fmt)
+{
+ apr_array_header_t *a = apr_array_make(p, 30,
+ sizeof(ap_errorlog_format_item));
+ char *res;
+ int seen_msg_fmt = 0;
+
+ while (s && *s) {
+ ap_errorlog_format_item *item =
+ (ap_errorlog_format_item *)apr_array_push(a);
+ memset(item, 0, sizeof(*item));
+ res = parse_errorlog_item(p, item, &s);
+ if (res) {
+ *err = res;
+ return NULL;
+ }
+ if (item->flags & AP_ERRORLOG_FLAG_MESSAGE) {
+ if (!want_msg_fmt) {
+ *err = "%M cannot be used in once-per-request or "
+ "once-per-connection formats";
+ return NULL;
+ }
+ seen_msg_fmt = 1;
+ }
+ }
+
+ if (want_msg_fmt && !seen_msg_fmt) {
+ *err = "main ErrorLogFormat must contain message format string '%M'";
+ return NULL;
+ }
+
+ return a;
+}
+
+static const char *set_errorlog_format(cmd_parms *cmd, void *dummy,
+ const char *arg1, const char *arg2)
+{
+ const char *err_string = NULL;
+ core_server_config *conf = ap_get_module_config(cmd->server->module_config,
+ &core_module);
+
+ if (!arg2) {
+ conf->error_log_format = parse_errorlog_string(cmd->pool, arg1,
+ &err_string, 1);
+ }
+ else if (!strcasecmp(arg1, "connection")) {
+ if (!conf->error_log_conn) {
+ conf->error_log_conn = apr_array_make(cmd->pool, 5,
+ sizeof(apr_array_header_t *));
+ }
+
+ if (arg2 && *arg2) {
+ apr_array_header_t **e;
+ e = (apr_array_header_t **) apr_array_push(conf->error_log_conn);
+ *e = parse_errorlog_string(cmd->pool, arg2, &err_string, 0);
+ }
+ }
+ else if (!strcasecmp(arg1, "request")) {
+ if (!conf->error_log_req) {
+ conf->error_log_req = apr_array_make(cmd->pool, 5,
+ sizeof(apr_array_header_t *));
+ }
+
+ if (arg2 && *arg2) {
+ apr_array_header_t **e;
+ e = (apr_array_header_t **) apr_array_push(conf->error_log_req);
+ *e = parse_errorlog_string(cmd->pool, arg2, &err_string, 0);
+ }
+ }
+ else {
+ err_string = "ErrorLogFormat type must be one of request, connection";
+ }
+
+ return err_string;
+}
+
+AP_DECLARE(void) ap_register_errorlog_handler(apr_pool_t *p, char *tag,
+ ap_errorlog_handler_fn_t *handler,
+ int flags)
+{
+ ap_errorlog_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
+ log_struct->func = handler;
+ log_struct->flags = flags;
+
+ apr_hash_set(errorlog_hash, tag, 1, (const void *)log_struct);
+}
+
+
/* Note --- ErrorDocument will now work from .htaccess files.
* The AllowOverride of Fileinfo allows webmasters to turn it off
*/
@@ -3199,6 +3435,8 @@ AP_INIT_TAKE1("ServerRoot", set_server_root, NULL, RSRC_CONF | EXEC_ON_READ,
AP_INIT_TAKE1("ErrorLog", set_server_string_slot,
(void *)APR_OFFSETOF(server_rec, error_fname), RSRC_CONF,
"The filename of the error log"),
+AP_INIT_TAKE12("ErrorLogFormat", set_errorlog_format, NULL, RSRC_CONF,
+ "Format string for the ErrorLog"),
AP_INIT_RAW_ARGS("ServerAlias", set_server_alias, NULL, RSRC_CONF,
"A name or names alternately used to access the server"),
AP_INIT_TAKE1("ServerPath", set_serverpath, NULL, RSRC_CONF,
@@ -3632,6 +3870,9 @@ AP_DECLARE(int) ap_sys_privileges_handlers(int inc)
static int core_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
{
ap_mutex_init(pconf);
+
+ ap_register_builtin_errorlog_handlers(pconf);
+
return APR_SUCCESS;
}
@@ -3843,6 +4084,8 @@ static int core_pre_connection(conn_rec *c, void *csd)
static void register_hooks(apr_pool_t *p)
{
+ errorlog_hash = apr_hash_make(p);
+
/* create_connection and pre_connection should always be hooked
* APR_HOOK_REALLY_LAST by core to give other modules the opportunity
* to install alternate network transports and stop other functions
diff --git a/server/log.c b/server/log.c
index a5ba651b59..42d711744a 100644
--- a/server/log.c
+++ b/server/log.c
@@ -29,6 +29,7 @@
#include "apr_lib.h"
#include "apr_signal.h"
#include "apr_portable.h"
+#include "apr_base64.h"
#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
@@ -378,7 +379,7 @@ static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
cmdtype = APR_SHELLCMD_ENV;
++fname;
}
-
+
/* Spawn a new child logger. If this is the main server_rec,
* the new child must use a dummy stderr since the current
* stderr might be a pipe to the old logger. Otherwise, the
@@ -546,106 +547,132 @@ AP_DECLARE(void) ap_error_log2stderr(server_rec *s) {
}
}
-static void log_error_core(const char *file, int line, int module_index,
- int level,
- apr_status_t status, const server_rec *s,
- const conn_rec *c,
- const request_rec *r, apr_pool_t *pool,
- const char *fmt, va_list args)
+static int cpystrn(char *buf, const char *arg, int buflen)
{
- char errstr[MAX_STRING_LEN];
-#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- char scratch[MAX_STRING_LEN];
-#endif
- apr_size_t len, errstrlen;
- apr_file_t *logf = NULL;
- const char *referer;
- int level_and_mask = level & APLOG_LEVELMASK;
+ char *end;
+ if (!arg)
+ return 0;
+ end = apr_cpystrn(buf, arg, buflen);
+ return end - buf;
+}
- if (r && r->connection) {
- c = r->connection;
- }
- if (s == NULL) {
- /*
- * If we are doing stderr logging (startup), don't log messages that are
- * above the default server log level unless it is a startup/shutdown
- * notice
- */
-#ifndef DEBUG
- if ((level_and_mask != APLOG_NOTICE)
- && (level_and_mask > ap_default_loglevel)) {
- return;
- }
-#endif
+static int log_remote_address(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->c)
+ return apr_snprintf(buf, buflen, "%s:%d", info->c->remote_ip,
+ info->c->remote_addr->port);
+ else
+ return 0;
+}
- logf = stderr_log;
+static int log_local_address(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->c)
+ return apr_snprintf(buf, buflen, "%s:%d", info->c->local_ip,
+ info->c->local_addr->port);
+ else
+ return 0;
+}
+
+static int log_pid(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ pid_t pid = getpid();
+ return apr_snprintf(buf, buflen, "%" APR_PID_T_FMT, pid);
+}
+
+static int log_tid(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+#if APR_HAS_THREADS
+ int result;
+
+ if (ap_mpm_query(AP_MPMQ_IS_THREADED, &result) == APR_SUCCESS
+ && result != AP_MPMQ_NOT_SUPPORTED)
+ {
+ apr_os_thread_t tid = apr_os_thread_current();
+ return apr_snprintf(buf, buflen, "%pT", &tid);
}
- else {
- int configured_level = r ? ap_get_request_module_loglevel(r, module_index) :
- c ? ap_get_conn_server_module_loglevel(c, s, module_index) :
- ap_get_server_module_loglevel(s, module_index);
- if (s->error_log) {
- /*
- * If we are doing normal logging, don't log messages that are
- * above the module's log level unless it is a startup/shutdown notice
- */
- if ((level_and_mask != APLOG_NOTICE)
- && (level_and_mask > configured_level)) {
- return;
- }
+#endif
+ return 0;
+}
- logf = s->error_log;
- }
- else {
- /*
- * If we are doing syslog logging, don't log messages that are
- * above the module's log level (including a startup/shutdown notice)
- */
- if (level_and_mask > configured_level) {
- return;
- }
+static int log_ctime(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ int time_len = buflen;
+ int option = AP_CTIME_OPTION_NONE;
+
+ while(arg && *arg) {
+ switch (*arg) {
+ case 'u': option |= AP_CTIME_OPTION_USEC;
+ break;
+ case 'c': option |= AP_CTIME_OPTION_COMPACT;
+ break;
}
+ arg++;
}
- if (logf && ((level & APLOG_STARTUP) != APLOG_STARTUP)) {
- int time_len;
+ ap_recent_ctime_ex(buf, apr_time_now(), option, &time_len);
- errstr[0] = '[';
- len = 1;
- time_len = MAX_STRING_LEN - len;
- ap_recent_ctime_ex(errstr + len, apr_time_now(),
- AP_CTIME_OPTION_USEC, &time_len);
- len += time_len -1;
- errstr[len++] = ']';
- errstr[len++] = ' ';
- } else {
- len = 0;
- }
-
- if ((level & APLOG_STARTUP) != APLOG_STARTUP) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "[%s] ", priorities[level_and_mask].t_name);
+ /* ap_recent_ctime_ex includes the trailing \0 in time_len */
+ return time_len - 1;
+}
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "[pid %" APR_PID_T_FMT, getpid());
-#if APR_HAS_THREADS
- {
- int result;
+static int log_loglevel(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->level < 0)
+ return 0;
+ else
+ return cpystrn(buf, priorities[info->level].t_name, buflen);
+}
- if (ap_mpm_query(AP_MPMQ_IS_THREADED, &result) == APR_SUCCESS
- && result != AP_MPMQ_NOT_SUPPORTED) {
- apr_os_thread_t tid = apr_os_thread_current();
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- ":tid %pT", &tid);
- }
+static int log_log_id(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ /*
+ * C: log conn log_id if available,
+ * c: log conn log id if available and not a once-per-request log line
+ * else: log request log id if available
+ */
+ if (arg && !strcasecmp(arg, "c")) {
+ if (info->c && (*arg != 'C' || !info->r)) {
+ return cpystrn(buf, info->c->log_id, buflen);
}
-#endif
- errstr[len++] = ']';
- errstr[len++] = ' ';
}
+ else if (info->r) {
+ return cpystrn(buf, info->r->log_id, buflen);
+ }
+ return 0;
+}
+
+static int log_keepalives(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (!info->c)
+ return 0;
+
+ return apr_snprintf(buf, buflen, "%d", info->c->keepalives);
+}
- if (file && level_and_mask >= APLOG_DEBUG) {
+static int log_module_name(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ return cpystrn(buf, ap_find_module_short_name(info->module_index), buflen);
+}
+
+static int log_file_line(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->file == NULL) {
+ return 0;
+ }
+ else {
+ const char *file = info->file;
#if defined(_OSD_POSIX) || defined(WIN32) || defined(__MVS__)
char tmp[256];
char *e = strrchr(file, '/');
@@ -677,75 +704,303 @@ static void log_error_core(const char *file, int line, int module_index,
file = p + 1;
}
#endif /*_OSD_POSIX || WIN32 */
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "%s(%d): ", file, line);
+ return apr_snprintf(buf, buflen, "%s(%d)", file, info->line);
+ }
+}
+
+static int log_apr_status(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ apr_status_t status = info->status;
+ int len = 0;
+ if (!status)
+ return 0;
+
+ if (status < APR_OS_START_EAIERR) {
+ len += apr_snprintf(buf + len, buflen - len,
+ "(%d)", status);
+ }
+ else if (status < APR_OS_START_SYSERR) {
+ len += apr_snprintf(buf + len, buflen - len,
+ "(EAI %d)", status - APR_OS_START_EAIERR);
+ }
+ else if (status < 100000 + APR_OS_START_SYSERR) {
+ len += apr_snprintf(buf + len, buflen - len,
+ "(OS %d)", status - APR_OS_START_SYSERR);
+ }
+ else {
+ len += apr_snprintf(buf + len, buflen - len,
+ "(os 0x%08x)", status - APR_OS_START_SYSERR);
}
+ apr_strerror(status, buf + len, buflen - len);
+ len += strlen(buf + len);
+ return len;
+}
+static int log_header(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ const char *header;
+ int len = 0;
+#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
+ char scratch[MAX_STRING_LEN];
+#endif
+
+ if ( info->r && (header = apr_table_get(info->r->headers_in, arg))
+#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
+ && ap_escape_errorlog_item(scratch, header, MAX_STRING_LEN)
+#endif
+ ) {
+ len = cpystrn(buf,
+#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
+ scratch,
+#else
+ header,
+#endif
+ buflen);
+ }
+ return len;
+}
+
+
+
+
+AP_DECLARE(void) ap_register_builtin_errorlog_handlers(apr_pool_t *p)
+{
+ ap_register_errorlog_handler(p, "a", log_remote_address, 0);
+ ap_register_errorlog_handler(p, "A", log_local_address, 0);
+ ap_register_errorlog_handler(p, "E", log_apr_status, 0);
+ ap_register_errorlog_handler(p, "F", log_file_line, 0);
+ ap_register_errorlog_handler(p, "i", log_header, 0);
+ ap_register_errorlog_handler(p, "k", log_keepalives, 0);
+ ap_register_errorlog_handler(p, "l", log_loglevel, 0);
+ ap_register_errorlog_handler(p, "L", log_log_id, 0);
+ ap_register_errorlog_handler(p, "m", log_module_name, 0);
+ ap_register_errorlog_handler(p, "P", log_pid, 0);
+ ap_register_errorlog_handler(p, "t", log_ctime, 0);
+ ap_register_errorlog_handler(p, "T", log_tid, 0);
+
+ /* XXX: TODO: envvars, notes */
+}
+
+static void add_log_id(const conn_rec *c, const request_rec *r)
+{
+ apr_uint64_t id, tmp;
+ pid_t pid;
+ int len;
+ char *encoded;
+
+ if (r && r->request_time) {
+ id = (apr_uint64_t)r->request_time;
+ }
+ else {
+ id = (apr_uint64_t)apr_time_now();
+ }
+
+ pid = getpid();
+ if (sizeof(pid_t) > 2) {
+ tmp = pid;
+ tmp = tmp << 40;
+ id ^= tmp;
+ pid = pid >> 24;
+ tmp = pid;
+ tmp = tmp << 56;
+ id ^= tmp;
+ }
+ else {
+ tmp = pid;
+ tmp = tmp << 40;
+ id ^= tmp;
+ }
+#if APR_HAS_THREADS
if (c) {
- /* XXX: TODO: add a method of selecting whether logged remote
- * addresses are in dotted quad or resolved form... dotted
- * quad is the most secure, which is why I'm implementing it
- * first. -djg
- */
+ apr_uintptr_t tmp2 = (apr_uintptr_t)c->current_thread;
+ tmp = tmp2;
+ tmp = tmp << 32;
+ id ^= tmp;
+ }
+#endif
+
+ /*
+ * The apr-util docs wrongly states encoded strings are not 0-terminated.
+ * Let's be save and allocate an additional byte.
+ */
+ len = 1 + apr_base64_encode_len(sizeof(id));
+ encoded = apr_palloc(r ? r->pool : c->pool, len);
+ apr_base64_encode(encoded, (char *)&id, sizeof(id));
+ encoded[11] = '\0'; /* omit last char which is always '=' */
+
+ /* need to cast const away */
+ if (r) {
+ ((request_rec *)r)->log_id = encoded;
+ }
+ else {
+ ((conn_rec *)c)->log_id = encoded;
+ }
+}
+
+/*
+ * This is used if no error log format is defined and during startup.
+ * It automatically omits the timestamp if logging to syslog.
+ */
+static int do_errorlog_default(const ap_errorlog_info *info, char *buf,
+ int buflen, int *errstr_start, int *errstr_end,
+ const char *errstr_fmt, va_list args)
+{
+ int len = 0;
+ int field_start = 0;
+ int item_len;
+#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
+ char scratch[MAX_STRING_LEN];
+#endif
+
+ if (!info->using_syslog && !info->startup) {
+ buf[len++] = '[';
+ len += log_ctime(info, "u", buf + len, buflen - len);
+ buf[len++] = ']';
+ buf[len++] = ' ';
+ }
+
+ if (!info->startup) {
+ buf[len++] = '[';
+ len += log_module_name(info, NULL, buf + len, buflen - len);
+ buf[len++] = ':';
+ len += log_loglevel(info, NULL, buf + len, buflen - len);
+ len += cpystrn(buf + len, "] [pid ", buflen - len);
+
+ len += log_pid(info, NULL, buf + len, buflen - len);
+#if APR_HAS_THREADS
+ field_start = len;
+ len += cpystrn(buf + len, ":tid ", buflen - len);
+ item_len = log_tid(info, NULL, buf + len, buflen - len);
+ if (!item_len)
+ len = field_start;
+ else
+ len += item_len;
+#endif
+ buf[len++] = ']';
+ buf[len++] = ' ';
+ }
+
+ if (info->level >= APLOG_DEBUG) {
+ item_len = log_file_line(info, NULL, buf + len, buflen - len);
+ if (item_len) {
+ len += item_len;
+ len += cpystrn(buf + len, ": ", buflen - len);
+ }
+ }
+
+ if (info->status) {
+ item_len = log_apr_status(info, NULL, buf + len, buflen - len);
+ if (item_len) {
+ len += item_len;
+ len += cpystrn(buf + len, ": ", buflen - len);
+ }
+ }
+
+ if (info->c) {
/*
* remote_ip can be client or backend server. If we have a scoreboard
* handle, it is likely a client.
*/
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- c->sbh ? "[client %s:%d] " : "[remote %s:%d] ",
- c->remote_ip, c->remote_addr->port);
- }
- if (status != 0) {
- if (status < APR_OS_START_EAIERR) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(%d)", status);
- }
- else if (status < APR_OS_START_SYSERR) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(EAI %d)", status - APR_OS_START_EAIERR);
- }
- else if (status < 100000 + APR_OS_START_SYSERR) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(OS %d)", status - APR_OS_START_SYSERR);
- }
- else {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(os 0x%08x)", status - APR_OS_START_SYSERR);
- }
- apr_strerror(status, errstr + len, MAX_STRING_LEN - len);
- len += strlen(errstr + len);
- if (MAX_STRING_LEN - len > 2) {
- errstr[len++] = ':';
- errstr[len++] = ' ';
- errstr[len] = '\0';
- }
+ len += apr_snprintf(buf + len, buflen - len,
+ info->c->sbh ? "[client %s:%d] " : "[remote %s:%d] ",
+ info->c->remote_ip, info->c->remote_addr->port);
}
- errstrlen = len;
+ /* the actual error message */
+ *errstr_start = len;
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- if (apr_vsnprintf(scratch, MAX_STRING_LEN - len, fmt, args)) {
- len += ap_escape_errorlog_item(errstr + len, scratch,
- MAX_STRING_LEN - len);
+ if (apr_vsnprintf(scratch, MAX_STRING_LEN, errstr_fmt, args)) {
+ len += ap_escape_errorlog_item(buf + len, scratch,
+ buflen - len);
+
}
#else
- len += apr_vsnprintf(errstr + len, MAX_STRING_LEN - len, fmt, args);
+ len += apr_vsnprintf(buf + len, buflen - len, errstr_fmt, args);
#endif
+ *errstr_end = len;
+
+ field_start = len;
+ len += cpystrn(buf + len, ", referer: ", buflen - len);
+ item_len = log_header(info, "Referer", buf + len, buflen - len);
+ if (item_len)
+ len += item_len;
+ else
+ len = field_start;
+
+ return len;
+}
- if ( r && (referer = apr_table_get(r->headers_in, "Referer"))
+static int do_errorlog_format(apr_array_header_t *fmt, ap_errorlog_info *info,
+ char *buf, int buflen, int *errstr_start,
+ int *errstr_end, const char *err_fmt, va_list args)
+{
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- && ap_escape_errorlog_item(scratch, referer, MAX_STRING_LEN - len)
+ char scratch[MAX_STRING_LEN];
#endif
- ) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- ", referer: %s",
+ int i;
+ int len = 0;
+ int field_start = 0;
+ int skipping = 0;
+ ap_errorlog_format_item *items = (ap_errorlog_format_item *)fmt->elts;
+
+ for (i = 0; i < fmt->nelts; ++i) {
+ ap_errorlog_format_item *item = &items[i];
+ if (item->flags & AP_ERRORLOG_FLAG_FIELD_SEP) {
+ if (skipping) {
+ skipping = 0;
+ }
+ else {
+ field_start = len;
+ }
+ }
+
+ if (item->flags & AP_ERRORLOG_FLAG_MESSAGE) {
+ /* the actual error message */
+ *errstr_start = len;
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- scratch
+ if (apr_vsnprintf(scratch, MAX_STRING_LEN, err_fmt, args)) {
+ len += ap_escape_errorlog_item(buf + len, scratch,
+ buflen - len);
+
+ }
#else
- referer
+ len += apr_vsnprintf(buf + len, buflen - len, err_fmt, args);
#endif
- );
+ *errstr_end = len;
+ }
+ else if (skipping) {
+ continue;
+ }
+ else {
+ int item_len = (*item->func)(info, item->arg, buf + len,
+ buflen - len);
+ if (!item_len) {
+ if (item->flags & AP_ERRORLOG_FLAG_REQUIRED) {
+ /* required item is empty. skip whole line */
+ buf[0] = '\0';
+ return 0;
+ }
+ else if (item->flags & AP_ERRORLOG_FLAG_NULL_AS_HYPHEN) {
+ buf[len++] = '-';
+ }
+ else {
+ len = field_start;
+ skipping = 1;
+ }
+ }
+ else {
+ len += item_len;
+ }
+ }
}
+ return len;
+}
+static void write_logline(char *errstr, apr_size_t len, apr_file_t *logf,
+ int level)
+{
/* NULL if we are logging to syslog */
if (logf) {
/* Truncate for the terminator (as apr_snprintf does) */
@@ -758,13 +1013,193 @@ static void log_error_core(const char *file, int line, int module_index,
}
#ifdef HAVE_SYSLOG
else {
- syslog(level_and_mask < LOG_PRIMASK ? level_and_mask : APLOG_DEBUG,
- "%s", errstr);
+ syslog(level < LOG_PRIMASK ? level : APLOG_DEBUG, "%s", errstr);
}
#endif
+}
- ap_run_error_log(file, line, module_index, level, status, s, r, pool,
- errstr + errstrlen);
+static void log_error_core(const char *file, int line, int module_index,
+ int level,
+ apr_status_t status, const server_rec *s,
+ const conn_rec *c,
+ const request_rec *r, apr_pool_t *pool,
+ const char *fmt, va_list args)
+{
+ char errstr[MAX_STRING_LEN];
+ apr_file_t *logf = NULL;
+ int level_and_mask = level & APLOG_LEVELMASK;
+ const request_rec *rmain = NULL;
+ core_server_config *sconf = NULL;
+ ap_errorlog_info info;
+
+ /* do we need to log once-per-req or once-per-conn info? */
+ int log_conn_info = 0, log_req_info = 0;
+ apr_array_header_t **lines = NULL;
+ int done = 0;
+ int line_number = 0;
+
+ if (r && r->connection) {
+ c = r->connection;
+ }
+
+ if (s == NULL) {
+ /*
+ * If we are doing stderr logging (startup), don't log messages that are
+ * above the default server log level unless it is a startup/shutdown
+ * notice
+ */
+#ifndef DEBUG
+ if ((level_and_mask != APLOG_NOTICE)
+ && (level_and_mask > ap_default_loglevel)) {
+ return;
+ }
+#endif
+
+ logf = stderr_log;
+ }
+ else {
+ int configured_level = r ? ap_get_request_module_loglevel(r, module_index) :
+ c ? ap_get_conn_server_module_loglevel(c, s, module_index) :
+ ap_get_server_module_loglevel(s, module_index);
+ if (s->error_log) {
+ /*
+ * If we are doing normal logging, don't log messages that are
+ * above the module's log level unless it is a startup/shutdown notice
+ */
+ if ((level_and_mask != APLOG_NOTICE)
+ && (level_and_mask > configured_level)) {
+ return;
+ }
+
+ logf = s->error_log;
+ }
+ else {
+ /*
+ * If we are doing syslog logging, don't log messages that are
+ * above the module's log level (including a startup/shutdown notice)
+ */
+ if (level_and_mask > configured_level) {
+ return;
+ }
+ }
+
+ sconf = ap_get_module_config(s->module_config, &core_module);
+ if (c && !c->log_id) {
+ add_log_id(c, NULL);
+ if (sconf->error_log_conn && sconf->error_log_conn->nelts > 0)
+ log_conn_info = 1;
+ }
+ if (r) {
+ if (r->main)
+ rmain = r->main;
+ else
+ rmain = r;
+
+ if (!rmain->log_id) {
+ /* XXX: do we need separate log ids for subrequests? */
+ if (sconf->error_log_req && sconf->error_log_req->nelts > 0)
+ log_req_info = 1;
+ /*
+ * XXX: potential optimization: only create log id if %L is
+ * XXX: actually used
+ */
+ add_log_id(c, rmain);
+ }
+ }
+ }
+
+ info.s = s;
+ info.c = c;
+ info.file = file;
+ info.line = line;
+ info.status = status;
+ info.using_syslog = (logf == NULL);
+ info.startup = ((level & APLOG_STARTUP) == APLOG_STARTUP);
+
+
+ while (!done) {
+ apr_array_header_t *log_format;
+ int len = 0, errstr_start = 0, errstr_end = 0;
+ /* XXX: potential optimization: format common prefixes only once */
+ if (log_conn_info) {
+ /* once-per-connection info */
+ if (line_number == 0) {
+ lines = (apr_array_header_t **)sconf->error_log_conn->elts;
+ info.r = NULL;
+ info.rmain = NULL;
+ info.level = -1;
+ info.module_index = APLOG_NO_MODULE;
+ }
+
+ log_format = lines[line_number++];
+
+ if (line_number == sconf->error_log_conn->nelts) {
+ /* this is the last line of once-per-connection info */
+ line_number = 0;
+ log_conn_info = 0;
+ }
+ }
+ else if (log_req_info) {
+ /* once-per-request info */
+ if (line_number == 0) {
+ lines = (apr_array_header_t **)sconf->error_log_req->elts;
+ info.r = rmain;
+ info.rmain = rmain;
+ info.level = -1;
+ info.module_index = APLOG_NO_MODULE;
+ }
+
+ log_format = lines[line_number++];
+
+ if (line_number == sconf->error_log_req->nelts) {
+ /* this is the last line of once-per-request info */
+ line_number = 0;
+ log_req_info = 0;
+ }
+ }
+ else {
+ /* the actual error message */
+ info.r = r;
+ info.rmain = rmain;
+ info.level = level_and_mask;
+ info.module_index = module_index;
+ log_format = sconf ? sconf->error_log_format : NULL;
+ done = 1;
+ }
+
+ /*
+ * prepare and log one line
+ */
+
+ if (log_format) {
+ len += do_errorlog_format(log_format, &info, errstr + len,
+ MAX_STRING_LEN - len,
+ &errstr_start, &errstr_end, fmt, args);
+ }
+ else {
+ len += do_errorlog_default(&info, errstr + len, MAX_STRING_LEN - len,
+ &errstr_start, &errstr_end, fmt, args);
+ }
+
+ if (!*errstr)
+ {
+ /*
+ * Don't log empty lines. This can happen with once-per-conn/req
+ * info if an item with AP_ERRORLOG_FLAG_REQUIRED is NULL.
+ */
+ continue;
+ }
+ write_logline(errstr, len, logf, level_and_mask);
+
+ if (!log_format) {
+ /* only pass the real error string to the hook */
+ errstr[errstr_end] = '\0';
+ ap_run_error_log(file, line, module_index, level, status, s, r, pool,
+ errstr + errstr_start);
+ }
+
+ *errstr = '\0';
+ }
}
AP_DECLARE(void) ap_log_error_(const char *file, int line, int module_index,
diff --git a/server/util_time.c b/server/util_time.c
index 4e0df9912e..3632d0d583 100644
--- a/server/util_time.c
+++ b/server/util_time.c
@@ -22,6 +22,9 @@
* */
#define AP_CTIME_USEC_LENGTH 7
+/* Length of ISO 8601 date/time */
+#define AP_CTIME_COMPACT_LEN 20
+
/* Cache for exploded values of recent timestamps
*/
@@ -169,7 +172,11 @@ AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
/* Calculate the needed buffer length */
- needed = APR_CTIME_LEN;
+ if (option & AP_CTIME_OPTION_COMPACT)
+ needed = AP_CTIME_COMPACT_LEN;
+ else
+ needed = APR_CTIME_LEN;
+
if (option & AP_CTIME_OPTION_USEC) {
needed += AP_CTIME_USEC_LENGTH;
}
@@ -187,18 +194,34 @@ AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
/* example without options: "Wed Jun 30 21:49:08 1993" */
/* 123456789012345678901234 */
+ /* example for compact format: "1993-06-30 21:49:08" */
+ /* 1234567890123456789 */
ap_explode_recent_localtime(&xt, t);
- s = &apr_day_snames[xt.tm_wday][0];
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = ' ';
- s = &apr_month_snames[xt.tm_mon][0];
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = ' ';
+ real_year = 1900 + xt.tm_year;
+ if (option & AP_CTIME_OPTION_COMPACT) {
+ int real_month = xt.tm_mon + 1;
+ *date_str++ = real_year / 1000 + '0';
+ *date_str++ = real_year % 1000 / 100 + '0';
+ *date_str++ = real_year % 100 / 10 + '0';
+ *date_str++ = real_year % 10 + '0';
+ *date_str++ = '-';
+ *date_str++ = real_month / 10 + '0';
+ *date_str++ = real_month % 10 + '0';
+ *date_str++ = '-';
+ }
+ else {
+ s = &apr_day_snames[xt.tm_wday][0];
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = ' ';
+ s = &apr_month_snames[xt.tm_mon][0];
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = ' ';
+ }
*date_str++ = xt.tm_mday / 10 + '0';
*date_str++ = xt.tm_mday % 10 + '0';
*date_str++ = ' ';
@@ -219,12 +242,13 @@ AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
usec = usec % div;
}
}
- *date_str++ = ' ';
- real_year = 1900 + xt.tm_year;
- *date_str++ = real_year / 1000 + '0';
- *date_str++ = real_year % 1000 / 100 + '0';
- *date_str++ = real_year % 100 / 10 + '0';
- *date_str++ = real_year % 10 + '0';
+ if (!(option & AP_CTIME_OPTION_COMPACT)) {
+ *date_str++ = ' ';
+ *date_str++ = real_year / 1000 + '0';
+ *date_str++ = real_year % 1000 / 100 + '0';
+ *date_str++ = real_year % 100 / 10 + '0';
+ *date_str++ = real_year % 10 + '0';
+ }
*date_str++ = 0;
return APR_SUCCESS;