# Logging with class

In two previous posts I've described how I currently compose log messages and how I do the actual logging. This post wraps up this particular topic for now with a couple of typeclasses, a default implementation, and an example showing how I use them.

## The typeclasses

First off I want a monad for the logging itself. It's just a collection of
functions taking a `LogMsg`

and returning unit (in a monad).

class Monad m => LoggerActions m where debug :: LogMsg -> m () info :: LogMsg -> m () warn :: LogMsg -> m () err :: LogMsg -> m () fatal :: LogMsg -> m ()

In order to provide a default implementation I also need a way to extract the logger itself.

class Monad m => HasLogger m where getLogger :: m Logger

## Default implementation

Using the two typeclasses above it's now possible to define a type with an
implementation of `LoggerActions`

that is usable with ```
deriving
via
```

.

newtype StdLoggerActions m a = MkStdZLA (m a) deriving (Functor, Applicative, Monad, MonadIO, HasLogger)

And its implementattion of `LoggerActions`

looks like this:

instance (HasLogger m, MonadIO m) => LoggerActions (StdLoggerActions m) where debug msg = getLogger >>= flip debugIO msg info msg = getLogger >>= flip infoIO msg warn msg = getLogger >>= flip warnIO msg err msg = getLogger >>= flip errIO msg fatal msg = getLogger >>= flip fatalIO msg

## An example

Using the definitions above is fairly straight forward. First a type the derives
its implementaiton of `LoggerActions`

from `StdLoggerActions`

.

newtype EnvT a = EnvT {runEnvT :: ReaderT Logger IO a} deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader Logger) deriving (LoggerActions) via (StdLoggerActions EnvT)

In order for it to work, and compile, it needs an implementation of `HasLogger`

too.

instance HasLogger EnvT where getLogger = ask

All that's left is a function using a constraint on `LoggerActions`

(`doStuff`

)
and a `main`

function creating a logger, constructing an `EnvT`

, and then
running `doStuff`

in it.

doStuff :: LoggerActions m => m () doStuff = do debug "a log line" info $ "another log line" #+ ["extras" .= (42 :: Int)] main :: IO () main = withLogger $ \logger -> runReaderT (runEnvT doStuff) logger