Composable Contravariant Comonadic Logging Library

Version on this page:0.2.0
LTS Haskell 22.29:
Stackage Nightly 2024-07-13:
Latest on Hackage:

See all snapshots co-log appears in

MPL-2.0 licensed by Kowainik, Alexander Vershilov
Maintained by [email protected]
This version can be pinned in stack with:co-log-0.2.0@sha256:3a57264ada352996d831920c9d233e7f96bf5414352e7937358d6e5ee90d6acd,3878
# co-log

[![MPL-2.0 license](](
[![Build status](](

Logging library based on [`co-log-core`](../co-log-core) package. Provides
ready-to-go implementation of logging. This README contains _How to_ tutorial on
using this library. This tutorial explains step by step how to integrate
`co-log` into small basic project, specifically how to replace `putStrLn` used
for logging with library provided logging.

All code below can be compiled and run with the following commands:

$ cabal new-build co-log
$ cabal new-exec readme

## Preamble: imports and language extensions

Since this is a literate haskell file, we need to specify all our language
extensions and imports up front.

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

import Colog (Message, WithLog, cmap, fmtMessage, logDebug, logInfo, logTextStdout, logWarning,
import Control.Monad.IO.Class (MonadIO, liftIO)

import Data.Semigroup ((<>))
import qualified Data.Text as Text
import qualified Data.Text.IO as TextIO


## Simple IO function example

Consider the following function that reads lines from `stdin` and outputs
different feedback depending on the line size.

processLinesBasic :: IO ()
processLinesBasic = do
line <- TextIO.getLine
case Text.length line of
0 -> do
-- here goes logging
TextIO.putStrLn ">>>> Empty input"
n -> do
TextIO.putStrLn ">>>> Correct input"
TextIO.putStrLn $ "Line length: " <> Text.pack (show n)

This code mixes application logic with logging of the steps. It's convenient to
have logging to observe behavior of the application. But `putStrLn` is very
simple and primitive way to log things.

## Using `co-log` library

In order to use `co-log` library, we need to refactor `processLinesBasic`
function in the following way:

processLinesLog :: (WithLog env Message m, MonadIO m) => m ()
processLinesLog = do
line <- liftIO TextIO.getLine
case Text.length line of
0 -> do
-- here goes logging
logWarning "Empty input"
n -> do
logDebug "Correct line"
logInfo $ "Line length: " <> Text.pack (show n)

Let's summarize required changes:

1. Make type more polymorphic: `(WithLog env Message m, MonadIO m) => m ()`
2. Add `liftIO` to all `IO` functions.
3. Replace `putStrLn` with proper `log*` function.

## Running actions

Let's run both functions:

main :: IO ()
main = do

let action = cmap fmtMessage logTextStdout
usingLoggerT action processLinesLog

And here is how output looks like:

![screenshot from 2018-09-17 20-52-01](


Change log

co-log uses PVP Versioning. The change log is available on GitHub.

0.2.0 — Nov 15, 2018

  • #45: Introduce approach for concurrent log writing.
  • #46: Moves logStringStdout, logStringStderr, logStringHandle, withLogStringFile from Colog.Actions to Colog.Core.IO
  • #77: Remove relude from dependencies. Add HLint check to Travis CI.
  • #64: Introduce basic benchmarks.
  • #20: Add experimental support for logger rotation (see Colog.Rotation module).
  • #39: Support GHC-8.2.2 and GHC-8.6.2.


  • #37: Add bounds to all dependencies. Move Prelude to the other-modules section.


  • Initially created.