Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions katip/src/Katip.hs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ module Katip
-- *** Temporarily Changing Logging Behavior
katipAddNamespace,
katipAddContext,
katipSetContext,
katipNoLogging,

-- * Included Scribes
Expand Down
62 changes: 57 additions & 5 deletions katip/src/Katip/Monadic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ module Katip.Monadic
runKatipContextT,
katipAddNamespace,
katipAddContext,
katipSetContext,
katipSetContextKeyFun,
KatipContextTState (..),
NoLoggingT (..),
askLoggerIO,
Expand Down Expand Up @@ -123,17 +125,38 @@ data AnyLogContext where
-- Additional note: you should not mappend LogContexts in any sort of
-- infinite loop, as it retains all data, so that would be a memory
-- leak.
newtype LogContexts = LogContexts (Seq AnyLogContext) deriving (Monoid, Semigroup)
--
-- PPS It's perfectly safe to use 'katipSetContext' in infinite loops
-- because it uses a map backing instead of a sequence.
data LogContexts = LogContexts
{ logContextsSetContexts :: !(KM.KeyMap Value)
, logContextsSetContextsKeys :: Verbosity -> KM.KeyMap Value -> PayloadSelection
, logContextsAddedContexts :: (Seq AnyLogContext)
}

instance Semigroup LogContexts where
(<>) (LogContexts a1 _ a2) (LogContexts preffered1 preferedFun b2) =
LogContexts { logContextsSetContexts = (preffered1 <> a1),
logContextsSetContextsKeys = preferedFun,
logContextsAddedContexts = (a2 <> b2)
}

allKeys :: Verbosity -> KM.KeyMap Value -> PayloadSelection
allKeys _ _ = AllKeys

instance Monoid LogContexts where
mempty = LogContexts mempty allKeys mempty

instance ToJSON LogContexts where
toJSON (LogContexts cs) =
toJSON (LogContexts keymap _ cs) =
-- flip mappend to get right-biased merge
Object $ FT.foldr (flip mappend) mempty $ fmap (\(AnyLogContext v) -> toObject v) cs
Object $ FT.foldr (flip mappend) mempty $ keymap :<| (fmap (\(AnyLogContext v) -> toObject v) cs)

instance ToObject LogContexts

instance LogItem LogContexts where
payloadKeys verb (LogContexts vs) = FT.foldr (flip mappend) mempty $ fmap payloadKeys' vs
payloadKeys verb (LogContexts keymap keyFun vs) = FT.foldr (flip mappend) mempty $
firstKeys :<| fmap payloadKeys' vs
where
-- To ensure AllKeys doesn't leak keys from other values when
-- combined, we resolve AllKeys to its equivalent SomeKeys
Expand All @@ -142,6 +165,11 @@ instance LogItem LogContexts where
AllKeys -> SomeKeys $ toKeys $ toObject v
x -> x

firstKeys = case keyFun verb keymap of
AllKeys -> SomeKeys $ toKeys keymap
x -> x


#if MIN_VERSION_aeson(2, 0, 0)
toKeys :: KM.KeyMap v -> [Text]
toKeys = fmap K.toText . KM.keys
Expand All @@ -155,7 +183,7 @@ toKeys = HM.keys
-- | Lift a log context into the generic wrapper so that it can
-- combine with the existing log context.
liftPayload :: (LogItem a) => a -> LogContexts
liftPayload = LogContexts . Seq.singleton . AnyLogContext
liftPayload x = mempty {logContextsAddedContexts = Seq.singleton $ AnyLogContext x}

-------------------------------------------------------------------------------

Expand Down Expand Up @@ -471,6 +499,30 @@ katipAddContext ::
m a
katipAddContext i = localKatipContext (<> (liftPayload i))


-- | Like 'katipAddContext' but doesn't leak memory under recursion.
katipSetContext ::
( ToJSON val,
KatipContext m
) =>
Key ->
val ->
m a ->
m a
katipSetContext key val = localKatipContext (\x -> x{ logContextsSetContexts = KM.insert key (toJSON val) $ logContextsSetContexts x})

-- | This provides an alternative way of doing key filtering for 'katipSetContext',
-- because it doesn't supports typeclass based filtering.
-- This is a bit more powerful however because it allows you to change
-- the filter at runtime.
-- It'd allow you to do more restrictive filtering for example when
-- the system is under load.
--
-- this *only* works for 'katipSetContext'
katipSetContextKeyFun :: KatipContext m => (Verbosity -> KM.KeyMap Value -> PayloadSelection) -> m a -> m a
katipSetContextKeyFun fun =
localKatipContext (\x -> x{ logContextsSetContextsKeys = fun})

newtype NoLoggingT m a = NoLoggingT
{ runNoLoggingT :: m a
}
Expand Down