Add common and combined access logging to httpd.
authordoug <doug@openbsd.org>
Fri, 1 Aug 2014 21:51:02 +0000 (21:51 +0000)
committerdoug <doug@openbsd.org>
Fri, 1 Aug 2014 21:51:02 +0000 (21:51 +0000)
ok reyk@

usr.sbin/httpd/httpd.conf.5
usr.sbin/httpd/httpd.h
usr.sbin/httpd/parse.y
usr.sbin/httpd/server_http.c

index aa651a0..c171b20 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: httpd.conf.5,v 1.12 2014/07/31 14:18:38 reyk Exp $
+.\"    $OpenBSD: httpd.conf.5,v 1.13 2014/08/01 21:51:02 doug Exp $
 .\"
 .\" Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 31 2014 $
+.Dd $Mdocdate: August 1 2014 $
 .Dt HTTPD.CONF 5
 .Os
 .Sh NAME
@@ -150,6 +150,17 @@ except
 .Ic listen on
 and
 .Ic location .
+.It Oo Ic no Oc Ic log Op Ar style
+Enable writing an access log to syslog.
+The
+.Ar style
+can be
+.Ar common
+or
+.Ar combined .
+It is similar to the Apache and nginx logging format.
+If not specified, the default is
+.Ar common .
 .It Ic root Ar directory
 Set the document root of the server.
 The
index 8ea75db..ad467b4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: httpd.h,v 1.27 2014/08/01 08:34:46 florian Exp $      */
+/*     $OpenBSD: httpd.h,v 1.28 2014/08/01 21:51:02 doug Exp $ */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -310,6 +310,12 @@ SPLAY_HEAD(client_tree, client);
        "\10\01NODELAY\02NO_NODELAY\03SACK\04NO_SACK"           \
        "\05SOCKET_BUFFER_SIZE\06IP_TTL\07IP_MINTTL\10NO_SPLICE"
 
+enum log_format {
+       LOG_FORMAT_NONE = -1,
+       LOG_FORMAT_COMMON = 0,
+       LOG_FORMAT_COMBINED,
+};
+
 struct server_config {
        u_int32_t                id;
        char                     name[MAXHOSTNAMELEN];
@@ -329,6 +335,8 @@ struct server_config {
        u_int8_t                 tcpipttl;
        u_int8_t                 tcpipminttl;
 
+       enum log_format          logformat;
+
        TAILQ_ENTRY(server_config) entry;
 };
 TAILQ_HEAD(serverhosts, server_config);
@@ -443,6 +451,7 @@ int  server_response(struct httpd *, struct client *);
 const char *
         server_http_host(struct sockaddr_storage *, char *, size_t);
 void    server_http_date(char *, size_t);
+int     server_log_http(struct client *, u_int, size_t);
 
 /* server_file.c */
 int     server_file(struct httpd *, struct client *);
index 91e0197..2ec30bd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.13 2014/07/31 14:18:38 reyk Exp $ */
+/*     $OpenBSD: parse.y,v 1.14 2014/08/01 21:51:02 doug Exp $ */
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -128,7 +128,7 @@ typedef struct {
 
 %token ALL AUTO DIRECTORY FCGI INDEX LISTEN LOCATION LOG NO ON PORT
 %token PREFORK ROOT SERVER SOCKET TYPES UPDATES VERBOSE
-%token ERROR INCLUDE
+%token ERROR INCLUDE COMMON COMBINED
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.number>      loglevel
@@ -301,6 +301,7 @@ serveroptsl : LISTEN ON STRING port {
                }
                | DIRECTORY dirflags
                | DIRECTORY '{' dirflags_l '}'
+               | logformat
                | fastcgi
                | LOCATION STRING               {
                        struct server   *s;
@@ -442,6 +443,17 @@ dirflags   : INDEX STRING          {
                }
                ;
 
+logformat      : LOG COMMON {
+                       srv->srv_conf.logformat = LOG_FORMAT_COMMON;
+               }
+               | LOG COMBINED {
+                       srv->srv_conf.logformat = LOG_FORMAT_COMBINED;
+               }
+               | NO LOG {
+                       srv->srv_conf.logformat = LOG_FORMAT_NONE;
+               }
+               ;
+
 types          : TYPES '{' optnl mediaopts_l '}'
                ;
 
@@ -575,6 +587,8 @@ lookup(char *s)
        static const struct keywords keywords[] = {
                { "all",                ALL },
                { "auto",               AUTO },
+               { "combined",           COMBINED },
+               { "common",             COMMON },
                { "directory",          DIRECTORY },
                { "fastcgi",            FCGI },
                { "include",            INCLUDE },
index 7849c61..719b970 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: server_http.c,v 1.25 2014/07/31 18:07:11 reyk Exp $   */
+/*     $OpenBSD: server_http.c,v 1.26 2014/08/01 21:51:02 doug Exp $   */
 
 /*
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -40,6 +40,7 @@
 #include <stdio.h>
 #include <err.h>
 #include <pwd.h>
+#include <syslog.h>
 #include <event.h>
 #include <fnmatch.h>
 
@@ -772,6 +773,9 @@ server_response_http(struct client *clt, u_int code,
        if (desc == NULL || (error = server_httperror_byid(code)) == NULL)
                return (-1);
 
+       if (server_log_http(clt, code, size) == -1)
+               return (-1);
+
        kv_purge(&desc->http_headers);
 
        /* Add error codes */
@@ -961,3 +965,88 @@ server_httperror_cmp(const void *a, const void *b)
        const struct http_error *eb = b;
        return (ea->error_code - eb->error_code);
 }
+
+int
+server_log_http(struct client *clt, u_int code, size_t len)
+{
+       static char tstamp[64];
+       static char ip[INET6_ADDRSTRLEN];
+       time_t t;
+       struct tm *tm;
+       struct server_config    *srv_conf;
+       struct http_descriptor  *desc;
+       struct kv               key, *agent, *referrer;
+
+       if ((srv_conf = clt->clt_srv_conf) == NULL)
+               return (-1);
+       if ((desc = clt->clt_desc) == NULL)
+               return (-1);
+       if (srv_conf->logformat == LOG_FORMAT_NONE)
+               return (0);
+
+       if ((t = time(NULL)) == -1)
+               return (-1);
+       if ((tm = localtime(&t)) == NULL)
+               return (-1);
+       if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0)
+               return (-1);
+
+       if (clt->clt_ss.ss_family == AF_INET) {
+               struct sockaddr_in *in = (struct sockaddr_in *)&clt->clt_ss;
+               if (inet_ntop(AF_INET, &in->sin_addr, ip, sizeof(ip)) == NULL)
+                       return (-1);
+       } else if (clt->clt_ss.ss_family == AF_INET6) {
+               struct sockaddr_in6 *in = (struct sockaddr_in6 *)&clt->clt_ss;
+               if (inet_ntop(AF_INET6, &in->sin6_addr, ip, sizeof(ip)) == NULL)
+                       return (-1);
+       } else
+               return (-1);
+
+       /*
+        * For details on common log format, see:
+        * https://httpd.apache.org/docs/current/mod/mod_log_config.html
+        */
+       switch (srv_conf->logformat) {
+       case LOG_FORMAT_COMMON:
+               log_info("%s %s - - [%s] \"%s %s%s%s%s%s\" %03d %zu",
+                   srv_conf->name, ip, tstamp,
+                   server_httpmethod_byid(desc->http_method),
+                   desc->http_path == NULL ? "" : desc->http_path,
+                   desc->http_query == NULL ? "" : "?",
+                   desc->http_query == NULL ? "" : desc->http_query,
+                   desc->http_version == NULL ? "" : " ",
+                   desc->http_version == NULL ? "" : desc->http_version,
+                   code, len);
+               break;
+
+       case LOG_FORMAT_COMBINED:
+               key.kv_key = "Referer"; /* sic */
+               if ((referrer = kv_find(&desc->http_headers, &key)) != NULL &&
+                   referrer->kv_value == NULL)
+                       referrer = NULL;
+
+               key.kv_key = "User-Agent";
+               if ((agent = kv_find(&desc->http_headers, &key)) != NULL &&
+                   agent->kv_value == NULL)
+                       agent = NULL;
+
+               log_info("%s %s - - [%s] \"%s %s%s%s%s%s\" %03d %zu"
+                   " \"%s\" \"%s\"",
+                   srv_conf->name, ip, tstamp,
+                   server_httpmethod_byid(desc->http_method),
+                   desc->http_path == NULL ? "" : desc->http_path,
+                   desc->http_query == NULL ? "" : "?",
+                   desc->http_query == NULL ? "" : desc->http_query,
+                   desc->http_version == NULL ? "" : " ",
+                   desc->http_version == NULL ? "" : desc->http_version,
+                   code, len,
+                   referrer == NULL ? "" : referrer->kv_value,
+                   agent == NULL ? "" : agent->kv_value);
+               break;
+
+       default:
+               return (-1);
+       }
+
+       return (0);
+}