{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Control.Monad.Logger.Extras where
import Control.Monad.Logger
import Data.ByteString.Char8 as C8
import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
import System.IO
import qualified System.Posix.Syslog as Posix
import System.Console.ANSI
runLoggerLoggingT :: LoggingT m a -> Logger -> m a
runLoggerLoggingT f logger = f `runLoggingT` unLogger logger
type LogF = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
newtype Logger = Logger { unLogger :: LogF }
deriving (Semigroup, Monoid)
logToStderr :: Logger
logToStderr = Logger $ defaultOutput stderr
logToStdout :: Logger
logToStdout = Logger $ defaultOutput stdout
logToNowhere :: Logger
logToNowhere = mempty
logToSyslog :: String -> Logger
logToSyslog tagstr = Logger $ \loc src lvl str -> do
let syslogPriority = case lvl of
LevelDebug -> Posix.Debug
LevelInfo -> Posix.Info
LevelWarn -> Posix.Warning
LevelError -> Posix.Error
LevelOther _ -> Posix.Info
out = defaultLogStr loc src lvl str
Posix.withSyslog tagstr [Posix.DelayedOpen] Posix.User $
unsafeUseAsCStringLen (fromLogStr out) $
Posix.syslog Nothing syslogPriority
colorize :: Logger -> Logger
colorize = colorizeWith defaultColors
colorizeWith :: [(LogLevel, Color)] -> Logger -> Logger
colorizeWith colorMap f = Logger $ \loc src lvl str ->
let c s = case lookup lvl colorMap of
Nothing -> str
Just color -> mapLogStrBS (wrapSGRColor color) s
in unLogger f loc src lvl $ c str
defaultColors :: [(LogLevel, Color)]
defaultColors =
[ (LevelDebug, Green)
, (LevelInfo, Blue)
, (LevelWarn, Yellow)
, (LevelError, Red)
]
mapLogStrBS :: ToLogStr msg => (ByteString -> msg) -> LogStr -> LogStr
mapLogStrBS f = toLogStr . f . fromLogStr
wrapSGRCode :: [SGR] -> ByteString -> ByteString
wrapSGRCode codes t = mconcat
[ C8.pack $ setSGRCode codes
, t
, C8.pack $ setSGRCode [Reset]
]
wrapSGRColor :: Color -> ByteString -> ByteString
wrapSGRColor c = wrapSGRCode [SetColor Foreground Vivid c]
test :: IO ()
test = do
let logger = colorize logToStderr <> logToSyslog "log-test"
flip runLoggerLoggingT logger $ do
logDebugN "This is a debug message."
logInfoN "This is an info message."
logWarnN "This is a warning."
logErrorN "This is an error!"