Commit Diff


commit - 2a28b0442431d06f1989c9c4861c442bca5c7dcf
commit + abd261d25bdfa01c44d07a9803db669bc62ddf76
blob - d5794340a90748c089d7dd4853c57a1720c94e95
blob + 7f02a965b3c99535ad5e6cb0707305a50aa7b375
--- gmid.c
+++ gmid.c
@@ -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 *me
 		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");
 
blob - a3c1b603b10b6c01ada46b854f38e494b12532ec
blob + 58999ba28be31ff75d8b30d617c2dcf37e0ebb77
--- gmid.conf.5
+++ gmid.conf.5
@@ -142,7 +142,52 @@ 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.
 This increases the performance and prevents delays when connecting to
blob - 734ad54ee4b25530727537e969aaeb313b944ca2
blob + e1805b429d6d3545b4ffa1ccdd1e674f5bca3ef9
--- gmid.h
+++ gmid.h
@@ -89,6 +89,13 @@
 /* forward declaration */
 struct privsep;
 struct privsep_proc;
+
+enum log_format {
+	LOG_FORMAT_CONDENSED,
+	LOG_FORMAT_COMMON,
+	LOG_FORMAT_COMBINED,
+	LOG_FORMAT_LEGACY,
+};
 
 struct parser {
 	char		*iri;
@@ -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;
blob - 674437b99bf55b57b54204be396b4a192fc14197
blob + 74b5c96999bbda365eb94af188246653d3baf22e
--- parse.y
+++ parse.y
@@ -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},
blob - 9020103f39852831fb9418fd6405ec23906d6cd2
blob + 4fc9b5913d4b8ccb3940ec86855cd3128b448cdb
--- regress/tests.sh
+++ 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