Log a message
msg with a particular importance
level.
This function is like
log, but it doesn't require a
MonadIO constraint. Instead, it asks for a
natural
transformation that will be used in order to run
STM
actions in
m.
First, this allows you to log from any
Monad that wraps
IO without necessarily having a
MonadIO instance. For
example:
newtype Foo = Foo (IO a)
deriving (Functor, Applicative, Monad)
log' (Foo . atomically)
:: Di level path msg -> level -> msg -> Foo ()
Second, this
log' function allows
m to be
STM
itself:
log' id
:: Di level path msg -> level -> msg -> STM ()
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
(log' id di lx mx >> retry) <|>
(log' id di ly my)
Furthermore, much like we were able to log from a
Foo that
wrapped
IO in the previous example, we are also able to log
from any monad wrapping
STM:
newtype Bar = Bar (STM a)
deriving (Functor, Applicative, Monad)
log' Bar
:: Di level path msg -> level -> msg -> Bar ()
This function returns immediately after queing the message for
asynchronously committing the message in a different thread. If you
want to explicitly wait for the message to be committed, 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 committed in the future.
Note regarding exceptions: Any exception thrown by the given
natural transformation 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.