log-warper-0.2.5: src/System/Wlog/Launcher.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ViewPatterns #-}
-- |
-- Module : System.Wlog.Parser
-- Copyright : (c) Serokell, 2016
-- License : GPL-3 (see the file LICENSE)
-- Maintainer : Serokell <hi@serokell.io>
-- 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