hslogger-template-2.0.0: src/System/Log/Logger/TH.hs
{-# LANGUAGE TemplateHaskell #-}
-- | This module provides functions that generate hslogger functions using
-- Template Haskell.
--
-- Notes:
--
-- * "System.Log.Logger" must be imported qualified, and the qualifier must
-- match the qualifier given to @deriveLoggers@ and/or @deriveNamedLoggers@.
--
-- * Don't forget to enable Template Haskell preprocessing: specify the pragma
-- @LANGUAGE TemplateHaskell@ at the top of your source file or
-- @extensions: TemplateHaskell@ in your cabal file.
module System.Log.Logger.TH
( deriveLoggers
, deriveNamedLoggers
)
where
import Control.Monad.Trans (MonadIO, liftIO)
import qualified Language.Haskell.TH as TH
import qualified System.Log.Logger as HSL
-- | Generate hslogger functions for a list of priorities.
--
-- Example usage:
--
-- > module Foo.Bar ( ... ) where
-- >
-- > import System.Log.Logger.TH (deriveLoggers)
-- > import qualified System.Log.Logger as HSL
-- >
-- > $(deriveLoggers "HSL" [HSL.DEBUG, HSL.INFO])
--
-- Used this way, @deriveLoggers@ would generate the following functions:
--
-- > infoM :: MonadIO m => String -> m ()
-- > infoM s = liftIO (HSL.infoM "Foo.Bar" s)
-- >
-- > debugM :: MonadIO m => String -> m ()
-- > debugM s = liftIO (HSL.debugM "Foo.Bar" s)
--
-- The other hslogger priorities follow the same pattern.
--
-- /In versions prior to 2.0.0, hslogger-template generated functions that/
-- /prepended the module name to the log message. I no longer feel that this/
-- /is correct behavior. Instead, please make use of hslogger's formatting/
-- /functionality. Example:/
--
-- > import System.IO (stderr)
-- >
-- > import System.Log.Formatter (simpleLogFormatter)
-- > import System.Log.Logger (rootLoggerName)
-- > import System.Log.Handler (setFormatter)
-- > import System.Log.Handler.Simple (streamHandler)
-- > import System.Log.Logger.TH (deriveLoggers)
-- >
-- > import qualified System.Log.Logger as HSL
-- >
-- > $(deriveLoggers "HSL" [HSL.DEBUG, HSL.INFO])
-- >
-- > handler <- streamHandler stderr HSL.DEBUG >>= \h -> return $
-- > setFormatter h $ simpleLogFormatter "$time $loggername $prio $msg"
-- > HSL.updateGlobalLogger rootLoggerName (HSL.setLevel HSL.DEBUG . HSL.setHandlers [handler])
deriveLoggers
:: String -- ^ Must match qualifier on import of "System.Log.Logger".
-> [HSL.Priority] -- ^ List of priorities for which to generate logging functions.
-> TH.Q [TH.Dec]
deriveLoggers qualifier priorities =
fmap TH.loc_module TH.location >>= \moduleName ->
fmap concat (mapM (deriveLogger qualifier moduleName Nothing) priorities)
-- | Like @deriveLoggers@, but allows you to specify a message prefix to be
-- automatically prepended to every log message.
deriveNamedLoggers
:: String -- ^ Message prefix, e.g., "SomeProgram: ".
-> String -- ^ Must match qualifier on import of "System.Log.Logger".
-> [HSL.Priority] -- ^ List of priorities for which to generate logging functions.
-> TH.Q [TH.Dec]
deriveNamedLoggers prefix qualifier priorities =
fmap TH.loc_module TH.location >>= \moduleName ->
fmap concat (mapM (deriveLogger qualifier moduleName (Just prefix)) priorities)
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
deriveLogger :: String -> String -> Maybe String -> HSL.Priority -> TH.Q [TH.Dec]
deriveLogger qualifier moduleName mprefix priority = do
sig <- TH.sigD thF [t| MonadIO m => String -> m () |]
body <- TH.funD thF
[ TH.clause
[TH.varP thS]
(TH.normalB
(TH.appE
(TH.varE 'liftIO)
(TH.appE
-- HSL.xM "Foo.Bar"
(TH.appE
(TH.varE thH)
(TH.stringE moduleName)
)
(case mprefix of
Just prefix ->
-- (++) prefix s
TH.appE
(TH.appE
(TH.varE '(++))
(TH.stringE prefix)
)
(TH.varE thS)
Nothing ->
TH.varE thS
)
)
)
)
[]
]
return [sig, body]
where
thF = TH.mkName functionName
thS = TH.mkName "s"
thH = TH.mkName (qualifier ++ "." ++ functionName)
functionName =
case priority of
HSL.DEBUG -> "debugM"
HSL.INFO -> "infoM"
HSL.NOTICE -> "noticeM"
HSL.WARNING -> "warningM"
HSL.ERROR -> "errorM"
HSL.CRITICAL -> "criticalM"
HSL.ALERT -> "alertM"
HSL.EMERGENCY -> "emergencyM"