-- | A simple logging middleware for WAI applications that supports the 'log-*' -- family of packages: -- -- Currently there are no logging options but contributions are welcome. -- When logging to @stdout@, the output looks like this: -- -- @ -- 2019-02-21 19:51:47 INFO my-server: Request received { -- \"url\": \"\/api\/myapi\", -- \"body-length\": \"KnownLength 0\", -- \"method\": \"GET\", -- \"user-agent\": \"curl\/7.54.0\", -- \"remote-host\": \"127.0.0.1:61249\" -- } -- 2019-02-21 19:51:47 INFO my-server: Sending response -- 2019-02-21 19:51:47 INFO my-server: Request complete { -- \"status\": { -- \"code\": 200, -- \"message\": \"OK\" -- }, -- \"time\": { -- \"process\": 2.224e-3, -- \"full\": 2.348e-3 -- } -- } -- @ module Network.Wai.Log ( logRequestsWith ) where import Data.Aeson () import Data.String.Conversions (ConvertibleStrings, StrictText, cs) import Data.Text (Text) import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime) import Log import Network.HTTP.Types.Status import Network.Wai -- | Given a logger, create a 'Middleware' that logs incoming requests, the -- response code, and how long it took to process and respond to the request. logRequestsWith :: (LogT IO () -> IO ()) -> Middleware logRequestsWith runLogger app req respond = do runLogger . logInfo "Request received" $ object [ "method" .= ts (requestMethod req) , "url" .= ts (rawPathInfo req) , "remote-host" .= show (remoteHost req) , "user-agent" .= fmap ts (requestHeaderUserAgent req) , "body-length" .= show (requestBodyLength req) ] tStart <- getCurrentTime app req $ \resp -> do tEnd <- getCurrentTime runLogger $ logInfo_ "Sending response" r <- respond resp tFull <- getCurrentTime runLogger . logInfo "Request complete" $ object [ "status" .= object [ "code" .= statusCode (responseStatus resp) , "message" .= ts (statusMessage (responseStatus resp)) ] , "time" .= object [ "full" .= diffSeconds tFull tStart , "process" .= diffSeconds tEnd tStart ] ] return r diffSeconds :: UTCTime -> UTCTime -> Double diffSeconds a b = realToFrac $ diffUTCTime a b ts :: ConvertibleStrings a StrictText => a -> Text ts = cs