{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-} module Program.Mighty.Report ( Reporter , initReporter , finReporter , report , reportDo , warpHandler , printStdout ) where #if __GLASGOW_HASKELL__ < 709 import Control.Applicative #endif import Control.Exception import qualified Control.Exception as E (catch) import Control.Monad import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as BS import Data.UnixTime import GHC.IO.Exception (IOErrorType(..)) import Network.Wai import Network.Wai.Handler.Warp (InvalidRequest) import Network.Wai.Handler.Warp.Internal (TimeoutThread(..)) import System.IO import System.IO.Error (ioeGetErrorType) import System.Posix (getProcessID) import Program.Mighty.ByteString data Method = FileOnly | FileAndStdout deriving Eq data Reporter = Reporter Method Handle initReporter :: Bool -> FilePath -> IO (Either SomeException Reporter) initReporter debug reportFile = try $ Reporter method <$> openFile reportFile AppendMode where method | debug = FileAndStdout | otherwise = FileOnly finReporter :: Reporter -> IO () finReporter (Reporter _ rpthdl) = hClose rpthdl report :: Reporter -> ByteString -> IO () report (Reporter method rpthdl) msg = handle (\(SomeException _) -> return ()) $ do pid <- BS.pack . show <$> getProcessID tm <- getUnixTime >>= formatUnixTime "%d %b %Y %H:%M:%S" let logmsg = BS.concat [tm, ": pid = ", pid, ": ", msg, "\n"] BS.hPutStr rpthdl logmsg hFlush rpthdl when (method == FileAndStdout) $ BS.putStr logmsg ---------------------------------------------------------------- reportDo :: Reporter -> IO () -> IO () reportDo rpt act = act `E.catch` warpHandler rpt Nothing ---------------------------------------------------------------- warpHandler :: Reporter -> Maybe Request -> SomeException -> IO () warpHandler rpt _ e = throwIO e `catches` handlers where handlers = [Handler ah, Handler th, Handler ih, Handler oh, Handler sh] ah :: AsyncException -> IO () ah ThreadKilled = norecode ah x = recode x th :: TimeoutThread -> IO () th TimeoutThread = norecode ih :: InvalidRequest -> IO () ih _ = norecode oh :: IOException -> IO () oh x | et `elem` ignEts = norecode | otherwise = recode x where et = ioeGetErrorType x ignEts = [ResourceVanished, InvalidArgument] sh :: SomeException -> IO () sh x = recode x norecode = return () recode :: Exception e => e -> IO () recode = report rpt . bshow ---------------------------------------------------------------- printStdout :: Maybe Request -> SomeException -> IO () printStdout _ x = print x >> hFlush stdout