wai-log-0.3.0.0: src/Network/Wai/Log/Internal.hs
{-# LANGUAGE RecordWildCards #-}
module Network.Wai.Log.Internal where
import Data.Aeson.Types (Value(..), object)
import Data.ByteString.Builder (Builder)
import Data.Text (Text)
import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime)
import Data.UUID (UUID)
import Data.UUID.V4 (nextRandom)
import Log (LogLevel)
import Network.Wai (Application, responseToStream)
import Network.Wai.Log.Options (Options(..), ResponseTime(..), requestUUID)
-- | This type matches the one returned by 'getLoggerIO'
type LoggerIO = UTCTime -> LogLevel -> Text -> Value -> IO ()
-- | Create a logging 'Middleware' that takes request UUID
-- given a 'LoggerIO' logging function and 'Options'
logRequestsWith :: LoggerIO -> Options -> (UUID -> Application) -> Application
logRequestsWith loggerIO Options{..} mkApp req respond = do
uuid <- nextRandom
logIO "Request received" $ logRequest uuid req
tStart <- getCurrentTime
mkApp uuid req $ \resp -> do
tEnd <- getCurrentTime
logIO "Sending response" . requestUUID $ uuid
r <- respond resp
tFull <- getCurrentTime
let processing = diffUTCTime tEnd tStart
full = diffUTCTime tFull tStart
times = ResponseTime{..}
_ <- case logBody of
Nothing ->
logIO "Request complete" $ logResponse uuid req resp Null times
Just bodyLogValueConstructorFunction ->
let (status, responseHeaders, bodyToIO) = responseToStream resp
mBodyLogValueConstructor =
bodyLogValueConstructorFunction req status responseHeaders
in case mBodyLogValueConstructor of
Nothing ->
logIO "Request complete" $ logResponse uuid req resp Null times
Just bodyLogValueConstructor ->
bodyToIO $ \streamingBodyToIO ->
let logWithBuilder :: Builder -> IO ()
logWithBuilder b = logIO "Request complete" $
logResponse uuid req resp (bodyLogValueConstructor b) times
in streamingBodyToIO logWithBuilder (return ())
return r
where
logIO message pairs = do
now <- getCurrentTime
loggerIO now logLevel message (object pairs)