wai-log 0.3.0.0 → 0.4.0.0
raw patch · 5 files changed
+79/−58 lines, 5 filesnew-uploaderPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Network.Wai.Log: logRequestUUID :: MonadLog m => UUID -> m a -> m a
+ Network.Wai.Log: [logGetRequestId] :: Options id -> Request -> IO id
+ Network.Wai.Log: logRequestId :: (MonadLog m, ToJSON id) => id -> m a -> m a
- Network.Wai.Log: Options :: LogLevel -> (UUID -> Request -> [Pair]) -> (UUID -> Request -> Response -> Value -> ResponseTime -> [Pair]) -> Maybe (Request -> Status -> ResponseHeaders -> Maybe (Builder -> Value)) -> Options
+ Network.Wai.Log: Options :: LogLevel -> (id -> Request -> [Pair]) -> (id -> Request -> Response -> Value -> ResponseTime -> [Pair]) -> Maybe (Request -> Status -> ResponseHeaders -> Maybe (Builder -> Value)) -> (Request -> IO id) -> Options id
- Network.Wai.Log: [logBody] :: Options -> Maybe (Request -> Status -> ResponseHeaders -> Maybe (Builder -> Value))
+ Network.Wai.Log: [logBody] :: Options id -> Maybe (Request -> Status -> ResponseHeaders -> Maybe (Builder -> Value))
- Network.Wai.Log: [logLevel] :: Options -> LogLevel
+ Network.Wai.Log: [logLevel] :: Options id -> LogLevel
- Network.Wai.Log: [logRequest] :: Options -> UUID -> Request -> [Pair]
+ Network.Wai.Log: [logRequest] :: Options id -> id -> Request -> [Pair]
- Network.Wai.Log: [logResponse] :: Options -> UUID -> Request -> Response -> Value -> ResponseTime -> [Pair]
+ Network.Wai.Log: [logResponse] :: Options id -> id -> Request -> Response -> Value -> ResponseTime -> [Pair]
- Network.Wai.Log: data Options
+ Network.Wai.Log: data Options id
- Network.Wai.Log: defaultLogRequest :: UUID -> Request -> [Pair]
+ Network.Wai.Log: defaultLogRequest :: ToJSON id => id -> Request -> [Pair]
- Network.Wai.Log: defaultLogResponse :: UUID -> Request -> Response -> Value -> ResponseTime -> [Pair]
+ Network.Wai.Log: defaultLogResponse :: ToJSON id => id -> Request -> Response -> Value -> ResponseTime -> [Pair]
- Network.Wai.Log: defaultOptions :: Options
+ Network.Wai.Log: defaultOptions :: Options UUID
- Network.Wai.Log: mkLogMiddleware :: MonadLog m => m LogMiddleware
+ Network.Wai.Log: mkLogMiddleware :: MonadLog m => m (LogMiddleware UUID)
- Network.Wai.Log: mkLogMiddlewareWith :: MonadLog m => Options -> m LogMiddleware
+ Network.Wai.Log: mkLogMiddlewareWith :: (MonadLog m, ToJSON id) => Options id -> m (LogMiddleware id)
- Network.Wai.Log: type LogMiddleware = (UUID -> Application) -> Application
+ Network.Wai.Log: type LogMiddleware id = (id -> Application) -> Application
Files
- CHANGELOG.md +8/−0
- src/Network/Wai/Log.hs +10/−9
- src/Network/Wai/Log/Internal.hs +11/−13
- src/Network/Wai/Log/Options.hs +48/−33
- wai-log.cabal +2/−3
CHANGELOG.md view
@@ -1,3 +1,11 @@+# wai-log-0.4 (2022-11-22)+* Parametrise the `LogMiddleware` type with an opaque type parameter for the request id+* Parametrise the `Options` record type with the opaque type parameter representing the request id+* Add new field to the `Options id` record type `logGetRequestId :: Request -> IO id` which represents a function for getting/generating a request id given a `Request`+* Update helper functions `logRequestId` and `requestId` to reflect changes to the request id type+* Add new helper function `mkOpaqueDefaultOptions` for building an `Options id` record for an opaque id+* Flatten the field structure of the log response emitted by `defaultLogResponse`+ # wai-log-0.3 (2020-11-02) * Rename `mkApplicationLogger` into `mkLogMiddleware`
src/Network/Wai/Log.hs view
@@ -9,11 +9,11 @@ -- "body_length": "KnownLength 136", -- "method": \"POST\", -- "user_agent": "curl/7.68.0",--- "request_uuid": "f2c89425-9ec4-4cd2-ae56-4bab23681fce",+-- "request_id": "f2c89425-9ec4-4cd2-ae56-4bab23681fce", -- "remote_host": "127.0.0.1:34694" -- } -- 2020-10-27 12:30:23 INFO eid-server: Sending response {--- "request_uuid": "f2c89425-9ec4-4cd2-ae56-4bab23681fce"+-- "request_id": "f2c89425-9ec4-4cd2-ae56-4bab23681fce" -- } -- 2020-10-27 12:30:23 INFO eid-server: Request complete { -- "response_body": null,@@ -27,7 +27,7 @@ -- "process": 2.97493e-3, -- "full": 3.159565e-3 -- },--- "request_uuid": "f2c89425-9ec4-4cd2-ae56-4bab23681fce"+-- "request_id": "f2c89425-9ec4-4cd2-ae56-4bab23681fce" -- } -- @ @@ -44,11 +44,12 @@ , defaultLogRequest , defaultLogResponse -- ** Helpers-, logRequestUUID+, logRequestId ) where import Prelude hiding (log) +import Data.Aeson (ToJSON) import Data.UUID (UUID) import Log (MonadLog, getLoggerIO) import Network.Wai@@ -60,18 +61,18 @@ -- that does not work when you want to pass logging context down to the -- 'Application'. ----- Instead we pass a 'UUID' to the 'Application', containting the--- @request_uuid@, so it can be logged in the application's context.-type LogMiddleware = (UUID -> Application) -> Application+-- Instead we pass an id to the 'Application', containing the+-- @request_id@, so it can be logged in the application's context.+type LogMiddleware id = (id -> Application) -> Application -- | Create a 'LogMiddleware' using 'defaultOptions' -- -- Use 'mkLogMiddlewareWith' for custom 'Options'-mkLogMiddleware :: MonadLog m => m LogMiddleware+mkLogMiddleware :: MonadLog m => m (LogMiddleware UUID) mkLogMiddleware = mkLogMiddlewareWith defaultOptions -- | Create a 'LogMiddleware' using the supplied 'Options'-mkLogMiddlewareWith :: MonadLog m => Options -> m LogMiddleware+mkLogMiddlewareWith :: (MonadLog m, ToJSON id) => Options id -> m (LogMiddleware id) mkLogMiddlewareWith options = do loggerIO <- getLoggerIO return $ logRequestsWith loggerIO options
src/Network/Wai/Log/Internal.hs view
@@ -1,30 +1,28 @@ {-# LANGUAGE RecordWildCards #-} module Network.Wai.Log.Internal where -import Data.Aeson.Types (Value(..), object)+import Data.Aeson.Types (ToJSON, 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)+import Network.Wai.Log.Options (Options(..), ResponseTime(..), requestId) -- | This type matches the one returned by 'getLoggerIO' type LoggerIO = UTCTime -> LogLevel -> Text -> Value -> IO () --- | Create a logging 'Middleware' that takes request UUID+-- | Create a logging 'Middleware' that takes request id -- given a 'LoggerIO' logging function and 'Options'-logRequestsWith :: LoggerIO -> Options -> (UUID -> Application) -> Application+logRequestsWith :: ToJSON id => LoggerIO -> Options id -> (id -> Application) -> Application logRequestsWith loggerIO Options{..} mkApp req respond = do- uuid <- nextRandom- logIO "Request received" $ logRequest uuid req+ reqId <- logGetRequestId req+ logIO "Request received" $ logRequest reqId req tStart <- getCurrentTime- mkApp uuid req $ \resp -> do+ mkApp reqId req $ \resp -> do tEnd <- getCurrentTime- logIO "Sending response" . requestUUID $ uuid+ logIO "Sending response" . requestId $ reqId r <- respond resp tFull <- getCurrentTime let processing = diffUTCTime tEnd tStart@@ -33,19 +31,19 @@ _ <- case logBody of Nothing ->- logIO "Request complete" $ logResponse uuid req resp Null times+ logIO "Request complete" $ logResponse reqId 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+ logIO "Request complete" $ logResponse reqId req resp Null times Just bodyLogValueConstructor -> bodyToIO $ \streamingBodyToIO -> let logWithBuilder :: Builder -> IO () logWithBuilder b = logIO "Request complete" $- logResponse uuid req resp (bodyLogValueConstructor b) times+ logResponse reqId req resp (bodyLogValueConstructor b) times in streamingBodyToIO logWithBuilder (return ()) return r
src/Network/Wai/Log/Options.hs view
@@ -7,16 +7,18 @@ , defaultLogRequest , defaultLogResponse -- * Helpers-, logRequestUUID-, requestUUID+, mkOpaqueDefaultOptions+, logRequestId+, requestId ) where -import Data.Aeson.Types (Pair, Value)+import Data.Aeson.Types (ToJSON, Pair, Value) import Data.ByteString.Builder (Builder) import Data.String.Conversions (ConvertibleStrings, StrictText, cs) import Data.Text (Text) import Data.Time.Clock (NominalDiffTime) import Data.UUID (UUID)+import Data.UUID.V4 (nextRandom) import Log import Network.HTTP.Types.Header (ResponseHeaders) import Network.HTTP.Types.Status (Status, statusCode, statusMessage)@@ -27,15 +29,17 @@ -- Logging response body involves extracting it from @Response@ via IO operations, -- therefore the @logBody@ option takes @Request@, @Status@ and @ResponseHeaders@ -- as arguments to decide whether the IO operations of body extraction have--- to be permormed.+-- to be performed. -- The resulting @Maybe@ function is the constructor of a loggable @Value@ -- from the body bytestring builder.-data Options = Options {+data Options id = Options { logLevel :: LogLevel- , logRequest :: UUID -> Request -> [Pair]- , logResponse :: UUID -> Request -> Response -> Value -> ResponseTime -> [Pair]+ , logRequest :: id -> Request -> [Pair]+ , logResponse :: id -> Request -> Response -> Value -> ResponseTime -> [Pair] -- | An optional constructor of the response body log value. , logBody :: Maybe (Request -> Status -> ResponseHeaders -> Maybe (Builder -> Value))+ -- | A function for getting the request id+ , logGetRequestId :: Request -> IO id } -- | Timing data@@ -52,16 +56,29 @@ -- { logLevel = 'LogInfo' -- , logRequest = 'defaultLogRequest' -- , logResponse = 'defaultLogResponse'+-- , logGetRequestId = 'const nextRandom' -- } -- @-defaultOptions :: Options+defaultOptions :: Options UUID defaultOptions = Options { logLevel = LogInfo , logRequest = defaultLogRequest , logResponse = defaultLogResponse , logBody = Nothing+ , logGetRequestId = const nextRandom } +-- | Build a default 'Options' record for an opaque id given a function+-- for retrieving an id.+mkOpaqueDefaultOptions :: ToJSON id => (Request -> IO id) -> Options id+mkOpaqueDefaultOptions getReqId = Options+ { logLevel = LogInfo+ , logRequest = defaultLogRequest+ , logResponse = defaultLogResponse+ , logBody = Nothing+ , logGetRequestId = getReqId+ }+ -- | Logs the following request values: -- -- * request_uuid@@ -70,9 +87,9 @@ -- * remote host -- * user agent -- * body-length-defaultLogRequest :: UUID -> Request -> [Pair]-defaultLogRequest uuid req =- [ "request_uuid" .= uuid+defaultLogRequest :: ToJSON id => id -> Request -> [Pair]+defaultLogRequest reqId req =+ [ "request_id" .= reqId , "method" .= ts (requestMethod req) , "url" .= ts (rawPathInfo req) , "remote_host" .= show (remoteHost req)@@ -82,7 +99,7 @@ -- | Logs the following values: ----- * request_uuid+-- * request_id -- * request method -- * request url path -- * response_body details provided as 'Value'@@ -92,31 +109,29 @@ -- * time processing -- -- Time is in seconds as that is how 'NominalDiffTime' is treated by default-defaultLogResponse :: UUID -> Request -> Response -> Value -> ResponseTime -> [Pair]-defaultLogResponse uuid req resp responseBody time =- [ "request_uuid" .= uuid- , "method" .= ts (requestMethod req)- , "url" .= ts (rawPathInfo req)- , "response_body" .= responseBody- , "status" .= object [ "code" .= statusCode (responseStatus resp)- , "message" .= ts (statusMessage (responseStatus resp))- ]- , "time" .= object [ "full" .= full time- , "process" .= processing time- ]- ]+defaultLogResponse :: ToJSON id => id -> Request -> Response -> Value -> ResponseTime -> [Pair]+defaultLogResponse reqId req resp responseBody time =+ [ "request_id" .= reqId+ , "method" .= ts (requestMethod req)+ , "url" .= ts (rawPathInfo req)+ , "response_body" .= responseBody+ , "response_code" .= statusCode (responseStatus resp)+ , "response_message" .= ts (statusMessage $ responseStatus resp)+ , "full_time" .= full time+ , "elapsed_time" .= processing time+ ] --- | Helper to consistently log the UUID in your application by adding--- @request_uuid@ field to log's 'localData'-logRequestUUID :: MonadLog m => UUID -> m a -> m a-logRequestUUID = localData . requestUUID+-- | Helper to consistently log the request id in your application by adding+-- @request_id@ field to log's 'localData'+logRequestId :: (MonadLog m, ToJSON id) => id -> m a -> m a+logRequestId = localData . requestId -- | Logs the following values: ----- * request_uuid-requestUUID :: UUID -> [Pair]-requestUUID uuid =- [ "request_uuid" .= uuid ]+-- * request_id+requestId :: ToJSON id => id -> [Pair]+requestId reqId =+ [ "request_id" .= reqId ] ts :: ConvertibleStrings a StrictText => a -> Text ts = cs
wai-log.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: wai-log-version: 0.3.0.0+version: 0.4.0.0 synopsis: A logging middleware for WAI applications @@ -23,8 +23,7 @@ build-type: Simple extra-source-files: CHANGELOG.md, README.md-tested-with: GHC ==7.10.3, GHC ==8.0.2, GHC ==8.2.2, GHC ==8.4.4,- GHC ==8.6.5, GHC ==8.8.4+tested-with: GHC ==8.10.7, GHC ==9.2.4 Source-repository head Type: git