1
1
{-# LANGUAGE OverloadedStrings #-}
2
{-# LANGUAGE RecordWildCards #-}
2
4
module Network.Wai.Middleware.RequestLogger
5
( -- * Basic stdout logging
8
-- * Create more versions
10
, RequestLoggerSettings
12
import System.IO (stdout, hFlush)
21
import System.IO (Handle, stdout)
13
22
import qualified Data.ByteString as BS
14
23
import Data.ByteString.Char8 (pack, unpack)
15
24
import Control.Monad.IO.Class (liftIO)
29
38
import System.IO.Unsafe
31
logHandle :: (BS.ByteString -> IO ()) -> Middleware
32
logHandle = logCallback
33
{-# DEPRECATED logHandle "Please use logCallback instead." #-}
34
logHandleDev :: (BS.ByteString -> IO ()) -> Middleware
35
logHandleDev = logCallbackDev
36
{-# DEPRECATED logHandleDev "Please use logCallbackDev instead." #-}
40
import Data.Default (Default (def))
41
import Network.Wai.Logger.Format (apacheFormat, IPAddrSource (..))
42
#if MIN_VERSION_fast_logger(0,3,0)
43
import System.Date.Cache (ondemandDateCacher)
45
import System.Log.FastLogger.Date (getDate, dateInit, ZonedDate)
48
data OutputFormat = Apache IPAddrSource
49
| Detailed Bool -- ^ use colors?
50
| CustomOutputFormat OutputFormatter
52
type OutputFormatter = ZonedDate -> Request -> Status -> Maybe Integer -> [LogStr]
54
data Destination = Handle Handle
58
type Callback = [LogStr] -> IO ()
60
data RequestLoggerSettings = RequestLoggerSettings
62
-- | Default value: @Detailed@ @True@.
63
outputFormat :: OutputFormat
64
-- | Only applies when using the @Handle@ constructor for @destination@.
66
-- Default value: @True@.
68
-- | Default: @Handle@ @stdout@.
69
, destination :: Destination
72
instance Default RequestLoggerSettings where
73
def = RequestLoggerSettings
74
{ outputFormat = Detailed True
76
, destination = Handle stdout
79
mkRequestLogger :: RequestLoggerSettings -> IO Middleware
80
mkRequestLogger RequestLoggerSettings{..} = do
81
(callback, mgetdate) <-
83
Handle h -> fmap fromLogger $ mkLogger autoFlush h
84
Logger l -> return $ fromLogger l
85
Callback c -> return (c, Nothing)
88
getdate <- dateHelper mgetdate
89
return $ apacheMiddleware callback ipsrc getdate
90
Detailed useColors -> detailedMiddleware callback useColors
91
CustomOutputFormat formatter -> do
92
getdate <- dateHelper mgetdate
93
return $ customMiddleware callback getdate formatter
95
fromLogger l = (loggerPutStr l, Just $ loggerDate l)
96
dateHelper mgetdate = do
99
#if MIN_VERSION_fast_logger(0, 3, 0)
101
(getter,_) <- ondemandDateCacher zonedDateCacheConf
104
Nothing -> fmap getDate dateInit
107
apacheMiddleware :: Callback -> IPAddrSource -> IO ZonedDate -> Middleware
108
apacheMiddleware cb ipsrc getdate = customMiddleware cb getdate $ apacheFormat ipsrc
110
customMiddleware :: Callback -> IO ZonedDate -> OutputFormatter -> Middleware
111
customMiddleware cb getdate formatter app req = do
113
date <- liftIO getdate
114
-- We use Nothing for the response size since we generally don't know it
115
liftIO $ cb $ formatter date req (responseStatus res) Nothing
38
118
-- | Production request logger middleware.
39
119
-- Implemented on top of "logCallback", but prints to 'stdout'
40
120
logStdout :: Middleware
41
logStdout = logCallback $ \bs -> hPutLogStr stdout [LB bs]
121
logStdout = unsafePerformIO $ mkRequestLogger def { outputFormat = Apache FromSocket }
43
123
-- | Development request logger middleware.
44
124
-- Implemented on top of "logCallbackDev", but prints to 'stdout'
46
126
-- Flushes 'stdout' on each request, which would be inefficient in production use.
47
127
-- Use "logStdout" in production.
48
128
logStdoutDev :: Middleware
49
logStdoutDev = logCallbackDev $ \bs -> hPutLogStr stdout [LB bs] >> hFlush stdout
52
logCallback :: (BS.ByteString -> IO ()) -- ^ A function that logs the ByteString log message.
54
logCallback cb app req = do
56
liftIO $ cb $ BS.concat
63
, maybe "" toBS $ lookup "Accept" $ requestHeaders req
72
toBS :: H.Ascii -> BS.ByteString
129
logStdoutDev = unsafePerformIO $ mkRequestLogger def
75
131
-- no black or white which are expected to be existing terminal colors.
76
colors :: IORef [Color]
77
colors = unsafePerformIO $ newIORef [
115
167
-- > GET [("LXwioiBG","")]
117
169
-- > Status: 304 Not Modified. static/css/normalize.css
118
logCallbackDev :: (BS.ByteString -> IO ()) -- ^ A function that logs the ByteString log message.
120
logCallbackDev cb app req = do
171
detailedMiddleware :: Callback -> Bool -> IO Middleware
172
detailedMiddleware cb useColors = do
176
icolors <- newIORef colors0
178
color <- liftIO $ atomicModifyIORef icolors rotateColors
179
return $ ansiColor color
180
else return (return return)
181
return $ detailedMiddleware' cb getAddColor
183
ansiColor color bs = [
184
pack $ setSGRCode [SetColor Foreground Vivid color]
186
, pack $ setSGRCode [Reset]
189
detailedMiddleware' :: Callback
190
-> (C.ResourceT IO (BS.ByteString -> [BS.ByteString]))
192
detailedMiddleware' cb getAddColor app req = do
121
193
let mlen = lookup "content-length" (requestHeaders req) >>= readInt