module Network.MoHWS.Logger.Access (
Handle,
Request(..),
start,
stop,
mkRequest,
log,
) where
import qualified Network.MoHWS.Logger as Logger
import qualified Network.MoHWS.HTTP.Header as Header
import qualified Network.MoHWS.HTTP.Response as Response
import qualified Network.MoHWS.Server.Request as ServerRequest
import Network.MoHWS.Utility (formatTimeSensibly, )
import Network.BSD (HostEntry, hostName, )
import qualified Network.Socket as Socket
import System.Time (ClockTime, toUTCTime, getClockTime, TimeDiff, timeDiffToString, )
import Control.Monad (liftM, liftM2, )
import Prelude hiding (log, )
type Handle = Logger.Handle Request
data Request = Request
{
request :: ServerRequest.T (),
response :: Response.T (),
serverHost :: HostEntry,
time :: ClockTime,
delay :: TimeDiff
}
start :: String -> FilePath -> IO Handle
start format file = Logger.start (mkLine format) file
class Monad m => Help m where
inet_ntoa :: Socket.HostAddress -> m String
instance Help IO where
inet_ntoa = Socket.inet_ntoa
infixr 5 +^+, ^:
(+^+) :: Monad m => m [a] -> m [a] -> m [a]
(+^+) = liftM2 (++)
(^:) :: Monad m => a -> m [a] -> m [a]
(^:) x = liftM (x:)
mkLine :: Help m => String -> Request -> m String
mkLine "" _ = return ""
mkLine ('%':'{':rest) r =
case span (/= '}') rest of
(str, '}':c:rest1) -> expand (Just str) c r +^+ mkLine rest1 r
_ -> '%' ^: '{' ^: mkLine rest r
mkLine ('%':c:rest) r = expand Nothing c r +^+ mkLine rest r
mkLine (c:rest) r = c ^: mkLine rest r
expand :: Help m => Maybe String -> Char -> Request -> m String
expand arg c info =
let resp = response info
sreq = request info
req = ServerRequest.clientRequest sreq
header _ Nothing = ""
header x (Just n) = unwords (Header.lookupMany (Header.makeName n) x)
addr = inet_ntoa (ServerRequest.clientAddress sreq)
in case c of
'b' -> return $ maybe "unknown" show $ Response.size (Response.body resp)
'f' -> return $ ServerRequest.serverFilename sreq
'h' -> maybe addr (return . hostName) (ServerRequest.clientName sreq)
'a' -> addr
'l' -> return "-"
'r' -> return $ show req
's' -> return $ show (Response.code resp)
't' -> return $ formatTimeSensibly (toUTCTime (time info))
'T' -> return $ timeDiffToString (delay info)
'v' -> return $ hostName (serverHost info)
'u' -> return "-"
'i' -> return $ header req arg
'o' -> return $ header resp arg
_ -> return ['%',c]
stop :: Handle -> IO ()
stop l = Logger.stop l
mkRequest :: ServerRequest.T body -> Response.T body -> HostEntry -> TimeDiff -> IO Request
mkRequest req resp host delay0 =
do time0 <- getClockTime
return $
Request {
request = fmap (const ()) req,
response = fmap (const ()) resp,
serverHost = host,
time = time0,
delay = delay0
}
log :: Handle -> Request -> IO ()
log l r = Logger.log l r