on master help sign in
Merge branch 'master' into katip-datadog
commit8c157b3fdcff83fef9dba0d2dba7c6909943e8a2 authorMichael Xavier <michael@michaelxavier.net> dateWed 19 Dec 2018, 10:05 (8 years ago) parentaf40998375b41b
14 files changed+127−69
katip-elasticsearch/examples/example.hs+1−1 katip-elasticsearch/katip-elasticsearch.cabal+2−2 katip-elasticsearch/src/Katip/Scribes/ElasticSearch/Internal.hs+4−4 katip-elasticsearch/test/Main.hs+1−1 katip/bench/Main.hs+5−2 katip/examples/example.hs+1−1 katip/examples/example_lens.hs+1−1 katip/katip.cabal+1−1 katip/src/Katip.hs+6−3 katip/src/Katip/Core.hs+82−35 katip/src/Katip/Scribes/Handle.hs+17−13 katip/test/Katip/Tests.hs+3−2 katip/test/Katip/Tests/Scribes/Handle.hs+2−2 stack.yaml+1−1
katip-elasticsearch/examples/example.hs+1−1
@@ -27,7 +27,7 @@ main = do
2727 bhe
2828 (IndexName "all-indices-prefixed-with")
2929 (MappingName "application-logs")
30− DebugS
30+ (permitItem DebugS)
3131 V3
3232 let mkLogEnv = registerScribe "es" esScribe defaultScribeSettings =<< initLogEnv "MyApp" "production"
3333 bracket mkLogEnv closeScribes $ \le -> runKatipT le $ do
katip-elasticsearch/katip-elasticsearch.cabal+2−2
@@ -1,7 +1,7 @@
11name: katip-elasticsearch
22synopsis: ElasticSearch scribe for the Katip logging framework.
33description: See README.md for more details.
4−version: 0.5.1.1
4+version: 0.6.0.0
55license: BSD3
66license-file: LICENSE
77author: Ozgun Ataman, Michael Xavier
@@ -35,7 +35,7 @@ library
3535 Katip.Scribes.ElasticSearch.Internal
3636 build-depends:
3737 base >=4.6 && <5
38− , katip >= 0.2.0.0 && < 0.8
38+ , katip >= 0.2.0.0 && < 0.9
3939 , bloodhound >= 0.13.0.0 && < 0.17
4040 , uuid >= 1.3.12 && < 1.4
4141 , aeson >=0.6
katip-elasticsearch/src/Katip/Scribes/ElasticSearch/Internal.hs+4−4
@@ -269,10 +269,10 @@ mkEsScribe
269269 -> IndexName v
270270 -- ^ Treated as a prefix if index sharding is enabled
271271 -> MappingName v
272− -> Severity
272+ -> PermitFunc
273273 -> Verbosity
274274 -> IO Scribe
275−mkEsScribe cfg@EsScribeCfg {..} env ix mapping sev verb = do
275+mkEsScribe cfg@EsScribeCfg {..} env ix mapping permit verb = do
276276 q <- newTBMQueueIO $ unEsQueueSize essQueueSize
277277 endSig <- newEmptyMVar
278278
@@ -308,10 +308,10 @@ mkEsScribe cfg@EsScribeCfg {..} env ix mapping sev verb = do
308308 putMVar endSig ()
309309
310310 let finalizer = putMVar endSig () >> takeMVar endSig
311− return (Scribe (logger q) finalizer)
311+ return (Scribe (logger q) finalizer permit)
312312 where
313313 logger :: forall a. LogItem a => TBMQueue (IndexName v, Value) -> Item a -> IO ()
314− logger q i = when (_itemSeverity i >= sev) $
314+ logger q i =
315315 void $ atomically $ tryWriteTBMQueue q (chooseIxn prx ix essIndexSharding i, itemJson' i)
316316 prx :: Typeable.Proxy v
317317 prx = Typeable.Proxy
katip-elasticsearch/test/Main.hs+1−1
@@ -161,7 +161,7 @@ setupSearch
161161setupSearch prx modScribeCfg = do
162162 bh prx (dropESSchema prx)
163163 mgr <- newManager defaultManagerSettings
164− mkEsScribe cfg (mkBHEnv prx (svr prx) mgr) (ixn prx) (mn prx) DebugS V3
164+ mkEsScribe cfg (mkBHEnv prx (svr prx) mgr) (ixn prx) (mn prx) (permitItem DebugS) V3
165165 where
166166 cfg :: EsScribeCfg v
167167 cfg = modScribeCfg $
katip/bench/Main.hs+5−2
@@ -92,7 +92,7 @@ setup dest = do
9292 return (tmp </> "katip-bench.log")
9393 DevNull -> return ("/dev/null")
9494 h <- openFile outFile WriteMode
95− s <- mkHandleScribe ColorIfTerminal h DebugS V0
95+ s <- mkHandleScribe ColorIfTerminal h (permitItem DebugS) V0
9696 let cleanupHandle = hClose h `finally` (when (dest == TempFile) (removeLink outFile))
9797 return s { scribeFinalizer = scribeFinalizer s `finally` cleanupHandle}
9898
@@ -101,4 +101,7 @@ setup dest = do
101101deriving instance NFData ThreadIdText
102102
103103instance NFData Scribe where
104− rnf (Scribe a b) = (a :: Item ExPayload -> IO ()) `seq` b `seq` ()
104+ rnf (Scribe a b p) = (a :: Item ExPayload -> IO ())
105+ `seq` b
106+ `seq` (p :: Item ExPayload -> IO Bool)
107+ `seq` ()
katip/examples/example.hs+1−1
@@ -36,7 +36,7 @@ main = do
3636 -- hot-swap scribes at runtime if you need to. 'closeScribes' is
3737 -- blocking and flushes all messages out of a scribe and cleans up
3838 -- resources that were allocated at creation.
39− handleScribe <- mkHandleScribe ColorIfTerminal stdout InfoS V2
39+ handleScribe <- mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V2
4040 let mkLogEnv = registerScribe "stdout" handleScribe defaultScribeSettings =<< initLogEnv "MyApp" "production"
4141 bracket mkLogEnv closeScribes $ \le -> do
4242 let s = MyState M.mempty mempty le
katip/examples/example_lens.hs+1−1
@@ -51,7 +51,7 @@ main = do
5151 -- hot-swap scribes at runtime if you need to. 'closeScribes' is
5252 -- blocking and flushes all messages out of a scribe and cleans up
5353 -- resources that were allocated at creation.
54− handleScribe <- mkHandleScribe ColorIfTerminal stdout InfoS V2
54+ handleScribe <- mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V2
5555 let mkLogEnv = registerScribe "stdout" handleScribe defaultScribeSettings =<< initLogEnv "MyApp" "production"
5656 bracket mkLogEnv closeScribes $ \le -> do
5757 let s = MyState M.mempty mempty le
katip/katip.cabal+1−1
@@ -1,5 +1,5 @@
11name: katip
2−version: 0.7.0.0
2+version: 0.8.0.0
33synopsis: A structured logging framework.
44description:
55 Katip is a structured logging framework. See README.md for more details.
katip/src/Katip.hs+6−3
@@ -11,8 +11,8 @@
1111--
1212-- main :: IO ()
1313-- main = do
14−-- handleScribe <- mkHandleScribe ColorIfTerminal stdout InfoS V2
15−-- let makeLogEnv = registerScribe "stdout" handleScribe defaultScribeSettings =<< initLogEnv "MyApp" "production"
14+-- handleScribe <- mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V2
15+-- let makeLogEnv = registerScribe "stdout" handleScribe defaultScribeSettings =<< initLogEnv \"MyApp\" \"production\"
1616-- -- closeScribes will stop accepting new logs, flush existing ones and clean up resources
1717-- bracket makeLogEnv closeScribes $ \le -> do
1818-- let initialContext = () -- this context will be attached to every log in your app and merged w/ subsequent contexts
@@ -153,7 +153,7 @@ module Katip
153153 , LogStr (..)
154154 , logStr, ls, showLS
155155
156− -- ** 'Katip' LoggingFunctions
156+ -- ** 'Katip' Logging Functions
157157 -- $katiplogging
158158 , Katip (..)
159159 , logF
@@ -186,6 +186,9 @@ module Katip
186186 , jsonFormat
187187
188188 -- * Tools for implementing Scribes
189+ , PermitFunc
190+ , permitAND
191+ , permitOR
189192 , permitItem
190193 , payloadObject
191194 , itemJson
katip/src/Katip/Core.hs+82−35
@@ -29,7 +29,7 @@ import qualified Control.Concurrent.Async as Async
2929import Control.Concurrent.STM
3030import qualified Control.Concurrent.STM.TBQueue as BQ
3131import Control.Exception.Safe
32−import Control.Monad (unless, void)
32+import Control.Monad (unless, void, when)
3333import Control.Monad.Base
3434import Control.Monad.IO.Class
3535import Control.Monad.IO.Unlift
@@ -532,45 +532,86 @@ itemJson verb a = toJSON $ a & itemPayload %~ payloadObject verb
532532--
533533-- Scribes should always take a 'Severity' and 'Verbosity'.
534534--
535−-- Severity is used to *exclude log messages* that are < the provided
536−-- Severity. For instance, if the user passes InfoS, DebugS items
537−-- should be ignored. Katip provides the 'permitItem' utility for this.
535+-- Severity is used to __exclude log messages__ that are lower than
536+-- the provided Severity. For instance, if the user passes InfoS,
537+-- DebugS items should be ignored. Katip provides the 'permitItem'
538+-- utility for this. The user or the scribe may use 'permitAND' and
539+-- 'permitOR' to further customize this filtering, even dynamically if
540+-- they wish to.
538541--
539542-- Verbosity is used to select keys from the log item's payload. Each
540543-- 'LogItem' instance describes what keys should be retained for each
541544-- Verbosity level. Use the 'payloadObject' utility for extracting the keys
542−-- that should be permitted.
545+-- that should be written.
543546--
544−-- There is no built-in mechanism in katip for telling a scribe that
545−-- its time to shut down. 'unregisterScribe' merely drops it from the
546−-- 'LogEnv'. This means there are 2 ways to handle resources as a scribe:
547+-- Scribes provide a finalizer IO action ('scribeFinalizer') that is
548+-- meant to synchronously flush any remaining writes and clean up any
549+-- resources acquired when the scribe was created. Internally, katip
550+-- keeps a buffer for each scribe's writes. When 'closeScribe' or
551+-- 'closeScribes' is called, that buffer stops accepting new log
552+-- messages and after the last item in its buffer is sent to 'liPush',
553+-- calls the finalizer. Thus, when the finalizer returns, katip can
554+-- assume that all resources are cleaned up and all log messages are
555+-- durably written.
547556--
548−-- 1. Pass in the resource when the scribe is created. Handle
549−-- allocation and release of the resource elsewhere. This is what the
550−-- Handle scribe does.
551−--
552−-- 2. Return a finalizing function that tells the scribe to shut
553−-- down. @katip-elasticsearch@'s @mkEsScribe@ returns an @IO (Scribe,
554−-- IO ())@. The finalizer will flush any queued log messages and shut
555−-- down gracefully before returning. This can be hooked into your
556−-- application's shutdown routine to ensure you never miss any log
557−-- messages on shutdown.
557+-- While katip internally buffers messages per 'ScribeSettings', it
558+-- sends them one at a time to the scribe. Depending on the scribe
559+-- itself, it may make sense for that scribe to keep its own internal
560+-- buffer to batch-send logs if writing items one at a time is not
561+-- efficient. The scribe implementer must be sure that on
562+-- finalization, all writes are committed synchronously.
563+
564+-- | Signature of a function passed to `Scribe` constructor and
565+-- mkScribe* functions that decides which messages to be
566+-- logged. Typically filters based on 'Severity', but can be
567+-- combined with other, custom logic with 'permitAND' and 'permitOR'
568+type PermitFunc = forall a. Item a -> IO Bool
569+
570+
571+-- | AND together 2 permit functions
572+permitAND :: PermitFunc -> PermitFunc -> PermitFunc
573+permitAND f1 f2 = \a -> liftA2 (&&) (f1 a) (f2 a)
574+
575+-- | OR together 2 permit functions
576+permitOR :: PermitFunc -> PermitFunc -> PermitFunc
577+permitOR f1 f2 = \a -> liftA2 (||) (f1 a) (f2 a)
578+
579+
558580data Scribe = Scribe {
559581 liPush :: forall a. LogItem a => Item a -> IO ()
582+ -- ^ How do we write an item to the scribe's output?
560583 , scribeFinalizer :: IO ()
561− -- ^ Provide a *blocking* finalizer to call when your scribe is
562− -- removed. If this is not relevant to your scribe, return () is
563− -- fine.
584+ -- ^ Provide a __blocking__ finalizer to call when your scribe is
585+ -- removed. All pending writes should be flushed synchronously. If
586+ -- this is not relevant to your scribe, return () is fine.
587+ , scribePermitItem :: PermitFunc
588+ -- ^ Provide a filtering function to allow the item to be logged,
589+ -- or not. It can check Severity or some string in item's
590+ -- body. The initial value of this is usually created from
591+ -- 'permitItem'. Scribes and users can customize this by ANDing
592+ -- or ORing onto the default with 'permitAND' or 'permitOR'
564593 }
565594
566595
596+whenM :: Monad m => m Bool -> m () -> m ()
597+whenM mbool = (>>=) mbool . flip when
598+
599+
600+-- | Combine two scribes. Publishes to the left scribe if the left
601+-- would permit the item and to the right scribe if the right would
602+-- permit the item. Finalizers are called in sequence from left to
603+-- right.
567604instance Semigroup Scribe where
568− (Scribe pushA finA) <> (Scribe pushB finB) =
569− Scribe (\item -> pushA item >> pushB item) (finA `finally` finB)
605+ (Scribe pushA finA permitA) <> (Scribe pushB finB permitB) =
606+ Scribe (\item -> whenM (permitA item) (pushA item)
607+ >> whenM (permitB item) (pushB item)
608+ )
609+ (finA `finally` finB)
610+ (permitOR permitA permitB)
570611
571612
572613instance Monoid Scribe where
573− mempty = Scribe (const (return ())) (return ())
614+ mempty = Scribe (const (return ())) (return ()) (permitItem DebugS)
574615 mappend = (<>)
575616
576617
@@ -589,8 +630,9 @@ data WorkerMessage where
589630
590631-------------------------------------------------------------------------------
591632-- | Should this item be logged given the user's maximum severity?
592−permitItem :: Severity -> Item a -> Bool
593−permitItem sev i = _itemSeverity i >= sev
633+-- Most new scribes will use this as a base for their 'PermitFunc'
634+permitItem :: Monad m => Severity -> Item a -> m Bool
635+permitItem sev item = return (_itemSeverity item >= sev)
594636
595637
596638-------------------------------------------------------------------------------
@@ -636,7 +678,9 @@ initLogEnv an env = LogEnv
636678
637679-------------------------------------------------------------------------------
638680-- | Add a scribe to the list. All future log calls will go to this
639−-- scribe in addition to the others.
681+-- scribe in addition to the others. Writes will be buffered per the
682+-- ScribeSettings to prevent slow scribes from slowing down
683+-- logging. Writes will be dropped if the buffer fills.
640684registerScribe
641685 :: Text
642686 -- ^ Name the scribe
@@ -660,7 +704,7 @@ registerScribe nm scribe ScribeSettings {..} le = do
660704
661705-------------------------------------------------------------------------------
662706spawnScribeWorker :: Scribe -> BQ.TBQueue WorkerMessage -> IO (Async.Async ())
663−spawnScribeWorker (Scribe write _) queue = Async.async go
707+spawnScribeWorker (Scribe write _ _) queue = Async.async go
664708 where
665709 go = do
666710 newCmd <- atomically (BQ.readTBQueue queue)
@@ -689,7 +733,7 @@ defaultScribeSettings = ScribeSettings 4096
689733
690734
691735-------------------------------------------------------------------------------
692−-- | Remove a scribe from the environment. This does *not* finalize
736+-- | Remove a scribe from the environment. This does __not__ finalize
693737-- the scribe. This mainly only makes sense to use with something like
694738-- MonadReader's @local@ function to temporarily disavow a single
695739-- logger for a block of code.
@@ -702,7 +746,7 @@ unregisterScribe nm = logEnvScribes %~ M.delete nm
702746
703747
704748-------------------------------------------------------------------------------
705−-- | Unregister *all* scribes. Note that this is *not* for closing or
749+-- | Unregister __all__ scribes. Note that this is __not__ for closing or
706750-- finalizing scribes, use 'closeScribes' for that. This mainly only
707751-- makes sense to use with something like MonadReader's @local@
708752-- function to temporarily disavow any loggers for a block of code.
@@ -714,9 +758,10 @@ clearScribes = logEnvScribes .~ mempty
714758
715759-------------------------------------------------------------------------------
716760-- | Finalize a scribe. The scribe is removed from the environment,
717−-- its finalizer is called and it can never be written to again. Note
718−-- that this will throw any exceptions yoru finalizer will throw, and
719−-- that LogEnv is immutable, so it will not be removed in that case.
761+-- its finalizer is called so that it can never be written to again
762+-- and all pending writes are flushed. Note that this will throw any
763+-- exceptions yoru finalizer will throw, and that LogEnv is immutable,
764+-- so it will not be removed in that case.
720765closeScribe
721766 :: Text
722767 -- ^ Name of the scribe
@@ -896,8 +941,9 @@ logItem a ns loc sev msg = do
896941 <*> _logEnvTimer
897942 <*> pure (_logEnvApp <> ns)
898943 <*> pure loc
899− FT.forM_ (M.elems _logEnvScribes) $ \ ScribeHandle {..} -> atomically (tryWriteTBQueue shChan (NewItem item))
900−
944+ FT.forM_ (M.elems _logEnvScribes) $ \ ScribeHandle {..} -> do
945+ whenM (scribePermitItem shScribe item) $
946+ void $ atomically (tryWriteTBQueue shChan (NewItem item))
901947
902948-------------------------------------------------------------------------------
903949tryWriteTBQueue
@@ -910,6 +956,7 @@ tryWriteTBQueue q a = do
910956 unless full (writeTBQueue q a)
911957 return (not full)
912958
959+
913960-------------------------------------------------------------------------------
914961-- | Log with full context, but without any code location.
915962logF
katip/src/Katip/Scribes/Handle.hs+17−13
@@ -4,7 +4,6 @@ module Katip.Scribes.Handle where
44import Control.Applicative as A
55import Control.Concurrent
66import Control.Exception (bracket_, finally)
7−import Control.Monad
87import Data.Aeson
98import Data.Text.Lazy (toStrict)
109import Data.Text.Lazy.Encoding (decodeUtf8)
@@ -63,7 +62,7 @@ data ColorStrategy
6362--
6463-- Returns the newly-created `Scribe`. The finalizer flushes the
6564-- handle. Handle mode is set to 'LineBuffering' automatically.
66−mkHandleScribe :: ColorStrategy -> Handle -> Severity -> Verbosity -> IO Scribe
65+mkHandleScribe :: ColorStrategy -> Handle -> PermitFunc -> Verbosity -> IO Scribe
6766mkHandleScribe = mkHandleScribeWithFormatter bracketFormat
6867
6968-- | Logs to a file handle such as stdout, stderr, or a file. Takes a custom
@@ -71,17 +70,22 @@ mkHandleScribe = mkHandleScribeWithFormatter bracketFormat
7170--
7271-- Returns the newly-created `Scribe`. The finalizer flushes the
7372-- handle. Handle mode is set to 'LineBuffering' automatically.
74−mkHandleScribeWithFormatter :: (forall a . LogItem a => ItemFormatter a) -> ColorStrategy -> Handle -> Severity -> Verbosity -> IO Scribe
75−mkHandleScribeWithFormatter itemFormatter cs h sev verb = do
73+mkHandleScribeWithFormatter :: (forall a . LogItem a => ItemFormatter a)
74+ -> ColorStrategy
75+ -> Handle
76+ -> PermitFunc
77+ -> Verbosity
78+ -> IO Scribe
79+mkHandleScribeWithFormatter itemFormatter cs h permitF verb = do
7680 hSetBuffering h LineBuffering
7781 colorize <- case cs of
7882 ColorIfTerminal -> hIsTerminalDevice h
7983 ColorLog b -> return b
8084 lock <- newMVar ()
8185 let logger i@Item{..} = do
82− when (permitItem sev i) $ bracket_ (takeMVar lock) (putMVar lock ()) $
86+ bracket_ (takeMVar lock) (putMVar lock ()) $
8387 T.hPutStrLn h $ toLazyText $ itemFormatter colorize verb i
84− return $ Scribe logger (hFlush h)
88+ return $ Scribe logger (hFlush h) permitF
8589
8690
8791-------------------------------------------------------------------------------
@@ -90,11 +94,11 @@ mkHandleScribeWithFormatter itemFormatter cs h sev verb = do
9094-- 'AppendMode' and will close the file handle on
9195-- 'closeScribe'/'closeScribes'. Does not do log coloring. Sets handle
9296-- to 'LineBuffering' mode.
93−mkFileScribe :: FilePath -> Severity -> Verbosity -> IO Scribe
94−mkFileScribe f sev verb = do
97+mkFileScribe :: FilePath -> PermitFunc -> Verbosity -> IO Scribe
98+mkFileScribe f permitF verb = do
9599 h <- openFile f AppendMode
96− Scribe logger finalizer <- mkHandleScribe (ColorLog False) h sev verb
97− return (Scribe logger (finalizer `finally` hClose h))
100+ Scribe logger finalizer permit <- mkHandleScribe (ColorLog False) h permitF verb
101+ return (Scribe logger (finalizer `finally` hClose h) permit)
98102
99103
100104-------------------------------------------------------------------------------
@@ -169,8 +173,8 @@ colorBySeverity withColor severity msg = case severity of
169173-- stdout. This is a decent example of how to build a LogEnv and is
170174-- best for scripts that just need a quick, reasonable set up to log
171175-- to stdout.
172−ioLogEnv :: Severity -> Verbosity -> IO LogEnv
173−ioLogEnv sev verb = do
176+ioLogEnv :: PermitFunc -> Verbosity -> IO LogEnv
177+ioLogEnv permit verb = do
174178 le <- initLogEnv "io" "io"
175− lh <- mkHandleScribe ColorIfTerminal stdout sev verb
179+ lh <- mkHandleScribe ColorIfTerminal stdout permit verb
176180 registerScribe "stdout" lh defaultScribeSettings le
katip/test/Katip/Tests.hs+3−2
@@ -141,6 +141,7 @@ loggingTests = testGroup "logging"
141141 let scribe = Scribe
142142 { liPush = \i -> atomically (modifyTVar' items (<> [toObject <$> i]))
143143 , scribeFinalizer = return ()
144+ , scribePermitItem = permitItem DebugS
144145 }
145146 le1 <- initLogEnv "tests" "test"
146147 le2 <- registerScribe "recorder" scribe defaultScribeSettings le1
@@ -154,7 +155,7 @@ trivialScribe :: IO (Scribe, TVar Bool)
154155trivialScribe = do
155156 finalizerCalled <- newTVarIO False
156157 let finalizer = atomically (writeTVar finalizerCalled True)
157− return (Scribe (const (return ())) finalizer, finalizerCalled)
158+ return (Scribe (const (return ())) finalizer (permitItem DebugS), finalizerCalled)
158159
159160
160161-------------------------------------------------------------------------------
@@ -164,7 +165,7 @@ brokenScribe scribeNum = do
164165 let finalizer = do
165166 atomically (writeTVar finalizerCalled True)
166167 throw (ScribeBroken scribeNum)
167− return (Scribe (const (return ())) finalizer, finalizerCalled)
168+ return (Scribe (const (return ())) finalizer (permitItem DebugS), finalizerCalled)
168169
169170
170171-------------------------------------------------------------------------------
katip/test/Katip/Tests/Scribes/Handle.hs+2−2
@@ -92,7 +92,7 @@ setup :: IO (FilePath, Handle, IO (), LogEnv)
9292setup = do
9393 tempDir <- getTemporaryDirectory
9494 (fp, h) <- openTempFile tempDir "katip.log"
95− s <- mkHandleScribe (ColorLog False) h DebugS V3
95+ s <- mkHandleScribe (ColorLog False) h (permitItem DebugS) V3
9696 le <- initLogEnv "katip-test" "test"
9797 le' <- registerScribe "handle" s defaultScribeSettings le
9898 return (fp, h, void (closeScribes le'), le')
@@ -111,7 +111,7 @@ setupFile = do
111111 tempDir <- getTemporaryDirectory
112112 (fp, h) <- openTempFile tempDir "katip.log"
113113 hClose h
114− s <- mkFileScribe fp DebugS V3
114+ s <- mkFileScribe fp (permitItem DebugS) V3
115115 le <- initLogEnv "katip-test" "test"
116116 le' <- registerScribe "handle" s defaultScribeSettings le
117117 return (fp, void (closeScribes le'), le')
stack.yaml+1−1
@@ -1,4 +1,4 @@
1−resolver: lts-11.14
1+resolver: lts-12.22
22packages:
33 - katip
44 - katip-elasticsearch