diff options
-rw-r--r-- | gmid.c | 67 | ||||
-rw-r--r-- | gmid.conf.5 | 45 | ||||
-rw-r--r-- | gmid.h | 8 | ||||
-rw-r--r-- | parse.y | 23 | ||||
-rw-r--r-- | regress/tests.sh | 7 |
5 files changed, 142 insertions, 8 deletions
@@ -84,11 +84,23 @@ void log_request(struct client *c, int code, const char *meta) { struct conf *conf = c->conf; + char tstamp[64], rfc3339[32]; char b[GEMINI_URL_LEN]; char *fmted; const char *t; + struct tm *tm; + time_t now; int ec; + if ((now = time(NULL)) == -1) + fatal("time"); + if ((tm = localtime(&now)) == NULL) + fatal("localtime"); + if (strftime(tstamp, sizeof(tstamp), "%d/%b%Y:%H:%M:%S %z", tm) == 0) + fatal("strftime"); + if (strftime(rfc3339, sizeof(rfc3339), "%FT%T%z", tm) == 0) + fatal("strftime"); + if (c->iri.schema != NULL) { /* serialize the IRI */ strlcpy(b, c->iri.schema, sizeof(b)); @@ -114,8 +126,59 @@ log_request(struct client *c, int code, const char *meta) strlcpy(b, t, sizeof(b)); } - ec = asprintf(&fmted, "%s:%s GET %s %d %s", c->rhost, c->rserv, b, - code, meta); + switch (conf->log_format) { + case LOG_FORMAT_LEGACY: + ec = asprintf(&fmted, "%s:%s GET %s %d %s", c->rhost, + c->rserv, b, code, meta); + break; + + case LOG_FORMAT_CONDENSED: + /* + * XXX the first '-' is the remote user name, we + * could use the client cert for it. + * + * XXX it should log the size of the response + */ + ec = asprintf(&fmted, "%s %s - %s %s 0 %d %s", rfc3339, + c->rhost, *c->domain == '\0' ? c->iri.host : c->domain, + b, code, meta); + break; + + /* + * Attempt to be compatible with the default Apache httpd' + * LogFormat "%h %l %u %t \"%r\" %>s %b" + * see <https://httpd.apache.org/docs/current/mod/mod_log_config.html> + */ + case LOG_FORMAT_COMMON: + /* + * XXX the second '-' is the remote user name, we + * could use the client cert for it. + * + * XXX it should log the size of the response. + */ + ec = asprintf(&fmted, "%s %s - - %s \"%s\" %d 0", + *c->domain == '\0' ? c->iri.host : c->domain, + c->rhost, tstamp, b, code); + break; + + /* + * Attempt to be compatible with the default nginx' log_format + * combined: + * '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'; + */ + case LOG_FORMAT_COMBINED: + default: + /* + * XXX the second '-' is the remote user name, we + * could use the client cert for it. + * + * XXX it should log the size of the response. + */ + ec = asprintf(&fmted, "%s - - [%s] \"%s\" %d 0 \"-\" \"\"", + c->rhost, tstamp, b, code); + break; + } + if (ec == -1) fatal("asprintf"); diff --git a/gmid.conf.5 b/gmid.conf.5 index a3c1b60..58999ba 100644 --- a/gmid.conf.5 +++ b/gmid.conf.5 @@ -142,6 +142,51 @@ Log the requests to .Ar file . The path is relative to the .Ic chroot . +.It Ic style Ar style +Set the logging style, defaults to +.Ic condensed . +The +.Ar style +can be one of: +.Bl -tag -width Ds +.It Ic common +Attempt to be compatible with the default Apache httpd log format. +Each line is formatted as follows: the matching host name, +the remote IP address, one dash +.Sq - , +Common Name of the client certificate +.Pq if provided, '-' otherwise , +the timestamp of the request, the request URI wrapped in double quotes, +the response code and the size of the response. +.It Ic combined +Attempt to be compatible with the default nginx log format. +Each line is formatted as follows: the remote IP address, one dash +.Sq - , +Common Name of the client certificate +.Pq if provided, '-' otherwise , +the timestamp wrapped in square brackets, the request URI wrapped in +double quotes, the response code, the size of the response, a dash +wrapped in double quotes and "". +The strangness of these two last fields is because Gemini doesn't have +the notion of the +.Dq Referer +header nor the +.Dq User-agent . +.It Ic condensed +The native +.Xr gmid 8 +format since 2.0. +Each line is formatted as follows: RFC 3339 date time, +remote IP address, Common Name of the client certificate +.Pq if provided, '-' otherwise , +the matching host name, the request URI, the size of the response, +the response code and meta. +.It Ic legacy +The pre-2.0 gmid native format. +Each line is formatted as follows: the remote IP address and port, the +.Sq GET +keyword, the request URI, the response code and meta. +.El .El .It Ic prefork Ar number Run the specified number of server processes. @@ -90,6 +90,13 @@ struct privsep; struct privsep_proc; +enum log_format { + LOG_FORMAT_CONDENSED, + LOG_FORMAT_COMMON, + LOG_FORMAT_COMBINED, + LOG_FORMAT_LEGACY, +}; + struct parser { char *iri; struct iri *parsed; @@ -242,6 +249,7 @@ struct conf { int prefork; int reload; char *log_access; + enum log_format log_format; int use_privsep_crypto; struct fcgihead fcgi; @@ -124,16 +124,16 @@ typedef struct { %token ACCESS ALIAS AUTO %token BLOCK -%token CA CERT CHROOT CLIENT +%token CA CERT CHROOT CLIENT COMBINED COMMON CONDENSED %token DEFAULT %token FASTCGI FOR_HOST %token INCLUDE INDEX IPV6 %token KEY -%token LANG LISTEN LOCATION LOG +%token LANG LEGACY LISTEN LOCATION LOG %token OCSP OFF ON %token PARAM PORT PREFORK PROTO PROTOCOLS PROXY %token RELAY_TO REQUIRE RETURN ROOT -%token SERVER SNI SOCKET STRIP SYSLOG +%token SERVER SNI SOCKET STRIP STYLE SYSLOG %token TCP TOEXT TYPE TYPES %token USE_TLS USER %token VERIFYNAME @@ -268,6 +268,18 @@ logopt : SYSLOG { free(conf->log_access); conf->log_access = $2; } + | STYLE COMMON { + conf->log_format = LOG_FORMAT_COMMON; + } + | STYLE COMBINED { + conf->log_format = LOG_FORMAT_COMBINED; + } + | STYLE CONDENSED { + conf->log_format = LOG_FORMAT_CONDENSED; + } + | STILE LEGACY { + conf->log_format = LOG_FORMAT_LEGACY; + } ; vhost : SERVER string { @@ -603,6 +615,9 @@ static const struct keyword { {"cert", CERT}, {"chroot", CHROOT}, {"client", CLIENT}, + {"combined", COMBINED}, + {"common", COMMON}, + {"condensed", CONDENSED}, {"default", DEFAULT}, {"fastcgi", FASTCGI}, {"for-host", FOR_HOST}, @@ -611,6 +626,7 @@ static const struct keyword { {"ipv6", IPV6}, {"key", KEY}, {"lang", LANG}, + {"legacy", LEGACY}, {"listen", LISTEN}, {"location", LOCATION}, {"log", LOG}, @@ -631,6 +647,7 @@ static const struct keyword { {"sni", SNI}, {"socket", SOCKET}, {"strip", STRIP}, + {"style", STYLE}, {"syslog", SYSLOG}, {"tcp", TCP}, {"to-ext", TOEXT}, diff --git a/regress/tests.sh b/regress/tests.sh index 9020103..4fc9b59 100644 --- a/regress/tests.sh +++ b/regress/tests.sh @@ -385,10 +385,11 @@ test_log_file() { fetch_hdr / check_reply '20 text/gemini' - # remove the <ip>:<port> leading part - awk '{$1 = ""; print substr($0, 2)}' log > log.edited + # remove the date and ip + awk '{$1 = ""; $2 = ""; print substr($0, 3)}' log > log.edited - echo GET gemini://localhost/ 20 text/gemini | cmp -s - log.edited + printf '%s\n' '- localhost gemini://localhost/ 0 20 text/gemini' \ + | cmp -s - log.edited if [ $? -ne 0 ]; then # keep the log for post-mortem analysis return 1 |