httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s.@apache.org
Subject svn commit: r992806 - in /httpd/httpd/trunk: ./ docs/manual/ docs/manual/mod/ include/ modules/loggers/ server/
Date Sun, 05 Sep 2010 15:44:21 GMT
Author: sf
Date: Sun Sep  5 15:44:19 2010
New Revision: 992806

URL: http://svn.apache.org/viewvc?rev=992806&view=rev
Log:
Add ErrorLogFormat directive for configuring the 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.

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/STATUS
    httpd/httpd/trunk/docs/manual/logs.xml
    httpd/httpd/trunk/docs/manual/mod/core.xml
    httpd/httpd/trunk/docs/manual/mod/mod_log_config.xml
    httpd/httpd/trunk/include/ap_mmn.h
    httpd/httpd/trunk/include/http_config.h
    httpd/httpd/trunk/include/http_core.h
    httpd/httpd/trunk/include/httpd.h
    httpd/httpd/trunk/include/util_time.h
    httpd/httpd/trunk/modules/loggers/mod_log_config.c
    httpd/httpd/trunk/server/config.c
    httpd/httpd/trunk/server/core.c
    httpd/httpd/trunk/server/log.c
    httpd/httpd/trunk/server/util_time.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Sun Sep  5 15:44:19 2010
@@ -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

Modified: httpd/httpd/trunk/STATUS
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/STATUS?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/STATUS (original)
+++ httpd/httpd/trunk/STATUS Sun Sep  5 15:44:19 2010
@@ -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.
 

Modified: httpd/httpd/trunk/docs/manual/logs.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/logs.xml?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/logs.xml (original)
+++ httpd/httpd/trunk/docs/manual/logs.xml Sun Sep  5 15:44:19 2010
@@ -92,6 +92,7 @@
       </modulelist>
       <directivelist>
         <directive module="core">ErrorLog</directive>
+        <directive module="core">ErrorLogFormat</directive>
         <directive module="core">LogLevel</directive>
       </directivelist>
     </related>

Modified: httpd/httpd/trunk/docs/manual/mod/core.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/core.xml?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/core.xml (original)
+++ httpd/httpd/trunk/docs/manual/mod/core.xml Sun Sep  5 15:44:19 2010
@@ -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>

Modified: httpd/httpd/trunk/docs/manual/mod/mod_log_config.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_log_config.xml?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_log_config.xml (original)
+++ httpd/httpd/trunk/docs/manual/mod/mod_log_config.xml Sun Sep  5 15:44:19 2010
@@ -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>
 

Modified: httpd/httpd/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Sun Sep  5 15:44:19 2010
@@ -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

Modified: httpd/httpd/trunk/include/http_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/http_config.h?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/include/http_config.h (original)
+++ httpd/httpd/trunk/include/http_config.h Sun Sep  5 15:44:19 2010
@@ -724,7 +724,7 @@ AP_DECLARE(const char *) ap_find_module_
 /**
  * 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);
 /**

Modified: httpd/httpd/trunk/include/http_core.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/http_core.h?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/include/http_core.h (original)
+++ httpd/httpd/trunk/include/http_core.h Sun Sep  5 15:44:19 2010
@@ -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_lo
 
 /* ----------------------------------------------------------------------
  *
+ * 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
  */
 

Modified: httpd/httpd/trunk/include/httpd.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/httpd.h?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/include/httpd.h (original)
+++ httpd/httpd/trunk/include/httpd.h Sun Sep  5 15:44:19 2010
@@ -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

Modified: httpd/httpd/trunk/include/util_time.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/util_time.h?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/include/util_time.h (original)
+++ httpd/httpd/trunk/include/util_time.h Sun Sep  5 15:44:19 2010
@@ -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
 
 
 /**

Modified: httpd/httpd/trunk/modules/loggers/mod_log_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/loggers/mod_log_config.c?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/loggers/mod_log_config.c (original)
+++ httpd/httpd/trunk/modules/loggers/mod_log_config.c Sun Sep  5 15:44:19 2010
@@ -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(re
 {
     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,
         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);

Modified: httpd/httpd/trunk/server/config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/config.c?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/server/config.c (original)
+++ httpd/httpd/trunk/server/config.c Sun Sep  5 15:44:19 2010
@@ -747,7 +747,7 @@ AP_DECLARE(const char *) ap_setup_prelin
     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_
 
 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];
 }
 

Modified: httpd/httpd/trunk/server/core.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/core.c?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/server/core.c (original)
+++ httpd/httpd/trunk/server/core.c Sun Sep  5 15:44:19 2010
@@ -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(a
     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_
     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_r
 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_handle
 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 
 
 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 

Modified: httpd/httpd/trunk/server/log.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/log.c?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/server/log.c (original)
+++ httpd/httpd/trunk/server/log.c Sun Sep  5 15:44:19 2010
@@ -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,
             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(ser
     }
 }
 
-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 *f
             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;
 
-    if (   r && (referer = apr_table_get(r->headers_in, "Referer"))
+    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;
+}
+
+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 *f
     }
 #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,

Modified: httpd/httpd/trunk/server/util_time.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/util_time.c?rev=992806&r1=992805&r2=992806&view=diff
==============================================================================
--- httpd/httpd/trunk/server/util_time.c (original)
+++ httpd/httpd/trunk/server/util_time.c Sun Sep  5 15:44:19 2010
@@ -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
 
 
     /* 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
 
     /* 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
             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;



Mime
View raw message