Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 07 Jun 2004 04:17:24 -0400
From:      Yarema <yds@CoolRat.org>
To:        Clement Laforet <clement@FreeBSD.org>
Cc:        Ports FreeBSD <ports@FreeBSD.org>
Subject:   apache2 & mod_log_config-st & mod_log_mysql
Message-ID:  <61670F38EE92BEC06E2BE009@volyn.coolrat.org>

next in thread | raw e-mail | index | archive | help
--==========B6C94F11EFDA6764AC7E==========
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Hello Clement,

I have a production setup using apache+mod_ssl with mod_log_sql.  (The 
latter leaves something to be desired.)  So I was very happy to discover 
mod_log_mysql for apache2, both of which you maintain.  While combing 
through the docs for mod_log_mysql at 
<http://bitbrook.de/software/mod_log_mysql/>; a few things struck me as 
somewhat incomplete.

The docs list as one of the requirements to have a fixed apr_reslist.c -- 
which is missing from the way you implemented the mod_log_mysql -> 
mod_log_config-st -> apache2 ports dependencies.  Then there's the whole 
issue of having mod_log_config-st in a separate port, itself missing the 
modified mod_logio.c, which returns i/o counts as numbers to 
mod_mod_log_config, not as strings as the original would do.

So here's what I propose.  How about dropping the mod_log_config-st 
entirely and mod_log_mysql dependency on it.  And replacing the whole mess 
with the four additional patches to the apache2 port that I'm attaching 
with this email.  So far as I can tell this does not break anything and 
does fix the two omissions mentioned above.  Not to mention just being 
simpler IMHO.

The patches were derived from the sources in 
<http://bitbrook.de/software/mod_log_mysql/mod_log_sources.tar.bz2>; except 
for apr_reslist.c, version 1.9 of  which was downloaded from 
<http://cvs.apache.org/viewcvs.cgi/apr-util/misc/apr_reslist.c>;

Seems like the Right Thing (TM) to me. :)  What do you think?

-- 
Yarema
http://yds.CoolRat.org
--==========B6C94F11EFDA6764AC7E==========
Content-Type: text/plain; charset=iso-8859-1;
 name="patch-srclib:apr-util:misc:apr_reslist.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="patch-srclib:apr-util:misc:apr_reslist.c"; size=3583

--- srclib/apr-util/misc/apr_reslist.c.orig	Fri Feb 13 04:52:43 2004
+++ srclib/apr-util/misc/apr_reslist.c	Mon Mar 15 08:21:26 2004
@@ -49,6 +49,7 @@
     int smax; /* soft maximum on the total number of resources */
     int hmax; /* hard maximum on the total number of resources */
     apr_interval_time_t ttl; /* TTL when we have too many resources */
+    apr_interval_time_t timeout; /* Timeout for waiting on resource */
     apr_reslist_constructor constructor;
     apr_reslist_destructor destructor;
     void *params; /* opaque data passed to constructor and destructor =
calls */
@@ -118,12 +119,9 @@
     res =3D apr_pcalloc(reslist->pool, sizeof(*res));
=20
     rv =3D reslist->constructor(&res->opaque, reslist->params, =
reslist->pool);
-    if (rv !=3D APR_SUCCESS) {
-        return rv;
-    }
=20
     *ret_res =3D res;
-    return APR_SUCCESS;
+    return rv;
 }
=20
 /**
@@ -132,14 +130,7 @@
  */
 static apr_status_t destroy_resource(apr_reslist_t *reslist, apr_res_t =
*res)
 {
-    apr_status_t rv;
-
-    rv =3D reslist->destructor(res->opaque, reslist->params, =
reslist->pool);
-    if (rv !=3D APR_SUCCESS) {
-        return rv;
-    }
-
-    return APR_SUCCESS;
+    return reslist->destructor(res->opaque, reslist->params, =
reslist->pool);
 }
=20
 static apr_status_t reslist_cleanup(void *data_)
@@ -187,6 +178,7 @@
         /* Create the resource */
         rv =3D create_resource(reslist, &res);
         if (rv !=3D APR_SUCCESS) {
+            free_container(reslist, res);
             apr_thread_mutex_unlock(reslist->listlock);
             return rv;
         }
@@ -313,7 +305,15 @@
      * a new one, or something becomes free. */
     else while (reslist->ntotal >=3D reslist->hmax
                 && reslist->nidle <=3D 0) {
-        apr_thread_cond_wait(reslist->avail, reslist->listlock);
+        if (reslist->timeout) {
+            if ((rv =3D apr_thread_cond_timedwait(reslist->avail,=20
+                reslist->listlock, reslist->timeout)) !=3D APR_SUCCESS) {
+                apr_thread_mutex_unlock(reslist->listlock);
+                return rv;
+            }
+        }
+        else
+            apr_thread_cond_wait(reslist->avail, reslist->listlock);
     }
     /* If we popped out of the loop, first try to see if there
      * are new resources available for immediate use. */
@@ -329,17 +329,13 @@
      * a resource to fill the slot and use it. */
     else {
         rv =3D create_resource(reslist, &res);
-
-        if (rv !=3D APR_SUCCESS) {
-           apr_thread_mutex_unlock(reslist->listlock);
-           return rv;
+        if (rv =3D=3D APR_SUCCESS) {
+            reslist->ntotal++;
+            *resource =3D res->opaque;
         }
-
-        reslist->ntotal++;
-        *resource =3D res->opaque;
         free_container(reslist, res);
         apr_thread_mutex_unlock(reslist->listlock);
-        return APR_SUCCESS;
+        return rv;
     }
 }
=20
@@ -356,6 +352,23 @@
     apr_thread_mutex_unlock(reslist->listlock);
=20
     return reslist_maint(reslist);
+}
+
+APU_DECLARE(void) apr_reslist_timeout_set(apr_reslist_t *reslist,
+                                          apr_interval_time_t timeout)
+{
+    reslist->timeout =3D timeout;
+}
+
+APU_DECLARE(apr_status_t) apr_reslist_invalidate(apr_reslist_t *reslist,
+                                                 void *resource)
+{
+    apr_status_t ret;
+    apr_thread_mutex_lock(reslist->listlock);
+    ret =3D reslist->destructor(resource, reslist->params, reslist->pool);
+    reslist->ntotal--;
+    apr_thread_mutex_unlock(reslist->listlock);
+    return ret;
 }
=20
 #endif  /* APR_HAS_THREADS */

--==========B6C94F11EFDA6764AC7E==========
Content-Type: text/plain; charset=iso-8859-1;
 name="patch-modules:loggers:mod_log_config.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="patch-modules:loggers:mod_log_config.c"; size=61326

--- modules/loggers/mod_log_config.c.orig	Wed Mar  3 06:07:50 2004
+++ modules/loggers/mod_log_config.c	Fri Oct 17 04:58:07 2003
@@ -17,7 +17,12 @@
  * Modified by djm@va.pubnix.com:
  * If no TransferLog is given explicitly, decline to log.
  *
- * This is module implements the TransferLog directive (same as the
+ * Modified by st:
+ * Added logic to give other log writers a more detailed view of the
+ * data to be logged.
+ * Added item %R, which returns the unmodified URL (this is not %U).
+ *
+ * This module implements the TransferLog directive (same as the
  * common log module), and additional directives, LogFormat and CustomLog.
  *
  *
@@ -27,12 +32,25 @@
  *                        a custom format is set with LogFormat
  *    LogFormat format    Set a log format from TransferLog files
  *    CustomLog fn format
- *                        Log to file fn with format given by the format
+ *                        Log to fn with format given by the format
  *                        argument
  *
  *    CookieLog fn        For backwards compatability with old Cookie
  *                        logging module - now deprecated.
  *
+ * fn is a URL-like descriptor of the form "writer:path". The exact format
+ * of "path" depends on the log writer to use.
+ * This module implements two default log writers, "file" and "pipe".
+ *
+ * For backwards compatibility: fn may also be a simple filesystem path=20
+ * without the "writer:"-part, and possibly prepended by a | to indicate
+ * logging into a pipe. These fn will be handled by the build-in file or
+ * pipe log writer.
+ * Furthermore, if a "writer:"-part exists in fn but no matching log =
writer=20
+ * was found, this module will fall back to log into a file named [fn]. In =

+ * other words: No error will be posted upon missing log writer, the =
default
+ * file log writer will be used.
+ *
  * There can be any number of TransferLog and CustomLog
  * commands. Each request will be logged to _ALL_ the
  * named files, in the appropriate format.
@@ -47,11 +65,11 @@
  *=20
  * Examples:
  *
- *    TransferLog    logs/access_log
+ *    TransferLog    file:logs/access_log
  *    <VirtualHost>
  *    LogFormat      "... custom format ..."
- *    TransferLog    log/virtual_only
- *    CustomLog      log/virtual_useragents "%t %{user-agent}i"
+ *    TransferLog    file:log/virtual_only
+ *    CustomLog      file:log/virtual_useragents "%t %{user-agent}i"
  *    </VirtualHost>
  *
  * This will log using CLF to access_log any requests handled by the
@@ -61,15 +79,15 @@
  *
  * Note that the NCSA referer and user-agent logs are easily added with
  * CustomLog:
- *   CustomLog   logs/referer  "%{referer}i -> %U"
- *   CustomLog   logs/agent    "%{user-agent}i"
+ *   CustomLog   file:logs/referer  "%{referer}i -> %U"
+ *   CustomLog   file:logs/agent    "%{user-agent}i"
  *
  * RefererIgnore functionality can be obtained with conditional
  * logging (SetEnvIf and CustomLog ... env=3D!VAR).
  *
  * But using this method allows much easier modification of the
  * log format, e.g. to log hosts along with UA:
- *   CustomLog   logs/referer "%{referer}i %U %h"
+ *   CustomLog   file:logs/referer "%{referer}i %U %h"
  *
  * The argument to LogFormat and CustomLog is a string, which can include
  * literal characters copied into the log files, and '%' directives as
@@ -91,8 +109,8 @@
  * %...{Foobar}o:  The contents of Foobar: header line(s) in the reply.
  * %...p:  the port the request was served to
  * %...P:  the process ID of the child that serviced the request.
- * %...{format}P: the process ID or thread ID of the child/thread that
- *                serviced the request
+ * %...{format}P: the process ID (pid) or thread ID (tid) of the=20
+ *                child/thread that serviced the request
  * %...r:  first line of request
  * %...s:  status.  For requests that got internally redirected, this
  *         is status of the *original* request --- %...>s for the last.
@@ -103,6 +121,7 @@
  * %...D:  the time taken to serve the request, in micro seconds.
  * %...u:  remote user (from auth; may be bogus if return status (%s) is =
401)
  * %...U:  the URL path requested.
+ * %...R:  the URL requested, unmodified
  * %...v:  the configured name of the server (i.e. which virtual host?)
  * %...V:  the server name according to the UseCanonicalName setting
  * %...m:  the request method
@@ -112,9 +131,9 @@
  *         'X' =3D connection aborted before the response completed.
  *         '+' =3D connection may be kept alive after the response is =
sent.
  *         '-' =3D 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.)
+ *
  * 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
@@ -144,7 +163,6 @@
 #include "apr_lib.h"
 #include "apr_hash.h"
 #include "apr_optional.h"
-#include "apr_anylock.h"
=20
 #define APR_WANT_STRFUNC
 #include "apr_want.h"
@@ -157,7 +175,6 @@
 #include "http_log.h"
 #include "http_protocol.h"
 #include "util_time.h"
-#include "ap_mpm.h"
=20
 #if APR_HAVE_UNISTD_H
 #include <unistd.h>
@@ -173,30 +190,35 @@
=20
 static int xfer_flags =3D (APR_WRITE | APR_APPEND | APR_CREATE);
 static apr_fileperms_t xfer_perms =3D APR_OS_DEFAULT;
+
+typedef struct {
+    union {
+        ap_log_handler_fn_t *func;
+        ap_log_ehandler_fn_t *efunc;
+    } handler;
+    int oldstyle;
+    int want_orig_default;
+} log_handler;
 static apr_hash_t *log_hash;
-static apr_status_t ap_default_log_writer(request_rec *r,
-                           void *handle,=20
-                           const char **strs,
-                           int *strl,
-                           int nelts,
-                           apr_size_t len);
-static apr_status_t ap_buffered_log_writer(request_rec *r,
-                           void *handle,=20
-                           const char **strs,
-                           int *strl,
-                           int nelts,
-                           apr_size_t len);
-static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,=20
-                                        const char* name);
-static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,=20
-                                        const char* name);
-
-static void ap_log_set_writer_init(ap_log_writer_init *handle);
-static void ap_log_set_writer(ap_log_writer *handle);
-static ap_log_writer *log_writer =3D ap_default_log_writer;
-static ap_log_writer_init *log_writer_init =3D ap_default_log_writer_init;
+
+typedef struct {
+    ap_log_ewriter_setup *setup;
+    ap_log_ewriter *write;
+    ap_log_ewriter_init *init;
+    ap_log_ewriter_exit *exit;
+} log_ewriter;
+static apr_hash_t *writer_hash;
+
+static void *ap_old_log_writer_init(apr_pool_t *p, server_rec *s,=20
+                                    const char* name);
+static apr_status_t ap_filepipe_log_ewriter(request_rec *r,
+                                            void *handle,=20
+                                            apr_array_header_t *data);
+
+static ap_log_writer *log_writer =3D NULL;
+static ap_log_writer_init *log_writer_init =3D NULL;
+
 static int buffered_logs =3D 0; /* default unbuffered */
-static apr_array_header_t *all_buffered_logs =3D NULL;
=20
 /* POSIX.1 defines PIPE_BUF as the maximum number of bytes that is
  * guaranteed to be atomic when writing a pipe.  And PIPE_BUF >=3D 512
@@ -242,23 +264,26 @@
  * request. format might be NULL, in which case the default_format
  * from the multi_log_state should be used, or if that is NULL as
  * well, use the CLF.=20
- * log_writer is NULL before the log file is opened and is
- * set to a opaque structure (usually a fd) after it is opened.
-=20
+ * writer_data is NULL before the log file is opened and is
+ * set to an opaque structure (usually a fd) after it is opened.
  */
+
 typedef struct {
     apr_file_t *handle;
     apr_size_t outcnt;
     char outbuf[LOG_BUFSIZE];
-    apr_anylock_t mutex;
 } buffered_log;
=20
 typedef struct {
     const char *fname;
     const char *format_string;
     apr_array_header_t *format;
-    void *log_writer;
+    log_ewriter *writer;
+    void *writer_data;
+
+    int condition_sense;
     char *condition_var;
+    apr_array_header_t *conditions;
 } config_log_state;
=20
 /*
@@ -267,168 +292,179 @@
  */
=20
 typedef struct {
-    ap_log_handler_fn_t *func;
+    log_handler *handler;
     char *arg;
     int condition_sense;
     int want_orig;
     apr_array_header_t *conditions;
 } log_format_item;
=20
-static char *format_integer(apr_pool_t *p, int i)
-{
-    return apr_itoa(p, i);
-}
-
-static char *pfmt(apr_pool_t *p, int i)
-{
-    if (i <=3D 0) {
-        return "-";
-    }
-    else {
-        return format_integer(p, i);
-    }
-}
-
-static const char *constant_item(request_rec *dummy, char *stuff)
+static void *constant_item(request_rec *r, char *stuff, =
ap_log_ehandler_data *d)
 {
-    return stuff;
+    d->data=3Dstuff;
+    d->arg=3DNULL;
+    d->type=3DAP_LOG_EHANDLER_RETURN_CONST;
 }
=20
-static const char *log_remote_host(request_rec *r, char *a)
+static void *log_remote_host(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    return ap_escape_logitem(r->pool, ap_get_remote_host(r->connection,
-                                                         =
r->per_dir_config,
-                                                         REMOTE_NAME, =
NULL));
+    d->data=3D(void *) ap_get_remote_host(r->connection,
+                                        r->per_dir_config,
+                                        REMOTE_NAME, NULL);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_remote_address(request_rec *r, char *a)
+static void *log_remote_address(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return r->connection->remote_ip;
+    d->data=3Dr->connection->remote_ip;
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_local_address(request_rec *r, char *a)
+static void *log_local_address(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return r->connection->local_ip;
+    d->data=3Dr->connection->local_ip;
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_remote_logname(request_rec *r, char *a)
+static void *log_remote_logname(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, ap_get_remote_logname(r));
+    d->data=3D(void *) ap_get_remote_logname(r);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_remote_user(request_rec *r, char *a)
+static void *log_remote_user(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    char *rvalue =3D r->user;
-
-    if (rvalue =3D=3D NULL) {
+    d->data =3D r->user;
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
+   =20
+    /*
+    if (d->data =3D=3D NULL) {
         rvalue =3D "-";
     }
-    else if (strlen(rvalue) =3D=3D 0) {
+    else if (strlen(d->data) =3D=3D 0) {
         rvalue =3D "\"\"";
     }
     else {
         rvalue =3D ap_escape_logitem(r->pool, rvalue);
     }
-
-    return rvalue;
+    */
 }
=20
-static const char *log_request_line(request_rec *r, char *a)
+static void *log_request_line(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
     /* NOTE: If the original request contained a password, we
      * re-write the request line here to contain XXXXXX instead:
      * (note the truncation before the protocol string for HTTP/0.9 =
requests)
      * (note also that r->the_request contains the unmodified request)
      */
-    return ap_escape_logitem(r->pool,
-                             (r->parsed_uri.password)
-                               ? apr_pstrcat(r->pool, r->method, " ",
-                                             apr_uri_unparse(r->pool,
-                                                             =
&r->parsed_uri, 0),
-                                             r->assbackwards ? NULL : " ",
-                                             r->protocol, NULL)
-                               : r->the_request);
+    d->data =3D (r->parsed_uri.password)
+              ? apr_pstrcat(r->pool, r->method, " ",
+                            apr_uri_unparse(r->pool,
+                                            &r->parsed_uri, 0),
+                                            r->assbackwards ? NULL : " ",
+                                            r->protocol, NULL)
+              : r->the_request;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_request_file(request_rec *r, char *a)
+static void *log_request_file(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, r->filename);
+    d->data =3D r->filename;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
-static const char *log_request_uri(request_rec *r, char *a)
+
+static void *log_request_uri(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    return ap_escape_logitem(r->pool, r->uri);
+    d->data =3D r->uri;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
-static const char *log_request_method(request_rec *r, char *a)
+
+static void *log_unparsed_request_uri(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, r->method);
+    d->data =3D r->unparsed_uri;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
-static const char *log_request_protocol(request_rec *r, char *a)
+
+static void *log_request_method(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, r->protocol);
+    d->data =3D (void *)r->method;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
-static const char *log_request_query(request_rec *r, char *a)
+static void *log_request_protocol(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return (r->args) ? apr_pstrcat(r->pool, "?",
-                                   ap_escape_logitem(r->pool, r->args), =
NULL)
-                     : "";
+    d->data =3D r->protocol;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
-static const char *log_status(request_rec *r, char *a)
+static void *log_request_query(request_rec *r, char *a, =
ap_log_ehandler_data *d)
+{=20
+    if (r->args)
+        d->data=3Dapr_pstrcat(r->pool, "?", r->args, NULL);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
+}
+static void *log_status(request_rec *r, char *a, ap_log_ehandler_data *d)
 {
-    return pfmt(r->pool, r->status);
+    if (r->status > 0) {
+        d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t));
+        *((ap_log_unumber_t *) d->data)=3Dr->status;
+    }
+    d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER;
 }
=20
-static const char *clf_log_bytes_sent(request_rec *r, char *a)
+static void *clf_log_bytes_sent(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
     if (!r->sent_bodyct || !r->bytes_sent) {
-        return "-";
+        d->data=3D"-";
     }
     else {
-        return apr_off_t_toa(r->pool, r->bytes_sent);
+        d->data=3Dapr_off_t_toa(r->pool, r->bytes_sent);
     }
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_bytes_sent(request_rec *r, char *a)
+static void *log_bytes_sent(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    if (!r->sent_bodyct || !r->bytes_sent) {
-        return "0";
-    }
-    else {
-        return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent);
+    if (r->header_only =3D=3D 0) {
+        d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t));
+        *((ap_log_unumber_t *) d->data)=3Dr->bytes_sent;
     }
+    d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER;
 }
=20
-
-static const char *log_header_in(request_rec *r, char *a)
+static void *log_header_in(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a));
+    d->data=3D(void *) apr_table_get(r->headers_in, a);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_header_out(request_rec *r, char *a)
+static void *log_header_out(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    const char *cp =3D apr_table_get(r->headers_out, a);
-    if (!strcasecmp(a, "Content-type") && r->content_type) {
-        cp =3D ap_field_noparam(r->pool, r->content_type);
-    }
-    if (cp) {
-        return ap_escape_logitem(r->pool, cp);
-    }
-    return ap_escape_logitem(r->pool, apr_table_get(r->err_headers_out, =
a));
+    if (!strcasecmp(a, "Content-type") && r->content_type)=20
+        d->data =3D ap_field_noparam(r->pool, r->content_type);
+     else
+        d->data =3D (void *) apr_table_get(r->headers_out, a);
+    if (! d->data)=20
+        d->data =3D (void *) apr_table_get(r->err_headers_out, a);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_note(request_rec *r, char *a)
+static void *log_note(request_rec *r, char *a, ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, apr_table_get(r->notes, a));
+    d->data =3D (void *) apr_table_get(r->notes, a);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
-static const char *log_env_var(request_rec *r, char *a)
+
+static void *log_env_var(request_rec *r, char *a, ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, apr_table_get(r->subprocess_env, =
a));
+    d->data =3D (void *) apr_table_get(r->subprocess_env, a);
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_cookie(request_rec *r, char *a)
+static void *log_cookie(request_rec *r, char *a, ap_log_ehandler_data *d)
 {
     const char *cookies;
     const char *start_cookie;
=20
+    d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
     if ((cookies =3D apr_table_get(r->headers_in, "Cookie"))) {
         if ((start_cookie =3D ap_strstr_c(cookies,a))) {
             char *cookie, *end_cookie;
@@ -439,169 +475,94 @@
             if (end_cookie) {
                 *end_cookie =3D '\0';
             }
-            return ap_escape_logitem(r->pool, cookie);
+            d->data=3Dcookie;
         }
     }
-    return NULL;
-}
-
-static const char *log_request_time_custom(request_rec *r, char *a,
-                                           apr_time_exp_t *xt)
-{
-    apr_size_t retcode;
-    char tstr[MAX_STRING_LEN];
-    apr_strftime(tstr, &retcode, sizeof(tstr), a, xt);
-    return apr_pstrdup(r->pool, tstr);
 }
=20
-#define DEFAULT_REQUEST_TIME_SIZE 32
-typedef struct {
-    unsigned t;
-    char timestr[DEFAULT_REQUEST_TIME_SIZE];
-    unsigned t_validate;
-} cached_request_time;
-
-#define TIME_CACHE_SIZE 4
-#define TIME_CACHE_MASK 3
-static cached_request_time request_time_cache[TIME_CACHE_SIZE];
-
-static const char *log_request_time(request_rec *r, char *a)
+static void *log_request_time(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    apr_time_exp_t xt;
-
-    /* ###  I think getting the time again at the end of the request
-     * just for logging is dumb.  i know it's "required" for CLF.
-     * folks writing log parsing tools don't realise that out of order
-     * times have always been possible (consider what happens if one
-     * process calculates the time to log, but then there's a context
-     * switch before it writes and before that process is run again the
-     * log rotation occurs) and they should just fix their tools rather
-     * than force the server to pay extra cpu cycles.  if you've got
-     * a problem with this, you can set the define.  -djg
-     */
-    if (a && *a) {              /* Custom format */
-        /* The custom time formatting uses a very large temp buffer
-         * on the stack.  To avoid using so much stack space in the
-         * common case where we're not using a custom format, the code
-         * for the custom format in a separate function.  (That's why
-         * log_request_time_custom is not inlined right here.)
-         */
-#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
-        ap_explode_recent_localtime(&xt, apr_time_now());
-#else
-        ap_explode_recent_localtime(&xt, r->request_time);
-#endif
-        return log_request_time_custom(r, a, &xt);
-    }
-    else {                      /* CLF format */
-        /* This code uses the same technique as =
ap_explode_recent_localtime():
-         * optimistic caching with logic to detect and correct race =
conditions.
-         * See the comments in server/util_time.c for more information.
-         */
-        cached_request_time* cached_time =3D apr_palloc(r->pool,
-                                                      =
sizeof(*cached_time));
+    d->type=3DAP_LOG_EHANDLER_RETURN_DATETIME;
+    d->data=3Dapr_palloc(r->pool,sizeof(apr_time_t));
 #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
-        apr_time_t request_time =3D apr_time_now();
+    *((apr_time_t *) d->data) =3D apr_time_now();
 #else
-        apr_time_t request_time =3D r->request_time;
+    *((apr_time_t *) d->data) =3D r->request_time;
 #endif
-        unsigned t_seconds =3D (unsigned)apr_time_sec(request_time);
-        unsigned i =3D t_seconds & TIME_CACHE_MASK;
-        memcpy(cached_time, &(request_time_cache[i]), =
sizeof(*cached_time));
-        if ((t_seconds !=3D cached_time->t) ||
-            (t_seconds !=3D cached_time->t_validate)) {
-
-            /* Invalid or old snapshot, so compute the proper time string
-             * and store it in the cache
-             */
-            char sign;
-            int timz;
-
-            ap_explode_recent_localtime(&xt, r->request_time);
-            timz =3D xt.tm_gmtoff;
-            if (timz < 0) {
-                timz =3D -timz;
-                sign =3D '-';
-            }
-            else {
-                sign =3D '+';
-            }
-            cached_time->t =3D t_seconds;
-            apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE,
-                         "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
-                         xt.tm_mday, apr_month_snames[xt.tm_mon],
-                         xt.tm_year+1900, xt.tm_hour, xt.tm_min, =
xt.tm_sec,
-                         sign, timz / (60*60), (timz % (60*60)) / 60);
-            cached_time->t_validate =3D t_seconds;
-            memcpy(&(request_time_cache[i]), cached_time,
-                   sizeof(*cached_time));
-        }
-        return cached_time->timestr;
-    }
 }
=20
-static const char *log_request_duration(request_rec *r, char *a)
+static void *log_request_duration(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    apr_time_t duration =3D apr_time_now() - r->request_time;
-    return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, =
apr_time_sec(duration));
+    d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t));
+    *((ap_log_unumber_t *) d->data) =3D apr_time_sec(apr_time_now() - =
r->request_time);
+    d->arg=3Da;
+    d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER;
 }
=20
-static const char *log_request_duration_microseconds(request_rec *r, char =
*a)
+static void *log_request_duration_microseconds(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return apr_psprintf(r->pool, "%" APR_TIME_T_FMT,=20
-                        (apr_time_now() - r->request_time));
+    d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t));
+    *((ap_log_unumber_t *) d->data) =3D apr_time_now() - r->request_time;
+    d->arg=3Da;
+    d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER;
 }
=20
 /* These next two routines use the canonical name:port so that log
  * parsers don't need to duplicate all the vhost parsing crud.
  */
-static const char *log_virtual_host(request_rec *r, char *a)
+static void *log_virtual_host(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
-    return ap_escape_logitem(r->pool, r->server->server_hostname);
+    d->data =3D r->server->server_hostname;
+    d->arg=3Da;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_server_port(request_rec *r, char *a)
+static void *log_server_port(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    return apr_psprintf(r->pool, "%u",
-                        r->server->port ? r->server->port : =
ap_default_port(r));
+    d->data=3Dapr_palloc(r->pool,sizeof(ap_log_unumber_t));
+    *((ap_log_unumber_t *) d->data) =3D r->server->port ? r->server->port =
: ap_default_port(r);
+    d->arg=3Da;
+    d->type=3DAP_LOG_EHANDLER_RETURN_UNUMBER;
 }
=20
 /* This respects the setting of UseCanonicalName so that
  * the dynamic mass virtual hosting trick works better.
  */
-static const char *log_server_name(request_rec *r, char *a)
+static void *log_server_name(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
-    return ap_escape_logitem(r->pool, ap_get_server_name(r));
+    d->data =3D (void *) ap_get_server_name(r);
+    d->arg=3Da;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
=20
-static const char *log_pid_tid(request_rec *r, char *a)
+static void *log_pid_tid(request_rec *r, char *a, ap_log_ehandler_data *d)
 {
-    if (*a =3D=3D '\0' || !strcmp(a, "pid")) {
-        return apr_psprintf(r->pool, "%" APR_PID_T_FMT, getpid());
+    d->arg=3Da;
+    if (!a || *a =3D=3D '\0' || !strcmp(a, "pid")) {
+        d->data =3D apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+        *((ap_log_unumber_t *) d->data) =3D getpid();
     }
-    else if (!strcmp(a, "tid")) {
 #if APR_HAS_THREADS
-        apr_os_thread_t tid =3D apr_os_thread_current();
-#else
-        int tid =3D 0; /* APR will format "0" anyway but an arg is needed =
*/
-#endif
-        return apr_psprintf(r->pool, "%pT", &tid);
+    else if (!strcmp(a, "tid")) {
+        d->data =3D apr_palloc(r->pool,sizeof(ap_log_unumber_t));
+        *((ap_log_unumber_t *) d->data) =3D apr_os_thread_current();
     }
-    /* bogus format */
-    return a;
+#endif
+    d->type =3D AP_LOG_EHANDLER_RETURN_UNUMBER;
 }
=20
-static const char *log_connection_status(request_rec *r, char *a)
+static void *log_connection_status(request_rec *r, char *a, =
ap_log_ehandler_data *d)
 {
     if (r->connection->aborted)
-        return "X";
-
-    if (r->connection->keepalive =3D=3D AP_CONN_KEEPALIVE &&=20
+        d->data =3D "X";
+     else if (r->connection->keepalive =3D=3D AP_CONN_KEEPALIVE &&=20
         (!r->server->keep_alive_max ||
-         (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
-        return "+";
-    }
-    return "-";
+         (r->server->keep_alive_max - r->connection->keepalives) > 0))=20
+        d->data =3D "+";
+     else
+        d->data =3D "-";
+    d->arg=3Da;
+    d->type =3D AP_LOG_EHANDLER_RETURN_STRING;
 }
=20
 /*****************************************************************
@@ -615,7 +576,7 @@
     const char *s;
     char *d;
=20
-    it->func =3D constant_item;
+    it->handler =3D (log_handler *)apr_hash_get(log_hash, "%", 1);
     it->conditions =3D NULL;
=20
     s =3D *sa;
@@ -674,7 +635,6 @@
 static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char =
**sa)
 {
     const char *s =3D *sa;
-    ap_log_handler *handler;
=20
     if (*s !=3D '%') {
         return parse_log_misc_string(p, it, sa);
@@ -686,14 +646,15 @@
=20
     if (*s =3D=3D '%') {
         it->arg =3D "%";
-        it->func =3D constant_item;
+        it->handler =3D (log_handler *)apr_hash_get(log_hash, "%", 1);
+
         *sa =3D ++s;
    =20
         return NULL;
     }
=20
     it->want_orig =3D -1;
-    it->arg =3D "";               /* For safety's sake... */
+    it->arg =3D NULL;               /* For safety's sake... */
=20
     while (*s) {
         int i;
@@ -744,8 +705,8 @@
             break;
=20
         default:
-            handler =3D (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
-            if (!handler) {
+            it->handler =3D (log_handler *)apr_hash_get(log_hash, s++, 1);
+            if (!it->handler) {
                 char dummy[2];
=20
                 dummy[0] =3D s[-1];
@@ -753,9 +714,8 @@
                 return apr_pstrcat(p, "Unrecognized LogFormat directive =
%",
                                dummy, NULL);
             }
-            it->func =3D handler->func;
             if (it->want_orig =3D=3D -1) {
-                it->want_orig =3D handler->want_orig_default;
+                it->want_orig =3D it->handler->want_orig_default;=20
             }
             *sa =3D s;
             return NULL;
@@ -776,9 +736,6 @@
             return NULL;
         }
     }
-
-    s =3D APR_EOL_STR;
-    parse_log_item(p, (log_format_item *) apr_array_push(a), &s);
     return a;
 }
=20
@@ -787,11 +744,106 @@
  * Actually logging.
  */
=20
-static const char *process_item(request_rec *r, request_rec *orig,
-                          log_format_item *item)
+static const char *format_request_time_custom(request_rec *r, char *a,
+                                              apr_time_exp_t *xt)
 {
-    const char *cp;
+    apr_size_t retcode;
+    char tstr[MAX_STRING_LEN];
+    apr_strftime(tstr, &retcode, sizeof(tstr), a, xt);
+    return apr_pstrdup(r->pool, tstr);
+}
+
+#define DEFAULT_REQUEST_TIME_SIZE 32
+typedef struct {
+    unsigned t;
+    char timestr[DEFAULT_REQUEST_TIME_SIZE];
+    unsigned t_validate;
+} cached_request_time;
+
+#define TIME_CACHE_SIZE 4
+#define TIME_CACHE_MASK 3
+static cached_request_time request_time_cache[TIME_CACHE_SIZE];
+
+static const char *format_request_time(request_rec *r, char *a, apr_time_t =
*t, cached_request_time *cache)
+{
+    apr_time_exp_t xt;
+
+    /* ###  I think getting the time again at the end of the request
+     * just for logging is dumb.  i know it's "required" for CLF.
+     * folks writing log parsing tools don't realise that out of order
+     * times have always been possible (consider what happens if one
+     * process calculates the time to log, but then there's a context
+     * switch before it writes and before that process is run again the
+     * log rotation occurs) and they should just fix their tools rather
+     * than force the server to pay extra cpu cycles.  if you've got
+     * a problem with this, you can set the define.  -djg
+     */
+    if (a && *a) {              /* Custom format */
+        /* The custom time formatting uses a very large temp buffer
+         * on the stack.  To avoid using so much stack space in the
+         * common case where we're not using a custom format, the code
+         * for the custom format in a separate function.  (That's why
+         * log_request_time_custom is not inlined right here.)
+         */
+#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
+        ap_explode_recent_localtime(&xt, apr_time_now());
+#else
+        ap_explode_recent_localtime(&xt, *t);
+#endif
+        return format_request_time_custom(r, a, &xt);
+    }
+    else {                      /* CLF format */
+        /* This code uses the same technique as =
ap_explode_recent_localtime():
+         * optimistic caching with logic to detect and correct race =
conditions.
+         * See the comments in server/util_time.c for more information.
+         */
+        cached_request_time* cached_time =3D apr_palloc(r->pool,
+                                                      =
sizeof(*cached_time));
+#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
+        apr_time_t request_time =3D apr_time_now();
+#else
+        apr_time_t request_time =3D *t;
+#endif
+        unsigned t_seconds =3D (unsigned)apr_time_sec(request_time);
+        unsigned i =3D t_seconds & TIME_CACHE_MASK;
+        memcpy(cached_time, &(request_time_cache[i]), =
sizeof(*cached_time));
+        if ((t_seconds !=3D cached_time->t) ||
+            (t_seconds !=3D cached_time->t_validate)) {
+
+            /* Invalid or old snapshot, so compute the proper time string
+             * and store it in the cache
+             */
+            char sign;
+            int timz;
+
+            ap_explode_recent_localtime(&xt, *t);
+            timz =3D xt.tm_gmtoff;
+            if (timz < 0) {
+                timz =3D -timz;
+                sign =3D '-';
+            }
+            else {
+                sign =3D '+';
+            }
+            cached_time->t =3D t_seconds;
+            apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE,
+                         "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
+                         xt.tm_mday, apr_month_snames[xt.tm_mon],
+                         xt.tm_year+1900, xt.tm_hour, xt.tm_min, =
xt.tm_sec,
+                         sign, timz / (60*60), timz % (60*60));
+            cached_time->t_validate =3D t_seconds;
+            memcpy(&(request_time_cache[i]), cached_time,
+                   sizeof(*cached_time));
+        }
+        return cached_time->timestr;
+    }
+}
+
=20
+static void process_item(request_rec *r, request_rec *orig,
+                         log_format_item *item,
+                         ap_log_ehandler_data *d)
+{
     /* First, see if we need to process this thing at all... */
=20
     if (item->conditions && item->conditions->nelts !=3D 0) {
@@ -808,14 +860,22 @@
=20
         if ((item->condition_sense && in_list)
             || (!item->condition_sense && !in_list)) {
-            return "-";
+            d->type=3DAP_LOG_EHANDLER_RETURN_STRING;
+            return;
         }
     }
=20
+
     /* We do.  Do it... */
=20
-    cp =3D (*item->func) (item->want_orig ? orig : r, item->arg);
-    return cp ? cp : "-";
+    if (item->handler->oldstyle) {
+        if (! (d->data =3D (void *)(*item->handler->handler.func) =
(item->want_orig ? orig : r, item->arg)))
+            d->data =3D "-";
+        d->type =3D AP_LOG_EHANDLER_RETURN_OLDSTYLE;
+    }
+    else {
+        (*item->handler->handler.efunc) (item->want_orig ? orig : r, =
item->arg, d);
+    }
 }
=20
 static void flush_log(buffered_log *buf)
@@ -837,9 +897,10 @@
     int i;
     apr_size_t len =3D 0;
     apr_array_header_t *format;
-    char *envar;
     apr_status_t rv;
-
+    apr_array_header_t *data;
+    ap_log_ehandler_data *d;
+       =20
     if (cls->fname =3D=3D NULL) {
         return DECLINED;
     }
@@ -849,25 +910,14 @@
      * to make.
      */
     if (cls->condition_var !=3D NULL) {
-        envar =3D cls->condition_var;
-        if (*envar !=3D '!') {
-            if (apr_table_get(r->subprocess_env, envar) =3D=3D NULL) {
-                return DECLINED;
-            }
-        }
-        else {
-            if (apr_table_get(r->subprocess_env, &envar[1]) !=3D NULL) {
-                return DECLINED;
-            }
+        if ((cls->condition_sense && apr_table_get(r->subprocess_env, =
cls->condition_var) !=3D NULL)
+            ||=20
+            (!cls->condition_sense && apr_table_get(r->subprocess_env, =
cls->condition_var) =3D=3D NULL))
+        {
+           return DECLINED;
         }
     }
=20
-    format =3D cls->format ? cls->format : default_format;
-
-    strs =3D apr_palloc(r->pool, sizeof(char *) * (format->nelts));
-    strl =3D apr_palloc(r->pool, sizeof(int) * (format->nelts));
-    items =3D (log_format_item *) format->elts;
-
     orig =3D r;
     while (orig->prev) {
         orig =3D orig->prev;
@@ -876,20 +926,84 @@
         r =3D r->next;
     }
=20
-    for (i =3D 0; i < format->nelts; ++i) {
-        strs[i] =3D process_item(r, orig, &items[i]);
+    if (cls->conditions && cls->conditions->nelts !=3D 0) {
+        int *conds =3D (int *) cls->conditions->elts;
+        int in_list =3D 0;
+
+        for (i =3D 0; i < cls->conditions->nelts; ++i) {
+            if (r->status =3D=3D conds[i]) {
+                in_list =3D 1;
+                break;
+            }
+        }
+
+        if ((cls->condition_sense && in_list)
+            || (!cls->condition_sense && !in_list)) {
+            return DECLINED;
+        }
     }
=20
+    format =3D cls->format ? cls->format : default_format;
+    data =3D apr_array_make(r->pool, format->nelts, =
sizeof(ap_log_ehandler_data));
+    items =3D (log_format_item *) format->elts;
     for (i =3D 0; i < format->nelts; ++i) {
-        len +=3D strl[i] =3D strlen(strs[i]);
+        d =3D (ap_log_ehandler_data *) apr_array_push(data);
+        d->data =3D NULL;
+        d->arg =3D items[i].arg;
+        process_item(r, orig, &items[i], d);
     }
-    if (!log_writer) {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
-                "log writer isn't correctly setup");
-         return HTTP_INTERNAL_SERVER_ERROR;
+       =20
+    if (cls->writer) { /* this is a new style writer */
+       rv =3D cls->writer->write(r, cls->writer_data, data);
+    }
+    else if (log_writer) { /* this is an old style writer */
+       strs =3D apr_palloc(r->pool, sizeof(char *) * (format->nelts));
+       strl =3D apr_palloc(r->pool, sizeof(int) * (format->nelts));
+       for (i =3D 0; i < data->nelts; ++i) {
+           d=3D&(((ap_log_ehandler_data*)(data->elts))[i]);
+           if ((d) && (d->data)) {
+               switch (d->type) {
+               case AP_LOG_EHANDLER_RETURN_OLDSTYLE:
+                   strs[i] =3D d->data;
+                   break;
+                  =20
+               case AP_LOG_EHANDLER_RETURN_CONST:
+                   strs[i] =3D d->data;
+                   break;
+              =20
+               case AP_LOG_EHANDLER_RETURN_STRING:
+                   if (strlen(d->data)=3D=3D0)
+                       strs[i] =3D "\"\"";
+                   else
+                       strs[i] =3D ap_escape_logitem(r->pool, d->data);
+                   break;
+                  =20
+               case AP_LOG_EHANDLER_RETURN_NUMBER:
+                   strs[i] =3D apr_psprintf(r->pool,"%" =
AP_LOG_NUMBER_T_FMT,*((ap_log_number_t *) d->data));
+                   break;
+              =20
+               case AP_LOG_EHANDLER_RETURN_UNUMBER:
+                   strs[i] =3D apr_psprintf(r->pool,"%" =
AP_LOG_UNUMBER_T_FMT,*((ap_log_unumber_t *) d->data));
+                   break;
+              =20
+               case AP_LOG_EHANDLER_RETURN_DATETIME:
+                   strs[i] =3D format_request_time(r,d->arg,d->data,NULL);
+                   break;
+               }
+           }
+           else {
+               strs[i]=3D"-";
+           }
+       }
+       for (i =3D 0; i < format->nelts; ++i) {
+           len +=3D strl[i] =3D strlen(strs[i]);
+       }
+       rv =3D log_writer(r, cls->writer_data, strs, strl, format->nelts, =
len);
+       /* xxx: do we return an error on log_writer? */
+    }
+    else { /* no writer at all, use our file writer as default */
+       rv =3D ap_filepipe_log_ewriter(r, cls->writer_data, data);
     }
-    rv =3D log_writer(r, cls->log_writer, strs, strl, format->nelts, len);
-    /* xxx: do we return an error on log_writer? */
     return OK;
 }
=20
@@ -1000,21 +1114,87 @@
     multi_log_state *mls =3D =
ap_get_module_config(cmd->server->module_config,
                                                 &log_config_module);
     config_log_state *cls;
-
+    char *pos;
+    log_ewriter *writer;
+    int i;
+       =20
     cls =3D (config_log_state *) apr_array_push(mls->config_logs);
     cls->condition_var =3D NULL;
+    cls->conditions =3D NULL;
     if (envclause !=3D NULL) {
-        if (strncasecmp(envclause, "env=3D", 4) !=3D 0) {
-            return "error in condition clause";
+        if (strncasecmp(envclause, "env=3D", 4) =3D=3D 0) {
+            i =3D 4;
+            if (cls->condition_sense =3D (envclause[i] =3D=3D '!')) {
+                i++;
+            }
+            if (envclause[i] =3D=3D '\0') {
+                return "missing environment variable name";
+            }
+            else {
+                cls->condition_var =3D apr_pstrdup(cmd->pool, =
&envclause[i]);
+            }
+        }
+        else if (strncasecmp(envclause, "status=3D", 7) =3D=3D 0) {
+            i =3D 7;
+            if (cls->condition_sense =3D (envclause[i] =3D=3D '!')) {
+                i++;
+            }
+            if (envclause[i] =3D=3D '\0') {
+                return "missing status code(s)";
+            }
+            else {
+                pos =3D (char*)&envclause[i];
+                while (*pos) {
+                    switch (*pos) {
+                    case ',':
+                        ++pos;
+                        break;
+
+                    case '0':
+                    case '1':
+                    case '2':
+                    case '3':
+                    case '4':
+                    case '5':
+                    case '6':
+                    case '7':
+                    case '8':
+                    case '9':
+                        i =3D *pos - '0';
+                        while (apr_isdigit(*++pos)) {
+                            i =3D i * 10 + (*pos) - '0';
+                        }
+                        if (!cls->conditions) {
+                            cls->conditions =3D apr_array_make(cmd->pool, =
4, sizeof(int));
+                        }
+                        *(int *) apr_array_push(cls->conditions) =3D i;
+                        break;
+                       =20
+                    default:
+                        return "illegal character within status code(s)";
+                    }
+                }
+            }
         }
-        if ((envclause[4] =3D=3D '\0')
-            || ((envclause[4] =3D=3D '!') && (envclause[5] =3D=3D '\0'))) =
{
-            return "missing environment variable name";
+        else {
+            return "error in condition clause";
         }
-        cls->condition_var =3D apr_pstrdup(cmd->pool, &envclause[4]);
     }
=20
     cls->fname =3D fn;
+    if ((pos =3D strchr(fn,':')) && (cls->writer =3D =
apr_hash_get(writer_hash, fn, pos-fn)) ) {
+        cls->fname=3Dpos+1;
+    }
+    else {
+        cls->writer=3DNULL;
+    }
+    /* xxx Doesn't return an error if a key ("bla:/file") is given but no=20
+     * corresponding writer exists. The reason is backwards compatibility,
+     * to be able to fall back to the old file write mechanism -- but is=20
+     * this really necessary? Is there anybody (except fellow Amigans)
+     * who uses : in file system paths?
+     */
+    =20
     cls->format_string =3D fmt;
     if (fmt =3D=3D NULL) {
         cls->format =3D NULL;
@@ -1022,8 +1202,7 @@
     else {
         cls->format =3D parse_log_string(cmd->pool, fmt, &err_string);
     }
-    cls->log_writer =3D NULL;
-
+    cls->writer_data =3D NULL;
     return err_string;
 }
=20
@@ -1041,10 +1220,6 @@
 static const char *set_buffered_logs_on(cmd_parms *parms, void *dummy, int =
flag)
 {
     buffered_logs =3D flag;
-    if (buffered_logs) {
-        ap_log_set_writer_init(ap_buffered_log_writer_init);
-        ap_log_set_writer(ap_buffered_log_writer);
-    }
     return NULL;
 }
 static const command_rec config_log_cmds[] =3D
@@ -1067,16 +1242,25 @@
                                          config_log_state *cls,
                                          apr_array_header_t =
*default_format)
 {
-    if (cls->log_writer !=3D NULL) {
+    if (cls->writer_data !=3D NULL) {
         return cls;             /* virtual config shared w/main server */
     }
=20
     if (cls->fname =3D=3D NULL) {
         return cls;             /* Leave it NULL to decline.  */
     }
-   =20
-    cls->log_writer =3D log_writer_init(p, s, cls->fname);
-    if (cls->log_writer =3D=3D NULL)
+
+    if (cls->writer) { /* new style style writer */
+        cls->writer_data =3D cls->writer->setup(p, s, cls->fname);
+    }
+    else if (log_writer_init) { /* old style writer */
+        cls->writer_data =3D log_writer_init(p, s, cls->fname);
+    }
+    else { /* default, takes care of old "|pipe" as well as simple "file" =
syntax */
+       cls->writer_data =3D ap_old_log_writer_init(p, s, cls->fname);
+    }
+      =20
+    if (cls->writer_data =3D=3D NULL)
         return NULL;=20
=20
     return cls;
@@ -1152,23 +1336,22 @@
     buffered_log *buf;
     int i;
=20
-    if (!buffered_logs)
-        return APR_SUCCESS;
-   =20
     for (; s; s =3D s->next) {
         mls =3D ap_get_module_config(s->module_config, =
&log_config_module);
-        log_list =3D NULL;
         if (mls->config_logs->nelts) {
             log_list =3D mls->config_logs;
         }
         else if (mls->server_config_logs) {
             log_list =3D mls->server_config_logs;
         }
+        else {
+            log_list =3D NULL;
+        }
         if (log_list) {
             clsarray =3D (config_log_state *) log_list->elts;
             for (i =3D 0; i < log_list->nelts; ++i) {
-                buf =3D clsarray[i].log_writer;
-                flush_log(buf);
+                if ((clsarray[i].writer) && (clsarray[i].writer->exit))
+                   clsarray[i].writer->exit(s, clsarray[i].writer_data);
             }
         }
     }
@@ -1178,17 +1361,10 @@
=20
 static int init_config_log(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt, =
server_rec *s)
 {
-    int res;
-
-    /* First init the buffered logs array, which is needed when opening =
the logs. */
-    if (buffered_logs) {
-        all_buffered_logs =3D apr_array_make(p, 5, sizeof(buffered_log =
*));
-    }
-
-    /* Next, do "physical" server, which gets default log fd and format
+    /* First, do "physical" server, which gets default log fd and format
      * for the virtual servers, if they don't override...
      */
-    res =3D open_multi_logs(s, p);
+    int res =3D open_multi_logs(s, p);
=20
     /* Then, virtual servers */
=20
@@ -1201,39 +1377,30 @@
=20
 static void init_child(apr_pool_t *p, server_rec *s)
 {
-    int mpm_threads;
-
-    ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
-
-    /* Now register the last buffer flush with the cleanup engine */
-    if (buffered_logs) {
-        int i;
-        buffered_log **array =3D (buffered_log **)all_buffered_logs->elts;
-       =20
-        apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
+    multi_log_state *mls;
+    apr_array_header_t *log_list;
+    config_log_state *clsarray;
+    buffered_log *buf;
+    int i;
=20
-        for (i =3D 0; i < all_buffered_logs->nelts; i++) {
-            buffered_log *this =3D array[i];
-           =20
-#if APR_HAS_THREADS
-            if (mpm_threads > 1) {
-                apr_status_t rv;
+    apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
=20
-                this->mutex.type =3D apr_anylock_threadmutex;
-                rv =3D apr_thread_mutex_create(&this->mutex.lock.tm,
-                                             APR_THREAD_MUTEX_DEFAULT,
-                                             p);
-                if (rv !=3D APR_SUCCESS) {
-                    ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
-                                 "could not initialize buffered log mutex, =
"
-                                 "transfer log may become corrupted");
-                    this->mutex.type =3D apr_anylock_none;
-                }
-            }
-            else
-#endif
-            {
-                this->mutex.type =3D apr_anylock_none;
+    for (; s; s =3D s->next) {
+        mls =3D ap_get_module_config(s->module_config, =
&log_config_module);
+        if (mls->config_logs->nelts) {
+            log_list =3D mls->config_logs;
+        }
+        else if (mls->server_config_logs) {
+            log_list =3D mls->server_config_logs;
+        }
+        else {
+            log_list =3D NULL;
+        }
+        if (log_list) {
+            clsarray =3D (config_log_state *) log_list->elts;
+            for (i =3D 0; i < log_list->nelts; ++i) {
+                if ((clsarray[i].writer) && (clsarray[i].writer->init))
+                   clsarray[i].writer->init(p, s, =
clsarray[i].writer_data);
             }
         }
     }
@@ -1242,173 +1409,257 @@
 static void ap_register_log_handler(apr_pool_t *p, char *tag,=20
                                     ap_log_handler_fn_t *handler, int def)
 {
-    ap_log_handler *log_struct =3D apr_palloc(p, sizeof(*log_struct));
-    log_struct->func =3D handler;
+    log_handler *log_struct =3D apr_palloc(p, sizeof(*log_struct));
+    log_struct->handler.func =3D handler;
     log_struct->want_orig_default =3D def;
+    log_struct->oldstyle =3D -1;
=20
     apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
 }
-static void ap_log_set_writer_init(ap_log_writer_init *handle)
+static void ap_register_log_ehandler(apr_pool_t *p, char *tag,=20
+                                     ap_log_ehandler_fn_t *handler, int =
def)
 {
-    log_writer_init =3D handle;
+    log_handler *log_struct =3D apr_palloc(p, sizeof(*log_struct));
+    log_struct->handler.efunc =3D handler;
+    log_struct->want_orig_default =3D def;
+    log_struct->oldstyle =3D 0;
+=20
+    apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
+}
=20
+static ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init =
*handle)
+{
+    ap_log_writer_init *old =3D log_writer_init;
+    log_writer_init =3D handle;
+   =20
+    return old;
 }
-static void ap_log_set_writer(ap_log_writer *handle)
+static ap_log_writer* ap_log_set_writer(ap_log_writer *handle)
 {
+    ap_log_writer *old =3D log_writer;
     log_writer =3D handle;
+   =20
+    return old;
 }
=20
-static apr_status_t ap_default_log_writer( request_rec *r,
-                           void *handle,=20
-                           const char **strs,
-                           int *strl,
-                           int nelts,
-                           apr_size_t len)
+static void ap_register_log_ewriter(apr_pool_t *p, char *key,=20
+                                    ap_log_ewriter_setup *setup,
+                                    ap_log_ewriter *write,
+                                    ap_log_ewriter_init *init,
+                                    ap_log_ewriter_exit *exit)
+{
+    log_ewriter *writer_struct =3D apr_palloc(p, sizeof(*writer_struct));
+    writer_struct->setup =3D setup;
+    writer_struct->write =3D write;
+    writer_struct->init =3D init;
+    writer_struct->exit =3D exit;
+    apr_hash_set(writer_hash, key, APR_HASH_KEY_STRING, (const void =
*)writer_struct);
+}
+
+static apr_status_t ap_filepipe_log_ewriter(request_rec *r,
+                                            void *handle,=20
+                                            apr_array_header_t *data)
=20
 {
     char *str;
     char *s;
+    const char **strs;
+    int *strl;
     int i;
+    int len =3D 0;
     apr_status_t rv;
+    buffered_log *buf =3D (buffered_log*)handle;
+    ap_log_ehandler_data *d;
=20
-    str =3D apr_palloc(r->pool, len + 1);
+    strs =3D apr_palloc(r->pool, sizeof(char *) * (data->nelts));
+    strl =3D apr_palloc(r->pool, sizeof(int) * (data->nelts));
+    for (i =3D 0; i < data->nelts; ++i) {
+        d=3D&(((ap_log_ehandler_data*)(data->elts))[i]);
+        if ((d) && (d->data)) {
+            switch (d->type)
+            {
+            case AP_LOG_EHANDLER_RETURN_OLDSTYLE:
+                strs[i] =3D d->data;
+                break;
+  =20
+            case AP_LOG_EHANDLER_RETURN_CONST:
+                strs[i] =3D d->data;
+                break;
+    =20
+            case AP_LOG_EHANDLER_RETURN_STRING:
+                if (strlen(d->data)=3D=3D0)
+                    strs[i] =3D "\"\"";
+                else
+                    strs[i] =3D ap_escape_logitem(r->pool, d->data);
+                break;
=20
-    for (i =3D 0, s =3D str; i < nelts; ++i) {
-        memcpy(s, strs[i], strl[i]);
-        s +=3D strl[i];
-    }
+            case AP_LOG_EHANDLER_RETURN_NUMBER:
+                strs[i] =3D apr_psprintf(r->pool,"%" =
AP_LOG_NUMBER_T_FMT,*((ap_log_number_t *) d->data));
+                break;
=20
-    rv =3D apr_file_write((apr_file_t*)handle, str, &len);
+            case AP_LOG_EHANDLER_RETURN_UNUMBER:
+                strs[i] =3D apr_psprintf(r->pool,"%" =
AP_LOG_UNUMBER_T_FMT,*((ap_log_unumber_t *) d->data));
+                break;
=20
-    return rv;
-}
-static void *ap_default_log_writer_init(apr_pool_t *p, server_rec *s,=20
-                                        const char* name)
-{
-    if (*name =3D=3D '|') {
-        piped_log *pl;
+            case AP_LOG_EHANDLER_RETURN_DATETIME:
+                strs[i] =3D format_request_time(r,d->arg,d->data,NULL);
+                break;
+            }
+        }
+        else {
+            strs[i]=3D"-";
+        }
+        len +=3D strl[i] =3D strlen(strs[i]);
+    }
+    len +=3D strlen(APR_EOL_STR);
=20
-        pl =3D ap_open_piped_log(p, name + 1);
-        if (pl =3D=3D NULL) {
-           return NULL;;
+    if (!buffered_logs) {
+        str =3D apr_palloc(r->pool, len + 1);
+        for (i =3D 0, s =3D str; i < data->nelts; ++i) {
+            memcpy(s, strs[i], strl[i]);
+            s +=3D strl[i];
         }
-        return ap_piped_log_write_fd(pl);
+        memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR));
+        rv =3D apr_file_write((apr_file_t*)handle, str, &len);
     }
     else {
-        const char *fname =3D ap_server_root_relative(p, name);
-        apr_file_t *fd;
-        apr_status_t rv;
-
-        if (!fname) {
-            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
-                            "invalid transfer log path %s.", name);
-            return NULL;
+        buffered_log *buf =3D (buffered_log*)handle;
+
+        if (len + buf->outcnt > LOG_BUFSIZE) {
+            flush_log(buf);
         }
-        rv =3D apr_file_open(&fd, fname, xfer_flags, xfer_perms, p);
-        if (rv !=3D APR_SUCCESS) {
-            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
-                            "could not open transfer log file %s.", =
fname);
-            return NULL;
+        if (len >=3D LOG_BUFSIZE) {
+            apr_size_t w;
+  =20
+            str =3D apr_palloc(r->pool, len + 1 );
+            for (i =3D 0, s =3D str; i < data->nelts; ++i) {
+                memcpy(s, strs[i], strl[i]);
+                s +=3D strl[i];
+            }
+            memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR));
+            w =3D len;
+            rv =3D apr_file_write(buf->handle, str, &w);
+        }
+        else {
+            for (i =3D 0, s =3D &buf->outbuf[buf->outcnt]; i < =
data->nelts; ++i) {
+                memcpy(s, strs[i], strl[i]);
+                s +=3D strl[i];
+            }
+            memcpy(s, APR_EOL_STR, strlen(APR_EOL_STR));
+            buf->outcnt +=3D len;
+            rv =3D APR_SUCCESS;
         }
-        return fd;
     }
+
+    return rv;
 }
-static void *ap_buffered_log_writer_init(apr_pool_t *p, server_rec *s,=20
-                                        const char* name)
+
+static void *init_buffered_logs(apr_pool_t *p, void *handle)=20
 {
     buffered_log *b;
-    b =3D apr_pcalloc(p, sizeof(buffered_log));
-    b->handle =3D ap_default_log_writer_init(p, s, name);
+    b =3D apr_palloc(p, sizeof(buffered_log));
+    b->handle =3D handle;
+    b->outcnt =3D 0;
    =20
-    if (b->handle) {
-        *(buffered_log **)apr_array_push(all_buffered_logs) =3D b;
+    if (b->handle)
         return b;
-    }
     else
         return NULL;
 }
-static apr_status_t ap_buffered_log_writer(request_rec *r,
-                                           void *handle,=20
-                                           const char **strs,
-                                           int *strl,
-                                           int nelts,
-                                           apr_size_t len)
=20
+static void *ap_file_log_writer_setup(apr_pool_t *p, server_rec *s,=20
+                                      const char* name)
 {
-    char *str;
-    char *s;
-    int i;
+    const char *fname =3D ap_server_root_relative(p, name);
+    apr_file_t *fd;
     apr_status_t rv;
-    buffered_log *buf =3D (buffered_log*)handle;
=20
-    if ((rv =3D APR_ANYLOCK_LOCK(&buf->mutex)) !=3D APR_SUCCESS) {
-        return rv;
+    if (!fname) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
+                     "invalid transfer log path %s.", name);
+        return NULL;
     }
-
-    if (len + buf->outcnt > LOG_BUFSIZE) {
-        flush_log(buf);
+    rv =3D apr_file_open(&fd, fname, xfer_flags, xfer_perms, p);
+    if (rv !=3D APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "could not open transfer log file %s.", fname);
+        return NULL;
     }
-    if (len >=3D LOG_BUFSIZE) {
-        apr_size_t w;
+    if (buffered_logs)
+       return init_buffered_logs(p, fd);
+     else
+       return fd;
+}
=20
-        str =3D apr_palloc(r->pool, len + 1);
-        for (i =3D 0, s =3D str; i < nelts; ++i) {
-            memcpy(s, strs[i], strl[i]);
-            s +=3D strl[i];
-        }
-        w =3D len;
-        rv =3D apr_file_write(buf->handle, str, &w);
-       =20
-    }
-    else {
-        for (i =3D 0, s =3D &buf->outbuf[buf->outcnt]; i < nelts; ++i) {
-            memcpy(s, strs[i], strl[i]);
-            s +=3D strl[i];
-        }
-        buf->outcnt +=3D len;
-        rv =3D APR_SUCCESS;
+static void *ap_pipe_log_writer_setup(apr_pool_t *p, server_rec *s,=20
+                                      const char* name)
+{
+    piped_log *pl;
+
+    pl =3D ap_open_piped_log(p, name);
+    if (pl =3D=3D NULL) {
+       return NULL;;
     }
+    if (buffered_logs)
+       return init_buffered_logs(p, ap_piped_log_write_fd(pl));
+     else
+       return ap_piped_log_write_fd(pl);
+}
=20
-    APR_ANYLOCK_UNLOCK(&buf->mutex);
-    return rv;
+static void *ap_old_log_writer_init(apr_pool_t *p, server_rec *s,
+                                    const char* name)
+{
+    if (*name =3D=3D '|')
+        return ap_pipe_log_writer_setup(p, s, name + 1);   =20
+    else
+        return ap_file_log_writer_setup(p, s, name);
+}
+
+static void ap_filepipe_log_ewriter_exit(server_rec *s, void *data)
+{
+    if (buffered_logs)
+        flush_log((buffered_log*)data);
 }
=20
 static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t =
*ptemp)
 {
-    static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) =
*log_pfn_register;
-   =20
-    log_pfn_register =3D =
APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+    static APR_OPTIONAL_FN_TYPE(ap_register_log_ehandler) =
*log_pfn_eregister;
=20
-    if (log_pfn_register) {
-        log_pfn_register(p, "h", log_remote_host, 0);
-        log_pfn_register(p, "a", log_remote_address, 0 );
-        log_pfn_register(p, "A", log_local_address, 0 );
-        log_pfn_register(p, "l", log_remote_logname, 0);
-        log_pfn_register(p, "u", log_remote_user, 0);
-        log_pfn_register(p, "t", log_request_time, 0);
-        log_pfn_register(p, "f", log_request_file, 0);
-        log_pfn_register(p, "b", clf_log_bytes_sent, 0);
-        log_pfn_register(p, "B", log_bytes_sent, 0);
-        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, "e", log_env_var, 0);
-        log_pfn_register(p, "V", log_server_name, 0);
-        log_pfn_register(p, "v", log_virtual_host, 0);
-        log_pfn_register(p, "p", log_server_port, 0);
-        log_pfn_register(p, "P", log_pid_tid, 0);
-        log_pfn_register(p, "H", log_request_protocol, 0);
-        log_pfn_register(p, "m", log_request_method, 0);
-        log_pfn_register(p, "q", log_request_query, 0);
-        log_pfn_register(p, "X", log_connection_status, 0);
-        log_pfn_register(p, "C", log_cookie, 0);
-        log_pfn_register(p, "r", log_request_line, 1);
-        log_pfn_register(p, "D", log_request_duration_microseconds, 1);
-        log_pfn_register(p, "T", log_request_duration, 1);
-        log_pfn_register(p, "U", log_request_uri, 1);
-        log_pfn_register(p, "s", log_status, 1);
-    }
+    =
ap_register_log_ewriter(p,"file",ap_file_log_writer_setup,ap_filepipe_log_ew=
riter,NULL,ap_filepipe_log_ewriter_exit);
+    =
ap_register_log_ewriter(p,"pipe",ap_pipe_log_writer_setup,ap_filepipe_log_ew=
riter,NULL,ap_filepipe_log_ewriter_exit);
=20
+    log_pfn_eregister =3D =
APR_RETRIEVE_OPTIONAL_FN(ap_register_log_ehandler);
+    if (log_pfn_eregister) {
+        log_pfn_eregister(p, "%", constant_item, 0);
+        log_pfn_eregister(p, "h", log_remote_host, 0);
+        log_pfn_eregister(p, "a", log_remote_address, 0 );
+        log_pfn_eregister(p, "A", log_local_address, 0 );
+        log_pfn_eregister(p, "l", log_remote_logname, 0);
+        log_pfn_eregister(p, "r", log_request_line, 1);
+        log_pfn_eregister(p, "u", log_remote_user, 0);
+        log_pfn_eregister(p, "s", log_status, 1);
+        log_pfn_eregister(p, "f", log_request_file, 0);
+        log_pfn_eregister(p, "U", log_request_uri, 1);
+        log_pfn_eregister(p, "m", log_request_method, 0);
+        log_pfn_eregister(p, "H", log_request_protocol, 0);
+        log_pfn_eregister(p, "q", log_request_query, 0);
+        log_pfn_eregister(p, "b", clf_log_bytes_sent, 0);
+        log_pfn_eregister(p, "B", log_bytes_sent, 0);
+        log_pfn_eregister(p, "i", log_header_in, 0);
+        log_pfn_eregister(p, "o", log_header_out, 0);
+        log_pfn_eregister(p, "n", log_note, 0);
+        log_pfn_eregister(p, "e", log_env_var, 0);
+        log_pfn_eregister(p, "C", log_cookie, 0);
+        log_pfn_eregister(p, "V", log_server_name, 0);
+        log_pfn_eregister(p, "v", log_virtual_host, 0);
+        log_pfn_eregister(p, "p", log_server_port, 0);
+        log_pfn_eregister(p, "D", log_request_duration_microseconds, 1);
+        log_pfn_eregister(p, "P", log_pid_tid, 0);
+        log_pfn_eregister(p, "R", log_unparsed_request_uri, 1);
+        log_pfn_eregister(p, "t", log_request_time, 0);
+        log_pfn_eregister(p, "T", log_request_duration, 1);
+        log_pfn_eregister(p, "X", log_connection_status, 0);
+      }
     return OK;
 }
=20
@@ -1429,6 +1680,10 @@
     APR_REGISTER_OPTIONAL_FN(ap_register_log_handler);
     APR_REGISTER_OPTIONAL_FN(ap_log_set_writer_init);
     APR_REGISTER_OPTIONAL_FN(ap_log_set_writer);
+
+    APR_REGISTER_OPTIONAL_FN(ap_register_log_ehandler);
+    writer_hash =3D apr_hash_make(p);
+    APR_REGISTER_OPTIONAL_FN(ap_register_log_ewriter);
 }
=20
 module AP_MODULE_DECLARE_DATA log_config_module =3D
@@ -1441,4 +1696,3 @@
     config_log_cmds,            /* command apr_table_t */
     register_hooks              /* register hooks */
 };
-

--==========B6C94F11EFDA6764AC7E==========
Content-Type: text/plain; charset=iso-8859-1;
 name="patch-modules:loggers:mod_log_config.h"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="patch-modules:loggers:mod_log_config.h"; size=4615

--- modules/loggers/mod_log_config.h.orig	Mon Feb  9 15:53:18 2004
+++ modules/loggers/mod_log_config.h	Sun Oct  5 07:08:08 2003
@@ -21,17 +21,49 @@
 #define _MOD_LOG_CONFIG_H 1
=20
 /**=20
- * callback function prototype for a external log handler
+ * callback function prototype for an external log handler
  */
+
+/* types of returned data */
+#define AP_LOG_EHANDLER_RETURN_CONST    -1 /* the text between %-items */
+#define AP_LOG_EHANDLER_RETURN_OLDSTYLE  0 /* ap_escape_logitem() string =
*/
+#define AP_LOG_EHANDLER_RETURN_STRING    1 /* raw string, not escaped */
+#define AP_LOG_EHANDLER_RETURN_NUMBER    2 /* signed number, =
ap_log_number_t */
+#define AP_LOG_EHANDLER_RETURN_UNUMBER   3 /* unsigned number, =
ap_log_unumber_t */
+#define AP_LOG_EHANDLER_RETURN_DATETIME  4 /* time, apr_time_t */
+
+#define ap_log_number_t apr_int64_t
+#define ap_log_unumber_t apr_uint64_t
+#define AP_LOG_NUMBER_T_FMT APR_INT64_T_FMT
+#define AP_LOG_UNUMBER_T_FMT APR_UINT64_T_FMT
+
+/* struct to hold returned data */
+typedef struct ap_log_ehandler_data {
+    int type;   /* AP_LOG_EHANDLER_RETURN_*, see above */
+    char *arg;  /* log item argument, as given in e.g. %{arg}t */
+    void *data; /* pointer to data or null if n/a */
+} ap_log_ehandler_data;
+
 typedef const char *ap_log_handler_fn_t(request_rec *r, char *a);
+typedef void *ap_log_ehandler_fn_t(request_rec *r, char *a, =
ap_log_ehandler_data *d);
+
+APR_DECLARE_OPTIONAL_FN(void, ap_register_log_handler,=20
+                        (apr_pool_t *p, char *tag, ap_log_handler_fn_t =
*func,
+                         int def));
+APR_DECLARE_OPTIONAL_FN(void, ap_register_log_ehandler,=20
+                        (apr_pool_t *p, char *tag, ap_log_ehandler_fn_t =
*func,
+                         int def));
=20
 /**
- * callback function prototype for a external writer initilization.
+ * callback function prototype for an external writer's initialization.
  */
 typedef void *ap_log_writer_init(apr_pool_t *p, server_rec *s,=20
                                  const char *name);
+typedef void *ap_log_ewriter_setup(apr_pool_t *p, server_rec *s,=20
+                                  const char *name);
+
 /**
- * callback which gets called where there is a log line to write.
+ * callback which gets called when there is a log line to write.
  */
 typedef apr_status_t ap_log_writer(
                             request_rec *r,
@@ -40,23 +72,43 @@
                             int *lengths,
                             int nelts,
                             apr_size_t len);
+typedef apr_status_t ap_log_ewriter(
+                            request_rec *r,
+                            void *handle,=20
+                            apr_array_header_t *data);
=20
-typedef struct ap_log_handler {
-    ap_log_handler_fn_t *func;
-    int want_orig_default;
-} ap_log_handler;
+/**
+ * callback function prototypes for each child's external writer init and =
exit
+ */
+typedef void ap_log_ewriter_init(apr_pool_t *p, server_rec *s, void =
*data);
+typedef void ap_log_ewriter_exit(server_rec *s, void *data);
=20
-APR_DECLARE_OPTIONAL_FN(void, ap_register_log_handler,=20
-                        (apr_pool_t *p, char *tag, ap_log_handler_fn_t =
*func,
-                         int def));
 /**
  * you will need to set your init handler *BEFORE* the open_logs=20
  * in mod_log_config gets executed
- */
-APR_DECLARE_OPTIONAL_FN(void, ap_log_set_writer_init,(ap_log_writer_init =
*func));
-/**=20
  * you should probably set the writer at the same time (ie..before =
open_logs)
+ *
+ * ap_log_set_writer() deprecated, better use ap_register_log_ewriter()
+ */
+
+APR_DECLARE_OPTIONAL_FN(ap_log_writer_init*, =
ap_log_set_writer_init,(ap_log_writer_init *func));
+APR_DECLARE_OPTIONAL_FN(ap_log_writer*, ap_log_set_writer, (ap_log_writer* =
func));
+
+/*
+ * Register a new log writer.
+ * p     pool, you get this as argument to your pre_config hook
+ * key   the scheme part of a CustomLog uri, identifies this log writer
+ * write write a line of log data
+ * setup called during ap_hook_open_logs
+ * init  called during ap_hook_child_init
+ * exit  called upon child exit (registered as pool cleanup from =
ap_hook_child_init())
  */
-APR_DECLARE_OPTIONAL_FN(void, ap_log_set_writer, (ap_log_writer* func));
+APR_DECLARE_OPTIONAL_FN(void, ap_register_log_ewriter,=20
+                        (apr_pool_t *p, char *key,=20
+                         ap_log_ewriter_setup *setup,
+                         ap_log_ewriter *write,
+                         ap_log_ewriter_init *init,
+                         ap_log_ewriter_exit *exit)
+                       );
=20
 #endif /* MOD_LOG_CONFIG */

--==========B6C94F11EFDA6764AC7E==========
Content-Type: text/plain; charset=iso-8859-1;
 name="patch-modules:loggers:mod_logio.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="patch-modules:loggers:mod_logio.c"; size=1974

--- modules/loggers/mod_logio.c.orig	Mon Feb  9 15:53:18 2004
+++ modules/loggers/mod_logio.c	Sun Oct  5 07:08:08 2003
@@ -29,7 +29,6 @@
 #include "apr_lib.h"
 #include "apr_hash.h"
 #include "apr_optional.h"
-
 #define APR_WANT_STRFUNC
 #include "apr_want.h"
=20
@@ -40,6 +39,7 @@
 #include "http_config.h"
 #include "http_connection.h"
 #include "http_protocol.h"
+#include "http_log.h"
=20
 module AP_MODULE_DECLARE_DATA logio_module;
=20
@@ -68,20 +68,23 @@
  * Format items...
  */
=20
-static const char *log_bytes_in(request_rec *r, char *a)
+static void *log_bytes_in(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
     logio_config_t *cf =3D =
ap_get_module_config(r->connection->conn_config,
                                               &logio_module);
-
-    return apr_off_t_toa(r->pool, cf->bytes_in);
+    d->type =3D AP_LOG_EHANDLER_RETURN_UNUMBER;
+    d->data =3D apr_palloc(r->pool, sizeof(ap_log_unumber_t));
+    *(ap_log_unumber_t*)d->data =3D cf->bytes_in;
 }
=20
-static const char *log_bytes_out(request_rec *r, char *a)
+static void *log_bytes_out(request_rec *r, char *a, ap_log_ehandler_data =
*d)
 {
     logio_config_t *cf =3D =
ap_get_module_config(r->connection->conn_config,
                                               &logio_module);
=20
-    return apr_off_t_toa(r->pool, cf->bytes_out);
+    d->type =3D AP_LOG_EHANDLER_RETURN_UNUMBER;
+    d->data =3D apr_palloc(r->pool, sizeof(ap_log_unumber_t));
+    *(ap_log_unumber_t*)d->data =3D cf->bytes_out;
 }
=20
 /*
@@ -153,9 +156,9 @@
=20
 static int logio_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t =
*ptemp)
 {
-    static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) =
*log_pfn_register;
+    static APR_OPTIONAL_FN_TYPE(ap_register_log_ehandler) =
*log_pfn_register;
    =20
-    log_pfn_register =3D =
APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+    log_pfn_register =3D =
APR_RETRIEVE_OPTIONAL_FN(ap_register_log_ehandler);
=20
     if (log_pfn_register) {
         log_pfn_register(p, "I", log_bytes_in, 0);

--==========B6C94F11EFDA6764AC7E==========--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?61670F38EE92BEC06E2BE009>