Logging made easy.
Please see the README on GitHub at https://github.com/schnecki/easy-logger#readme
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)