di-monad-1.3.1: mtl flavoured typeful hierarchical structured logging for di-core.

Safe HaskellNone
LanguageHaskell2010

Di.Monad

Contents

Description

This module offers a monadic alternative to the “bare” logging API offered by Di.Core.

Whereas Di.Core expects you to explicitly pass around a Di object, Di.Monad offers a MonadDi typeclass, as well functions operating on MonadDi instances, as its public facing API.

Di.Monad exports MonadDi instances for all of the monad transformer types in transformers and pipes.

Nevertheless, be aware that these two APIs are compatible, so you may choose to use the monadic API for some parts of your application, the “bare” API for some other parts, and everything will compose and behave as expected. Usually, runDiT is the boundary between these two APIs.

Di.Monad also provides a DiT monad transformer that has an instance of the MonadDi typeclass and you can readily use out of the box. DiT also implements instances for all of the typeclasses in base, mtl, and exceptions.

Import this module as follows:

import Di.Core as Di (new)
import Di.Monad as Di
Synopsis

MonadDi

class Monad m => MonadDi level path msg m | m -> level path msg where Source #

A MonadDi allows interacting with a Di through a mtl-like monadic API, rather than through the “bare” API proposed by Di.Core.

Nevertheless, be aware that these two APIs are compatible, so you may choose to use the monadic API for some parts of your application, the “bare” API for some other parts, and everything will compose and behave as expected. Usually, runDiT is the boundary between these two APIs, although not necessarily.

Semantically, MonadDi m is a “reader monad” that carries as its environment a Di and natural transformation from STM to m.

Minimal complete definition

local

Methods

ask :: m (Di level path msg) Source #

Get the Di inside m, unmodified.

Idempotence law:

ask >> ask  ==  ask

ask :: (MonadTrans t, MonadDi level path msg n, m ~ t n) => m (Di level path msg) Source #

Get the Di inside m, unmodified.

Idempotence law:

ask >> ask  ==  ask

local :: (Di level path msg -> Di level path msg) -> m a -> m a Source #

Run m a with a modified Di:

local (const x) ask  ==  pure x

Identity law:

local id x  ==  x

Distributive law:

local f . local g  ==  local (f . g)

Idempotence law:

local f (pure ()) >> x  ==  x

natSTM :: STM a -> m a Source #

Natural transformation from STM to m.

Notice that it is not necessary for this natural transformation to be a monad morphism as well. That is, atomically is acceptable.

natSTM :: (MonadTrans t, MonadDi level path msg n, m ~ t n) => STM a -> m a Source #

Natural transformation from STM to m.

Notice that it is not necessary for this natural transformation to be a monad morphism as well. That is, atomically is acceptable.

Instances
MonadDi level path msg m => MonadDi level path msg (ListT m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: ListT m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> ListT m a -> ListT m a Source #

natSTM :: STM a -> ListT m a Source #

MonadDi level path msg m => MonadDi level path msg (MaybeT m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: MaybeT m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> MaybeT m a -> MaybeT m a Source #

natSTM :: STM a -> MaybeT m a Source #

MonadDi level path msg m => MonadDi level path msg (SelectT r m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: SelectT r m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> SelectT r m a -> SelectT r m a Source #

natSTM :: STM a -> SelectT r m a Source #

(Monoid w, MonadDi level path msg m) => MonadDi level path msg (AccumT w m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: AccumT w m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> AccumT w m a -> AccumT w m a Source #

natSTM :: STM a -> AccumT w m a Source #

MonadDi level path msg m => MonadDi level path msg (IdentityT m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: IdentityT m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> IdentityT m a -> IdentityT m a Source #

natSTM :: STM a -> IdentityT m a Source #

MonadDi level path msg m => MonadDi level path msg (ExceptT e m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: ExceptT e m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> ExceptT e m a -> ExceptT e m a Source #

natSTM :: STM a -> ExceptT e m a Source #

(Monoid w, MonadDi level path msg m) => MonadDi level path msg (WriterT w m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: WriterT w m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> WriterT w m a -> WriterT w m a Source #

natSTM :: STM a -> WriterT w m a Source #

(Monoid w, MonadDi level path msg m) => MonadDi level path msg (WriterT w m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: WriterT w m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> WriterT w m a -> WriterT w m a Source #

natSTM :: STM a -> WriterT w m a Source #

MonadDi level path msg m => MonadDi level path msg (StateT s m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: StateT s m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> StateT s m a -> StateT s m a Source #

natSTM :: STM a -> StateT s m a Source #

MonadDi level path msg m => MonadDi level path msg (StateT s m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: StateT s m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> StateT s m a -> StateT s m a Source #

natSTM :: STM a -> StateT s m a Source #

MonadDi level path msg m => MonadDi level path msg (ContT r m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: ContT r m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> ContT r m a -> ContT r m a Source #

natSTM :: STM a -> ContT r m a Source #

MonadDi level path msg m => MonadDi level path msg (ReaderT r m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: ReaderT r m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> ReaderT r m a -> ReaderT r m a Source #

natSTM :: STM a -> ReaderT r m a Source #

(Monoid w, MonadDi level path msg m) => MonadDi level path msg (RWST r w s m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: RWST r w s m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> RWST r w s m a -> RWST r w s m a Source #

natSTM :: STM a -> RWST r w s m a Source #

(Monoid w, MonadDi level path msg m) => MonadDi level path msg (RWST r w s m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: RWST r w s m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> RWST r w s m a -> RWST r w s m a Source #

natSTM :: STM a -> RWST r w s m a Source #

Monad m => MonadDi level path msg (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: DiT level path msg m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> DiT level path msg m a -> DiT level path msg m a Source #

natSTM :: STM a -> DiT level path msg m a Source #

MonadDi level path msg m => MonadDi level path msg (Proxy a' a b' b m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: Proxy a' a b' b m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> Proxy a' a b' b m a0 -> Proxy a' a b' b m a0 Source #

natSTM :: STM a0 -> Proxy a' a b' b m a0 Source #

log :: MonadDi level path msg m => level -> msg -> m () Source #

Log a message with the given importance level.

This function returns immediately after queing the message for logging. The actual printing of the log message will happen in a different thread, asynchronously. If you want to explicitly wait for the message to be logged, then call flush afterwards.

Log messages are rendered in FIFO order, and their timestamp records the time when this log function was called, rather than the time when the log message is printed in the future.

Note regarding exceptions: Any exception thrown by natSTM will be thrown here. Synchronous exceptions that happen due to failures in the actual committing of the log message, which itself is performed in a different thread, are ignored (they should be handled in the function passed to new instead). If an asynchronous exception kills the logging thread, then you will synchronously get ExceptionInLoggingWorker here, but by the time that happens, that same exception will have already already been thrown asynchronously to this same thread anyway, so unless you did something funny to recover from that exception, you will have died already.

flush :: MonadDi level path msg m => m () Source #

Block until all messages being logged have finished processing.

Manually calling flush is not usually necessary because all log messages are processed as soon as possible, and with ensures that no log message is left unprocessed. However, the actual printing of log messages happens asynchronously, meaning there might be log messages still waiting to be processed. A call to flush will block until all pending log messages have been processed.

Please see log to understand how exceptions behave in this function (hint: they behave unsurprisingly).

push :: MonadDi level path msg m => path -> m a -> m a Source #

Run the given action under a deeper path.

filter Source #

Arguments

:: MonadDi level path msg m 
=> (level -> Seq path -> msg -> Bool)

Whether a particular log entry with the given level, paths and msg should be logged.

The given paths indicate where the log call was made from, with an empty Seq representing log calls made at the current depth level (see push). The leftmost path in the Seq is the most immediate child, while the rightmost is the most distand child (i.e., the path closest to the place where log call actually took place).

-> m a 
-> m a 

Require that any logged messages within the given action satisfy the given predicate in order to be accepted for processing. Logged messages that don't satisfy the predicate will be silently discarded.

Identity:

filter (\_ _ _ -> True)  ==  id

Composition:

filter (\l ps m -> f l ps m && g l ps m)  ==  filter f . filter g

Commutativity:

filter f . filter g  ==  filter g . filter f

throw :: (MonadDi level path msg m, Exception e) => e -> m a Source #

Throw an Exception, but not without logging it first according to the rules established by onException, and further restricted by the rules established by filter.

If the exception doesn't need to be logged, according to the policy set with onException, then this function behaves just as throwSTM.

WARNING: Note that when m is STM, or ultimately runs on STM, then throw will not log the exception, just throw it. This might change in the future if we figure out how to make it work safely.

onException :: MonadDi level path msg m => (SomeException -> Maybe (level, Seq path, msg)) -> m a -> m a Source #

Within the passed given m a, exceptions thrown with throw could could be logged as a msg with a particular level if both the passed in function returns Just, and filter so allows it afterwards.

If the given function returns Nothing, then no logging is performed.

The returned Seq path will extend the path at the throw call site before sending the log. The leftmost path is closest to the root.

Composition:

onException f . onException g   ==   onException (g e *> f e)

Notice that the level, paths and msg resulting from g are discarded, yet its policy regarding whether to log or not is preserved in the same way as filter. That is, onException can't accept an exception already rejected by a previous use of onException, but it can reject a previously accepted one.

DiT

data DiT level path msg m a Source #

A DiT level path msg m is a “reader monad” that carries as its environment a Di level path msg and natural transformation from STM to m.

The most primitive way to build a DiT is through diT.

The most primitive way to run a DiT is through runDiT'.

Instances
Monad m => MonadDi level path msg (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: DiT level path msg m (Di level path msg) Source #

local :: (Di level path msg -> Di level path msg) -> DiT level path msg m a -> DiT level path msg m a Source #

natSTM :: STM a -> DiT level path msg m a Source #

MonadWriter w m => MonadWriter w (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

writer :: (a, w) -> DiT level path msg m a #

tell :: w -> DiT level path msg m () #

listen :: DiT level path msg m a -> DiT level path msg m (a, w) #

pass :: DiT level path msg m (a, w -> w) -> DiT level path msg m a #

MonadState s m => MonadState s (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

get :: DiT level path msg m s #

put :: s -> DiT level path msg m () #

state :: (s -> (a, s)) -> DiT level path msg m a #

MonadReader r m => MonadReader r (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

ask :: DiT level path msg m r #

local :: (r -> r) -> DiT level path msg m a -> DiT level path msg m a #

reader :: (r -> a) -> DiT level path msg m a #

MonadError e m => MonadError e (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

throwError :: e -> DiT level path msg m a #

catchError :: DiT level path msg m a -> (e -> DiT level path msg m a) -> DiT level path msg m a #

MonadTrans (DiT level path msg) Source # 
Instance details

Defined in Di.Monad

Methods

lift :: Monad m => m a -> DiT level path msg m a #

Monad m => Monad (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

(>>=) :: DiT level path msg m a -> (a -> DiT level path msg m b) -> DiT level path msg m b #

(>>) :: DiT level path msg m a -> DiT level path msg m b -> DiT level path msg m b #

return :: a -> DiT level path msg m a #

fail :: String -> DiT level path msg m a #

Functor m => Functor (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

fmap :: (a -> b) -> DiT level path msg m a -> DiT level path msg m b #

(<$) :: a -> DiT level path msg m b -> DiT level path msg m a #

MonadFix m => MonadFix (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

mfix :: (a -> DiT level path msg m a) -> DiT level path msg m a #

MonadFail m => MonadFail (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

fail :: String -> DiT level path msg m a #

Applicative m => Applicative (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

pure :: a -> DiT level path msg m a #

(<*>) :: DiT level path msg m (a -> b) -> DiT level path msg m a -> DiT level path msg m b #

liftA2 :: (a -> b -> c) -> DiT level path msg m a -> DiT level path msg m b -> DiT level path msg m c #

(*>) :: DiT level path msg m a -> DiT level path msg m b -> DiT level path msg m b #

(<*) :: DiT level path msg m a -> DiT level path msg m b -> DiT level path msg m a #

MonadZip m => MonadZip (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

mzip :: DiT level path msg m a -> DiT level path msg m b -> DiT level path msg m (a, b) #

mzipWith :: (a -> b -> c) -> DiT level path msg m a -> DiT level path msg m b -> DiT level path msg m c #

munzip :: DiT level path msg m (a, b) -> (DiT level path msg m a, DiT level path msg m b) #

MonadIO m => MonadIO (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

liftIO :: IO a -> DiT level path msg m a #

Alternative m => Alternative (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

empty :: DiT level path msg m a #

(<|>) :: DiT level path msg m a -> DiT level path msg m a -> DiT level path msg m a #

some :: DiT level path msg m a -> DiT level path msg m [a] #

many :: DiT level path msg m a -> DiT level path msg m [a] #

MonadPlus m => MonadPlus (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

mzero :: DiT level path msg m a #

mplus :: DiT level path msg m a -> DiT level path msg m a -> DiT level path msg m a #

MonadThrow m => MonadThrow (DiT level path msg m) Source #

Throw an Exception from the underlying m, without logging it.

If you want to log the Exception as you throw it, use throw instead.

Instance details

Defined in Di.Monad

Methods

throwM :: Exception e => e -> DiT level path msg m a #

MonadCatch m => MonadCatch (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

catch :: Exception e => DiT level path msg m a -> (e -> DiT level path msg m a) -> DiT level path msg m a #

MonadMask m => MonadMask (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

mask :: ((forall a. DiT level path msg m a -> DiT level path msg m a) -> DiT level path msg m b) -> DiT level path msg m b #

uninterruptibleMask :: ((forall a. DiT level path msg m a -> DiT level path msg m a) -> DiT level path msg m b) -> DiT level path msg m b #

generalBracket :: DiT level path msg m a -> (a -> ExitCase b -> DiT level path msg m c) -> (a -> DiT level path msg m b) -> DiT level path msg m (b, c) #

MonadCont m => MonadCont (DiT level path msg m) Source # 
Instance details

Defined in Di.Monad

Methods

callCC :: ((a -> DiT level path msg m b) -> DiT level path msg m a) -> DiT level path msg m a #

diT :: ((forall x. STM x -> m x) -> Di level path msg -> m a) -> DiT level path msg m a Source #

Build a DiT.

forall nat di.
   runDiT' nat di (diT (\nat' di' -> pure (nat', di')))
       == pure (nat, di)

runDiT Source #

Arguments

:: MonadIO m 
=> Di level path msg 
-> DiT level path msg m a 
-> m a 

Run a DiT.

forall di.
   runDiT di (diT (\nat' di' -> pure (nat', di')))
       == pure (natSTM, di)

This is like runDiT', but specialized to run with an underlying MonadIO.

runDiT  ==  runDiT' (liftIO . atomically)

Please notice that runDiT doesn't perform a flush on the given Di before returning. You are responsible for doing that (or, more likely, new will do it for you).

Also, notice that runDiT is a monad morphism from DiT m to m.

runDiT' Source #

Arguments

:: (forall x. STM x -> m x) 
-> Di level path msg 
-> DiT level path msg m a 
-> m a 

Run a DiT.

forall nat di.
   runDiT' nat di (diT (\nat' di' -> pure (nat', di')))
       == pure (nat, di)

runDiT' is like runDiT. However it doesn't require a MonadIO constraint. Instead, it takes the natural transformation that will be used by natSTM as an argument.

First, this allows any monad that wraps IO without necessarily having a MonadIO instance to work with MonadDi. For example:

newtype Foo = Foo (IO a)
  deriving (Functor, Applicative, Monad)

runDiT' (Foo . atomically)
     :: Di level path msg
     -> DiT level path msg Foo a
     -> Foo a

Second, this allows m to be STM itself:

runDiT' id
     :: Di level path msg
     -> DiT level path msg STM a
     -> STM a

The semantics of logging from within STM are those of any other STM transaction: That is, a log message is commited only once to the outside world if and when the STM transaction succeeds. That is, the following example will only ever commit the log containing ly and my, and not the one containing lx and mx.

atomically $ runDiT' id $ do
   (log id lx mx >> lift retry) <|>
   (log id ly my)

Of course, runDiT' works as well if you decide to wrap STM with your own monad type:

newtype Bar = Bar (STM a)
  deriving (Functor, Applicative, Monad)

runDiT' Bar
     :: Di level path msg
     -> DiT level path msg Bar a
     -> Bar a

Additionally, notice that runDiT' itself is a monad morphism from DiT level path msg m to m which doesn't perform any side effects of its own. Particularly, the given Di remains unaffected. So you can use it as many times you want.

forall f di x.
   runDiT' f di (lift x)  ==  x

Please notice that runDiT doesn't perform a flush on the given Di before returning. You are responsible for doing that (or, more likely, new will do it for you).

hoistDiT Source #

Arguments

:: (forall x. n x -> m x)

Natural transformation from n to m.

-> (forall x. m x -> n x)

Monad morphism from m to n.

-> DiT level path msg m a -> DiT level path msg n a

Monad morphism from DiT m to DiT n.

Lift a monad morphism from m to n to a monad morphism from DiT level path msg m to DiT level path msg n.

Notice that DiT itself is not a functor in the category of monads, so it can't be an instance of MFunctor from the mmorph package. However, it becomes one if you pair it with a natural transformation nat :: forall x. n x -> m x. That is:

forall nat.  such that nat is a natural transformation
   hoistDiT nat  ==  hoist

In practical terms, it means that most times you can “hoist” a DiT anyway, just not through hoist.

localDiT Source #

Arguments

:: (Di level path msg -> Di level' path' msg') 
-> DiT level' path' msg' m a 
-> DiT level path msg m a 

Run a DiT with a modified Di:

localDiT (const x) ask  ==  pure x

Notice that, contrary to local, this allows changing the type of Di, which means that you can use localDiT with contralevel, contrapath or contramsg to change the types of level, path, or message you DiT works with.

localDiT (contralevel (f :: level -> level'))
    :: DiT level' path msg m a
    -> DiT level path msg m a
localDiT (contrapath (f :: path -> path'))
    :: DiT level path' msg m a
    -> DiT level path msg m a
localDiT (contramsg (f :: msg -> msg'))
    :: DiT level path msg' m a
    -> DiT level path msg m a

Identity law:

localDiT id x  ==  x

Distributive law:

localDiT f . localDiT g  ==  localDiT (f . g)

Idempotence law:

localDiT f (pure ()) >> x  ==  x