How to show HTTP status code in web server logs?

Viewed 1322

I have a web application written using Racket's web server:

#lang racket
(require web-server/servlet-env)

; Some web app code here ...

;; Start the web server.
(serve/servlet request-handler
               #:log-file "/dev/stdout")

When I start the web server and access the web app through the browser, the logs appear like this:

127.0.0.1 - - [25/Dec/2018:12:34:56 +0000] "GET /servlets/standalone.rkt HTTP/1.1" - -
127.0.0.1 - - [25/Dec/2018:12:34:56 +0000] "GET /favicon.ico HTTP/1.1" - -

But where is the HTTP status code? With incomplete logs like this, how will I know if any of the pages resulted in 500, 403, 404, etc.?

How can I make the web server logs show the HTTP status code?

UPDATE: Issue on GitHub.

1 Answers

The built-in logging is just request logging -- not request and response logging. I agree; that omits some important information.

I suggest doing your own logging. At least, that's what I do.

In fact, I usually have a whole chain of wrappers around the dispatch procedure from dispatch-rules. One of which does logging.

For example:

(serve/servlet (~> ;Note: requests go UP this chain, responses DOWN
                dispatch
                wrap-gzip
                wrap-not-modified
                wrap-authorize
                wrap-authenticate
                wrap-http->https
                wrap-timed-and-logged)
               #:servlet-path      "/"
               #:servlet-regexp    #px""
               #:listen-ip         #f
               #:port              (current-internal-port)
               #:servlet-responder error-responder)

Example definition of wrap-timed-and-logged:

(define handler? (-> request? response?))
(define wrapper? (-> handler? handler?))

(define/contract ((wrap-timed-and-logged handler) req) wrapper?
  (define t0 (current-inexact-milliseconds))

  (define resp (handler req))

  (define t1 (current-inexact-milliseconds))
  (define dur (round (- t1 t0)))

  ;; Let's use "structured logging" here to make it easier to search,
  ;; and do things like create CloudWatch metrics from CloudWatch Logs
  ;; filters (they have a syntax to extract things from JSON.)
  (log-info
   (jsexpr->string
    (hasheq 'request  (hasheq 'method  (~a (request-method req))
                              'ip      (request-client-ip req)
                              'path    (url->string (request-uri req))
                              'headers (headers->hasheq (request-headers/raw req)))
            'response (hasheq 'code     (response-code resp)
                              'headers  (headers->hasheq (response-headers resp))
                              'duration dur))))

  resp)

(define (headers->hasheq hs)
  (for/hasheq ([h (in-list hs)])
    (values (string->symbol (~a (header-field h)))
            (~a (header-value h)))))

Example definition of error-responder:

;; Don't show exception info to end users! Instead log it.
(define (error-responder url exn)
  (log-error "Exception responding to ~v:\n~a"
             (url->string url)
             (exn->string exn))
  (response/full 500 #"Oops"
                 (current-seconds)
                 #f '() '()))
Related