di-0.1: Easy and powerful typeful logging without monad towers.

Safe HaskellSafe
LanguageHaskell2010

Di

Contents

Description

Intended module usage:

import Di (Di)
import qualified Di

Synopsis

Documentation

data Di path msg Source #

Di path msg allows you to to log messages of type msg, under a scope identified by path (think of path as a filesystem path).

Each msg gets logged together with its Level, path and the UTCTime timestamp stating when the logging requests was made.

Even though logging is usually associated with rendering text, Di makes no assumption about the types of the msg values being logged, nor the path values that convey their scope. Instead, it delays conversion from these precise types into the ultimately desired raw representation as much as possible. This makes it possible to log more precise information (for example, logging a datatype of your own without having to convert it to text first), richer scope paths (for example, the scope could be a Map that gets enriched with more information as we push down the path). This improves type safety, as well as the composability of the path and msg values. In particular, path and msg are contravariant values (see the path and msg functions).

Contrary to other logging approaches based on monadic interfaces, a Di is a value that is expected to be passed around explicitly. A Di can be safely used concurrently, and messages are rendered in the order they were submitted for logging, both in the case of synchronous logging (e.g., err) and asynchronous logging (e.g., err').

Di is pronounced as "dee" (not "die" nor "dye" nor "day"). "Di" is the spanish word for an imperative form of the verb "decir", which in english means "to say".

mkDi Source #

Arguments

:: (MonadIO m, Monoid path) 
=> (Level -> UTCTime -> path -> msg -> IO ()) 
-> m (Di path msg) 

Build a Di from a logging function.

push :: Di path msg -> path -> Di path msg Source #

Push a new path to the Di.

The passed in Di can continue to be used even after using push or the returned Di.

See mkDiTextStderr for an example behaviour.

path :: Di path' msg -> (path -> path') -> Di path msg Source #

A Di is contravariant in its path argument.

This function is used to go from a more general to a more specific type of path. For example, [Int] is a more specific type than [String], since the former clearly conveys the idea of a list of numbers, whereas the latter could be a list of anything that is representable as String, such as dictionary words. We can convert from the more general to the more specific path type using this path function:

path (x :: Di [String] msg) (map show) :: Di [Int] msg

The Monoidal behavior of the original path' is preserved in the resulting Di.

msg :: Di path msg' -> (msg -> msg') -> Di path msg Source #

A Di is contravariant in its msg argument.

This function is used to go from a more general to a more specific type of msg. For example, Int is a more specific type than String, since the former clearly conveys the idea of a numbers, whereas the latter could be a anything that is representable as String, such as a dictionary word. We can convert from the more general to the more specific msg type using this msg function:

msg (x :: Di path String) show :: Di path Int

level :: Di path msg -> Level -> Di path msg Source #

Returns a new Di on which messages below the given Level are not logged, where ther ordering of levels is as follow:

DBG < INF < WRN < ERR

For example, level x WRN will prevent DBG and INF from being logged.

Notice that level di x will allow messages with a level greater than or equal to x even if they had been previously silenced in the given di.

Synchronous logging

dbg :: MonadIO m => Di path msg -> msg -> m () Source #

Synchronously log a message with DBG level.

inf :: MonadIO m => Di path msg -> msg -> m () Source #

Synchronously log a message with INF level.

wrn :: MonadIO m => Di path msg -> msg -> m () Source #

Synchronously log a message with WRN level.

err :: MonadIO m => Di path msg -> msg -> m () Source #

Synchronously log a message with ERR level.

Asynchronous logging

dbg' :: MonadIO m => Di path msg -> msg -> m () Source #

Asynchronously log a message with DBG level by queueing it in FIFO order to be logged in a different thread as soon as possible. The timestamp of the logged message will correctly represent the time of the dbg' call.

WARNING This function returns immediately, which makes it ideal for usage in tight loops. However, if logging the message fails later, you won't be able to catch the relevant exception.

inf' :: MonadIO m => Di path msg -> msg -> m () Source #

Asynchronously log a message with INF level by queueing it in FIFO order to be logged in a different thread as soon as possible. The timestamp of the logged message will correctly represent the time of the inf' call.

WARNING This function returns immediately, which makes it ideal for usage in tight loops. However, if logging the message fails later, you won't be able to catch the relevant exception.

wrn' :: MonadIO m => Di path msg -> msg -> m () Source #

Asynchronously log a message with WRN level by queueing it in FIFO order to be logged in a different thread as soon as possible. The timestamp of the logged message will correctly represent the time of the wrn' call.

WARNING This function returns immediately, which makes it ideal for usage in tight loops. However, if logging the message fails later, you won't be able to catch the relevant exception.

err' :: MonadIO m => Di path msg -> msg -> m () Source #

Asynchronously log a message with ERR level by queueing it in FIFO order to be logged in a different thread as soon as possible. The timestamp of the logged message will correctly represent the time of the err' call.

WARNING This function returns immediately, which makes it ideal for usage in tight loops. However, if logging the message fails later, you won't be able to catch the relevant exception.

Backends

mkDiTextStderr :: MonadIO m => m (Di Text Text) Source #

Text is written to stderr using the system's locale encoding.

> d0 <- mkDiTextStderr
> dbg d0 "a"
DBG 2017-05-06T19:01:27:306168750000Z: a
> let d1 = push d0 "f/oo"       -- '/' is converted to '.'
> inf d1 "b"
INF 2017-05-06T19:01:27:314333636000Z f.oo: b
> let d2 = push d1 "b ar"        -- ' ' is converted to '_'
> wrn d2 "c"
WRN 2017-05-06T19:01:27:322092498000Z f.oo/b_ar: c
> let d3 = push d2 "qux"
> err d3 "d"
ERR 2017-05-06T19:01:27:326704385000Z f.oo/b_ar/qux: d
> err d0 "e\nf"                  -- d0, of course, still works
ERR 2017-05-06T19:01:27:823167007000Z: e\nf

mkDiTextFileHandle :: MonadIO m => Handle -> m (Di Text Text) Source #

Text is written to Handle using the system's locale encoding. See mkDiTextStderr for example output.