module Ganeti.Logging
( setupLogging
, MonadLog(..)
, Priority(..)
, logDebug
, logInfo
, logNotice
, logWarning
, logError
, logCritical
, logAlert
, logEmergency
, SyslogUsage(..)
, syslogUsageToRaw
, syslogUsageFromRaw
, withErrorLogAt
, isDebugMode
) where
import Control.Monad
import Control.Monad.Error (Error(..), MonadError(..), catchError)
import Control.Monad.Reader
import qualified Control.Monad.RWS.Strict as RWSS
import qualified Control.Monad.State.Strict as SS
import Control.Monad.Trans.Identity
import Control.Monad.Trans.Maybe
import System.Log.Logger
import System.Log.Handler.Simple
import System.Log.Handler.Syslog
import System.Log.Handler (setFormatter, LogHandler)
import System.Log.Formatter
import System.IO
import Ganeti.BasicTypes (ResultT(..))
import Ganeti.THH
import qualified Ganeti.ConstantUtils as ConstantUtils
$(declareLADT ''String "SyslogUsage"
[ ("SyslogNo", "no")
, ("SyslogYes", "yes")
, ("SyslogOnly", "only")
])
logFormatter :: String
-> Bool
-> Bool
-> LogFormatter a
logFormatter prog mt syslog =
let parts = [ if syslog
then "[$pid]:"
else "$time: " ++ prog ++ " pid=$pid"
, if mt then if syslog then " ($tid)" else "/$tid"
else ""
, " $prio $msg"
]
in tfLogFormatter "%F %X,%q %Z" $ concat parts
openFormattedHandler :: (LogHandler a) => Bool
-> LogFormatter a -> IO a -> IO [a]
openFormattedHandler False _ _ = return []
openFormattedHandler True fmt opener = do
handler <- opener
return [setFormatter handler fmt]
setupLogging :: Maybe String
-> String
-> Bool
-> Bool
-> Bool
-> SyslogUsage
-> IO ()
setupLogging logf program debug stderr_logging console syslog = do
let level = if debug then DEBUG else INFO
destf = if console then Just ConstantUtils.devConsole else logf
fmt = logFormatter program True False
file_logging = syslog /= SyslogOnly
updateGlobalLogger rootLoggerName (setLevel level)
stderr_handlers <- openFormattedHandler stderr_logging fmt $
streamHandler stderr level
file_handlers <- case destf of
Nothing -> return []
Just path -> openFormattedHandler file_logging fmt $
fileHandler path level
let handlers = file_handlers ++ stderr_handlers
updateGlobalLogger rootLoggerName $ setHandlers handlers
when (syslog /= SyslogNo) $ do
syslog_handler <- openlog program [PID] DAEMON INFO
updateGlobalLogger rootLoggerName $ addHandler syslog_handler
class Monad m => MonadLog m where
logAt :: Priority -> String -> m ()
instance MonadLog IO where
logAt = logM rootLoggerName
deriving instance (MonadLog m) => MonadLog (IdentityT m)
instance (MonadLog m) => MonadLog (MaybeT m) where
logAt p = lift . logAt p
instance (MonadLog m) => MonadLog (ReaderT r m) where
logAt p = lift . logAt p
instance (MonadLog m) => MonadLog (SS.StateT s m) where
logAt p = lift . logAt p
instance (MonadLog m, Monoid w) => MonadLog (RWSS.RWST r w s m) where
logAt p = lift . logAt p
instance (MonadLog m, Error e) => MonadLog (ResultT e m) where
logAt p = lift . logAt p
logDebug :: (MonadLog m) => String -> m ()
logDebug = logAt DEBUG
logInfo :: (MonadLog m) => String -> m ()
logInfo = logAt INFO
logNotice :: (MonadLog m) => String -> m ()
logNotice = logAt NOTICE
logWarning :: (MonadLog m) => String -> m ()
logWarning = logAt WARNING
logError :: (MonadLog m) => String -> m ()
logError = logAt ERROR
logCritical :: (MonadLog m) => String -> m ()
logCritical = logAt CRITICAL
logAlert :: (MonadLog m) => String -> m ()
logAlert = logAt ALERT
logEmergency :: (MonadLog m) => String -> m ()
logEmergency = logAt EMERGENCY
isDebugMode :: IO Bool
isDebugMode = (Just DEBUG ==) . getLevel <$> getRootLogger
withErrorLogAt :: (MonadLog m, MonadError e m, Show e)
=> Priority -> String -> m a -> m a
withErrorLogAt prio msg = flip catchError $ \e -> do
logAt prio (msg ++ ": " ++ show e)
throwError e