co-log-core-0.1.1: Composable Contravariant Comonadic Logging Library

Safe HaskellNone
LanguageHaskell2010

Colog.Core.Action

Contents

Description

Implements core data types and combinators for logging actions.

Synopsis

Core type and instances

newtype LogAction m msg Source #

Polymorphic and very general logging action type.

  • msg type variables is an input for logger. It can be Text or custom logging messsage with different fields that you want to format in future.
  • m type variable is for monadic action inside which logging is happening. It can be either IO or some custom pure monad.

Key design point here is that LogAction is:

Constructors

LogAction 

Fields

Instances
Contravariant (LogAction m) Source # 
Instance details

Defined in Colog.Core.Action

Methods

contramap :: (a -> b) -> LogAction m b -> LogAction m a #

(>$) :: b -> LogAction m b -> LogAction m a #

Applicative m => Semigroup (LogAction m a) Source #

This instance allows you to join multiple logging actions into single one.

For example, if you have two actions like these:

logToStdout :: LogAction IO String  -- outputs String to terminal
logToFile   :: LogAction IO String  -- appends String to some file

You can create new LogAction that perform both actions one after another using Semigroup:

logToBoth :: LogAction IO String  -- outputs String to both terminal and some file
logToBoth = logToStdout <> logToFile
Instance details

Defined in Colog.Core.Action

Methods

(<>) :: LogAction m a -> LogAction m a -> LogAction m a #

sconcat :: NonEmpty (LogAction m a) -> LogAction m a #

stimes :: Integral b => b -> LogAction m a -> LogAction m a #

Applicative m => Monoid (LogAction m a) Source # 
Instance details

Defined in Colog.Core.Action

Methods

mempty :: LogAction m a #

mappend :: LogAction m a -> LogAction m a -> LogAction m a #

mconcat :: [LogAction m a] -> LogAction m a #

HasLog (LogAction m msg) msg m Source # 
Instance details

Defined in Colog.Core.Class

Methods

getLogAction :: LogAction m msg -> LogAction m msg Source #

setLogAction :: LogAction m msg -> LogAction m msg -> LogAction m msg Source #

(<&) :: LogAction m msg -> msg -> m () infix 5 Source #

Operator version of unLogAction. Note that because of the types, something like:

action <& msg1 <& msg2

doesn't make sense. Instead you want:

action msg1> action <& msg2

In addition, because <& has higher precedence than the other operators in this module, the following:

f >$< action <& msg

is equivalent to:

(f >$< action) <& msg

(&>) :: msg -> LogAction m msg -> m () infix 5 Source #

A flipped version of <&.

It shares the same precedence as <&, so make sure to surround lower precedence operators in parentheses:

msg &> (f >$< action)

Semigroup combinators

foldActions :: (Foldable t, Applicative m) => t (LogAction m a) -> LogAction m a Source #

Joins some Foldable of LogActions into single LogAction using Semigroup instance for LogAction. This is basically specialized version of fold function.

Contravariant combinators

cfilter :: Applicative m => (msg -> Bool) -> LogAction m msg -> LogAction m msg Source #

Takes predicate and performs given logging action only if predicate returns True on input logging message.

cmap :: (a -> b) -> LogAction m b -> LogAction m a Source #

This combinator is contramap from contravariant functor. It is useful when you have something like

data LogRecord = LR
    { lrName    :: LoggerName
    , lrMessage :: Text
    }

and you need to provide LogAction which consumes LogRecord

logRecordAction :: LogAction m LogRecord

when you only have action that consumes Text

logTextAction :: LogAction m Text

With cmap you can do the following:

logRecordAction :: LogAction m LogRecord
logRecordAction = cmap lrMesssage logTextAction

This action will print only lrMessage from LogRecord. But if you have formatting function like this:

formatLogRecord :: LogRecord -> Text

you can apply it instead of lrMessage to log formatted LogRecord as Text.

(>$<) :: (a -> b) -> LogAction m b -> LogAction m a infixr 3 Source #

Operator version of cmap.

>>> 1 &> (show >$< logStringStdout)
1

(>$) :: b -> LogAction m b -> LogAction m a infixl 4 Source #

This combinator is >$ from contravariant functor. Replaces all locations in the output with the same value. The default definition is contramap . const, so this is a more efficient version.

>>> "Hello?" &> ("OUT OF SERVICE" >$ logStringStdout)
OUT OF SERVICE
>>> ("OUT OF SERVICE" >$ logStringStdout) <& 42
OUT OF SERVICE

cmapM :: Monad m => (a -> m b) -> LogAction m b -> LogAction m a Source #

cmapM combinator is similar to cmap but allows to call monadic functions (functions that require extra context) to extend consumed value. Consider the following example.

You have this logging record:

data LogRecord = LR
    { lrTime    :: UTCTime
    , lrMessage :: Text
    }

and you also have logging consumer inside IO for such record:

logRecordAction :: LogAction IO LogRecord

But you need to return consumer only for Text messages:

logTextAction :: LogAction IO Text

If you have function that can extend Text to LogRecord like the function below:

withTime :: Text -> IO LogRecord
withTime msg = do
    time <- getCurrentTime
    pure (LR time msg)

you can achieve desired behavior with cmapM in the following way:

logTextAction :: LogAction IO Text
logTextAction = cmapM withTime myAction

Divisible combinators

divide :: Applicative m => (a -> (b, c)) -> LogAction m b -> LogAction m c -> LogAction m a Source #

divide combinator from Divisible type class.

>>> logInt = LogAction print
>>> "ABC" &> divide (\s -> (s, length s)) logStringStdout logInt
ABC
3

conquer :: Applicative m => LogAction m a Source #

conquer combinator from Divisible type class.

Concretely, this is a LogAction that does nothing:

>>> conquer <& "hello?"
>>> "hello?" &> conquer

(>*<) :: Applicative m => LogAction m a -> LogAction m b -> LogAction m (a, b) infixr 4 Source #

Operator version of divide id.

>>> logInt = LogAction print
>>> (logStringStdout >*< logInt) <& ("foo", 1)
foo
1
>>> (logInt >*< logStringStdout) <& (1, "foo")
1
foo

(>*) :: Applicative m => LogAction m a -> LogAction m () -> LogAction m a infixr 4 Source #

Perform a constant log action after another.

>>> logHello = LogAction (const (putStrLn "Hello!"))
>>> "Greetings!" &> (logStringStdout >* logHello)
Greetings!
Hello!

(*<) :: Applicative m => LogAction m () -> LogAction m a -> LogAction m a infixr 4 Source #

A flipped version of >*

Decidable combinators

lose :: (a -> Void) -> LogAction m a Source #

lose combinator from Decidable type class.

choose :: (a -> Either b c) -> LogAction m b -> LogAction m c -> LogAction m a Source #

choose combinator from Decidable type class.

>>> logInt = LogAction print
>>> f = choose (\a -> if a < 0 then Left "Negative" else Right a)
>>> f logStringStdout logInt <& 1
1
>>> f logStringStdout logInt <& (-1)
Negative

(>|<) :: LogAction m a -> LogAction m b -> LogAction m (Either a b) infixr 3 Source #

Operator version of choose id.

>>> dontPrintInt = LogAction (const (putStrLn "Not printing Int"))
>>> Left 1 &> (dontPrintInt >|< logStringStdout)
Not printing Int
>>> (dontPrintInt >|< logStringStdout) <& Right ":)"
:)

Comonadic combinators

extract :: Monoid msg => LogAction m msg -> m () Source #

If msg is Monoid then extract performs given log action by passing mempty to it.

>>> logPrint :: LogAction IO [Int]; logPrint = LogAction print
>>> extract logPrint
[]

extend :: Semigroup msg => (LogAction m msg -> m ()) -> LogAction m msg -> LogAction m msg Source #

This is a comonadic extend. It allows you to chain different transformations on messages.

>>> f (LogAction l) = l ".f1" *> l ".f2"
>>> g (LogAction l) = l ".g"
>>> unLogAction logStringStdout "foo"
foo
>>> unLogAction (extend f logStringStdout) "foo"
foo.f1
foo.f2
>>> unLogAction (extend g $ extend f logStringStdout) "foo"
foo.g.f1
foo.g.f2
>>> unLogAction (logStringStdout =>> f =>> g) "foo"
foo.g.f1
foo.g.f2

(=>>) :: Semigroup msg => LogAction m msg -> (LogAction m msg -> m ()) -> LogAction m msg infixl 1 Source #

extend with the arguments swapped. Dual to >>= for a Monad.

(<<=) :: Semigroup msg => (LogAction m msg -> m ()) -> LogAction m msg -> LogAction m msg infixr 1 Source #

extend in operator form.