RSS Feed

Lisp Project of the Day

lack-middleware-accesslog

You can support this project by donating at:

Donate using PatreonDonate using Liberapay

lack-middleware-accesslogweb

Documentation🥺
Docstrings🥺
Tests 😀
Examples:
RepositoryActivity😀
CI 😀

Yesterday, I've showed you how does Clack work. A web app is a function which returns a list with the response or a function which should return create such list pass it to the provided callback.

Now we'll add a logging to our app with the lack-middleware-accesslog system.

To do this, we must wrap our app's function into another function like this:

;; Now we'll create a simple app:
POFTHEDAY> (defparameter *app*
             (lambda (env)
               '(200 (:content-type "text/plain")
                 ("Hello, World"))))

;; And wrap it into the middleware:
POFTHEDAY> (defparameter *app-with-access-log*
             (funcall lack.middleware.accesslog:*lack-middleware-accesslog*
                      *app*))

;; Now it's time to start our app:
POFTHEDAY> (clack:clackup *app-with-access-log*
                          :port 8000)

Woo server is started.
Listening on 127.0.0.1:8000.

;; And to make a test request:
POFTHEDAY> (values (dex:get "http://localhost:8000/"))
127.0.0.1 - [21/Jun/2020:22:50:52 +03:00] "GET / HTTP/1.1" 
  200 12 "-" "Dexador/0.9.14 (SBCL 2.0.2); Darwin; 19.5.0"
"Hello, World"

Look how a log message was printed to the STDOUT.

Now let's see how does this middleware works. Here is its content. It is a little bit complicated because the middleware needs to handle cases when an app returns a function instead of the normal response:

(defparameter *lack-middleware-accesslog*
  (let ((no-body '#:no-body))
    (lambda (app &key
              (logger
               (lambda (output) (format t "~&~A~%" output)))
              (formatter #'default-formatter))
      (lambda (env)
        (funcall-with-cb
         app env
         (lambda (res)
           (funcall logger
                    (funcall formatter env res (now)))
           res)))))
  "Middleware for logging requests")

I'll show you a simpler version of this logging middleware:

;; Here is the middleware:
POFTHEDAY> (defun simple-logging (app)
             (lambda (env)
               (let ((response
                       (funcall app env)))
                 (format t "~A ~A -> ~A~%"
                         (getf env :request-method)
                         (getf env :path-info)
                         (car response))
                 response)))

;; And this is an example how we can apply it to our app:
POFTHEDAY> (defparameter *app-with-simple-log*
             (simple-logging *app*))

POFTHEDAY> (clack:clackup *app-with-simple-log*
                          :port 8000)
Hunchentoot server is started.
Listening on 127.0.0.1:8000.

POFTHEDAY> (values (dex:get "http://localhost:8000/"))
GET / -> 200
"Hello, World"

POFTHEDAY> (values (dex:get "http://localhost:8000/foo/bar"))
GET /foo/bar -> 200
"Hello, World"

Tomorrow we'll try the better way to apply middlewares to a Clack app.


Brought to you by 40Ants under Creative Commons License