module Control.Logging (
Level(..)
,LogFormatter
,getLogLevel
,getLogContext
,LogAnnotation(..)
,LATime(..)
,LAContext(..)
,LALevel(..)
,LAThread(..)
,LogConfig(..)
,LogHeader(..)
,defaultLogConfig
,fileLogConfig
,handleLogConfig
,Logging
,runLogging
,withLogContext
,withLogHeader
,withLogLevel
,logLine
,logPrint
,debug
,printDebug
,info
,printInfo
,warn
,printWarn
,err
,printErr
,crit
,printCrit
) where
import Control.Concurrent
import Control.Monad
import Control.Monad.Trans
import Data.Monoid
import Data.Time
import System.IO
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Maybe
type Logging c = ?log :: LogConfig c
data Level =
Debug
| Info
| Warn
| Err
| Crit
deriving (Read, Show, Eq, Ord, Enum)
type LogFormatter c = MaybeT (ReaderT (c, Level) IO)
class LogAnnotation c l where
logFormat :: l -> LogFormatter c String
getLogLevel :: LogFormatter c Level
getLogLevel = lift $ asks snd
getLogContext :: LogFormatter c c
getLogContext = lift $ asks fst
instance LogAnnotation c String where
logFormat = return
data LALevel = LALevel
instance LogAnnotation c LALevel where
logFormat _ = show <$> getLogLevel
newtype LATime = LAT String
instance LogAnnotation c LATime where
logFormat (LAT fmt) =
formatTime defaultTimeLocale fmt <$> liftIO (getCurrentTime)
newtype LAContext c = LC (c -> String)
instance LogAnnotation c (LAContext c) where
logFormat (LC f) = lift $ asks (f . fst)
data LAThread = LAThread
instance LogAnnotation c LAThread where
logFormat _ = show <$> liftIO myThreadId
data LogHeader c = forall l. LogAnnotation c l => LH l
data LogConfig c = LogConfig {
logLevel :: Level
,logHeader :: [LogHeader c]
,logIO :: String -> IO ()
,logContext :: c
}
defaultLogConfig :: c -> LogConfig c
defaultLogConfig = LogConfig Info hdr putStrLn where
hdr = [LH $ LAT "%F %X%Q", LH LALevel]
fileLogConfig :: FilePath -> c -> IO (LogConfig c)
fileLogConfig p c = do
h <- openFile p AppendMode
return $ (defaultLogConfig c){ logIO = hPutStrLn h }
handleLogConfig :: Handle -> c -> IO (LogConfig c)
handleLogConfig h c = do
return $ (defaultLogConfig c){ logIO = hPutStrLn h }
runLogging :: LogConfig c -> (Logging c => a) -> a
runLogging c a = let ?log = c in a
withLogContext :: Logging c => (c -> c) -> (Logging c => a) -> a
withLogContext f = runLogging conf where
LogConfig a b c d = ?log
conf = LogConfig a b c (f d)
withLogHeader :: (Logging c, LogAnnotation c l) => l -> (Logging c => a) -> a
withLogHeader l = runLogging conf where
LogConfig a b c d = ?log
conf = LogConfig a (b ++ [LH l]) c d
withLogLevel :: Logging c => Level -> (Logging c => a) -> a
withLogLevel a = runLogging conf where
LogConfig _ b c d = ?log
conf = LogConfig a b c d
logLine :: (MonadIO m, Logging c) => Level -> String -> m ()
logLine lev lin = logLine' where
LogConfig ml lh io c = ?log
logLine' = when (lev >= ml) $ liftIO $ do
hdr'' <- flip runReaderT (c, lev) $
forM lh $ \(LH h) -> runMaybeT (logFormat h)
let hdr' = (fmap (++ " ")) <$> hdr''
Just hdr = mconcat hdr' <> Just lin
liftIO $ io hdr
logPrint :: (MonadIO m, Show s, Logging c) => Level -> s -> m ()
logPrint lev = logLine lev . show
debug :: (MonadIO m, Logging c) => String -> m ()
debug = logLine Debug
printDebug :: (MonadIO m, Show s, Logging c) => s -> m ()
printDebug = logPrint Debug
info :: (MonadIO m, Logging c) => String -> m ()
info = logLine Info
printInfo :: (MonadIO m, Show s, Logging c) => s -> m ()
printInfo = logPrint Info
warn :: (MonadIO m, Logging c) => String -> m ()
warn = logLine Warn
printWarn :: (MonadIO m, Show s, Logging c) => s -> m ()
printWarn = logPrint Warn
err :: (MonadIO m, Logging c) => String -> m ()
err = logLine Err
printErr :: (MonadIO m, Show s, Logging c) => s -> m ()
printErr = logPrint Err
crit :: (MonadIO m, Logging c) => String -> m ()
crit = logLine Crit
printCrit :: (MonadIO m, Show s, Logging c) => s -> m ()
printCrit = logPrint Crit