You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/10/09 19:58:53 UTC

svn commit: r1530739 - in /subversion/trunk/subversion/svnserve: logger.c logger.h serve.c server.h svnserve.c

Author: stefan2
Date: Wed Oct  9 17:58:52 2013
New Revision: 1530739

URL: http://svn.apache.org/r1530739
Log:
In svnserve, encapsulate the log_file, log_file_mutex pair into a 
logger_t data structure and prove an small API for it.  Make all
of svnserve use the new, nicer API.

* subversion/svnserve/logger.h
  (): new file
  (logger_t): declare new data strcuture
  (cleanup_logger): new pool cleanup function
  (logger__create,
   logger__log_error,
   logger__write): declare new logger API

* subversion/svnserve/logger.c
  (): new file
  (logger_t): define new data strcuture
  (logger__create,
   logger__log_error,
   logger__write): implement new logger API

* subversion/svnserve/server.h
  (log_error): drop
  (server_baton_t,
   serve_params_t): use new logger object instead of plain files

* subversion/svnserve/serve.c
  (log_write,
   log_error_internal,
   log_server_error): drop
  (log_error): greatly simplify
  (error_create_and_log,
   log_fail_and_flush,
   log_command,
   log_authz_denied,
   handle_config_error,
   lookup_access,
   must_have_access,
   add_lock_tokens,
   unlock_paths,
   lock_many,
   unlock_many,
   fs_warning_func,
   serve): update to use the new logger API

* subversion/svnserve/svnserve.c
  (serve_socket): use new logger API
  (main): ditto; update log init code

Added:
    subversion/trunk/subversion/svnserve/logger.c
    subversion/trunk/subversion/svnserve/logger.h
Modified:
    subversion/trunk/subversion/svnserve/serve.c
    subversion/trunk/subversion/svnserve/server.h
    subversion/trunk/subversion/svnserve/svnserve.c

Added: subversion/trunk/subversion/svnserve/logger.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/logger.c?rev=1530739&view=auto
==============================================================================
--- subversion/trunk/subversion/svnserve/logger.c (added)
+++ subversion/trunk/subversion/svnserve/logger.c Wed Oct  9 17:58:52 2013
@@ -0,0 +1,154 @@
+/*
+ * logger.c : Implementation of the SvnServe logger API
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+
+
+#define APR_WANT_STRFUNC
+#include <apr_want.h>
+
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_pools.h"
+#include "svn_time.h"
+
+#include "private/svn_mutex.h"
+
+#include "svn_private_config.h"
+#include "logger.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>   /* For getpid() */
+#endif
+
+struct logger_t
+{
+  /* actual log file object */
+  apr_file_t *file;
+
+  /* mutex used to serialize access to this structure */
+  svn_mutex__t *mutex;
+
+  /* private pool used for temporary allocations */
+  apr_pool_t *pool;
+};
+
+/* Pool cleanup handler.  Make sure we destroy our private pool as well. */
+static apr_status_t cleanup_logger(void *data)
+{
+  logger_t *logger = data;
+  svn_pool_destroy(logger->pool);
+
+  return APR_SUCCESS;
+}
+
+svn_error_t *
+logger__create(logger_t **logger,
+               const char *filename,
+               apr_pool_t *pool)
+{
+  logger_t *result = apr_pcalloc(pool, sizeof(*result));
+
+  SVN_ERR(svn_io_file_open(&result->file, filename,
+                           APR_WRITE | APR_CREATE | APR_APPEND,
+                           APR_OS_DEFAULT, pool));
+  SVN_ERR(svn_mutex__init(&result->mutex, TRUE, pool));
+  result->pool = svn_pool_create(NULL);
+
+  apr_pool_cleanup_register(pool, result, cleanup_logger,
+                            apr_pool_cleanup_null);
+  
+  *logger = result;
+
+  return SVN_NO_ERROR;
+}
+
+void
+logger__log_error(logger_t *logger,
+                  svn_error_t *err,
+                  repository_t *repository,
+                  client_info_t *client_info)
+{
+  if (logger && err)
+    {
+      const char *timestr, *continuation;
+      const char *user, *repos, *remote_host;
+      char errbuf[256];
+      /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
+      char errstr[8192];
+
+      svn_error_clear(svn_mutex__lock(logger->mutex));
+
+      timestr = svn_time_to_cstring(apr_time_now(), logger->pool);
+      remote_host = client_info && client_info->remote_host
+                  ? client_info->remote_host
+                  : "-";
+      user = client_info && client_info->user
+           ? client_info->user
+           : "-";
+      repos = repository && repository->repos_name
+            ? repository->repos_name
+             : "-";
+
+      continuation = "";
+      while (err)
+        {
+          const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf));
+          /* based on httpd-2.2.4/server/log.c:log_error_core */
+          apr_size_t len = apr_snprintf(errstr, sizeof(errstr),
+                                        "%" APR_PID_T_FMT
+                                        " %s %s %s %s ERR%s %s %ld %d ",
+                                        getpid(), timestr, remote_host, user,
+                                        repos, continuation,
+                                        err->file ? err->file : "-", err->line,
+                                        err->apr_err);
+
+          len += escape_errorlog_item(errstr + len, message,
+                                      sizeof(errstr) - len);
+          /* Truncate for the terminator (as apr_snprintf does) */
+          if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) {
+            len = sizeof(errstr) - sizeof(APR_EOL_STR);
+          }
+          strcpy(errstr + len, APR_EOL_STR);
+          len += strlen(APR_EOL_STR);
+          svn_error_clear(svn_io_file_write(logger->file, errstr, &len,
+                                            logger->pool));
+
+          continuation = "-";
+          err = err->child;
+        }
+      svn_pool_clear(logger->pool);
+      
+      svn_error_clear(svn_mutex__unlock(logger->mutex, SVN_NO_ERROR));
+    }
+}
+
+svn_error_t *
+logger__write(logger_t *logger,
+              const char *errstr,
+              apr_size_t len)
+{
+  SVN_MUTEX__WITH_LOCK(logger->mutex,
+                       svn_io_file_write(logger->file, errstr, &len,
+                                         logger->pool));
+  return SVN_NO_ERROR;
+}

Added: subversion/trunk/subversion/svnserve/logger.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/logger.h?rev=1530739&view=auto
==============================================================================
--- subversion/trunk/subversion/svnserve/logger.h (added)
+++ subversion/trunk/subversion/svnserve/logger.h Wed Oct  9 17:58:52 2013
@@ -0,0 +1,71 @@
+/*
+ * logger.h : Public definitions for the Repository Cache
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "server.h"
+
+
+
+/* Opaque svnserve log file writer data structure.  Access to the log
+ * file will be serialized among threads within the same process.
+ */
+typedef struct logger_t logger_t;
+
+/* In POOL, create a writer object for log file FILENAME and return it
+ * in *LOGGER.  The log file will be flushed & closed when POOL gets
+ * cleared or destroyed.
+ */
+svn_error_t *
+logger__create(logger_t **logger,
+               const char *filename,
+               apr_pool_t *pool);
+
+/* Write the first LEN bytes from ERRSTR to the log file managed by LOGGER.
+ */
+svn_error_t *
+logger__write(logger_t *logger,
+              const char *errstr,
+              apr_size_t len);
+
+/* Write a description of ERR with additional information from REPOSITORY
+ * and CLIENT_INFO to the log file managed by LOGGER.  REPOSITORY as well
+ * as CLIENT_INFO may be NULL.  If either ERR or LOGGER are NULL, this
+ * becomes a no-op.
+ */
+void
+logger__log_error(logger_t *logger,
+                  svn_error_t *err,
+                  repository_t *repository,
+                  client_info_t *client_info);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LOGGER_H */

Modified: subversion/trunk/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/serve.c?rev=1530739&r1=1530738&r2=1530739&view=diff
==============================================================================
--- subversion/trunk/subversion/svnserve/serve.c (original)
+++ subversion/trunk/subversion/svnserve/serve.c Wed Oct  9 17:58:52 2013
@@ -61,6 +61,7 @@
 #endif
 
 #include "server.h"
+#include "logger.h"
 
 typedef struct commit_callback_baton_t {
   apr_pool_t *pool;
@@ -104,104 +105,23 @@ typedef struct authz_baton_t {
   svn_ra_svn_conn_t *conn;
 } authz_baton_t;
 
-/* Write LEN bytes of ERRSTR to LOG_FILE with svn_io_file_write(). */
-static svn_error_t *
-log_write(apr_file_t *log_file, const char *errstr, apr_size_t len,
-          apr_pool_t *pool)
-{
-  return svn_io_file_write(log_file, errstr, &len, pool);
-}
-
-static void
-log_error_internal(svn_error_t *err, apr_file_t *log_file,
-                   const char *remote_host, const char *user,
-                   const char *repos, apr_pool_t *pool)
-{
-  const char *timestr, *continuation;
-  char errbuf[256];
-  /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */
-  char errstr[8192];
-
-  timestr = svn_time_to_cstring(apr_time_now(), pool);
-  remote_host = (remote_host ? remote_host : "-");
-  user = (user ? user : "-");
-  repos = (repos ? repos : "-");
-
-  continuation = "";
-  while (err != NULL)
-    {
-      const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf));
-      /* based on httpd-2.2.4/server/log.c:log_error_core */
-      apr_size_t len = apr_snprintf(errstr, sizeof(errstr),
-                                    "%" APR_PID_T_FMT
-                                    " %s %s %s %s ERR%s %s %ld %d ",
-                                    getpid(), timestr, remote_host, user,
-                                    repos, continuation,
-                                    err->file ? err->file : "-", err->line,
-                                    err->apr_err);
-
-      len += escape_errorlog_item(errstr + len, message,
-                                  sizeof(errstr) - len);
-      /* Truncate for the terminator (as apr_snprintf does) */
-      if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) {
-        len = sizeof(errstr) - sizeof(APR_EOL_STR);
-      }
-      strcpy(errstr + len, APR_EOL_STR);
-      len += strlen(APR_EOL_STR);
-      svn_error_clear(log_write(log_file, errstr, len, pool));
-
-      continuation = "-";
-      err = err->child;
-    }
-}
-
-
-void
-log_error(svn_error_t *err, apr_file_t *log_file,
-          svn_mutex__t *log_file_mutex, const char *remote_host,
-          const char *user, const char *repos, apr_pool_t *pool)
-{
-  if (err == SVN_NO_ERROR)
-    return;
-
-  if (log_file == NULL)
-    return;
-
-  if (log_file_mutex)
-    {
-      /* if the mutex functions fail, things look pretty grim.
-       * 
-       * Our best hope is to try to get some info into the log before
-       * everything breaks apart.  Therefore, ignore any mutex errors.
-       */
-      svn_error_clear(svn_mutex__lock(log_file_mutex));
-      log_error_internal(err, log_file, remote_host, user, repos, pool);
-      svn_error_clear(svn_mutex__unlock(log_file_mutex, SVN_NO_ERROR));
-    }
-  else
-    log_error_internal(err, log_file, remote_host, user, repos, pool);
-}
-
-/* Call log_error with log_file, remote_host, user, repos and remote_host
-   arguments from SERVER. */
+/* svn_error_create() a new error, log_server_error() it, and
+   return it. */
 static void
-log_server_error(svn_error_t *err, server_baton_t *server,
-                 apr_pool_t *pool)
+log_error(svn_error_t *err, server_baton_t *server)
 {
-  log_error(err, server->log_file, server->log_file_mutex,
-            server->client_info->remote_host,
-            server->client_info->user, server->repository->repos_name, pool);
+  logger__log_error(server->logger, err, server->repository,
+                    server->client_info);
 }
 
 /* svn_error_create() a new error, log_server_error() it, and
    return it. */
 static svn_error_t *
 error_create_and_log(apr_status_t apr_err, svn_error_t *child,
-                     const char *message, server_baton_t *server,
-                     apr_pool_t *pool)
+                     const char *message, server_baton_t *server)
 {
   svn_error_t *err = svn_error_create(apr_err, child, message);
-  log_server_error(err, server, pool);
+  log_error(err, server);
   return err;
 }
 
@@ -213,7 +133,7 @@ log_fail_and_flush(svn_error_t *err, ser
 {
   svn_error_t *io_err;
 
-  log_server_error(err, server, pool);
+  log_error(err, server);
   io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
   svn_error_clear(err);
   SVN_ERR(io_err);
@@ -230,7 +150,7 @@ static svn_error_t *log_command(server_b
   va_list ap;
   apr_size_t nbytes;
 
-  if (b->log_file == NULL)
+  if (b->logger == NULL)
     return SVN_NO_ERROR;
 
   remote_host = svn_ra_svn_conn_remote_host(conn);
@@ -248,7 +168,7 @@ static svn_error_t *log_command(server_b
                       b->repository->repos_name, log);
   nbytes = strlen(line);
 
-  return log_write(b->log_file, line, nbytes, pool);
+  return logger__write(b->logger, line, nbytes);
 }
 
 /* Log an authz failure */
@@ -260,7 +180,7 @@ log_authz_denied(const char *path,
 {
   const char *timestr, *remote_host, *line;
 
-  if (b->log_file == NULL)
+  if (!b->logger)
     return SVN_NO_ERROR;
 
   if (!b->client_info || !b->client_info->user)
@@ -279,7 +199,7 @@ log_authz_denied(const char *path,
                       (required & svn_authz_write ? "write" : "read"),
                       (path && path[0] ? path : "/"));
 
-  return log_write(b->log_file, line, strlen(line), pool);
+  return logger__write(b->logger, line, strlen(line));
 }
 
 /* If CFG specifies a path to the password DB, read that DB and store it
@@ -433,15 +353,14 @@ load_authz_config(repository_t *reposito
  */
 static svn_error_t *
 handle_config_error(svn_error_t *error,
-                    server_baton_t *server,
-                    apr_pool_t *pool)
+                    server_baton_t *server)
 {
   if (   error
       && (   error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG
           || error->apr_err == SVN_ERR_AUTHN_FAILED))
     {
       apr_status_t apr_err = error->apr_err;
-      log_server_error(error, server, pool);
+      log_error(error, server);
 
       /* Now that we've logged the error, clear it and return a
        * nice, generic error to the user:
@@ -825,7 +744,7 @@ static svn_boolean_t lookup_access(apr_p
   /* If an error made lookup fail, deny access. */
   if (err)
     {
-      log_server_error(err, baton, pool);
+      log_error(err, baton);
       svn_error_clear(err);
       return FALSE;
     }
@@ -891,7 +810,7 @@ static svn_error_t *must_have_access(svn
   if (! lookup_access(pool, b, required, path, needs_username))
     return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
                             error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
-                                                 NULL, NULL, b, pool),
+                                                 NULL, NULL, b),
                             NULL);
 
   /* Else, access is granted, and there is much rejoicing. */
@@ -1415,7 +1334,7 @@ static svn_error_t *add_lock_tokens(cons
 
       if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE))
         return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
-                                    sb, pool);
+                                    sb);
 
       token = token_item->u.string->data;
       SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token));
@@ -1457,7 +1376,7 @@ static svn_error_t *unlock_paths(const a
          errors. */
       err = svn_repos_fs_unlock(sb->repository->repos, full_path, token,
                                 FALSE, iterpool);
-      log_server_error(err, sb, iterpool);
+      log_error(err, sb);
       svn_error_clear(err);
     }
 
@@ -2822,7 +2741,7 @@ static svn_error_t *lock_many(svn_ra_svn
       if (! lookup_access(pool, b, svn_authz_write, full_path, TRUE))
         {
           err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
-                                     b, pool);
+                                     b);
           break;
         }
 
@@ -2944,7 +2863,7 @@ static svn_error_t *unlock_many(svn_ra_s
                           ! break_lock))
         return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
                                 error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
-                                                     NULL, NULL, b, pool),
+                                                     NULL, NULL, b),
                                 NULL);
 
       err = svn_repos_fs_unlock(b->repository->repos, full_path, token,
@@ -3476,7 +3395,7 @@ static void
 fs_warning_func(void *baton, svn_error_t *err)
 {
   fs_warning_baton_t *b = baton;
-  log_server_error(err, b->server, b->pool);
+  log_error(err, b->server);
   /* TODO: Keep log_pool in the server baton, cleared after every log? */
   svn_pool_clear(b->pool);
 }
@@ -3645,12 +3564,11 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
   repository.use_sasl = FALSE;
 
   b.read_only = params->read_only;
-  b.log_file = params->log_file;
-  b.log_file_mutex = params->log_file_mutex;
   b.pool = pool;
   b.vhost = params->vhost;
 
   b.repository = &repository;
+  b.logger = params->logger;
   b.client_info = get_client_info(conn, params, pool);
 
   /* construct FS configuration parameters */
@@ -3745,7 +3663,7 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
   err = handle_config_error(find_repos(client_url, params->root,
                                        b.vhost, b.read_only,
                                        params->cfg, b.repository,
-                                       cap_words, pool), &b, pool);
+                                       cap_words, pool), &b);
   if (!err)
     {
       if (repository.anon_access == NO_ACCESS
@@ -3754,21 +3672,18 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
                   && !repository.use_sasl)))
         err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                    "No access allowed to this repository",
-                                   &b, pool);
+                                   &b);
     }
   if (!err)
     {
       SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE));
       if (current_access(&b) == NO_ACCESS)
         err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
-                                   "Not authorized for access",
-                                   &b, pool);
+                                   "Not authorized for access", &b);
     }
   if (err)
     {
-      log_error(err, b.log_file, b.log_file_mutex,
-                svn_ra_svn_conn_remote_host(conn), b.client_info->user,
-                NULL, pool);
+      log_error(err, &b);
       io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
       svn_error_clear(err);
       SVN_ERR(io_err);

Modified: subversion/trunk/subversion/svnserve/server.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/server.h?rev=1530739&r1=1530738&r2=1530739&view=diff
==============================================================================
--- subversion/trunk/subversion/svnserve/server.h (original)
+++ subversion/trunk/subversion/svnserve/server.h Wed Oct  9 17:58:52 2013
@@ -77,8 +77,7 @@ typedef struct client_info_t {
 typedef struct server_baton_t {
   repository_t *repository; /* repository-specific data to use */
   client_info_t *client_info; /* client-specific data to use */
-  apr_file_t *log_file;    /* Log filehandle. */
-  svn_mutex__t *log_file_mutex; /* Serializes access to log_file.
+  struct logger_t *logger; /* Log file data structure.
                               May be NULL even if log_file is not. */
   svn_boolean_t read_only; /* Disallow write access (global flag) */
   svn_boolean_t vhost;     /* Use virtual-host-based path to repo. */
@@ -113,11 +112,8 @@ typedef struct serve_params_t {
      per-repository svnserve.conf are not read. */
   svn_config_t *cfg;
 
-  /* A filehandle open for writing logs to; possibly NULL. */
-  apr_file_t *log_file;
-
-  /* Mutex to serialize access to log_file; possibly NULL. */
-  svn_mutex__t *log_file_mutex;
+  /* logging data structure; possibly NULL. */
+  struct logger_t *logger;
 
   /* Username case normalization style. */
   enum username_case_type username_case;
@@ -178,16 +174,6 @@ svn_error_t *cyrus_auth_request(svn_ra_s
 apr_size_t escape_errorlog_item(char *dest, const char *source,
                                 apr_size_t buflen);
 
-/* Log ERR to LOG_FILE if LOG_FILE is not NULL.  In multi-threaded
-   servers, you should also provide a LOG_FILE_MUTEX object to serialize
-   access to the log file.   Include REMOTE_HOST, USER, and REPOS in the
-   log if they are not NULL.  Allocate temporary char buffers in POOL
-   (which caller can then clear or dispose of). */
-void
-log_error(svn_error_t *err, apr_file_t *log_file,
-          svn_mutex__t *log_file_mutex, const char *remote_host,
-          const char *user, const char *repos, apr_pool_t *pool);
-
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/trunk/subversion/svnserve/svnserve.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/svnserve.c?rev=1530739&r1=1530738&r2=1530739&view=diff
==============================================================================
--- subversion/trunk/subversion/svnserve/svnserve.c (original)
+++ subversion/trunk/subversion/svnserve/svnserve.c Wed Oct  9 17:58:52 2013
@@ -78,6 +78,7 @@
 #endif
 
 #include "server.h"
+#include "logger.h"
 
 /* The strategy for handling incoming connections.  Some of these may be
    unavailable due to platform limitations. */
@@ -516,8 +517,8 @@ serve_socket(apr_socket_t *usock,
   /* process the actual request and log errors */
   err = serve(conn, params, pool);
   if (err)
-    log_error(err, params->log_file, params->log_file_mutex,
-              svn_ra_svn_conn_remote_host(conn), NULL, NULL, pool);
+    logger__log_error(params->logger, err, NULL,
+                      get_client_info(conn, params, pool));
 
   return svn_error_trace(err);
 }
@@ -665,8 +666,7 @@ int main(int argc, const char *argv[])
   params.base = NULL;
   params.cfg = NULL;
   params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
-  params.log_file = NULL;
-  params.log_file_mutex = NULL;
+  params.logger = NULL;
   params.vhost = FALSE;
   params.username_case = CASE_ASIS;
   params.memory_cache_size = (apr_uint64_t)-1;
@@ -922,12 +922,7 @@ int main(int argc, const char *argv[])
     }
 
   if (log_filename)
-    {
-      SVN_INT_ERR(svn_io_file_open(&params.log_file, log_filename,
-                                  APR_WRITE | APR_CREATE | APR_APPEND,
-                                  APR_OS_DEFAULT, pool));
-      SVN_INT_ERR(svn_mutex__init(&params.log_file_mutex, TRUE, pool));
-    }
+    SVN_INT_ERR(logger__create(&params.logger, log_filename, pool));
 
   if (params.tunnel_user && run_mode != run_mode_tunnel)
     {
@@ -1255,9 +1250,7 @@ int main(int argc, const char *argv[])
           else
             {
               err = svn_error_wrap_apr(status, "apr_proc_fork");
-              log_error(err, params.log_file, params.log_file_mutex,
-                        NULL, NULL, NULL, /* ip, user, repos */
-                        socket_pool);
+              logger__log_error(params.logger, err, NULL, NULL);
               svn_error_clear(err);
               apr_socket_close(usock);
             }