Blammo 1.1.3.0 → 1.2.0.0
raw patch · 8 files changed
+170/−116 lines, 8 filesdep −monad-loggerdep ~basedep ~clockdep ~containersPVP ok
version bump matches the API change (PVP)
Dependencies removed: monad-logger
Dependency ranges changed: base, clock, containers, dlist, exceptions, http-types, lens, unliftio, vector, wai
API changes (from Hackage documentation)
+ Blammo.Logging: WithLogger :: ReaderT env m a -> WithLogger env m a
+ Blammo.Logging: newtype WithLogger env m a
+ Blammo.Logging: runWithLogger :: env -> WithLogger env m a -> m a
+ Blammo.Logging: withLogger :: MonadUnliftIO m => LogSettings -> (Logger -> m a) -> m a
+ Blammo.Logging.Logger: runLogAction :: (MonadIO m, ToLogStr msg) => Logger -> Loc -> LogSource -> LogLevel -> msg -> m ()
+ Blammo.Logging.Logger: withLogger :: MonadUnliftIO m => LogSettings -> (Logger -> m a) -> m a
+ Blammo.Logging.WithLogger: WithLogger :: ReaderT env m a -> WithLogger env m a
+ Blammo.Logging.WithLogger: instance (Control.Monad.IO.Class.MonadIO m, Blammo.Logging.Internal.Logger.HasLogger env) => Control.Monad.Logger.MonadLogger (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: instance (Control.Monad.IO.Class.MonadIO m, Blammo.Logging.Internal.Logger.HasLogger env) => Control.Monad.Logger.MonadLoggerIO (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: instance GHC.Base.Applicative m => GHC.Base.Applicative (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: instance GHC.Base.Functor m => GHC.Base.Functor (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: instance GHC.Base.Monad m => Control.Monad.Reader.Class.MonadReader env (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: instance GHC.Base.Monad m => GHC.Base.Monad (Blammo.Logging.WithLogger.WithLogger env m)
+ Blammo.Logging.WithLogger: newtype WithLogger env m a
+ Blammo.Logging.WithLogger: runWithLogger :: env -> WithLogger env m a -> m a
Files
- Blammo.cabal +24/−20
- CHANGELOG.md +9/−1
- README.lhs +32/−34
- README.md +32/−34
- src/Blammo/Logging.hs +12/−22
- src/Blammo/Logging/Logger.hs +31/−2
- src/Blammo/Logging/WithLogger.hs +28/−0
- src/Network/Wai/Middleware/Logging.hs +2/−3
Blammo.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: Blammo-version: 1.1.3.0+version: 1.2.0.0 license: MIT license-file: LICENSE maintainer: Freckle Education@@ -31,6 +31,7 @@ Blammo.Logging.Terminal Blammo.Logging.Terminal.LogPiece Blammo.Logging.Test+ Blammo.Logging.WithLogger Data.Aeson.Compat Network.Wai.Middleware.Logging System.Log.FastLogger.Compat@@ -39,8 +40,9 @@ other-modules: Paths_Blammo default-language: Haskell2010 default-extensions:- DerivingStrategies LambdaCase NoImplicitPrelude OverloadedStrings- RecordWildCards TypeApplications+ DerivingStrategies GeneralizedNewtypeDeriving LambdaCase+ NoImplicitPrelude OverloadedStrings RecordWildCards+ TypeApplications ghc-options: -Weverything -Wno-all-missed-specialisations@@ -50,26 +52,26 @@ build-depends: aeson >=1.5.2.0,- base >=4.11.1.0 && <5,+ base >=4.12.0.0 && <5, bytestring >=0.10.8.2, case-insensitive >=1.2.0.11,- clock >=0.7.2,- containers >=0.5.11.0,- dlist >=0.8.0.5,+ clock >=0.8,+ containers >=0.6.0.1,+ dlist >=0.8.0.7, envparse >=0.5.0,- exceptions >=0.10.0,+ exceptions >=0.10.4, fast-logger >=3.2.3,- http-types >=0.12.2,- lens >=4.16.1,+ http-types >=0.12.3,+ lens >=4.17.1, monad-logger-aeson >=0.4.0.3, mtl >=2.2.2, text >=1.2.3.1, time >=1.8.0.2,- unliftio >=0.2.9.0,+ unliftio >=0.2.12, unliftio-core >=0.1.2.0, unordered-containers >=0.2.10.0,- vector >=0.12.0.2,- wai >=3.2.1.2+ vector >=0.12.1.2,+ wai >=3.2.2.1 if impl(ghc >=9.8) ghc-options: -Wno-missing-role-annotations@@ -87,8 +89,9 @@ other-modules: Paths_Blammo default-language: Haskell2010 default-extensions:- DerivingStrategies LambdaCase NoImplicitPrelude OverloadedStrings- RecordWildCards TypeApplications+ DerivingStrategies GeneralizedNewtypeDeriving LambdaCase+ NoImplicitPrelude OverloadedStrings RecordWildCards+ TypeApplications ghc-options: -Weverything -Wno-all-missed-specialisations@@ -99,9 +102,9 @@ build-depends: Blammo, aeson >=1.5.2.0,- base >=4.11.1.0 && <5,+ base >=4.12.0.0 && <5,+ lens >=4.17.1, markdown-unlit >=0.5.0,- monad-logger >=0.3.39, mtl >=2.2.2, text >=1.2.3.1 @@ -128,8 +131,9 @@ default-language: Haskell2010 default-extensions:- DerivingStrategies LambdaCase NoImplicitPrelude OverloadedStrings- RecordWildCards TypeApplications+ DerivingStrategies GeneralizedNewtypeDeriving LambdaCase+ NoImplicitPrelude OverloadedStrings RecordWildCards+ TypeApplications ghc-options: -Weverything -Wno-all-missed-specialisations@@ -140,7 +144,7 @@ build-depends: Blammo, aeson >=1.5.2.0,- base >=4.11.1.0 && <5,+ base >=4.12.0.0 && <5, bytestring >=0.10.8.2, envparse >=0.5.0, hspec >=2.7.9,
CHANGELOG.md view
@@ -1,4 +1,12 @@-## [_Unreleased_](https://github.com/freckle/blammo/compare/v1.1.3.0...main)+## [_Unreleased_](https://github.com/freckle/blammo/compare/v1.2.0.0...main)++## [v1.2.0.0](https://github.com/freckle/blammo/compare/v1.1.3.0...v1.2.0.0)++- New in `Blammo.Logging`: `withLogger`, `WithLogger(..), runWithLogger`+- New in `Blammo.Logging.Logger`: `runLogAction`+- WAI middleware no longer performs a log flush. Wrap your entire application+ in either `withLoggerLoggingT` or `withLogger` to ensure a log flush at+ application shutdown. ## [v1.1.3.0](https://github.com/freckle/blammo/compare/v1.1.2.3...v1.1.3.0)
README.lhs view
@@ -26,8 +26,7 @@ ```haskell {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DerivingVia #-} module Main (module Main) where @@ -37,9 +36,9 @@ import Data.Text (Text) import GHC.Generics (Generic) import Text.Markdown.Unlit ()-import Control.Monad.IO.Class (MonadIO (liftIO))-import Control.Monad.Logger (Loc, LogStr, ToLogStr (toLogStr))-import Control.Monad.Reader (asks, MonadReader, ReaderT (runReaderT))+import Control.Lens (lens)+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Reader (MonadReader, ReaderT (runReaderT)) ``` --> @@ -142,7 +141,7 @@ ## Configuration | Setting | Setter | Environment variable and format |-| --- | --- | --- |+| ----------- | --------------------------- | ----------------------------------------- | | Format | `setLogSettingsFormat` | `LOG_FORMAT=tty\|json` | | Level(s) | `setLogSettingsLevels` | `LOG_LEVEL=<level>[,<source:level>,...]` | | Destination | `setLogSettingsDestination` | `LOG_DESTINATION=stdout\|stderr\|@<path>` |@@ -199,14 +198,17 @@ If your app monad is not a transformer stack containing `LoggingT` (ex: the [ReaderT pattern](https://www.fpcomplete.com/blog/readert-design-pattern/)), you-can implement a custom instance of `MonadLogger`:+can derive `MonadLogger` via `WithLogger`: ```haskell data AppEnv = AppEnv- { appLogFunc :: Loc -> LogSource -> LogLevel -> LogStr -> IO ()+ { appLogger :: Logger -- ... } +instance HasLogger AppEnv where+ loggerL = lens appLogger $ \x y -> x {appLogger = y}+ newtype App a = App { unApp :: ReaderT AppEnv IO a } deriving newtype@@ -216,11 +218,8 @@ , MonadIO , MonadReader AppEnv )--instance MonadLogger App where- monadLoggerLog loc logSource logLevel msg = do- logFunc <- asks appLogFunc- liftIO $ logFunc loc logSource logLevel (toLogStr msg)+ deriving (MonadLogger, MonadLoggerIO)+ via (WithLogger AppEnv IO) runApp :: AppEnv -> App a -> IO a runApp env action =@@ -237,20 +236,18 @@ action2 ``` -To retrieve the log function from Blammo, use `askLoggerIO` (from-`MonadLoggerIO`) with `runSimpleLoggingT` (or `runLoggerLoggingT` if you need-more customization options), when you initialize the app:+Initialize the app with `withLogger`. ```haskell main2 :: IO ()-main2 = do- logFunc <- runSimpleLoggingT askLoggerIO- let appEnv =- AppEnv- { appLogFunc = logFunc- -- ...- }- runApp appEnv app+main2 =+ withLogger defaultLogSettings $ \logger -> do+ let appEnv =+ AppEnv+ { appLogger = logger+ -- ...+ }+ runApp appEnv app ``` ## Integration with RIO@@ -299,11 +296,11 @@ instance HasLogger App where -- ... -runApp :: ReaderT App (LoggingT IO) a -> IO a-runApp f = do- logger <- newLogger defaultLogSettings- app <- App logger <$> runLoggerLoggingT logger awsDiscover- runLoggerLoggingT app $ runReaderT f app+runApp :: MonadUnliftIO m => ReaderT App m a -> m a+runApp f =+ withLogger defaultLogSettings $ \logger -> do+ aws <- runWithLogger logger awsDiscover+ runReaderT f $ App logger aws awsDiscover :: (MonadIO m, MonadLoggerIO m) => m AWS.Env awsDiscover = do@@ -342,6 +339,8 @@ ## Integration with Warp ```hs+import qualified Network.Wai.Handler.Warp as Warp+ instance HasLogger App where -- ... @@ -349,11 +348,10 @@ warpSettings app = setOnException onEx $ defaultSettings where onEx _req ex =- when (defaultShouldDisplayException ex)- $ runLoggerLoggingT app+ when (Warp.defaultShouldDisplayException ex)+ $ runWithLogger app $ logError- $ "Warp exception"- :# ["exception" .= displayException ex]+ $ "Warp exception" :# ["exception" .= displayException ex] ``` ## Integration with Yesod@@ -366,7 +364,7 @@ -- ... messageLoggerSource app _logger loc source level msg =- runLoggerLoggingT app $ monadLoggerLog loc source level msg+ runWithLogger app $ monadLoggerLog loc source level msg ``` ---
README.md view
@@ -26,8 +26,7 @@ ```haskell {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DerivingVia #-} module Main (module Main) where @@ -37,9 +36,9 @@ import Data.Text (Text) import GHC.Generics (Generic) import Text.Markdown.Unlit ()-import Control.Monad.IO.Class (MonadIO (liftIO))-import Control.Monad.Logger (Loc, LogStr, ToLogStr (toLogStr))-import Control.Monad.Reader (asks, MonadReader, ReaderT (runReaderT))+import Control.Lens (lens)+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Reader (MonadReader, ReaderT (runReaderT)) ``` --> @@ -142,7 +141,7 @@ ## Configuration | Setting | Setter | Environment variable and format |-| --- | --- | --- |+| ----------- | --------------------------- | ----------------------------------------- | | Format | `setLogSettingsFormat` | `LOG_FORMAT=tty\|json` | | Level(s) | `setLogSettingsLevels` | `LOG_LEVEL=<level>[,<source:level>,...]` | | Destination | `setLogSettingsDestination` | `LOG_DESTINATION=stdout\|stderr\|@<path>` |@@ -199,14 +198,17 @@ If your app monad is not a transformer stack containing `LoggingT` (ex: the [ReaderT pattern](https://www.fpcomplete.com/blog/readert-design-pattern/)), you-can implement a custom instance of `MonadLogger`:+can derive `MonadLogger` via `WithLogger`: ```haskell data AppEnv = AppEnv- { appLogFunc :: Loc -> LogSource -> LogLevel -> LogStr -> IO ()+ { appLogger :: Logger -- ... } +instance HasLogger AppEnv where+ loggerL = lens appLogger $ \x y -> x {appLogger = y}+ newtype App a = App { unApp :: ReaderT AppEnv IO a } deriving newtype@@ -216,11 +218,8 @@ , MonadIO , MonadReader AppEnv )--instance MonadLogger App where- monadLoggerLog loc logSource logLevel msg = do- logFunc <- asks appLogFunc- liftIO $ logFunc loc logSource logLevel (toLogStr msg)+ deriving (MonadLogger, MonadLoggerIO)+ via (WithLogger AppEnv IO) runApp :: AppEnv -> App a -> IO a runApp env action =@@ -237,20 +236,18 @@ action2 ``` -To retrieve the log function from Blammo, use `askLoggerIO` (from-`MonadLoggerIO`) with `runSimpleLoggingT` (or `runLoggerLoggingT` if you need-more customization options), when you initialize the app:+Initialize the app with `withLogger`. ```haskell main2 :: IO ()-main2 = do- logFunc <- runSimpleLoggingT askLoggerIO- let appEnv =- AppEnv- { appLogFunc = logFunc- -- ...- }- runApp appEnv app+main2 =+ withLogger defaultLogSettings $ \logger -> do+ let appEnv =+ AppEnv+ { appLogger = logger+ -- ...+ }+ runApp appEnv app ``` ## Integration with RIO@@ -299,11 +296,11 @@ instance HasLogger App where -- ... -runApp :: ReaderT App (LoggingT IO) a -> IO a-runApp f = do- logger <- newLogger defaultLogSettings- app <- App logger <$> runLoggerLoggingT logger awsDiscover- runLoggerLoggingT app $ runReaderT f app+runApp :: MonadUnliftIO m => ReaderT App m a -> m a+runApp f =+ withLogger defaultLogSettings $ \logger -> do+ aws <- runWithLogger logger awsDiscover+ runReaderT f $ App logger aws awsDiscover :: (MonadIO m, MonadLoggerIO m) => m AWS.Env awsDiscover = do@@ -342,6 +339,8 @@ ## Integration with Warp ```hs+import qualified Network.Wai.Handler.Warp as Warp+ instance HasLogger App where -- ... @@ -349,11 +348,10 @@ warpSettings app = setOnException onEx $ defaultSettings where onEx _req ex =- when (defaultShouldDisplayException ex)- $ runLoggerLoggingT app+ when (Warp.defaultShouldDisplayException ex)+ $ runWithLogger app $ logError- $ "Warp exception"- :# ["exception" .= displayException ex]+ $ "Warp exception" :# ["exception" .= displayException ex] ``` ## Integration with Yesod@@ -366,7 +364,7 @@ -- ... messageLoggerSource app _logger loc source level msg =- runLoggerLoggingT app $ monadLoggerLog loc source level msg+ runWithLogger app $ monadLoggerLog loc source level msg ``` ---
src/Blammo/Logging.hs view
@@ -13,6 +13,7 @@ , setLogSettingsConcurrency , Logger , HasLogger (..)+ , withLogger , newLogger , runLoggerLoggingT @@ -29,10 +30,12 @@ , myThreadContext , Pair - -- ** Transformer+ -- ** Transformers , MonadLogger (..) , MonadLoggerIO (..) , LoggingT+ , WithLogger (..)+ , runWithLogger -- ** Common logging functions @@ -54,36 +57,23 @@ , logOtherNS ) where -import Prelude- import Blammo.Logging.LogSettings import Blammo.Logging.Logger-import Control.Lens ((^.))+import Blammo.Logging.WithLogger+import Control.Lens (view) import Control.Monad.Catch (MonadMask) import Control.Monad.IO.Unlift (MonadUnliftIO) import Control.Monad.Logger.Aeson import Data.Aeson (Series) import Data.Aeson.Types (Pair)-import Data.ByteString (ByteString) import UnliftIO.Exception (finally) +-- | Initialize logging, pass a 'Logger' to the callback, and clean up at the end.+--+-- Applications should avoid calling this more than once in their lifecycle. runLoggerLoggingT :: (MonadUnliftIO m, HasLogger env) => env -> LoggingT m a -> m a-runLoggerLoggingT env f = (`finally` flushLogStr logger) $ do- runLoggingT- (filterLogger (getLoggerShouldLog logger) f)- (loggerOutput logger $ getLoggerReformat logger)+runLoggerLoggingT env f =+ runLoggingT f (runLogAction logger) `finally` flushLogStr logger where- logger = env ^. loggerL--loggerOutput- :: Logger- -> (LogLevel -> ByteString -> ByteString)- -> Loc- -> LogSource- -> LogLevel- -> LogStr- -> IO ()-loggerOutput logger reformat =- defaultOutputWith $ defaultOutputOptions $ \logLevel bytes -> do- pushLogStrLn logger $ toLogStr $ reformat logLevel bytes+ logger = view loggerL env
src/Blammo/Logging/Logger.hs view
@@ -1,6 +1,7 @@ module Blammo.Logging.Logger ( Logger , HasLogger (..)+ , withLogger , newLogger , flushLogger , pushLogger@@ -11,6 +12,7 @@ , getLoggerShouldColor , pushLogStrLn , flushLogStr+ , runLogAction -- * Testing , newTestLogger@@ -28,7 +30,7 @@ import Blammo.Logging.Test hiding (getLoggedMessages) import qualified Blammo.Logging.Test as LoggedMessages import Control.Lens (view)-import Control.Monad (unless)+import Control.Monad (unless, when) import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Logger.Aeson import Control.Monad.Reader (MonadReader)@@ -49,7 +51,34 @@ , newStderrLoggerSetN , newStdoutLoggerSetN )-import UnliftIO.Exception (throwString)+import UnliftIO (MonadUnliftIO)+import UnliftIO.Exception (finally, throwString)++-- | Initialize logging, pass a 'Logger' to the callback, and clean up at the end.+--+-- Applications should avoid calling this more than once in their lifecycle.+withLogger :: MonadUnliftIO m => LogSettings -> (Logger -> m a) -> m a+withLogger settings f = do+ logger <- newLogger settings+ f logger `finally` flushLogStr logger++-- | Write a message to the 'Logger', unless the logger's filter options+-- reject it based on its 'LogSource' and 'LogLevel'+runLogAction+ :: (MonadIO m, ToLogStr msg)+ => Logger+ -> Loc+ -> LogSource+ -> LogLevel+ -> msg+ -> m ()+runLogAction logger loc source level msg =+ liftIO $+ when (lShouldLog logger source level) $+ defaultOutputWith options loc source level (toLogStr msg)+ where+ options = defaultOutputOptions $ \logLevel bytes ->+ pushLogStrLn logger $ toLogStr $ getLoggerReformat logger logLevel bytes getLoggerLogSettings :: Logger -> LogSettings getLoggerLogSettings = lLogSettings
+ src/Blammo/Logging/WithLogger.hs view
@@ -0,0 +1,28 @@+module Blammo.Logging.WithLogger (WithLogger (..), runWithLogger) where++import Prelude++import Blammo.Logging.Logger (HasLogger (..), runLogAction)+import Control.Lens (view)+import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Logger.Aeson (MonadLogger (..), MonadLoggerIO (..))+import Control.Monad.Reader (MonadReader, ReaderT (ReaderT), asks)++-- | Useful with the @DerivingVia@ language extension to derive+-- 'MonadLogger' for your application monad+newtype WithLogger env m a = WithLogger (ReaderT env m a)+ deriving newtype (Functor, Applicative, Monad, MonadIO, MonadReader env)++runWithLogger :: env -> WithLogger env m a -> m a+runWithLogger env (WithLogger (ReaderT f)) = f env++instance (MonadIO m, HasLogger env) => MonadLogger (WithLogger env m) where+ monadLoggerLog loc source level msg = do+ logger <- asks (view loggerL)+ runLogAction logger loc source level msg++instance (MonadIO m, HasLogger env) => MonadLoggerIO (WithLogger env m) where+ askLoggerIO = do+ logger <- asks (view loggerL)+ pure $ \loc source level msg ->+ liftIO $ runLogAction logger loc source level msg
src/Network/Wai/Middleware/Logging.hs view
@@ -139,15 +139,14 @@ requestLoggerWith :: HasLogger env => Config -> env -> Middleware requestLoggerWith config env app req respond =- runLoggerLoggingT env $ withRunInIO $ \runInIO -> do+ withRunInIO $ \runInIO -> do begin <- getTime app req $ \resp -> do recvd <- respond resp duration <- toMillis . subtract begin <$> getTime- recvd <$ runInIO (logResponse config duration req resp)+ recvd <$ runInIO (runWithLogger env $ logResponse config duration req resp) where getTime = Clock.getTime Clock.Monotonic- toMillis x = fromIntegral (Clock.toNanoSecs x) / nsPerMs logResponse :: MonadLogger m => Config -> Double -> Request -> Response -> m ()