-- ----------------------------------------------------------------------------- -- Copyright 2002, Simon Marlow. -- Copyright 2006, Bjorn Bringert. -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are -- met: -- -- * Redistributions of source code must retain the above copyright notice, -- this list of conditions and the following disclaimer. -- -- * Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- * Neither the name of the copyright holder(s) nor the names of -- contributors may be used to endorse or promote products derived from -- this software without specific prior written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- ----------------------------------------------------------------------------- module AccessLogger ( AccessLoggerHandle, AccessLogRequest(..), startAccessLogger, stopAccessLogger, mkAccessLogRequest, logAccessLogRequest ) where import Logger import Headers import Response import ServerRequest import Util import Network.BSD (HostEntry, hostName) import Network.Socket (inet_ntoa) import System.Time import System.IO.Unsafe (unsafePerformIO) type AccessLoggerHandle = LoggerHandle AccessLogRequest data AccessLogRequest = AccessLogRequest { log_request :: ServerRequest, log_response :: Response, log_server_host :: HostEntry, log_time :: ClockTime, log_delay :: TimeDiff } startAccessLogger :: String -> FilePath -> IO AccessLoggerHandle startAccessLogger format file = startLogger f file where f = mkLogLine format mkLogLine :: String -> AccessLogRequest -> String mkLogLine "" _ = "" mkLogLine ('%':'{':rest) r = case span (/= '}') rest of (str, '}':c:rest1) -> expand (Just str) c r ++ mkLogLine rest1 r _ -> '%':'{':mkLogLine rest r mkLogLine ('%':c:rest) r = expand Nothing c r ++ mkLogLine rest r mkLogLine (c:rest) r = c : mkLogLine rest r expand :: Maybe String -> Char -> AccessLogRequest -> String expand arg c info = case c of 'b' -> let len = responseBodyLength (respBody resp) in if len == 0 then "-" else show len 'f' -> serverFilename sreq -- %h is the hostname if hostnameLookups is on, otherwise the -- IP address. 'h' -> maybe addr hostName (clientName sreq) 'a' -> addr 'l' -> "-" -- FIXME: does anyone use identd these days? 'r' -> show req -- ToDo: 'p' -> canonical port number of server 's' -> show (respCode resp) 't' -> formatTimeSensibly (toUTCTime (log_time info)) 'T' -> timeDiffToString (log_delay info) 'v' -> hostName (log_server_host info) 'u' -> "-" -- FIXME: implement HTTP auth 'i' -> header req arg 'o' -> header resp arg -- ToDo: other stuff _ -> ['%',c] where resp = log_response info sreq = log_request info req = clientRequest sreq host = clientName (log_request info) header _ Nothing = "" header x (Just n) = unwords (lookupHeaders (mkHeaderName n) x) addr = unsafePerformIO (inet_ntoa (clientAddress sreq)) stopAccessLogger :: AccessLoggerHandle -> IO () stopAccessLogger l = stopLogger l mkAccessLogRequest :: ServerRequest -> Response -> HostEntry -> TimeDiff -> IO AccessLogRequest mkAccessLogRequest req resp host delay = do time <- getClockTime return $ AccessLogRequest { log_request = req, log_response = resp, log_server_host = host, log_time = time, log_delay = delay } logAccessLogRequest :: AccessLoggerHandle -> AccessLogRequest -> IO () logAccessLogRequest l r = logMessage l r