heavy-logger-0.3.1.0: src/System/Log/Heavy/Backends.hs
{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances, ExistentialQuantification, TypeFamilies, GeneralizedNewtypeDeriving, StandaloneDeriving, MultiParamTypeClasses, UndecidableInstances #-}
-- | This module contains implementation of most commonly used logging backends.
-- You can write your own backends, by implementing an instance of @IsLogBackend@
-- type class.
module System.Log.Heavy.Backends
(
-- $description
-- * Backends
FastLoggerBackend,
SyslogBackend,
ChanLoggerBackend,
ParallelBackend,
NullBackend,
Filtering, filtering, excluding,
LogBackendSettings (..),
-- * Default settings
defStdoutSettings,
defStderrSettings,
defFileSettings,
defaultSyslogSettings,
defaultSyslogFormat
) where
import Control.Monad
import Control.Monad.Trans (liftIO)
import Control.Concurrent
import qualified Data.ByteString.Unsafe as BSU
import qualified Data.Text.Format.Heavy as F
import qualified System.Posix.Syslog as Syslog
import System.Log.FastLogger as FL
import Foreign.C.String (CString, newCString)
import Foreign.Marshal.Alloc (free)
import System.Log.Heavy.Types
import System.Log.Heavy.Level
import System.Log.Heavy.Format
import System.Log.Heavy.Util
-- $description
--
-- This module contains several implementation of logging backend.
-- A backend is some kind of target, where your messages will go.
-- Each backend has its own specific settings.
--
-- Backends provided are:
--
-- * Fast-logger backend. It allows to write messages to stdout, stderr or arbitrary file.
--
-- * Syslog backend.
--
-- * Chan backend.
--
-- * Null backend. This discards all messages. Can be used to disable logging.
--
-- There are also some backend combinators:
--
-- * Filtering - passes messages, that match specified filter,
-- to underlying backend.
--
-- * FilteringM - similar to Filtering, but allows to change the filter in runtime.
--
-- * Parallel - writes messages to several backends in parallel.
--
-- * Dynamic - allows to change underlying backend or its settings in runtime.
--
-- | Default settings for fast-logger stdout output
defStdoutSettings :: LogBackendSettings FastLoggerBackend
defStdoutSettings = FastLoggerSettings defaultLogFormat (FL.LogStdout FL.defaultBufSize)
-- | Default settings for fast-logger stderr output
defStderrSettings :: LogBackendSettings FastLoggerBackend
defStderrSettings = FastLoggerSettings defaultLogFormat (FL.LogStderr FL.defaultBufSize)
-- | Default settings for fast-logger file output.
-- This implies log rotation when log file size reaches 10Mb.
defFileSettings :: FilePath -> LogBackendSettings FastLoggerBackend
defFileSettings path = FastLoggerSettings defaultLogFormat (FL.LogFile spec FL.defaultBufSize)
where spec = FL.FileLogSpec path (10*1024*1024) 3
-- | Fast-logger logging backend.
data FastLoggerBackend = FastLoggerBackend {
flbSettings :: LogBackendSettings FastLoggerBackend,
flbTimedLogger :: TimedFastLogger,
flbCleanup :: IO ()
}
instance IsLogBackend FastLoggerBackend where
-- | Settings of fast-logger backend. This mostly reflects settings of fast-logger itself.
data LogBackendSettings FastLoggerBackend = FastLoggerSettings {
lsFormat :: F.Format -- ^ Log message format
, lsType :: FL.LogType -- ^ Fast-logger target settings
}
initLogBackend settings = do
tcache <- newTimeCache simpleTimeFormat'
(logger, cleanup) <- newTimedFastLogger tcache (lsType settings)
return $ FastLoggerBackend settings logger cleanup
cleanupLogBackend b = do
flbCleanup b
makeLogger backend msg = do
let settings = flbSettings backend
let format = lsFormat settings
let logger = flbTimedLogger backend
logger $ formatLogMessage format msg
-- | Default settings for syslog backend
defaultSyslogSettings :: LogBackendSettings SyslogBackend
defaultSyslogSettings = SyslogSettings defaultSyslogFormat "application" [] Syslog.User
-- | Default log message format fof syslog backend:
-- @[{level}] {source}: {message}@
defaultSyslogFormat :: F.Format
defaultSyslogFormat = "[{level}] {source}: {message}"
-- | Syslog logging backend.
data SyslogBackend = SyslogBackend {
sbSettings :: LogBackendSettings SyslogBackend,
sbIdent :: CString,
sbTimeCache :: IO FormattedTime
}
instance IsLogBackend SyslogBackend where
-- | Settings for syslog backend. This mostly reflects syslog API.
data LogBackendSettings SyslogBackend = SyslogSettings {
ssFormat :: F.Format -- ^ Log message format. Usually you do not want to put time here,
-- because syslog writes time to log by itself by default.
, ssIdent :: String -- ^ Syslog source identifier. Usually the name of your program.
, ssOptions :: [Syslog.Option] -- ^ Syslog options
, ssFacility :: Syslog.Facility -- ^ Syslog facility. It is usally User, if you are writing user-space
-- program.
}
initLogBackend settings = do
ident <- newCString (ssIdent settings)
tcache <- newTimeCache simpleTimeFormat'
Syslog.openlog ident (ssOptions settings) (ssFacility settings)
return $ SyslogBackend settings ident tcache
cleanupLogBackend backend = do
free $ sbIdent backend
Syslog.closelog
makeLogger backend msg = do
let settings = sbSettings backend
let format = ssFormat settings
facility = ssFacility settings
tcache = sbTimeCache backend
time <- tcache
let str = formatLogMessage format msg time
BSU.unsafeUseAsCStringLen (fromLogStr str) $
Syslog.syslog (Just facility) (levelToPriority $ lmLevel msg)
-- | Logging backend which writes all messages to the @Chan@
data ChanLoggerBackend = ChanLoggerBackend {
clChan :: Chan LogMessage -- ^ @Chan@ where write messages to
}
instance IsLogBackend ChanLoggerBackend where
data LogBackendSettings ChanLoggerBackend =
ChanLoggerSettings (Chan LogMessage)
initLogBackend (ChanLoggerSettings chan) =
return $ ChanLoggerBackend chan
cleanupLogBackend _ = return ()
makeLogger backend msg = do
liftIO $ writeChan (clChan backend) msg
-- | Logging backend that writes log messages to several other backends in parallel.
data ParallelBackend = ParallelBackend ![AnyLogBackend]
instance IsLogBackend ParallelBackend where
data LogBackendSettings ParallelBackend = ParallelLogSettings [LoggingSettings]
wouldWriteMessage (ParallelBackend list) msg = do
results <- sequence [wouldWriteMessage backend msg | backend <- list]
return $ or results
makeLogger (ParallelBackend list) msg =
forM_ list $ \(AnyLogBackend backend) -> makeLogger backend msg
initLogBackend (ParallelLogSettings list) = do
backends <- do
forM list $ \(LoggingSettings settings) -> do
backend <- initLogBackend settings
return $ AnyLogBackend backend
return $ ParallelBackend backends
cleanupLogBackend (ParallelBackend list) =
forM_ (reverse list) $ \(AnyLogBackend backend) -> cleanupLogBackend backend
-- | Messages filtering backend. This backend passes a message to underlying backend,
-- if this message conforms to specified filter.
data Filtering b = FilteringBackend (LogMessage -> Bool) b
-- | Specify filter as @LogFilter@.
filtering :: IsLogBackend b => LogFilter -> LogBackendSettings b -> LogBackendSettings (Filtering b)
filtering fltr b = Filtering (checkLogLevel fltr) b
-- | Exclude messages by filter.
excluding :: IsLogBackend b => LogFilter -> LogBackendSettings b -> LogBackendSettings (Filtering b)
excluding fltr b = Filtering ex b
where
ex msg = not $ checkContextFilter' [LogContextFilter Nothing (Just fltr)] (lmSource msg) (lmLevel msg)
instance IsLogBackend b => IsLogBackend (Filtering b) where
data LogBackendSettings (Filtering b) = Filtering (LogMessage -> Bool) (LogBackendSettings b)
wouldWriteMessage (FilteringBackend fltr _) msg = do
return $ fltr msg
makeLogger (FilteringBackend fltr backend) msg = do
when (fltr msg) $ do
makeLogger backend msg
initLogBackend (Filtering fltr settings) = do
backend <- initLogBackend settings
return $ FilteringBackend fltr backend
cleanupLogBackend (FilteringBackend _ b) = cleanupLogBackend b
-- | Null logging backend, which discards all messages
-- (passes them to @/dev/null@, if you wish).
-- This can be used to disable logging.
data NullBackend = NullBackend
instance IsLogBackend NullBackend where
data LogBackendSettings NullBackend = NullLogSettings
wouldWriteMessage _ _ = return False
makeLogger _ _ = return ()
initLogBackend _ = return NullBackend
cleanupLogBackend _ = return ()