hbro-1.3.0.0: library/Hbro/Logger.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Hbro.Logger
( module X
, LogMessage(..)
, MonadThreadedLogger(..)
, ThreadedLoggingT
, runThreadedLoggingT
, logErrors
, logErrors_
) where
-- {{{ Imports
import Hbro.Error
import Hbro.Event
import Hbro.Prelude hiding (runReaderT)
import Control.Monad.Base
import Control.Monad.Logger.Extended as X
import Control.Monad.Reader
import Control.Monad.Trans.Resource
import Data.Text (justifyLeft)
import Data.Text.Encoding
import Data.Text.Encoding.Error
import System.Log.FastLogger as X
-- }}}
-- | Log event
data LogMessage = LogMessage deriving(Show)
instance Event LogMessage where
type Input LogMessage = (Loc, LogSource, LogLevel, Text)
describeInput _ _ = Nothing
class (MonadLogger m) => MonadThreadedLogger m where
addLogHandler :: (Input LogMessage -> IO ()) -> m ()
instance (Monad m, MonadThreadedLogger m) => MonadThreadedLogger (ExceptT e m) where
addLogHandler = lift . addLogHandler
instance (Monad m, MonadThreadedLogger m) => MonadThreadedLogger (ResourceT m) where
addLogHandler = lift . addLogHandler
newtype ThreadedLoggingT m a = ThreadedLoggingT { unThreadedLoggingT :: ReaderT (Signal LogMessage, LogLevel) m a }
deriving instance (Applicative m) => Applicative (ThreadedLoggingT m)
deriving instance (Functor m) => Functor (ThreadedLoggingT m)
deriving instance (Monad m) => Monad (ThreadedLoggingT m)
deriving instance (MonadIO m) => MonadIO (ThreadedLoggingT m)
deriving instance (MonadResource m) => MonadResource (ThreadedLoggingT m)
deriving instance (MonadThrow m) => MonadThrow (ThreadedLoggingT m)
deriving instance MonadTrans ThreadedLoggingT
instance MonadBase b m => MonadBase b (ThreadedLoggingT m) where
liftBase = liftBaseDefault
instance MonadTransControl ThreadedLoggingT where
type StT ThreadedLoggingT a = StT (ReaderT (Signal LogMessage, LogLevel)) a
liftWith = defaultLiftWith ThreadedLoggingT unThreadedLoggingT
restoreT = defaultRestoreT ThreadedLoggingT
instance MonadBaseControl b m => MonadBaseControl b (ThreadedLoggingT m) where
type StM (ThreadedLoggingT m) a = ComposeSt ThreadedLoggingT m a
liftBaseWith = defaultLiftBaseWith
restoreM = defaultRestoreM
instance (MonadIO m, Functor m) => MonadLogger (ThreadedLoggingT m) where
monadLoggerLog loc source level message = ThreadedLoggingT . void . runFailT $ do
(loggerSignal, levelRef) <- Control.Monad.Reader.ask
guard $ level >= levelRef
emit' loggerSignal (loc, source, level, decodeUtf8With lenientDecode . fromLogStr $ toLogStr message)
instance (ControlIO m, MonadResource m) => MonadThreadedLogger (ThreadedLoggingT m) where
addLogHandler f = ThreadedLoggingT $ do
(loggerSignal, _) <- Control.Monad.Reader.ask
void $ addHandler loggerSignal (io . f)
runThreadedLoggingT :: (ControlIO m, MonadResource m) => LogLevel -> ThreadedLoggingT m b -> m b
runThreadedLoggingT logLevel f = do
loggerSignal <- newSignal LogMessage
addHandler loggerSignal $ \(_loc, _source, level, message) -> io . putStrLn $ formatLevel level ++ " " ++ message
result <- flip runReaderT (loggerSignal, logLevel) $ unThreadedLoggingT f
closeSignal' loggerSignal
return result
formatLevel :: LogLevel -> Text
formatLevel LevelDebug = "DEBUG"
formatLevel LevelInfo = "INFO "
formatLevel LevelWarn = "WARN "
formatLevel LevelError = "ERROR"
formatLevel (LevelOther a) = justifyLeft 5 ' ' . take 5 $ tshow a
-- | Like 'catchError', except that the error is automatically logged, then discarded.
logErrors :: (MonadLogger m, Functor m, MonadError Text m) => m a -> m (Maybe a)
logErrors f = catchError (Just <$> f) $ \e -> error e >> return Nothing
-- | Like 'logErrors', but discards the result.
logErrors_ :: (MonadLogger m, Functor m, MonadError Text m) => m a -> m ()
logErrors_ = void . logErrors