{-# LANGUAGE UndecidableInstances #-}
-- | The 'LogWriter' type encapsulates an 'IO' action to write 'LogEvent's.
module Control.Eff.Log.Writer
( -- * 'LogWriter' Definition
LogWriter (..),
-- * LogWriter Reader Effect
LogWriterReader,
localLogWriterReader,
askLogWriter,
runLogWriterReader,
-- * LogWriter utilities
liftWriteLogMessage,
noOpLogWriter,
filteringLogWriter,
mappingLogWriter,
mappingLogWriterIO,
ioHandleLogWriter,
stdoutLogWriter,
)
where
import Control.Eff
import Control.Eff.Log.Message
import Control.Eff.Log.MessageRenderer
import Control.Eff.Reader.Strict
import Control.Monad
( when,
(>=>),
)
import Data.Text (Text)
import qualified Data.Text.IO as Text
import qualified System.IO as IO
-- | A function that takes a log message and returns an effect that
-- /logs/ the message.
newtype LogWriter = MkLogWriter
{ runLogWriter :: LogEvent -> IO ()
}
instance Semigroup LogWriter where
(MkLogWriter l) <> (MkLogWriter r) = MkLogWriter (l >> r)
instance Monoid LogWriter where
mempty = MkLogWriter (const (pure ()))
-- | A 'Reader' effect for 'LogWriter's.
--
-- @since 0.31.0
type LogWriterReader = Reader LogWriter
-- | Provide the 'LogWriter'
--
-- Exposed for custom extensions, if in doubt use 'withLogging'.
runLogWriterReader :: LogWriter -> Eff (Reader LogWriter ': e) a -> Eff e a
runLogWriterReader = runReader
-- | Get the current 'LogWriter'.
askLogWriter :: Member LogWriterReader e => Eff e LogWriter
askLogWriter = ask
-- | Modify the current 'LogWriter'.
localLogWriterReader ::
forall e a.
Member LogWriterReader e =>
(LogWriter -> LogWriter) ->
Eff e a ->
Eff e a
localLogWriterReader = local
-- | Write a message using the 'LogWriter' found in the environment.
liftWriteLogMessage ::
(Member LogWriterReader e, Lifted IO e) =>
LogEvent ->
Eff e ()
liftWriteLogMessage m = do
w <- askLogWriter
lift (runLogWriter w m)
-- | This 'LogWriter' will discard all messages.
--
-- NOTE: This is just an alias for 'mempty'
noOpLogWriter :: LogWriter
noOpLogWriter = mempty
-- | A 'LogWriter' that applies a predicate to the 'LogEvent' and delegates to
-- to the given writer of the predicate is satisfied.
filteringLogWriter :: LogPredicate -> LogWriter -> LogWriter
filteringLogWriter p lw =
MkLogWriter (\msg -> when (p msg) (runLogWriter lw msg))
-- | A 'LogWriter' that applies a function to the 'LogEvent' and delegates the result to
-- to the given writer.
mappingLogWriter :: (LogEvent -> LogEvent) -> LogWriter -> LogWriter
mappingLogWriter f lw = MkLogWriter (runLogWriter lw . f)
-- | Like 'mappingLogWriter' allow the function that changes the 'LogEvent' to have effects.
mappingLogWriterIO ::
(LogEvent -> IO LogEvent) -> LogWriter -> LogWriter
mappingLogWriterIO f lw = MkLogWriter (f >=> runLogWriter lw)
-- | Append the 'LogEvent' to an 'IO.Handle' after rendering it.
--
-- @since 0.31.0
ioHandleLogWriter :: IO.Handle -> LogEventReader Text -> LogWriter
ioHandleLogWriter outH r = MkLogWriter (Text.hPutStrLn outH . r)
-- | Render a 'LogEvent' to 'IO.stdout'.
--
-- This function will also set the 'IO.BufferMode' of 'IO.stdout' to 'IO.LineBuffering'.
--
-- See 'ioHandleLogWriter'.
--
-- @since 0.31.0
stdoutLogWriter :: LogEventReader Text -> IO LogWriter
stdoutLogWriter render = do
IO.hSetBuffering IO.stdout IO.LineBuffering
return (ioHandleLogWriter IO.stdout render)