log-warper 1.8.2 → 1.8.3
raw patch · 6 files changed
+433/−2 lines, 6 filesdep ~mtldep ~textnew-component:exe:pure-how-to
Dependency ranges changed: mtl, text
Files
- CHANGES.md +6/−0
- README.md +27/−0
- examples/PureLogging.lhs +182/−0
- examples/PureLogging.md +182/−0
- log-warper.cabal +22/−2
- src/System/Wlog/Launcher.hs +14/−0
CHANGES.md view
@@ -1,3 +1,9 @@+1.8.3+=====++* [#79](https://github.com/serokell/log-warper/issues/79):+ Add `launchWithConfig` to `Launcher` module.+ 1.8.2 =====
+ README.md view
@@ -0,0 +1,27 @@+log-warper+==========++[](https://travis-ci.org/serokell/log-warper)+[](https://hackage.haskell.org/package/log-warper)+[](http://stackage.org/lts/package/log-warper)+[](http://stackage.org/nightly/package/log-warper)+[](https://opensource.org/licenses/MIT)++Auxilary logging library, which is wrapper over+[hslogger](http://hackage.haskell.org/package/hslogger) but allows+to keep logger name into monadic context, making logging less boilerplate.++Key features:++1. Output is colored :star:+2. Supports logging initialization from `.yaml` configuration file+3. Has wrapper for pure logging via `WriterT`+4. Supports log rotation+5. Flexibles and easy setting up of loggers (using monoidal builders and lenses)+6. Ability to acquire last `N` megabytes of logs from in-memory cache++Contributing+------------++> **This project uses [`universum`](https://github.com/serokell/universum)+> as default prelude**
+ examples/PureLogging.lhs view
@@ -0,0 +1,182 @@+# Pure logging++One of the features that `log-warper` supports is *pure logging*.+All related documentation and functions can be found in the [`PureLogging`][pure] module.+In this document we will try to show how to use logging features+in the pure functions.++All the code below can be compiled and run with these commands++```bash+stack build log-warper+stack exec pure-how-to+```+and you would see the logging output.++## Preamble: imports and language extensions++Since this is a literate haskell file, we need to specify all our language extensions+and imports up front.++``` haskell+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++module Main where+++import Control.Monad.State.Strict (MonadState (..), State, evalState, get, lift, modify')+import Data.Semigroup ((<>))++import System.Wlog (NamedPureLogger, WithLogger, WithLoggerIO, launchNamedPureLogWith,+ launchSimpleLogging, logDebug, logInfo)++import qualified Data.Text as Text++```++## Pure code++Let's consider that you have some pure monadic action where you want to add logging to.+You can't use `putStrLn` or anything like that because you don't have `IO` in your type.+But you still need to be able to inspect intermediate states of your computation.+That's why you need another feature to have some ability to log some information there.++Let's have a closer look at that.++So, we will have some simple pure `Monad` for our simple example.+Imagine that we have a newtype `PureCounter` which is actually wrapper on `State Int`+where `Int` represents some accumulator.++```haskell++-- | Pure monad to work with counter.+newtype PureCounter a = PureCounter+ { runPureCounter :: State Int a+ } deriving (Functor, Applicative, Monad)+```++Also we have a class `MonadCounter` where it will all work in.+With this class we will be able to get the current counter+and to increase the counter by one++```haskell++-- | 'MonadCounter' class.+class Monad m => MonadCounter m where+ -- | Return the current counter.+ getCounter :: m Int+ -- | Returns the resulting counter after incrementation.+ incCounter :: m Int++```++Let's implement the instance for our data type++```haskell++instance MonadCounter PureCounter where+ getCounter = PureCounter $ get+ incCounter = PureCounter $ modify' (+ 1) >> get++```++We need a way to evaluate our pure computation, so let's add such function.++```haskell++-- | Evaluates the 'PureCounter'.+evalPureCounter :: PureCounter a -> a+evalPureCounter pc = evalState (runPureCounter pc) 0++```+And of course let's have a function which works only for `MonadCounter` for testing purposes++```haskell++-- | Increases the counter twice.+doubleInc :: MonadCounter m => m Int+doubleInc = incCounter >> incCounter++```++That `doubleInc` seems to be good candidate to add some logging, cause+it's doing some smart stuff and it would be great to have that info logged properly.++```haskell++doubleIncWithLog :: (MonadCounter m, WithLogger m) => m ()+doubleIncWithLog = do+ counterBefore <- getCounter+ logDebug $ "Before incrementation " <> Text.pack (show counterBefore)+ counterAfter <- doubleInc+ logInfo $ "After incrementation " <> Text.pack (show counterAfter)++```+You see how we added [`logDebug`][logDebug] and [`logInfo`][logInfo]+functions into our code. But these functions demands `m` to be in+class `WithLogger`.++In order to actually observe the results of logging we need+to print the result inside the `IO` monad thus we need+to have constraint `WithLoggerIO`.+Let's add such function that will be called from `main`.++```haskell++-- | Runs 'doubleIncWithLog' with logging handling.+runCounterWithLog :: (WithLoggerIO m) => m ()+runCounterWithLog =+ launchNamedPureLogWith evalPureCounter doubleIncWithLog++```++Here you see [`launchNamedPureLogWith`][lnplw] which is basically+makes logging happen. It gathers all the logs that were written in+`doubleIncWithLog` and will take them out of there.++Also take into consideration that along with `launchNamedPureLogWith`+there is [`launchNamedPureLog`][lnpl] for your needs.++We also need to implement instance of `MonadCounter` for [`NamedPureLogger`][NPL]+data type from `log-warper` library to be able to launch pure logging.+This data type is responsible for collecting logs. Hopefully, implementing+instances for this data type is rather easy!++```haskell++instance MonadCounter m => MonadCounter (NamedPureLogger m) where+ getCounter = lift getCounter+ incCounter = lift incCounter++```++And will launch our function with default logging configurations (see [`launchSimpleLogging`][simple] function).++```haskell++main :: IO ()+main = launchSimpleLogging "counter" runCounterWithLog++```++So in this example we wanted to share the ideas of usage of logging subsystem+in pure environment. See ho it's working:++```bash+$ stack exec pure-how-to+[counter:DEBUG] [2017-12-21 13:13:18.30 UTC] Before incrementation 0+[counter:INFO] [2017-12-21 13:13:18.30 UTC] After incrementation 2++```++++[pure]: http://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html+[logDebug]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-CanLog.html#v:logDebug+[logInfo]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-CanLog.html#v:logInfo+[lnplw]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html#v:launchNamedPureLogWith+[lnpl]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html#v:runNamedPureLog+[NPL]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html#t:NamedPureLogger+[simple]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-Launcher.html#v:launchSimpleLogging
+ examples/PureLogging.md view
@@ -0,0 +1,182 @@+# Pure logging++One of the features that `log-warper` supports is *pure logging*.+All related documentation and functions can be found in the [`PureLogging`][pure] module.+In this document we will try to show how to use logging features+in the pure functions.++All the code below can be compiled and run with these commands++```bash+stack build log-warper+stack exec pure-how-to+```+and you would see the logging output.++## Preamble: imports and language extensions++Since this is a literate haskell file, we need to specify all our language extensions+and imports up front.++``` haskell+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}++module Main where+++import Control.Monad.State.Strict (MonadState (..), State, evalState, get, lift, modify')+import Data.Semigroup ((<>))++import System.Wlog (NamedPureLogger, WithLogger, WithLoggerIO, launchNamedPureLogWith,+ launchSimpleLogging, logDebug, logInfo)++import qualified Data.Text as Text++```++## Pure code++Let's consider that you have some pure monadic action where you want to add logging to.+You can't use `putStrLn` or anything like that because you don't have `IO` in your type.+But you still need to be able to inspect intermediate states of your computation.+That's why you need another feature to have some ability to log some information there.++Let's have a closer look at that.++So, we will have some simple pure `Monad` for our simple example.+Imagine that we have a newtype `PureCounter` which is actually wrapper on `State Int`+where `Int` represents some accumulator.++```haskell++-- | Pure monad to work with counter.+newtype PureCounter a = PureCounter+ { runPureCounter :: State Int a+ } deriving (Functor, Applicative, Monad)+```++Also we have a class `MonadCounter` where it will all work in.+With this class we will be able to get the current counter+and to increase the counter by one++```haskell++-- | 'MonadCounter' class.+class Monad m => MonadCounter m where+ -- | Return the current counter.+ getCounter :: m Int+ -- | Returns the resulting counter after incrementation.+ incCounter :: m Int++```++Let's implement the instance for our data type++```haskell++instance MonadCounter PureCounter where+ getCounter = PureCounter $ get+ incCounter = PureCounter $ modify' (+ 1) >> get++```++We need a way to evaluate our pure computation, so let's add such function.++```haskell++-- | Evaluates the 'PureCounter'.+evalPureCounter :: PureCounter a -> a+evalPureCounter pc = evalState (runPureCounter pc) 0++```+And of course let's have a function which works only for `MonadCounter` for testing purposes++```haskell++-- | Increases the counter twice.+doubleInc :: MonadCounter m => m Int+doubleInc = incCounter >> incCounter++```++That `doubleInc` seems to be good candidate to add some logging, cause+it's doing some smart stuff and it would be great to have that info logged properly.++```haskell++doubleIncWithLog :: (MonadCounter m, WithLogger m) => m ()+doubleIncWithLog = do+ counterBefore <- getCounter+ logDebug $ "Before incrementation " <> Text.pack (show counterBefore)+ counterAfter <- doubleInc+ logInfo $ "After incrementation " <> Text.pack (show counterAfter)++```+You see how we added [`logDebug`][logDebug] and [`logInfo`][logInfo]+functions into our code. But these functions demands `m` to be in+class `WithLogger`.++In order to actually observe the results of logging we need+to print the result inside the `IO` monad thus we need+to have constraint `WithLoggerIO`.+Let's add such function that will be called from `main`.++```haskell++-- | Runs 'doubleIncWithLog' with logging handling.+runCounterWithLog :: (WithLoggerIO m) => m ()+runCounterWithLog =+ launchNamedPureLogWith evalPureCounter doubleIncWithLog++```++Here you see [`launchNamedPureLogWith`][lnplw] which is basically+makes logging happen. It gathers all the logs that were written in+`doubleIncWithLog` and will take them out of there.++Also take into consideration that along with `launchNamedPureLogWith`+there is [`launchNamedPureLog`][lnpl] for your needs.++We also need to implement instance of `MonadCounter` for [`NamedPureLogger`][NPL]+data type from `log-warper` library to be able to launch pure logging.+This data type is responsible for collecting logs. Hopefully, implementing+instances for this data type is rather easy!++```haskell++instance MonadCounter m => MonadCounter (NamedPureLogger m) where+ getCounter = lift getCounter+ incCounter = lift incCounter++```++And will launch our function with default logging configurations (see [`launchSimpleLogging`][simple] function).++```haskell++main :: IO ()+main = launchSimpleLogging "counter" runCounterWithLog++```++So in this example we wanted to share the ideas of usage of logging subsystem+in pure environment. See ho it's working:++```bash+$ stack exec pure-how-to+[counter:DEBUG] [2017-12-21 13:13:18.30 UTC] Before incrementation 0+[counter:INFO] [2017-12-21 13:13:18.30 UTC] After incrementation 2++```++++[pure]: http://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html+[logDebug]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-CanLog.html#v:logDebug+[logInfo]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-CanLog.html#v:logInfo+[lnplw]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html#v:launchNamedPureLogWith+[lnpl]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html#v:runNamedPureLog+[NPL]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-PureLogging.html#t:NamedPureLogger+[simple]: https://hackage.haskell.org/package/log-warper-1.8.2/docs/System-Wlog-Launcher.html#v:launchSimpleLogging
log-warper.cabal view
@@ -1,5 +1,5 @@ name: log-warper-version: 1.8.2+version: 1.8.3 synopsis: Flexible, configurable, monadic and pretty logging homepage: https://github.com/serokell/log-warper license: MIT@@ -8,7 +8,12 @@ maintainer: Serokell <hi@serokell.io> copyright: 2016-2017 Serokell category: Logging-extra-source-files: CHANGES.md, examples/HowTo.md, examples/HowTo.lhs+extra-source-files: CHANGES.md+ , README.md+ , examples/HowTo.md+ , examples/HowTo.lhs+ , examples/PureLogging.md+ , examples/PureLogging.lhs stability: experimental build-type: Simple cabal-version: >=1.18@@ -101,6 +106,21 @@ build-depends: base >= 4.7 && < 5 , log-warper , markdown-unlit >= 0.4.0+ , text >= 1.2.2.1++ hs-source-dirs: examples+ default-language: Haskell2010+ ghc-options: -threaded -Wall+ -fno-warn-orphans+ -pgmL markdown-unlit++executable pure-how-to+ main-is: PureLogging.lhs++ build-depends: base >= 4.7 && < 5+ , log-warper+ , markdown-unlit >= 0.4.0+ , mtl , text >= 1.2.2.1 hs-source-dirs: examples
src/System/Wlog/Launcher.hs view
@@ -31,6 +31,7 @@ , defaultConfig , launchFromFile , launchSimpleLogging+ , launchWithConfig , parseLoggerConfig , setupLogging ) where@@ -126,6 +127,19 @@ cfg@LoggerConfig{..} <- parseLoggerConfig loggerConfigPath let builtConfig = cfg <> configBuilder setupLogging Nothing builtConfig++-- | Sets up given logging configurations,+-- runs the action with the given 'LoggerName'.+launchWithConfig :: (MonadIO m, MonadMask m)+ => LoggerConfig+ -> LoggerName+ -> LoggerNameBox m a+ -> m a+launchWithConfig config loggerName action =+ bracket_+ (setupLogging Nothing config)+ removeAllHandlers+ (usingLoggerName loggerName action) -- | Initializes logging using given 'FilePath' to logger configurations, -- runs the action with the given 'LoggerName'.