contra-tracer: Arrow and contravariant tracers

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

A simple interface for logging, tracing and monitoring

[Skip to Readme]


Change log None available
Dependencies base (<5), contravariant [details]
License BSD-3-Clause
Copyright 2021 Alexander Vieth
Author Alexander Vieth
Category Logging
Source repo head: git clone
Uploaded by alexvieth at 2021-01-06T17:07:55Z




Maintainer's Corner

For package maintainers and hackage trustees

Readme for contra-tracer-

[back to package description]


Logging and monitoring should be treated as a software feature and not just a debugging tool. Yet it's common to find logging definitions which use monads and typeclasses to do what is essentially printf debugging: the log items are big types like text or JSON, and these items may be logged any where, any time, because the logging implementation is part of some omnipresent application monad.

This package provides a minimal set of definitions for contravariant tracing. It is intended to express the pattern--found in logging and monitoring--in which domain-specific values (e.g. events, statistics) are provided to domain-agnostic processors (e.g. syslog). A program which has a Tracer m t in scope is able to log any t with side-effects in m. By choosing t to be as small as possible, the program becomes more modular, because a tracer on a bigger type is also a tracer on a smaller type:

-- This is from Data.Functor.Contravariant
-- (t -> s) shows that s is bigger than t
contramap :: (t -> s) -> Tracer m s -> Tracer m t

The Contravariant instance on Tracer m is the mechanism by which a domain-agnostic tracer is adapted to stand in where a domain-specific tracer is required. This is why we call it contravariant tracing.

-- Puts text to stdout.
stdoutTracer :: Tracer IO Text

-- A somain-specific event type.
data Event = EventA | EventB Int

-- The log format for an Event.
eventToText :: Event -> Text

-- The stdoutTracer becomes an Event tracer by way of the Contravariant
-- instance.
eventTracer :: Tracer IO Event
eventTracer = contramap eventToText stdoutTracer

This style is intended to discourage the use of very big types like text or JSON within programs which do logging. When expressed in this style, such a program will not even have the possibility of doing the printf debugging style referred to in the opening paragraph, because there is no Tracer m Text in scope!

-- Some action that can use any tracer on Event in IO.
-- It does not need to be able to log text in order to run.
action :: Tracer IO Event -> IO Int
action tracer = do
  traceWith tracer EventA
  stuff <- doSomething
  traceWith tracer (EventB 42)
  pure stuff

main :: IO ()
main = do
  _ <- action eventTracer
  pure ()

Arrow tracers

The contravariant tracer is defined in terms of the arrow tracer found in the module Control.Tracer.Arrow. The motivation for the arrow tracer is to encourage the programmer to throw in traceWith calls liberally, and leave them in so that they will not bit-rot, with the understanding that there will be no runtime overhead if tracing is disabled. This module is only relevant when dealing with a tracer which will ignore certain inputs but not others. With the arrow representation, it is often possible to judge that a tracer will not emit anything, without forcing the input, so that the traceWith call becomes a no-op.