{-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ViewPatterns #-} -- | -- Module : System.Wlog.Parser -- Copyright : (c) Serokell, 2016 -- License : GPL-3 (see the file LICENSE) -- Maintainer : Serokell -- Stability : experimental -- Portability : POSIX, GHC -- -- Parser for configuring and initializing logger from YAML file. -- Logger configuration should look like this: -- -- > rotation: # [optional] parameters for logging rotation -- > logLimit: 1024 # max size of log file in bytes -- > keepFiles: 3 # number of files with logs to keep including current one -- > node: # logger named «node» -- > severity: Warning # severity for logger «node» -- > comm: # logger named «node.comm» -- > severity: Info # severity for logger «node.comm» -- > file: patak.jpg # messages will be also printed to patak.jpg -- -- And this configuration corresponds two loggers with 'LoggerName'`s -- @node@ and @node.comm@. module System.Wlog.Launcher ( initLoggingFromYaml , initLoggingFromYamlWithMapper , parseLoggerConfig , traverseLoggerConfig ) where #if PatakDebugSkovorodaBARDAQ import qualified Data.ByteString.Char8 as BS (putStrLn) import Data.Yaml.Pretty (defConfig, encodePretty) #endif import Control.Error.Util ((?:)) import Control.Exception (throwIO) import Control.Monad (join) import Control.Monad.Extra (whenJust) import Control.Monad.IO.Class (MonadIO (liftIO)) import Data.Foldable (for_) import qualified Data.HashMap.Strict as HM hiding (HashMap) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Data.Text (unpack) import Data.Yaml (decodeFileEither) import System.Directory (createDirectoryIfMissing) import System.FilePath (()) import System.Log (Priority) import System.Log.Handler (LogHandler) import System.Log.Handler.Simple (fileHandler) import System.Log.Logger (addHandler, updateGlobalLogger) import System.Wlog.Formatter (setStdoutFormatter) import System.Wlog.LoggerConfig (LoggerConfig (..), LoggerTree (..), RotationParameters) import System.Wlog.LoggerName (LoggerName (..)) import System.Wlog.Roller (rotationFileHandler) import System.Wlog.Wrapper (Severity (Debug, Warning), convertSeverity, initLogging, setSeverityMaybe) data HandlerFabric = forall h . LogHandler h => HandlerFabric (FilePath -> Priority -> IO h) -- | This function traverses 'LoggerConfig' initializing all subloggers -- with 'Severity' and redirecting output in file handlers. Also takes -- 'FilePath' prefix to create directory for file handlers output. Create -- logging rotation if appropriate arguments are passed. traverseLoggerConfig :: MonadIO m => (LoggerName -> LoggerName) -- ^ mapper to transform logger names during traversal -> Maybe RotationParameters -- ^ rotation parameters for roller handle -> LoggerTree -- ^ given 'LoggerConfig' to traverse -> Maybe FilePath -- ^ prefix for file handlers -> m () traverseLoggerConfig logMapper mrot tree (fromMaybe "." -> handlerPrefix) = do liftIO $ createDirectoryIfMissing True handlerPrefix initLogging Warning processLoggers mempty tree where handlerFabric :: HandlerFabric handlerFabric = case mrot of Nothing -> HandlerFabric fileHandler Just rot -> HandlerFabric $ rotationFileHandler rot processLoggers :: MonadIO m => LoggerName -> LoggerTree -> m () processLoggers parent LoggerTree{..} = do setSeverityMaybe parent ltSeverity whenJust ltFile $ \fileName -> liftIO $ do let filePriority = convertSeverity $ ltSeverity ?: Debug let handlerPath = handlerPrefix fileName case handlerFabric of HandlerFabric fabric -> do thisLoggerHandler <- setStdoutFormatter True <$> fabric handlerPath filePriority updateGlobalLogger (loggerName parent) $ addHandler thisLoggerHandler for_ (HM.toList ltSubloggers) $ \(name, loggerConfig) -> do let thisLoggerName = LoggerName $ unpack name let thisLogger = parent <> logMapper thisLoggerName processLoggers thisLogger loggerConfig -- | Parses logger config from given file path. parseLoggerConfig :: MonadIO m => FilePath -> m LoggerConfig parseLoggerConfig loggerConfigPath = liftIO $ join $ either throwIO return <$> decodeFileEither loggerConfigPath -- | Initialize logger hierarchy from configuration file. -- See this module description. initLoggingFromYamlWithMapper :: MonadIO m => (LoggerName -> LoggerName) -> FilePath -> Maybe FilePath -> m () initLoggingFromYamlWithMapper loggerMapper loggerConfigPath handlerPrefix = do cfg@LoggerConfig{..} <- parseLoggerConfig loggerConfigPath #if PatakDebugSkovorodaBARDAQ liftIO $ BS.putStrLn $ encodePretty defConfig cfg #endif traverseLoggerConfig loggerMapper lcRotation lcTree handlerPrefix initLoggingFromYaml :: MonadIO m => FilePath -> Maybe FilePath -> m () initLoggingFromYaml = initLoggingFromYamlWithMapper id