Giter Site home page Giter Site logo

co-log / co-log Goto Github PK

View Code? Open in Web Editor NEW
253.0 10.0 49.0 487 KB

๐Ÿ““ Flexible and configurable modern #Haskell logging framework

Home Page: https://kowainik.github.io/projects/co-log

License: Mozilla Public License 2.0

Haskell 100.00%
haskell logging contravariant comonad semigroup composability haskell-library hacktoberfest

co-log's Introduction

co-log

GitHub CI MPL-2.0 license

co-log is a composable and configurable logging framework. It combines all the benefits of Haskell idioms to provide a reasonable and convenient interface. Although the library design uses some advanced concepts in its core, we are striving to provide beginner-friendly API. The library also provides the complete documentation with a lot of beginner-friendly examples, explanations and tutorials to guide users. The combination of a pragmatic approach to logging and fundamental Haskell abstractions allows us to create a highly composable and configurable logging framework.


If you're interested in how different Haskell typeclasses are used to implement core functions of co-log, you can read the following blog post which goes into detail about the internal implementation specifics:

Co-Log Family

Co-Log is a family of repositories for a composable and configurable logging framework co-log.

Here is the list of currently available repositories and libraries that you can check out:

co-log-core lightweight package with basic data types and general idea which depends only on base Hackage
co-log taggless final implementation of logging library based on co-log-core Hackage
co-log-polysemy implementation of logging library based on co-log-core and the polysemy extensible effects library. Hackage
co-log-benchmarks benchmarks of the co-log library -

co-log library

Logging library based on 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 build
$ cabal 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 Control.Monad.IO.Class (MonadIO, liftIO)

import Colog (Message, WithLog, cmap, fmtMessage, logDebug, logInfo, logTextStdout, logWarning,
              usingLoggerT)

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"
            processLinesBasic
        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"
            processLinesLog
        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
    processLinesBasic

    let action = cmap fmtMessage logTextStdout
    usingLoggerT action processLinesLog

And here is how output looks like:

screenshot from 2018-09-17 20-52-01

More Tutorials

To provide a more user-friendly introduction to the library, we've created the tutorial series which introduces the main concepts behind co-log smoothly, please check more details here.

co-log's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

co-log's Issues

Implement pure version of logging monad

Currently we can log only in IO monad. But it would be great to have some pure logging monad that keeps all logger messages in its context. So that later we can dispatch those messages in some IO monad.

Add `logShowStdout` to Colog.Core.IO

This came up in #61 while resolving #58:

In the documentation examples it was often needed to log a value implementing Show, e.g. Int, (), etc.
I think this would a useful function to add to Colog.Core.IO.
logShowStdout is just a first idea of a name; it matches logStringStdout, but maybe we want logPrint
or something else instead.

Write some tests

Tests can be implemented on top of the existing PureLoggerT monad

Cleanup some extensions

I've noticed that co-log packages don't use all our standard default extensions.

Also I see very weird other-extensions: CPP which should be removed. So this issue is about:

  1. Removing other-extensions

  2. Adding all our company-wide accepted extensions to co-log/co-log.cabal package only

Custom `Severity` for `Message`

Different logging frameworks implement different range of message severity, e.g. 0-7 for syslog and log4j (up to a choice of names), 1-7 in cisco, slf4j has a mechanism for custom severity levels, and so on.

From the point of view of formatting/filtering all these severity level implementations look the same.

I'd say it could be useful to have a typeclass MSeverity with required instances/formatting functions with an instance MSeverity Severity, and then parameterize Message by an instance of MSeverity.

What do you think?

Add some default actions to `co-log` library

Specifically, print String/[L]Text/[L]ByteString to stdout/stderr/file. I propose the following name scheme for these actions:

log[Type][Handler]

For example,

logStringStdout :: LogAction IO String

Add rich message type

We should be able to write some messages with Severity (see #6), timestamp, etc. It would be great if this message can be extensible. But we will think of it later. First, let's just create this type.

Add contravariant combinators for logging

(>$<) :: Contravariant f => (b -> a) -> f a -> f b
(>*<) :: Divisible     f => f a -> f b -> f (a, b)
(>|<) :: Decidable     f => f a -> f b -> f (Either a b)
(>*)  :: Divisible     f => f a -> f () -> f a
(*<)  :: Divisible     f => f () -> f a -> f a

infixr 3 >$<
infixr 4 >*<
infixr 3 >|<
infixr 4 >*
infixr 4 *<

Add benchmarks result to the readme

Ideally I would like to see pure function that takes list of pairs -- benchmark name and result and generates markdown table which we can then insert to the README easily in case we want to update benchmark results.

Add `cmapMaybe` function

I'm talking about this function:

cmapMaybe :: (a -> Maybe b) -> LogAction m b -> LogAction m a

Maybe with better name.

Performance benchmarks

Hi, this is a cool looking library. Can I persuade you to do some performance benchmarks vs other logging libraries?

Knowing if and where the machinery causes too much runtime overhead would lessen the barrier to adoption.

`stack test` fails to build project

Running stack build at the project root builds everything correctly, but when running stack test
the following error shows up:

Error: While constructing the build plan, the following exceptions were encountered:

In the dependencies for Cabal-2.2.0.1:
    time-1.9.2 from stack configuration does not match >=1.4 && <1.9  (latest matching version is 1.8.0.4)
needed due to co-log-core-0.1.1 -> Cabal-2.2.0.1

In the dependencies for directory-1.3.1.5:
    time-1.9.2 from stack configuration does not match >=1.4 && <1.9  (latest matching version is 1.8.0.4)
needed due to co-log-core-0.1.1 -> directory-1.3.1.5

In the dependencies for unix-2.7.2.2:
    time-1.9.2 from stack configuration does not match >=1.2 && <1.9  (latest matching version is 1.8.0.4)
needed due to co-log-core-0.1.1 -> unix-2.7.2.2

Some different approaches to resolving this:

  * Set 'allow-newer: true' to ignore all version constraints and build anyway.

  * Consider trying 'stack solver', which uses the cabal-install solver to attempt to find some working build configuration. This can be convenient when dealing with many
    complicated constraint errors, but results may be unpredictable.

  * Recommended action: try adding the following to your extra-deps in /home/lucas/Desktop/Code/Haskell/co-log/stack.yaml:

- time-1.8.0.4

Plan construction failed.

Replacing time-1.9.2 with time-1.8.0.4 fixes this, as well as simply removing time from the extra dependencies.

Would like to know if other people can reproduce.

Bump up to `relude-0.4.0`

Checklist:

  1. Update bounds in .cabal file.
  2. Update in stack.yaml
  3. Update HLint rules.
  4. Add check for HLint rules to the Travis CI and fix all HLint warnings and hints.

co-log-benchmark.cabal references a non existent file

The following warning is generated when building the project:

Warning: File listed in co-log-benchmark/co-log-benchmark.cabal file does not exist: CHANGELOG.md

This is because the cabal file references CHANGELOG.md, but the changelog is named ChangeLog.md.

Either the cabal file should change, or the file should be renamed to CHANGELOG (which would match the rest of the project)

Implement `co-log-polysemy` package

Just a proof of concept. co-log is based on the MTL approach. But to show that the approach is extensible, we can implement one more package based on free monads.

Add fancy operator to pass `msg` to `LogAction m msg`

Currently, in order to pass msg to LogAction m msg you need to write the following code:

unLogAction logAction msg

This might not be convenient. It would be much cooler to have some fancy operator to pass logs to actions, like this one:

logAction <| msg

Maybe <| is not the best choice, since it's already a popular operator name. So I'm open to proposal for better names.

Move from `Text` formatting to `Builder` (for `ByteString`)

Benchmark results show that ByteString.putStrLn works faster than Text.putStrLn so it probably makes sense to use ByteString as formatted representation of types like Message. Though, it looks like first we need to do more research in this area to be 100% sure.

Move log actions that work with `String` from `co-log` to `co-log-core` package

I would like to move the following functions to the Colog.Core.IO module:

https://github.com/kowainik/co-log/blob/695a27fe0111918d2e810c9170a2388015595fd9/co-log/src/Colog/Actions.hs#L32-L52

They use only types from the base package so they won't introduce extra dependencies. But they are still useful. And they can be used as an example of very basic and default LogAction implementations. Also, later they can be used for testing with doctest

Add more Haddocks to the `co-log-core` package

The space is empty. But I guess something can be added there. Like, short description of every module (like in Relude) and maybe some usage example.

I will describe documentation improvements under this issue:

  • Top-level Haddock for Colog.Core module
  • Paragraphs in Colog.Core.Action about each instance and data type
  • Use <$ or &> operators in the doctest examples for consistency
  • Add note about co-log IO actions in the Colog.Core.IO
  • Add laws for Colog.Core.Class
  • Add README.md to extra-docs
  • Add Haskell logo to badges
  • Mention tutorial series in the main README.md

Add function that can log `Exception`

I imagine to have function like:

logException :: forall e m . (WithLog env Message, Exception e) => e -> m ()

Also would be nice to have function like catchLog but for this we need to decide what exception library to use...

Add Lens' to the HasLog typeclass

The HasLog typeclass has all functions required for implementing custom lens. Since lenses are reusable, because they are just type alias, I propose to add the following field to the HasLog typeclass with the default definition:

logActionL :: Lens' env (LogAction m msg)
logActionL = ...

So we need to introduce our own Lens' type alias (and reexport it) and add logActionL lens to the HasLog typeclass with the default definition (so people can implement their own definition if they want.

With this approach we won't depend on any *lens* package but still will provide lens-like interface using ideas from the following blog post:

Improve benchmarks table

  • Add one more column: time divided by 10K. To get approximate time of a single message
  • Add one more row like the last one Message{Time,ThreadId} > format > stdout but with encoding text to ByteString and using logByteStringStdout at top level

Create series of examples with tutorials

Currently all tutorials are here: https://github.com/kowainik/co-log/tree/master/co-log/example

Proposed by @vrom911. Basically, have examples directory with different complete usage examples of co-log library. I propose to have the following examples:

  • Basic example where you pass LogAction IO String as an argument and feed messages to it with <& operator
  • Same example but with LoggerT monad and simple message
  • Same example but with LoggerT monad and rich message
  • Basic example with LoggerT but not printing threadId
  • Like with LoggerT monad but using withLog function to change logger action locally
  • Using PureLogger
  • Using custom monad with LogAction inside Env
  • Logger combinators
  • Logger rotation (#133)

Should be good for now ๐Ÿ‘ Maybe there will be some code duplication between examples but I think that it's better to have all code in single module for the example to be able to see full code.

Not to say that each example should contain pretty good documentation ๐Ÿ˜

Things to do after finishing:

  • Remove play-co-log executable
  • Rename example directory to tutorials
  • Change README for co-log

Add `doctest` tests to `co-log-core` package

Blocked by #46

It's a great idea to test all functions in Colog.Core package using doctest. After #46 is implemented, logStringStdout action can be used as testing example. This will improve documentation significantly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.