little-logger-1.0.2: src/LittleLogger.hs
module LittleLogger
( textLogStr
, LogAction (..)
, defaultLogAction
, filterActionSeverity
, newLogAction
, runLogAction
, handleLogAction
, openLoggingHandle
, closeLoggingHandle
, fileLogAction
, HasLogAction (..)
, WithLogAction
, askLogAction
, LogActionWrapperM (..)
, LogActionT (..)
, runLogActionT
, LogActionM
, runLogActionM
-- Re-exports
, MonadLogger (..)
, Loc (..)
, LogSource
, LogLevel (..)
, LogStr
, ToLogStr (..)
, logDebugN
, logInfoN
, logWarnN
, logErrorN
, logOtherN
)
where
import Control.Monad (when)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.IO.Unlift (MonadUnliftIO, askRunInIO)
import Control.Monad.Logger.CallStack
( Loc (..)
, LogLevel (..)
, LogSource
, LogStr
, MonadLogger (..)
, ToLogStr (..)
, defaultOutput
, fromLogStr
, logDebugN
, logErrorN
, logInfoN
, logOtherN
, logWarnN
)
import Control.Monad.Reader (MonadReader, ReaderT (..), asks)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Lens.Micro (Lens')
import Lens.Micro.Extras (view)
import System.IO
( BufferMode (LineBuffering)
, Handle
, IOMode (AppendMode)
, hClose
, hSetBuffering
, openFile
, stderr
, withFile
)
textLogStr :: LogStr -> Text
textLogStr = decodeUtf8 . fromLogStr
newtype LogAction = LogAction {unLogAction :: Loc -> LogSource -> LogLevel -> LogStr -> IO ()}
instance Semigroup LogAction where
LogAction act1 <> LogAction act2 = LogAction (\loc src lvl msg -> act1 loc src lvl msg >> act2 loc src lvl msg)
instance Monoid LogAction where
mempty = LogAction (\_ _ _ _ -> pure ())
mappend = (<>)
newLogAction :: (MonadUnliftIO m) => (Loc -> LogSource -> LogLevel -> LogStr -> m ()) -> m LogAction
newLogAction act = fmap (\run -> LogAction (\loc src lvl msg -> run (act loc src lvl msg))) askRunInIO
runLogAction :: (MonadIO m) => LogAction -> Loc -> LogSource -> LogLevel -> LogStr -> m ()
runLogAction (LogAction act) loc src lvl msg = liftIO (act loc src lvl msg)
defaultLogAction :: LogAction
defaultLogAction = LogAction (defaultOutput stderr)
filterActionSeverity :: LogLevel -> LogAction -> LogAction
filterActionSeverity lim (LogAction act) =
LogAction (\loc src lvl msg -> when (lvl >= lim) (act loc src lvl msg))
handleLogAction :: Handle -> LogAction
handleLogAction = LogAction . defaultOutput
openLoggingHandle :: (MonadIO m) => FilePath -> m Handle
openLoggingHandle fp = do
handle <- liftIO (openFile fp AppendMode)
liftIO (hSetBuffering handle LineBuffering)
pure handle
closeLoggingHandle :: (MonadIO m) => Handle -> m ()
closeLoggingHandle = liftIO . hClose
fileLogAction :: (MonadUnliftIO m) => FilePath -> (LogAction -> m a) -> m a
fileLogAction fp f = do
run <- askRunInIO
liftIO $ withFile fp AppendMode $ \handle -> do
hSetBuffering handle LineBuffering
run (f (handleLogAction handle))
class HasLogAction env where
logActionL :: Lens' env LogAction
instance HasLogAction LogAction where
logActionL = id
type WithLogAction env m = (MonadIO m, MonadReader env m, HasLogAction env)
askLogAction :: (MonadReader env m, HasLogAction env) => m LogAction
askLogAction = asks (view logActionL)
-- | Use deriving-via with this wrapper to add MonadLogger instances to your types
newtype LogActionWrapperM env m a = LogActionM {unLogActionM :: m a}
deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader env)
instance (WithLogAction env m) => MonadLogger (LogActionWrapperM env m) where
monadLoggerLog loc src lvl msg = do
LogAction act <- askLogAction
liftIO (act loc src lvl (toLogStr msg))
newtype LogActionT m a = LogActionT {unLogActionT :: ReaderT LogAction m a}
deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader LogAction)
deriving (MonadLogger) via (LogActionWrapperM LogAction (LogActionT m))
runLogActionT :: LogActionT m a -> LogAction -> m a
runLogActionT = runReaderT . unLogActionT
type LogActionM a = LogActionT IO a
runLogActionM :: LogActionM a -> LogAction -> IO a
runLogActionM = runLogActionT