Skip to content

Commit e4879ce

Browse files
authored
Add WithLogger monad for deriving via (#44)
Library changes: * add withLogger, runLogAction * add WithLogger monad transformer, runWithLogger * remove runLoggerLoggingT from WAI middleware * major version bump Readme: * avoid bad use of withLoggerLoggingT in examples * recommend WithLogger in the "use without LoggingT" section Internal changes: * upgrade checkout action * automatically generate stack matrix * switch to new hlint-run action * drop LTS 12
1 parent 9c349f6 commit e4879ce

13 files changed

+143
-259
lines changed

.github/workflows/ci.yml

+15-12
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,36 @@ on:
66
branches: main
77

88
jobs:
9+
generate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- id: generate
14+
uses: freckle/stack-action/generate-matrix@v5
15+
outputs:
16+
stack-yamls: ${{ steps.generate.outputs.stack-yamls }}
17+
918
test:
1019
runs-on: ubuntu-latest
20+
needs: generate
1121

1222
strategy:
1323
matrix:
14-
stack-yaml:
15-
- stack-nightly.yaml # ghc-9.4
16-
- stack.yaml # ghc-9.2
17-
- stack-lts-19.33.yaml # ghc-9.0
18-
- stack-lts-18.28.yaml # ghc-8.10
19-
- stack-lts-16.31.yaml # ghc-8.8
20-
- stack-lts-14.27.yaml # ghc-8.6
21-
- stack-lts-12.26.yaml # ghc-8.4
24+
stack-yaml: ${{ fromJSON(needs.generate.outputs.stack-yamls) }}
2225
fail-fast: false
2326

2427
steps:
25-
- uses: actions/checkout@v3
28+
- uses: actions/checkout@v4
2629
- uses: freckle/stack-action@v4
2730
with:
2831
stack-yaml: ${{ matrix.stack-yaml }}
2932

3033
lint:
3134
runs-on: ubuntu-latest
3235
steps:
33-
- uses: actions/checkout@v3
34-
- uses: haskell/actions/hlint-setup@v2
35-
- uses: haskell/actions/hlint-run@v2
36+
- uses: actions/checkout@v4
37+
- uses: haskell-actions/hlint-setup@v2
38+
- uses: haskell-actions/hlint-run@v2
3639
with:
3740
fail-on: warning
3841
path: '["src/", "tests/"]'

Blammo.cabal

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cabal-version: 1.18
55
-- see: https://github.com/sol/hpack
66

77
name: Blammo
8-
version: 1.1.3.0
8+
version: 1.2.0.0
99
synopsis: Batteries-included Structured Logging library
1010
description: Please see README.md
1111
category: Utils
@@ -36,6 +36,7 @@ library
3636
Blammo.Logging.Terminal
3737
Blammo.Logging.Terminal.LogPiece
3838
Blammo.Logging.Test
39+
Blammo.Logging.WithLogger
3940
Data.Aeson.Compat
4041
Network.Wai.Middleware.Logging
4142
System.Log.FastLogger.Compat
@@ -45,6 +46,7 @@ library
4546
src
4647
default-extensions:
4748
DerivingStrategies
49+
GeneralizedNewtypeDeriving
4850
LambdaCase
4951
NoImplicitPrelude
5052
OverloadedStrings
@@ -88,6 +90,7 @@ test-suite readme
8890
Paths_Blammo
8991
default-extensions:
9092
DerivingStrategies
93+
GeneralizedNewtypeDeriving
9194
LambdaCase
9295
NoImplicitPrelude
9396
OverloadedStrings
@@ -98,8 +101,8 @@ test-suite readme
98101
Blammo
99102
, aeson
100103
, base <5
104+
, lens
101105
, markdown-unlit
102-
, monad-logger
103106
, mtl
104107
, text
105108
default-language: Haskell2010
@@ -123,6 +126,7 @@ test-suite spec
123126
tests
124127
default-extensions:
125128
DerivingStrategies
129+
GeneralizedNewtypeDeriving
126130
LambdaCase
127131
NoImplicitPrelude
128132
OverloadedStrings

CHANGELOG.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
## [_Unreleased_](https://github.com/freckle/blammo/compare/v1.1.3.0...main)
1+
## [_Unreleased_](https://github.com/freckle/blammo/compare/v1.2.0.0...main)
2+
3+
## [v1.2.0.0](https://github.com/freckle/blammo/compare/v1.1.3.0...v1.2.0.0)
4+
5+
- New in `Blammo.Logging`: `withLogger`, `WithLogger(..), runWithLogger`
6+
- New in `Blammo.Logging.Logger`: `runLogAction`
7+
- WAI middleware no longer performs a log flush. Wrap your entire application
8+
in either `withLoggerLoggingT` or `withLogger` to ensure a log flush at
9+
application shutdown.
210

311
## [v1.1.3.0](https://github.com/freckle/blammo/compare/v1.1.2.3...v1.1.3.0)
412

README.lhs

+32-34
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ All built on the well-known `MonadLogger` interface and using an efficient
2626
```haskell
2727
{-# LANGUAGE DeriveAnyClass #-}
2828
{-# LANGUAGE DeriveGeneric #-}
29-
{-# LANGUAGE DerivingStrategies #-}
30-
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
29+
{-# LANGUAGE DerivingVia #-}
3130
3231
module Main (module Main) where
3332
@@ -37,9 +36,9 @@ import Data.Aeson
3736
import Data.Text (Text)
3837
import GHC.Generics (Generic)
3938
import Text.Markdown.Unlit ()
40-
import Control.Monad.IO.Class (MonadIO (liftIO))
41-
import Control.Monad.Logger (Loc, LogStr, ToLogStr (toLogStr))
42-
import Control.Monad.Reader (asks, MonadReader, ReaderT (runReaderT))
39+
import Control.Lens (lens)
40+
import Control.Monad.IO.Class (MonadIO)
41+
import Control.Monad.Reader (MonadReader, ReaderT (runReaderT))
4342
```
4443
-->
4544
@@ -142,7 +141,7 @@ setting the format to `json` will automatically enable it (with
142141
## Configuration
143142
144143
| Setting | Setter | Environment variable and format |
145-
| --- | --- | --- |
144+
| ----------- | --------------------------- | ----------------------------------------- |
146145
| Format | `setLogSettingsFormat` | `LOG_FORMAT=tty\|json` |
147146
| Level(s) | `setLogSettingsLevels` | `LOG_LEVEL=<level>[,<source:level>,...]` |
148147
| Destination | `setLogSettingsDestination` | `LOG_DESTINATION=stdout\|stderr\|@<path>` |
@@ -199,14 +198,17 @@ runAppT app f = runLoggerLoggingT app $ runReaderT f app
199198
200199
If your app monad is not a transformer stack containing `LoggingT` (ex: the
201200
[ReaderT pattern](https://www.fpcomplete.com/blog/readert-design-pattern/)), you
202-
can implement a custom instance of `MonadLogger`:
201+
can derive `MonadLogger` via `WithLogger`:
203202
204203
```haskell
205204
data AppEnv = AppEnv
206-
{ appLogFunc :: Loc -> LogSource -> LogLevel -> LogStr -> IO ()
205+
{ appLogger :: Logger
207206
-- ...
208207
}
209208
209+
instance HasLogger AppEnv where
210+
loggerL = lens appLogger $ \x y -> x {appLogger = y}
211+
210212
newtype App a = App
211213
{ unApp :: ReaderT AppEnv IO a }
212214
deriving newtype
@@ -216,11 +218,8 @@ newtype App a = App
216218
, MonadIO
217219
, MonadReader AppEnv
218220
)
219-
220-
instance MonadLogger App where
221-
monadLoggerLog loc logSource logLevel msg = do
222-
logFunc <- asks appLogFunc
223-
liftIO $ logFunc loc logSource logLevel (toLogStr msg)
221+
deriving (MonadLogger, MonadLoggerIO)
222+
via (WithLogger AppEnv IO)
224223
225224
runApp :: AppEnv -> App a -> IO a
226225
runApp env action =
@@ -237,20 +236,18 @@ app = do
237236
action2
238237
```
239238
240-
To retrieve the log function from Blammo, use `askLoggerIO` (from
241-
`MonadLoggerIO`) with `runSimpleLoggingT` (or `runLoggerLoggingT` if you need
242-
more customization options), when you initialize the app:
239+
Initialize the app with `withLogger`.
243240
244241
```haskell
245242
main2 :: IO ()
246-
main2 = do
247-
logFunc <- runSimpleLoggingT askLoggerIO
248-
let appEnv =
249-
AppEnv
250-
{ appLogFunc = logFunc
251-
-- ...
252-
}
253-
runApp appEnv app
243+
main2 =
244+
withLogger defaultLogSettings $ \logger -> do
245+
let appEnv =
246+
AppEnv
247+
{ appLogger = logger
248+
-- ...
249+
}
250+
runApp appEnv app
254251
```
255252
256253
## Integration with RIO
@@ -299,11 +296,11 @@ data App = App
299296
instance HasLogger App where
300297
-- ...
301298
302-
runApp :: ReaderT App (LoggingT IO) a -> IO a
303-
runApp f = do
304-
logger <- newLogger defaultLogSettings
305-
app <- App logger <$> runLoggerLoggingT logger awsDiscover
306-
runLoggerLoggingT app $ runReaderT f app
299+
runApp :: MonadUnliftIO m => ReaderT App m a -> m a
300+
runApp f =
301+
withLogger defaultLogSettings $ \logger -> do
302+
aws <- runWithLogger logger awsDiscover
303+
runReaderT f $ App logger aws
307304
308305
awsDiscover :: (MonadIO m, MonadLoggerIO m) => m AWS.Env
309306
awsDiscover = do
@@ -342,18 +339,19 @@ waiMiddleware app =
342339
## Integration with Warp
343340
344341
```hs
342+
import qualified Network.Wai.Handler.Warp as Warp
343+
345344
instance HasLogger App where
346345
-- ...
347346
348347
warpSettings :: App -> Settings
349348
warpSettings app = setOnException onEx $ defaultSettings
350349
where
351350
onEx _req ex =
352-
when (defaultShouldDisplayException ex)
353-
$ runLoggerLoggingT app
351+
when (Warp.defaultShouldDisplayException ex)
352+
$ runWithLogger app
354353
$ logError
355-
$ "Warp exception"
356-
:# ["exception" .= displayException ex]
354+
$ "Warp exception" :# ["exception" .= displayException ex]
357355
```
358356
359357
## Integration with Yesod
@@ -366,7 +364,7 @@ instance Yesod App where
366364
-- ...
367365
368366
messageLoggerSource app _logger loc source level msg =
369-
runLoggerLoggingT app $ monadLoggerLog loc source level msg
367+
runWithLogger app $ monadLoggerLog loc source level msg
370368
```
371369
372370
---

package.yaml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Blammo
2-
version: 1.1.3.0
2+
version: 1.2.0.0
33
maintainer: Freckle Education
44
category: Utils
55
github: freckle/blammo
@@ -37,6 +37,7 @@ dependencies:
3737

3838
default-extensions:
3939
- DerivingStrategies
40+
- GeneralizedNewtypeDeriving
4041
- LambdaCase
4142
- NoImplicitPrelude
4243
- OverloadedStrings
@@ -91,7 +92,7 @@ tests:
9192
dependencies:
9293
- Blammo
9394
- aeson
95+
- lens
9496
- markdown-unlit
95-
- monad-logger
9697
- mtl
9798
- text

src/Blammo/Logging.hs

+12-22
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module Blammo.Logging
1313
, setLogSettingsConcurrency
1414
, Logger
1515
, HasLogger (..)
16+
, withLogger
1617
, newLogger
1718
, runLoggerLoggingT
1819

@@ -29,10 +30,12 @@ module Blammo.Logging
2930
, myThreadContext
3031
, Pair
3132

32-
-- ** Transformer
33+
-- ** Transformers
3334
, MonadLogger (..)
3435
, MonadLoggerIO (..)
3536
, LoggingT
37+
, WithLogger (..)
38+
, runWithLogger
3639

3740
-- ** Common logging functions
3841

@@ -54,36 +57,23 @@ module Blammo.Logging
5457
, logOtherNS
5558
) where
5659

57-
import Prelude
58-
5960
import Blammo.Logging.LogSettings
6061
import Blammo.Logging.Logger
61-
import Control.Lens ((^.))
62+
import Blammo.Logging.WithLogger
63+
import Control.Lens (view)
6264
import Control.Monad.Catch (MonadMask)
6365
import Control.Monad.IO.Unlift (MonadUnliftIO)
6466
import Control.Monad.Logger.Aeson
6567
import Data.Aeson (Series)
6668
import Data.Aeson.Types (Pair)
67-
import Data.ByteString (ByteString)
6869
import UnliftIO.Exception (finally)
6970

71+
-- | Initialize logging, pass a 'Logger' to the callback, and clean up at the end.
72+
--
73+
-- Applications should avoid calling this more than once in their lifecycle.
7074
runLoggerLoggingT
7175
:: (MonadUnliftIO m, HasLogger env) => env -> LoggingT m a -> m a
72-
runLoggerLoggingT env f = (`finally` flushLogStr logger) $ do
73-
runLoggingT
74-
(filterLogger (getLoggerShouldLog logger) f)
75-
(loggerOutput logger $ getLoggerReformat logger)
76+
runLoggerLoggingT env f =
77+
runLoggingT f (runLogAction logger) `finally` flushLogStr logger
7678
where
77-
logger = env ^. loggerL
78-
79-
loggerOutput
80-
:: Logger
81-
-> (LogLevel -> ByteString -> ByteString)
82-
-> Loc
83-
-> LogSource
84-
-> LogLevel
85-
-> LogStr
86-
-> IO ()
87-
loggerOutput logger reformat =
88-
defaultOutputWith $ defaultOutputOptions $ \logLevel bytes -> do
89-
pushLogStrLn logger $ toLogStr $ reformat logLevel bytes
79+
logger = view loggerL env

0 commit comments

Comments
 (0)