log package:di-core

Log a message msg with a particular importance level. Notice that function requires a MonadIO constraint. If you want to log from other monads that don't satisfy this constraint but are somehow able to perform or build STM actions, then use log' instead.
log = log' (liftIO . atomically)
Refer to log' for more documentation.
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.
Importance level of the logged message (e.g., “info”, “warning”, “error”, etc.).
Human-readable message itself.
Path where the logged message was created from. The leftmost path is the root path. The rightmost path is the path closest to where the log was generated.
First known timestamp when the log was generated. We use SystemTime rather than UTCTime because it is cheaper to obtain and to render. You can use systemToUTCTime to convert it if necessary.
This exception is thrown if somebody tries to log or flush a message when the logging worker is not running.
In new f g, if new's internal worker thread unexpectedly dies with an exception, either synchronous or asynchronous, then that exception will be wrapped in ExceptionInLoggingWorker and thrown to g's thread. Since this exception will be delivered to g asynchronously, it will be further wrapped in SomeAsyncException.
SomeAsyncException
(ExceptionInLoggingWorker (culprit :: SomeException))
If you receive this exception, it means that the thread responsible for running f died. This is very unlikely to happen unless an asynchronous exception killed the worker thread, in which case you should not try to recover from the situation. Notice that synchronous exceptions from f itself are always muted. f's author is responsible for handling those if necessary.