wai-extra 3.0.26.1 → 3.0.27
raw patch · 4 files changed
+95/−8 lines, 4 files
Files
- ChangeLog.md +4/−0
- Network/Wai/Middleware/RequestLogger.hs +56/−6
- Network/Wai/Middleware/RequestLogger/JSON.hs +34/−1
- wai-extra.cabal +1/−1
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for wai-extra +## 3.0.27++* Add custom request log formatter which includes response headers [#762](https://github.com/yesodweb/wai/pull/762)+ ## 3.0.26.1 * When available, supply the response size to custom loggers
Network/Wai/Middleware/RequestLogger.hs view
@@ -14,6 +14,7 @@ , OutputFormat (..) , OutputFormatter , OutputFormatterWithDetails+ , OutputFormatterWithDetailsAndHeaders , Destination (..) , Callback , IPAddrSource (..)@@ -49,15 +50,43 @@ import Network.Wai.Header (contentLength) import Data.Text.Encoding (decodeUtf8') -data OutputFormat = Apache IPAddrSource- | Detailed Bool -- ^ use colors?- | CustomOutputFormat OutputFormatter- | CustomOutputFormatWithDetails OutputFormatterWithDetails+data OutputFormat+ = Apache IPAddrSource+ | Detailed Bool -- ^ use colors?+ | CustomOutputFormat OutputFormatter+ | CustomOutputFormatWithDetails OutputFormatterWithDetails+ | CustomOutputFormatWithDetailsAndHeaders OutputFormatterWithDetailsAndHeaders type OutputFormatter = ZonedDate -> Request -> Status -> Maybe Integer -> LogStr-type OutputFormatterWithDetails =- ZonedDate -> Request -> Status -> Maybe Integer -> NominalDiffTime -> [S8.ByteString] -> B.Builder -> LogStr +type OutputFormatterWithDetails+ = ZonedDate+ -> Request+ -> Status+ -> Maybe Integer+ -> NominalDiffTime+ -> [S8.ByteString]+ -> B.Builder+ -> LogStr++-- | Same as @OutputFormatterWithDetails@ but with response headers included+--+-- This is useful if you wish to include arbitrary application data in your+-- logs, e.g., an authenticated user ID, which you would set in a response+-- header in your application and retrieve in the log formatter.+--+-- @since 3.0.27+type OutputFormatterWithDetailsAndHeaders+ = ZonedDate -- ^ When the log message was generated+ -> Request -- ^ The WAI request+ -> Status -- ^ HTTP status code+ -> Maybe Integer -- ^ Response size+ -> NominalDiffTime -- ^ Duration of the request+ -> [S8.ByteString] -- ^ The request body+ -> B.Builder -- ^ Raw response+ -> [Header] -- ^ The response headers+ -> LogStr+ data Destination = Handle Handle | Logger LoggerSet | Callback Callback@@ -108,6 +137,9 @@ CustomOutputFormatWithDetails formatter -> do getdate <- getDateGetter flusher return $ customMiddlewareWithDetails callbackAndFlush getdate formatter+ CustomOutputFormatWithDetailsAndHeaders formatter -> do+ getdate <- getDateGetter flusher+ return $ customMiddlewareWithDetailsAndHeaders callbackAndFlush getdate formatter apacheMiddleware :: ApacheLoggerActions -> Middleware apacheMiddleware ala app req sendResponse = app req $ \res -> do@@ -138,6 +170,24 @@ readIORef builderIO return rspRcv +customMiddlewareWithDetailsAndHeaders :: Callback -> IO ZonedDate -> OutputFormatterWithDetailsAndHeaders -> Middleware+customMiddlewareWithDetailsAndHeaders cb getdate formatter app req sendResponse = do+ (req', reqBody) <- getRequestBody req+ t0 <- getCurrentTime+ app req' $ \res -> do+ t1 <- getCurrentTime+ date <- liftIO getdate+ let msize = contentLength (responseHeaders res)+ builderIO <- newIORef $ B.byteString ""+ res' <- recordChunks builderIO res+ rspRcv <- sendResponse res'+ _ <- do+ rawResponse <- readIORef builderIO+ let status = responseStatus res'+ duration = t1 `diffUTCTime` t0+ resHeaders = responseHeaders res'+ liftIO . cb $ formatter date req' status msize duration reqBody rawResponse resHeaders+ return rspRcv -- | Production request logger middleware. -- -- This uses the 'Apache' logging format, and takes IP addresses for clients from
Network/Wai/Middleware/RequestLogger/JSON.hs view
@@ -1,7 +1,10 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE CPP #-} -module Network.Wai.Middleware.RequestLogger.JSON (formatAsJSON) where+module Network.Wai.Middleware.RequestLogger.JSON+ ( formatAsJSON+ , formatAsJSONWithHeaders+ ) where import qualified Data.ByteString.Builder as BB (toLazyByteString) import Data.ByteString.Lazy (toStrict)@@ -40,6 +43,30 @@ , "time" .= decodeUtf8With lenientDecode date ]) <> "\n" +-- | Same as @formatAsJSON@ but with response headers included+--+-- This is useful for passing arbitrary data from your application out to the+-- WAI layer for it to be logged, but you may need to be careful to+-- subsequently redact any headers which may contain sensitive data.+--+-- @since 3.0.27+formatAsJSONWithHeaders :: OutputFormatterWithDetailsAndHeaders+formatAsJSONWithHeaders date req status resSize duration reqBody res resHeaders =+ toLogStr (encode $+ object+ [ "request" .= requestToJSON duration req reqBody+ , "response" .= object+ [ "status" .= statusCode status+ , "size" .= resSize+ , "headers" .= responseHeadersToJSON resHeaders+ , "body" .=+ if statusCode status >= 400+ then Just . decodeUtf8With lenientDecode . toStrict . BB.toLazyByteString $ res+ else Nothing+ ]+ , "time" .= decodeUtf8With lenientDecode date+ ]) <> "\n"+ word32ToHostAddress :: Word32 -> Text word32ToHostAddress = T.intercalate "." . map (T.pack . show) . fromIPv4 . fromHostAddress @@ -88,6 +115,12 @@ requestHeadersToJSON = toJSON . map hToJ where -- Redact cookies hToJ ("Cookie", _) = toJSON ("Cookie" :: Text, "-RDCT-" :: Text)+ hToJ hd = headerToJSON hd++responseHeadersToJSON :: [Header] -> Value+responseHeadersToJSON = toJSON . map hToJ where+ -- Redact cookies+ hToJ ("Set-Cookie", _) = toJSON ("Set-Cookie" :: Text, "-RDCT-" :: Text) hToJ hd = headerToJSON hd headerToJSON :: Header -> Value
wai-extra.cabal view
@@ -1,5 +1,5 @@ Name: wai-extra-Version: 3.0.26.1+Version: 3.0.27 Synopsis: Provides some basic WAI handlers and middleware. description: Provides basic WAI handler and middleware functionality: