BSD-3-Clause licensed by Manuel Schneckenreither
Maintained by [email protected]
This version can be pinned in stack with:easy-logger-0.1.0.7@sha256:3f74b2d412dc36446678fdf9255f376dc426955e455787c9c88364d4318abe91,1758

Easy Logging for Haskell

Easy-logger can be used to easily create logs, without handling any Monad. Just log as you go. The package provides logging for code that lives in the IO Monad, implements MonadIO m, as well as for pure code.

Usage

Initialise the logger for your package and start logging:

{-# LANGUAGE TemplateHaskell     #-}
import           EasyLogger
import qualified Data.Text  as T

main :: IO ()
main = do
  $(initLogger) (LogFile "package.log") LogDebug
  $(logInfo) ("Starting App" :: T.Text)
  ...
  # At the end of your program, flush the buffers:
  finalizeAllLoggers

The log output looks like this:

$ cat package.log
[INFO #22-Oct-2021 12:27:23] Starting App                           @(main:Main

If you use libraries, including your own package library, to log from a file outside of Main.hs you need to make the library package name available and enable the logging specifically. For this create following file:

{-# LANGUAGE TemplateHaskell #-}
module MyPackage.Logging
    ( enableMyPackageLogging
    , disableMyPackageLogging
    ) where

import           EasyLogger

enableMyPackageLogging :: LogDestination -> LogLevel -> IO ()
enableMyPackageLogging = $(initLogger)

disableMyPackageLogging :: IO ()
disableMyPackageLogging = $(finalizeLogger)

and then enable it:

{-# LANGUAGE TemplateHaskell     #-}
import           EasyLogger
import           MyPackage.Logging
import qualified Data.Text  as T

main :: IO ()
main = do
  $(initLogger) (LogFile "package.log") LogDebug
  enableMyPackageLogging (LogFile "package.log") LogDebug
  ...
  # At the end of your program, flush the buffers:
  finalizeAllLoggers

You can also include the logs of all the libraries that you use and which use the easy-logger package for logging:

{-# LANGUAGE TemplateHaskell     #-}
import           EasyLogger
import qualified Data.Text                          as T

main :: IO ()
main = do
  $(initLoggerAllPackages) (LogFile "package.log") LogAll True
  $(logInfo) ("Starting App" :: T.Text)
  ...
  # At the end of your program, flush the buffers:
  finalizeAllLoggers

You want to log in pure code without wrapping anything in a Monad? Here you go:

fromEither :: Either String Int -> Int
fromEither (Right v)  = v
fromEither (Left str) = $(pureLogPrintError) ("Parse error: " <> T.pack str) defaultValue
defaultValue = 0

Under the hood pureLogPrintError uses unsafePerformIO to log. The return value, i.e. defaultValue in the example, ensures that the log is actually executed when the error occurs.

Log Levels and Destinations

The available log levels are:

-- | Log Level. Levels are sorted. `All` < `Debug` < `Info` < `Warning` < `Error`.
--   None disables all logging. Default: All
data LogLevel
  = LogNone
  | LogAll
  | LogDebug
  | LogInfo
  | LogWarning
  | LogError
  deriving (Show, Read, Bounded, Enum, Eq, Ord)

The logger can be used to log to stderr, stdout or a file:

-- | Logging destination. See also `setLoggingDestination`.
data LogDestination
  = LogStdErr
  | LogStdOut
  | LogFile FilePath

Library

If you are exposing a library (including your own library), let your user turn on/off the logging for your library. The Template-Haskell code ensures that your package name is provided to the logger, and thus logging for this module only is turned on/off.

{-# LANGUAGE TemplateHaskell #-}
module My.Great.Package.Logging
    ( enableMyGreatPackageLogging
    , disableMyGreatPackageLogging
    ) where

import           EasyLogger

enableMyGreatPackageLogging :: LogDestination -> LogLevel -> IO ()
enableMyGreatPackageLogging = $(initLogger)

disableMyGreatPackageLogging :: IO ()
disableMyGreatPackageLogging = $(finalizeLogger)

Changes

Changelog for easy-logger

Unreleased changes