![ci](https://ci.mangoiv.com/api/badges/12/status.svg) [![hackage](https://img.shields.io/hackage/v/unclogging.svg)](https://hackage.haskell.org/package/unclogging) # unclogging View repository: https://git.mangoiv.com/mangoiv/unclogging Simple, yet extensible concurrent logging system based on publish/subscribe. The frontend to this library is based on importing a specific logging frontend. Currently there are `Control.Effect.Unclog.{Json,Text}` and `Unclog.IO.{Json,Text}` for `aeson` and `text` based logging and use with `fused-effects` and `MonadUnliftIO` respectively, exposing functions with the same names, `debug`, `info`, `warn` and `fatal` for the four common log levels. Subscribers watch a channel of logs and are constructed and destructed by the toplevel logging handler, like `runUnclogging` or `withLoggingWithSubscribers`. To construct your own subscribers, check out the `Unclog.Subscriber` module, more specifically, the `mkSubscriber`, `mkSubscriberSimple` and `bracketSubscriber` functions. The library makes use of template haskell to efficiently create static information at compile time. This means that the logging frontend functions are of type `Q Exp`, which isn't very telling. Due to limitations for typed template haskell we currently cannot perform better though. If you're familiar with typed template haskell, you can imagine the logging functions to have a type similar to `Code m (frontend -> m ())` (more about this right below). In reality, as soon as you're splicing in a logging function, e.g. by using `$info`, the type will "materialise" and you will obtain a function of type `frontend -> m ()` where `frontend` is either `Data.Aeson.Object` if you're using the json frontend or `Data.Text.Text` if you're using the text frontend. `m` will be something according to the effect system you're using. E.g. in the fused-effects case it will emit a `Has Log sig m` constraint and in the io case it will emit a `MonadUnliftIO` constraint. ## example for standalone usage, i.e. `MonadUnliftIO` and `text` frontend ```haskell -- import utilities that are useful for logging import Unclog -- the logging frontend has to be specific to the effect system used import Unclog.Text import Unclog.Subscribers (withLoggingWithSubscribers) main :: IO () main = do let subs = [colourSubscriber Info stdout, simpleSubscriber Fatal stderr, fileSubscriber Debug "bla.log"] withLoggingWithSubscribers subs \logger -> do $info logger "info" $debug logger "some important debug info" ``` ## example for use with `fused-effects` and `aeson` frontend ```haskell import Unclog -- logging frontend specific to fused-effects import Control.Effect.Unclog.Json import Control.Carrier.Unclog -- for the construction of JSON objects. import Data.Aeson main :: IO () main = do let subs = [colourSubscriber Info stdout, simpleSubscriber Fatal stderr, fileSubscriber Debug "bla.log"] -- as you can see, we don't have to pass around the logger explicitly, which makes it a bit more concise and -- also a tad more safe runUnclogging subs do $info ["msg" .= String "hello world"] $debug ["msg" .= String "message with number", "number" .= Number 1] ``` ## more verbose example with multiple threads involved ```haskell import Unclog import Unclog.Json import Data.Aeson import UnliftIO import Data.String (fromString) import Control.Monad (replicateM_, void) main :: IO () main = do let subs = [colourSubscriber Info stdout, simpleSubscriber Fatal stderr, fileSubscriber Debug "bla.log"] withLoggingWithSubscribers subs \logger -> do -- spawn a couple of tasks that write to the logger channel concurrently let spawnTask (i :: Int) = do $info logger ["msg" .= String ("I am task " <> fromString (show i))] replicateM_ 2 do replicateM_ 2 do $debug logger ["msg" .= String ("debugging from " <> fromString (show i))] $warn logger ["warning" .= String ("warning from " <> fromString (show i))] $fatal logger ["error" .= String ("fatal from " <> fromString (show i))] void $ runConcurrently $ traverse @[] (Concurrently . spawnTask) [1 .. 5] ``` ## the looks of it This is how the coloured logging looks with above example, as you can see, both the simple logger writes the fatal events to stderr witout colouring them, and the coloured logger writes everything from `Info` upwards. ![screenshot of coloured log scenario](./assets/screenshot-log-colour.png)