on master help sign in
Add context helpers for KatipContextT, docs

This is for #14

commit2163df6e727e7a63faa3b03f69650d9b666f3dd2 authorMichael Xavier <michael@michaelxavier.net> dateFri 1 Apr 2016, 10:05 (11 years ago) parent334af8e
5 files changed+146−15
katip/examples/example.hs+1−1 katip/examples/example_lens.hs+1−1 katip/src/Katip.hs+100−7 katip/src/Katip/Core.hs+20−6 katip/src/Katip/Monadic.hs+24
katip/examples/example.hs+1−1
@@ -22,7 +22,7 @@ import Katip
2222-- lens_example for a slightly cleaner and more general pattern.
2323main :: IO ()
2424main = do
25− le <- initLogEnv "main" "production"
25+ le <- initLogEnv "MyApp" "production"
2626 -- We'll set up a scribe that logs to stdout and will only log item
2727 -- fields permitted for Verbosity 2 and will throw out Debug
2828 -- messages entirely. Note that katip provides facilities like
katip/examples/example_lens.hs+1−1
@@ -38,7 +38,7 @@ makeClassy ''MyState
3838-- lens_example for a slightly cleaner and more general pattern.
3939main :: IO ()
4040main = do
41− le <- initLogEnv "main" "production"
41+ le <- initLogEnv "MyApp" "production"
4242 -- We'll set up a scribe that logs to stdout and will only log item
4343 -- fields permitted for Verbosity 2 and will throw out Debug
4444 -- messages entirely. Note that katip provides facilities like
katip/src/Katip.hs+100−7
@@ -26,9 +26,7 @@ module Katip
2626 (
2727
2828 -- * Framework Types
29−
30− Katip (..)
31− , Namespace (..)
29+ Namespace (..)
3230 , Environment (..)
3331 , Severity (..)
3432 , renderSeverity
@@ -41,9 +39,6 @@ module Katip
4139 , PayloadSelection (..)
4240 , Scribe (..)
4341 , LogEnv (..)
44− , KatipContext(..)
45− , AnyLogContext
46− , LogContexts, liftPayload
4742 , SimpleLogPayload, sl
4843
4944 -- ** @lens@-compatible Lenses
@@ -60,7 +55,7 @@ module Katip
6055 , itemLoc
6156 , logEnvHost
6257 , logEnvPid
63− , logEnvNs
58+ , logEnvApp
6459 , logEnvEnv
6560 , logEnvTimer
6661 , logEnvScribes
@@ -79,15 +74,23 @@ module Katip
7974 , LogStr (..)
8075 , logStr, ls, showLS
8176
77+ -- ** 'Katip' LoggingFunctions
78+ -- $katiplogging
79+ , Katip (..)
8280 , logF
8381 , logMsg
8482 , logT
8583 , logItem
8684 , logException
85+ -- ** 'KatipContext' Logging Functions
86+ -- $katipcontextlogging
87+ , KatipContext (..)
8788 , logFM
8889 , logTM
8990 , logItemM
9091 , logExceptionM
92+ , AnyLogContext
93+ , LogContexts, liftPayload
9194
9295 -- * Included Scribes
9396 , mkHandleScribe
@@ -101,6 +104,8 @@ module Katip
101104 -- * KatipContextT - Utility transformer that provides Katip and KatipContext instances
102105 , KatipContextT
103106 , runKatipContextT
107+ , katipAddNamespace
108+ , katipAddContext
104109 ) where
105110
106111-------------------------------------------------------------------------------
@@ -108,3 +113,91 @@ import Katip.Core
108113import Katip.Monadic
109114import Katip.Scribes.Handle
110115-------------------------------------------------------------------------------
116+
117+
118+{- $katiplogging
119+
120+ These logging functions use the basic 'Katip' constraint and thus
121+will require varying degrees of explicit detail such as 'Namespace'
122+and individual log items to be passed in. These can be described as
123+the primitives of Katip logging. If you find yourself making multiple
124+log statements within a logical logging context for your app, you may
125+want to look into the 'KatipContext' family of logging functions like
126+'logFM' and 'logTM'. 'KatipContext' in most applications should be
127+considered the default. Here's an example of the pain point:
128+
129+@
130+doDatabaseThings = do
131+ connId <- getConnectionId
132+ logF (ConnectionIDContext connId) "database" InfoS "Doing database stuff"
133+ \-\- ...
134+ logF (ConnectionIDContext connId) "database" InfoS "Wow, passing in the same context is getting tedious"
135+@
136+
137+Another pain point to look out for is nesting actions that log in
138+eachother. Let's say you were writing a web app. You want to capture
139+some detail such as the user's ID in the logs, but you also want that
140+info to show up in doDatabaseThings' logs so you can associate those
141+two pieces of information:
142+
143+
144+@
145+webRequestHandler = do
146+ uid <- getUserId
147+ logF (UserIDContext uid) "web" InfoS "Starting web request"
148+ doDatabaseThings
149+@
150+
151+In the above example, doDatabaseThings would overwrite that
152+UderIDContext with its own context and namespace. Sometimes this is
153+what you want and that's why 'logF' and other functions which only
154+require 'Katip' exist. If you are interested in combining log
155+contexts and namespaces, see 'KatipContext'.
156+-}
157+
158+
159+{- $katipcontextlogging
160+
161+ These logging functions use the 'KatipContext' constraint which is a
162+superclass of 'Katip' that also has a mechanism for keeping track of
163+the current context and namespace. This means a few things:
164+
165+1. Functions that use 'KatipContext' like 'logFM' and 'logTM' do not
166+require you to pass in 'LogItem's or 'Namespaces', they pull them from
167+the monadic environment.
168+
169+2. It becomes easy to add functions which add namespaces and/or
170+contexts to the current stack of them. You can (and should) make that
171+action scoped to a monadic action so that when it finishes, the
172+previous context and namespace will be automatically restored.
173+
174+
175+'KatipContextT' provides a simple, 'ReaderT'-based implementation of
176+the 'KatipContext' typeclass, and provides 'katipAddContext' and
177+'katipAddNamespace' functions to append to the context for the
178+duration of a block:
179+
180+
181+@
182+main = do
183+ le <- initLogEnv "MyApp" "production"
184+ \-\- set up scribes here
185+ runKatipContext le () "main" $ do
186+ katipAddNamespace "nextlevel" $ do
187+ $(logTM) InfoS "Logs here will have namespace MyApp.main.nextlevel"
188+
189+ katipAddContext TrivialContext $ do
190+ $(logTM) InfoS "Logs here will have context from TrivialContext"
191+
192+ katipAddContext AnotherContext $ do
193+ $(logTM) InfoS "Logs here will have context from TrivialContext *merged with* context from AnotherContext!"
194+
195+ $(logTM) InfoS "Log context restored to () and namespace to MyApp.main"
196+@
197+
198+'katipAddNamespace' and 'katipAddContext' are one-liners, implemented
199+in terms of 'local' from 'MonadReader'. If you have a custom monad
200+transformer stack and want to add your own version of these, check out
201+<https://github.com/Soostone/katip/tree/master/katip/examples these
202+examples>.
203+-}
katip/src/Katip/Core.hs+20−6
@@ -483,12 +483,18 @@ permitItem sev i = _itemSeverity i >= sev
483483data LogEnv = LogEnv {
484484 _logEnvHost :: HostName
485485 , _logEnvPid :: ProcessID
486− , _logEnvNs :: Namespace
486+ , _logEnvApp :: Namespace
487+ -- ^ Name of application. This will typically never change. This
488+ -- field gets prepended to the namespace of your individual log
489+ -- messages. For example, if your app is MyApp and you write a log
490+ -- using "logItem" and the namespace "WebServer", the final
491+ -- namespace will be "MyApp.WebServer"
487492 , _logEnvEnv :: Environment
488493 , _logEnvTimer :: IO UTCTime
489494 -- ^ Action to fetch the timestamp. You can use something like
490495 -- 'AutoUpdate' for high volume logs but note that this may cause
491− -- some output forms to display logs out of order.
496+ -- some output forms to display logs out of order. Alternatively,
497+ -- you could just use 'getCurrentTime'.
492498 , _logEnvScribes :: M.Map Text Scribe
493499 }
494500makeLenses ''LogEnv
@@ -547,8 +553,16 @@ clearScribes = logEnvScribes .~ mempty
547553
548554
549555-------------------------------------------------------------------------------
550−-- | Monads where katip logging actions can be performed
551−class MonadIO m => Katip m where
556+-- | Monads where katip logging actions can be performed. Katip is the
557+-- most basic logging monad. You will typically use this directly if
558+-- you either don't want to use namespaces/contexts heavily or if you
559+-- want to pass in specific contexts and/or namespaces at each log site.
560+--
561+-- For something more powerful, look at the docs for 'KatipContext',
562+-- which keeps a namespace and merged context. You can write simple
563+-- functions that add additional namespacing and merges additional
564+-- context on the fly.
565+class MonadIO m => Katip m where
552566 getLogEnv :: m LogEnv
553567
554568
@@ -623,7 +637,7 @@ logItem a ns loc sev msg = do
623637 LogEnv{..} <- getLogEnv
624638 liftIO $ do
625639 item <- Item
626− <$> pure _logEnvNs
640+ <$> pure _logEnvApp
627641 <*> pure _logEnvEnv
628642 <*> pure sev
629643 <*> (mkThreadIdText <$> myThreadId)
@@ -632,7 +646,7 @@ logItem a ns loc sev msg = do
632646 <*> pure a
633647 <*> pure msg
634648 <*> _logEnvTimer
635− <*> pure (_logEnvNs <> ns)
649+ <*> pure (_logEnvApp <> ns)
636650 <*> pure loc
637651 FT.forM_ (M.elems _logEnvScribes) $ \ (Scribe h) -> h item
638652
katip/src/Katip/Monadic.hs+24
@@ -28,6 +28,8 @@ module Katip.Monadic
2828 -- * KatipContextT - Utility transformer that provides Katip and KatipContext instances
2929 , KatipContextT(..)
3030 , runKatipContextT
31+ , katipAddNamespace
32+ , katipAddContext
3133 , KatipContextTState(..)
3234 ) where
3335
@@ -284,3 +286,25 @@ runKatipContextT :: (LogItem c) => LogEnv -> c -> Namespace -> KatipContextT m a
284286runKatipContextT le ctx ns = flip runReaderT lts . unKatipContextT
285287 where
286288 lts = KatipContextTState le (liftPayload ctx) ns
289+
290+
291+-------------------------------------------------------------------------------
292+katipAddNamespace
293+ :: (Monad m)
294+ => Namespace
295+ -> KatipContextT m a
296+ -> KatipContextT m a
297+katipAddNamespace ns (KatipContextT f) =
298+ KatipContextT (local (\r -> r { ltsNamespace = (ltsNamespace r) <> ns}) f)
299+
300+
301+-------------------------------------------------------------------------------
302+katipAddContext
303+ :: ( LogItem i
304+ , Monad m
305+ )
306+ => i
307+ -> KatipContextT m a
308+ -> KatipContextT m a
309+katipAddContext i (KatipContextT f) =
310+ KatipContextT (local (\r -> r { ltsContext = (ltsContext r) <> liftPayload i}) f)