changeset 5702:777202558122

Added syslog support for error_log and access_log directives.
author Vladimir Homutov <vl@nginx.com>
date Mon, 12 May 2014 16:34:15 +0400
parents 1209b8a7b077
children 7deb01451486
files auto/sources src/core/ngx_core.h src/core/ngx_log.c src/core/ngx_log.h src/core/ngx_syslog.c src/core/ngx_syslog.h src/core/ngx_times.c src/core/ngx_times.h src/http/modules/ngx_http_log_module.c src/http/ngx_http_request.h src/os/unix/ngx_process_cycle.c
diffstat 11 files changed, 544 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/auto/sources
+++ b/auto/sources
@@ -37,7 +37,8 @@ CORE_DEPS="src/core/nginx.h \
            src/core/ngx_resolver.h \
            src/core/ngx_open_file_cache.h \
            src/core/ngx_crypt.h \
-           src/core/ngx_proxy_protocol.h"
+           src/core/ngx_proxy_protocol.h \
+           src/core/ngx_syslog.h"
 
 
 CORE_SRCS="src/core/nginx.c \
@@ -69,7 +70,8 @@ CORE_SRCS="src/core/nginx.c \
            src/core/ngx_resolver.c \
            src/core/ngx_open_file_cache.c \
            src/core/ngx_crypt.c \
-           src/core/ngx_proxy_protocol.c"
+           src/core/ngx_proxy_protocol.c \
+           src/core/ngx_syslog.c"
 
 
 REGEX_MODULE=ngx_regex_module
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -77,6 +77,7 @@ typedef void (*ngx_connection_handler_pt
 #include <ngx_open_file_cache.h>
 #include <ngx_os.h>
 #include <ngx_connection.h>
+#include <ngx_syslog.h>
 #include <ngx_proxy_protocol.h>
 
 
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -148,6 +148,12 @@ ngx_log_error_core(ngx_uint_t level, ngx
             break;
         }
 
+        if (log->writer) {
+            log->writer(log, level, errstr, p - errstr);
+            log = log->next;
+            continue;
+        }
+
         (void) ngx_write_fd(log->file->fd, errstr, p - errstr);
 
         if (log->file->fd == ngx_stderr) {
@@ -366,15 +372,33 @@ ngx_log_init(u_char *prefix)
 ngx_int_t
 ngx_log_open_default(ngx_cycle_t *cycle)
 {
-    static ngx_str_t  error_log = ngx_string(NGX_ERROR_LOG_PATH);
+    ngx_log_t         *log;
+    static ngx_str_t   error_log = ngx_string(NGX_ERROR_LOG_PATH);
 
-    if (cycle->new_log.file == NULL) {
-        cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);
-        if (cycle->new_log.file == NULL) {
+    if (ngx_log_get_file_log(&cycle->new_log) != NULL) {
+        return NGX_OK;
+    }
+
+    if (cycle->new_log.log_level != 0) {
+        /* there are some error logs, but no files */
+
+        log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));
+        if (log == NULL) {
             return NGX_ERROR;
         }
 
-        cycle->new_log.log_level = NGX_LOG_ERR;
+        log->log_level = NGX_LOG_ERR;
+        ngx_log_insert(&cycle->new_log, log);
+
+    } else {
+        /* no error logs at all */
+        log = &cycle->new_log;
+        log->log_level = NGX_LOG_ERR;
+    }
+
+    log->file = ngx_conf_open_file(cycle, &error_log);
+    if (log->file == NULL) {
+        return NGX_ERROR;
     }
 
     return NGX_OK;
@@ -390,7 +414,8 @@ ngx_log_redirect_stderr(ngx_cycle_t *cyc
         return NGX_OK;
     }
 
-    fd = cycle->log->file->fd;
+    /* file log always exists when we are called */
+    fd = ngx_log_get_file_log(cycle->log)->file->fd;
 
     if (fd != ngx_stderr) {
         if (ngx_set_stderr(fd) == NGX_FILE_ERROR) {
@@ -405,6 +430,21 @@ ngx_log_redirect_stderr(ngx_cycle_t *cyc
 }
 
 
+ngx_log_t *
+ngx_log_get_file_log(ngx_log_t *head)
+{
+    ngx_log_t  *log;
+
+    for (log = head; log; log = log->next) {
+        if (log->file != NULL) {
+            return log;
+        }
+    }
+
+    return NULL;
+}
+
+
 static char *
 ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)
 {
@@ -482,8 +522,9 @@ ngx_error_log(ngx_conf_t *cf, ngx_comman
 char *
 ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head)
 {
-    ngx_log_t  *new_log;
-    ngx_str_t  *value, name;
+    ngx_log_t          *new_log;
+    ngx_str_t          *value, name;
+    ngx_syslog_peer_t  *peer;
 
     if (*head != NULL && (*head)->log_level == 0) {
         new_log = *head;
@@ -506,13 +547,30 @@ ngx_log_set_log(ngx_conf_t *cf, ngx_log_
         ngx_str_null(&name);
         cf->cycle->log_use_stderr = 1;
 
-    } else {
-        name = value[1];
-    }
+        new_log->file = ngx_conf_open_file(cf->cycle, &name);
+        if (new_log->file == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+
+     } else if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
+        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
+        if (peer == NULL) {
+            return NGX_CONF_ERROR;
+        }
 
-    new_log->file = ngx_conf_open_file(cf->cycle, &name);
-    if (new_log->file == NULL) {
-        return NGX_CONF_ERROR;
+        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        new_log->writer = ngx_syslog_writer;
+        new_log->wdata = peer;
+
+    } else {
+        new_log->file = ngx_conf_open_file(cf->cycle, &value[1]);
+        if (new_log->file == NULL) {
+            return NGX_CONF_ERROR;
+        }
     }
 
     if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) {
--- a/src/core/ngx_log.h
+++ b/src/core/ngx_log.h
@@ -43,6 +43,8 @@
 
 
 typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);
+typedef void (*ngx_log_writer_pt) (ngx_log_t *log, ngx_uint_t level,
+    u_char *buf, size_t len);
 
 
 struct ngx_log_s {
@@ -54,6 +56,9 @@ struct ngx_log_s {
     ngx_log_handler_pt   handler;
     void                *data;
 
+    ngx_log_writer_pt    writer;
+    void                *wdata;
+
     /*
      * we declare "action" as "char *" because the actions are usually
      * the static strings and in the "u_char *" case we have to override
@@ -227,6 +232,7 @@ void ngx_cdecl ngx_log_stderr(ngx_err_t 
 u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);
 ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle);
 ngx_int_t ngx_log_redirect_stderr(ngx_cycle_t *cycle);
+ngx_log_t *ngx_log_get_file_log(ngx_log_t *head);
 char *ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head);
 
 
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_syslog.c
@@ -0,0 +1,332 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_SYSLOG_MAX_STR                                                    \
+     NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1                  \
+     + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */                               \
+     + 32 /* tag */ + 2 /* colon, space */
+
+
+static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);
+static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);
+static void ngx_syslog_cleanup(void *data);
+
+
+static char  *facilities[] = {
+    "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp",
+    "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0",
+    "local1", "local2", "local3", "local4", "local5", "local6", "local7",
+    NULL
+};
+
+/* note 'error/warn' like in nginx.conf, not 'err/warning' */
+static char  *severities[] = {
+    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL
+};
+
+static ngx_log_t    ngx_syslog_dummy_log;
+static ngx_event_t  ngx_syslog_dummy_event;
+
+
+char *
+ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
+{
+    peer->pool = cf->pool;
+    peer->facility = NGX_CONF_UNSET_UINT;
+    peer->severity = NGX_CONF_UNSET_UINT;
+
+    if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (peer->server.sockaddr == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "no syslog server specified");
+        return NGX_CONF_ERROR;
+    }
+
+    if (peer->facility == NGX_CONF_UNSET_UINT) {
+        peer->facility = 23; /* local7 */
+    }
+
+    if (peer->severity == NGX_CONF_UNSET_UINT) {
+        peer->severity = 6; /* info */
+    }
+
+    if (peer->tag.data == NULL) {
+        ngx_str_set(&peer->tag, "nginx");
+    }
+
+    peer->conn.fd = (ngx_socket_t) -1;
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
+{
+    u_char      *p, *comma, c;
+    size_t       len;
+    ngx_str_t   *value;
+    ngx_url_t    u;
+    ngx_uint_t   i;
+
+    value = cf->args->elts;
+
+    p = value[1].data + sizeof("syslog:") - 1;
+
+    for ( ;; ) {
+        comma = (u_char *) ngx_strchr(p, ',');
+
+        if (comma != NULL) {
+            len = comma - p;
+            *comma = '\0';
+
+        } else {
+            len = value[1].data + value[1].len - p;
+        }
+
+        if (ngx_strncmp(p, "server=", 7) == 0) {
+
+            if (peer->server.sockaddr != NULL) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate syslog \"server\"");
+                return NGX_CONF_ERROR;
+            }
+
+            ngx_memzero(&u, sizeof(ngx_url_t));
+
+            u.url.data = p + 7;
+            u.url.len = len - 7;
+            u.default_port = 514;
+
+            if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+                if (u.err) {
+                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                       "%s in syslog server \"%V\"",
+                                       u.err, &u.url);
+                }
+
+                return NGX_CONF_ERROR;
+            }
+
+            peer->server = u.addrs[0];
+
+        } else if (ngx_strncmp(p, "facility=", 9) == 0) {
+
+            if (peer->facility != NGX_CONF_UNSET_UINT) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate syslog \"facility\"");
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 0; facilities[i] != NULL; i++) {
+
+                if (ngx_strcmp(p + 9, facilities[i]) == 0) {
+                    peer->facility = i;
+                    goto next;
+                }
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "unknown syslog facility \"%s\"", p + 9);
+            return NGX_CONF_ERROR;
+
+        } else if (ngx_strncmp(p, "severity=", 9) == 0) {
+
+            if (peer->severity != NGX_CONF_UNSET_UINT) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate syslog \"severity\"");
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 0; severities[i] != NULL; i++) {
+
+                if (ngx_strcmp(p + 9, severities[i]) == 0) {
+                    peer->severity = i;
+                    goto next;
+                }
+            }
+
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "unknown syslog severity \"%s\"", p + 9);
+            return NGX_CONF_ERROR;
+
+        } else if (ngx_strncmp(p, "tag=", 4) == 0) {
+
+            if (peer->tag.data != NULL) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "duplicate syslog \"tag\"");
+                return NGX_CONF_ERROR;
+            }
+
+            /*
+             * RFC 3164: the TAG is a string of ABNF alphanumeric characters
+             * that MUST NOT exceed 32 characters.
+             */
+            if (len - 4 > 32) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                   "syslog tag length exceeds 32");
+                return NGX_CONF_ERROR;
+            }
+
+            for (i = 4; i < len; i++) {
+                c = ngx_tolower(p[i]);
+
+                if (c < '0' || (c > '9' && c < 'a') || c > 'z') {
+                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                       "syslog \"tag\" only allows "
+                                       "alphanumeric characters");
+                    return NGX_CONF_ERROR;
+                }
+            }
+
+            peer->tag.data = p + 4;
+            peer->tag.len = len - 4;
+
+        } else {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "unknown syslog parameter \"%s\"", p);
+            return NGX_CONF_ERROR;
+        }
+
+    next:
+
+        if (comma == NULL) {
+            break;
+        }
+
+        p = comma + 1;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+u_char *
+ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)
+{
+    ngx_uint_t  pri;
+
+    pri = peer->facility * 8 + peer->severity;
+
+    return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time,
+                       &ngx_cycle->hostname, &peer->tag);
+}
+
+
+void
+ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
+    size_t len)
+{
+    u_char             *p, msg[NGX_SYSLOG_MAX_STR];
+    ngx_uint_t          head_len;
+    ngx_syslog_peer_t  *peer;
+
+    peer = log->wdata;
+
+    if (peer->processing) {
+        return;
+    }
+
+    peer->processing = 1;
+    peer->severity = level - 1;
+
+    p = ngx_syslog_add_header(peer, msg);
+    head_len = p - msg;
+
+    len -= NGX_LINEFEED_SIZE;
+
+    if (len > NGX_SYSLOG_MAX_STR - head_len) {
+        len = NGX_SYSLOG_MAX_STR - head_len;
+    }
+
+    p = ngx_snprintf(p, len, "%s", buf);
+
+    (void) ngx_syslog_send(peer, msg, p - msg);
+
+    peer->processing = 0;
+}
+
+
+ssize_t
+ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)
+{
+    if (peer->conn.fd == (ngx_socket_t) -1) {
+        if (ngx_syslog_init_peer(peer) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    if (ngx_send) {
+        return ngx_send(&peer->conn, buf, len);
+
+    } else {
+        /* event module has not yet set ngx_io */
+        return ngx_os_io.send(&peer->conn, buf, len);
+    }
+}
+
+
+static ngx_int_t
+ngx_syslog_init_peer(ngx_syslog_peer_t *peer)
+{
+    ngx_socket_t         fd;
+    ngx_pool_cleanup_t  *cln;
+
+    peer->conn.read = &ngx_syslog_dummy_event;
+    peer->conn.write = &ngx_syslog_dummy_event;
+    peer->conn.log = &ngx_syslog_dummy_log;
+
+    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;
+
+    cln = ngx_pool_cleanup_add(peer->pool, 0);
+    if (cln == NULL) {
+        return NGX_ERROR;
+    }
+
+    cln->data = peer;
+    cln->handler = ngx_syslog_cleanup;
+
+    fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);
+    if (fd == (ngx_socket_t) -1) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+                      ngx_socket_n " failed");
+        return NGX_ERROR;
+    }
+
+    if (ngx_nonblocking(fd) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+                      ngx_nonblocking_n " failed");
+        return NGX_ERROR;
+    }
+
+    if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+                      "connect() failed");
+        return NGX_ERROR;
+    }
+
+    peer->conn.fd = fd;
+    return NGX_OK;
+}
+
+
+static void
+ngx_syslog_cleanup(void *data)
+{
+    ngx_syslog_peer_t  *peer = data;
+
+    if (peer->conn.fd != (ngx_socket_t) -1) {
+        (void) ngx_close_socket(peer->conn.fd);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_syslog.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_SYSLOG_H_INCLUDED_
+#define _NGX_SYSLOG_H_INCLUDED_
+
+
+typedef struct {
+    ngx_pool_t       *pool;
+    ngx_uint_t        facility;
+    ngx_uint_t        severity;
+    ngx_str_t         tag;
+
+    ngx_addr_t        server;
+    ngx_connection_t  conn;
+    ngx_uint_t        processing;  /* unsigned processing:1; */
+} ngx_syslog_peer_t;
+
+
+char *ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer);
+u_char *ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf);
+void ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
+    size_t len);
+ssize_t ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len);
+
+
+#endif /* _NGX_SYSLOG_H_INCLUDED_ */
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -29,6 +29,7 @@ volatile ngx_str_t       ngx_cached_err_
 volatile ngx_str_t       ngx_cached_http_time;
 volatile ngx_str_t       ngx_cached_http_log_time;
 volatile ngx_str_t       ngx_cached_http_log_iso8601;
+volatile ngx_str_t       ngx_cached_syslog_time;
 
 #if !(NGX_WIN32)
 
@@ -50,6 +51,8 @@ static u_char            cached_http_log
                                     [sizeof("28/Sep/1970:12:00:00 +0600")];
 static u_char            cached_http_log_iso8601[NGX_TIME_SLOTS]
                                     [sizeof("1970-09-28T12:00:00+06:00")];
+static u_char            cached_syslog_time[NGX_TIME_SLOTS]
+                                    [sizeof("Sep 28 12:00:00")];
 
 
 static char  *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
@@ -63,6 +66,7 @@ ngx_time_init(void)
     ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
     ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;
     ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;
+    ngx_cached_syslog_time.len = sizeof("Sep 28 12:00:00") - 1;
 
     ngx_cached_time = &cached_time[0];
 
@@ -73,7 +77,7 @@ ngx_time_init(void)
 void
 ngx_time_update(void)
 {
-    u_char          *p0, *p1, *p2, *p3;
+    u_char          *p0, *p1, *p2, *p3, *p4;
     ngx_tm_t         tm, gmt;
     time_t           sec;
     ngx_uint_t       msec;
@@ -166,6 +170,11 @@ ngx_time_update(void)
                        tp->gmtoff < 0 ? '-' : '+',
                        ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
 
+    p4 = &cached_syslog_time[slot][0];
+
+    (void) ngx_sprintf(p4, "%s %2d %02d:%02d:%02d",
+                       months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,
+                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
 
     ngx_memory_barrier();
 
@@ -174,6 +183,7 @@ ngx_time_update(void)
     ngx_cached_err_log_time.data = p1;
     ngx_cached_http_log_time.data = p2;
     ngx_cached_http_log_iso8601.data = p3;
+    ngx_cached_syslog_time.data = p4;
 
     ngx_unlock(&ngx_time_lock);
 }
@@ -184,7 +194,7 @@ ngx_time_update(void)
 void
 ngx_time_sigsafe_update(void)
 {
-    u_char          *p;
+    u_char          *p, *p2;
     ngx_tm_t         tm;
     time_t           sec;
     ngx_time_t      *tp;
@@ -224,9 +234,16 @@ ngx_time_sigsafe_update(void)
                        tm.ngx_tm_mday, tm.ngx_tm_hour,
                        tm.ngx_tm_min, tm.ngx_tm_sec);
 
+    p2 = &cached_syslog_time[slot][0];
+
+    (void) ngx_sprintf(p2, "%s %2d %02d:%02d:%02d",
+                       months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,
+                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
+
     ngx_memory_barrier();
 
     ngx_cached_err_log_time.data = p;
+    ngx_cached_syslog_time.data = p2;
 
     ngx_unlock(&ngx_time_lock);
 }
--- a/src/core/ngx_times.h
+++ b/src/core/ngx_times.h
@@ -40,6 +40,7 @@ extern volatile ngx_str_t    ngx_cached_
 extern volatile ngx_str_t    ngx_cached_http_time;
 extern volatile ngx_str_t    ngx_cached_http_log_time;
 extern volatile ngx_str_t    ngx_cached_http_log_iso8601;
+extern volatile ngx_str_t    ngx_cached_syslog_time;
 
 /*
  * milliseconds elapsed since epoch and truncated to ngx_msec_t,
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -66,6 +66,7 @@ typedef struct {
     ngx_http_log_script_t      *script;
     time_t                      disk_full_time;
     time_t                      error_log_time;
+    ngx_syslog_peer_t          *syslog_peer;
     ngx_http_log_fmt_t         *format;
     ngx_http_complex_value_t   *filter;
 } ngx_http_log_t;
@@ -240,7 +241,8 @@ static ngx_int_t
 ngx_http_log_handler(ngx_http_request_t *r)
 {
     u_char                   *line, *p;
-    size_t                    len;
+    size_t                    len, size;
+    ssize_t                   n;
     ngx_str_t                 val;
     ngx_uint_t                i, l;
     ngx_http_log_t           *log;
@@ -294,6 +296,16 @@ ngx_http_log_handler(ngx_http_request_t 
             }
         }
 
+        if (log[l].syslog_peer) {
+
+            /* length of syslog's PRI and HEADER message parts */
+            len += sizeof("<255>Jan 01 00:00:00 ") - 1
+                   + ngx_cycle->hostname.len + 1
+                   + log[l].syslog_peer->tag.len + 2;
+
+            goto alloc_line;
+        }
+
         len += NGX_LINEFEED_SIZE;
 
         buffer = log[l].file ? log[l].file->data : NULL;
@@ -332,6 +344,8 @@ ngx_http_log_handler(ngx_http_request_t 
             }
         }
 
+    alloc_line:
+
         line = ngx_pnalloc(r->pool, len);
         if (line == NULL) {
             return NGX_ERROR;
@@ -339,10 +353,33 @@ ngx_http_log_handler(ngx_http_request_t 
 
         p = line;
 
+        if (log[l].syslog_peer) {
+            p = ngx_syslog_add_header(log[l].syslog_peer, line);
+        }
+
         for (i = 0; i < log[l].format->ops->nelts; i++) {
             p = op[i].run(r, p, &op[i]);
         }
 
+        if (log[l].syslog_peer) {
+
+            size = p - line;
+
+            n = ngx_syslog_send(log[l].syslog_peer, line, size);
+
+            if (n < 0) {
+                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                              "send() to syslog failed");
+
+            } else if ((size_t) n != size) {
+                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+                              "send() to syslog has written only %z of %uz",
+                              n, size);
+            }
+
+            continue;
+        }
+
         ngx_linefeed(p);
 
         ngx_http_log_write(r, &log[l], line, p - line);
@@ -1080,6 +1117,7 @@ ngx_http_log_merge_loc_conf(ngx_conf_t *
     log->script = NULL;
     log->disk_full_time = 0;
     log->error_log_time = 0;
+    log->syslog_peer = NULL;
 
     lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
     fmt = lmcf->formats.elts;
@@ -1103,6 +1141,7 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
     ngx_msec_t                         flush;
     ngx_str_t                         *value, name, s, filter;
     ngx_http_log_t                    *log;
+    ngx_syslog_peer_t                 *peer;
     ngx_http_log_buf_t                *buffer;
     ngx_http_log_fmt_t                *fmt;
     ngx_http_log_main_conf_t          *lmcf;
@@ -1138,6 +1177,23 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
 
     ngx_memzero(log, sizeof(ngx_http_log_t));
 
+
+    if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
+
+        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
+        if (peer == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
+            return NGX_CONF_ERROR;
+        }
+
+        log->syslog_peer = peer;
+
+        goto process_formats;
+    }
+
     n = ngx_http_script_variables_count(&value[1]);
 
     if (n == 0) {
@@ -1171,6 +1227,8 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
         }
     }
 
+process_formats:
+
     if (cf->args->nelts >= 3) {
         name = value[2];
 
@@ -1199,6 +1257,17 @@ ngx_http_log_set_log(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
+    if (log->syslog_peer != NULL) {
+        if (cf->args->nelts > 3) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "parameter \"%V\" is not supported by syslog",
+                               &value[3]);
+            return NGX_CONF_ERROR;
+        }
+
+        return NGX_CONF_OK;
+    }
+
     size = 0;
     flush = 0;
     gzip = 0;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -587,6 +587,8 @@ extern ngx_http_header_out_t   ngx_http_
                                                                               \
     c->log->file = l->file;                                                   \
     c->log->next = l->next;                                                   \
+    c->log->writer = l->writer;                                               \
+    c->log->wdata = l->wdata;                                                 \
     if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {                    \
         c->log->log_level = l->log_level;                                     \
     }
--- a/src/os/unix/ngx_process_cycle.c
+++ b/src/os/unix/ngx_process_cycle.c
@@ -710,11 +710,13 @@ ngx_master_process_exit(ngx_cycle_t *cyc
      * ngx_cycle->pool is already destroyed.
      */
 
-    ngx_exit_log_file.fd = ngx_cycle->log->file->fd;
 
-    ngx_exit_log = *ngx_cycle->log;
+    ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);
+
+    ngx_exit_log_file.fd = ngx_exit_log.file->fd;
     ngx_exit_log.file = &ngx_exit_log_file;
     ngx_exit_log.next = NULL;
+    ngx_exit_log.writer = NULL;
 
     ngx_exit_cycle.log = &ngx_exit_log;
     ngx_exit_cycle.files = ngx_cycle->files;
@@ -1065,11 +1067,12 @@ ngx_worker_process_exit(ngx_cycle_t *cyc
      * ngx_cycle->pool is already destroyed.
      */
 
-    ngx_exit_log_file.fd = ngx_cycle->log->file->fd;
+    ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);
 
-    ngx_exit_log = *ngx_cycle->log;
+    ngx_exit_log_file.fd = ngx_exit_log.file->fd;
     ngx_exit_log.file = &ngx_exit_log_file;
     ngx_exit_log.next = NULL;
+    ngx_exit_log.writer = NULL;
 
     ngx_exit_cycle.log = &ngx_exit_log;
     ngx_exit_cycle.files = ngx_cycle->files;