aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gmid.c67
-rw-r--r--gmid.conf.545
-rw-r--r--gmid.h8
-rw-r--r--parse.y23
-rw-r--r--regress/tests.sh7
5 files changed, 142 insertions, 8 deletions
diff --git a/gmid.c b/gmid.c
index d579434..7f02a96 100644
--- a/gmid.c
+++ b/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 *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.
diff --git a/gmid.h b/gmid.h
index 734ad54..e1805b4 100644
--- a/gmid.h
+++ b/gmid.h
@@ -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;
diff --git a/parse.y b/parse.y
index 674437b..74b5c96 100644
--- a/parse.y
+++ b/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},
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