di-monad-1.0.2: 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 # 

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 # 

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 (IdentityT * m) Source # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 # 

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 a -> Proxy a' a b' b m a Source #

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

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.

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

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 are handled by attempting to log the message to stderr as a fallback if possible. Asynchronous exceptions happening as part of the committing process will be thrown in a different thread, and are not not explicitly handled. Pure exceptions originating from the filter function will be thrown here. In practical terms, this means that unless you know what you are doing, you should just call log' without worrying about it ever throwing exceptions.

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 # 

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 # 

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 # 

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 # 

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 #

MonadTrans (DiT level path msg) Source # 

Methods

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

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

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 # 

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 # 

Methods

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

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

Methods

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

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

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 # 

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 # 

Methods

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

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

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 # 

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 # 

Methods

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

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

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 # 

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 # 

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 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