@@ -53,8 +53,10 @@ import qualified Control.Monad.Trans.State.Strict as Strict (StateT)
53import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT)
53import qualified Control.Monad.Trans.Writer.Strict as Strict (WriterT)
54import Control.Monad.Writer
54import Control.Monad.Writer
55import Data.Aeson
55import Data.Aeson
56+import qualified Data.Foldable as FT
56import qualified Data.HashMap.Strict as HM
57import qualified Data.HashMap.Strict as HM
57import Data.Monoid as M
58import Data.Monoid as M
59+import Data.Sequence as Seq
58import Data.Text (Text)
60import Data.Text (Text)
59import Language.Haskell.TH
61import Language.Haskell.TH
60-------------------------------------------------------------------------------
62-------------------------------------------------------------------------------
@@ -72,17 +74,29 @@ data AnyLogContext where
72-- 'LogContext' instance for combining multiple payload policies. This
74-- 'LogContext' instance for combining multiple payload policies. This
73-- is critical for log contexts deep down in a stack to be able to
75-- is critical for log contexts deep down in a stack to be able to
74-- inject their own context without worrying about other context that
76-- inject their own context without worrying about other context that
75−-- has already been set.
77+-- has already been set. Also note that contexts are treated as a
78+-- sequence and '<>' will be appended to the right hand side of the
79+-- sequence. If there are conflicting keys in the contexts, the /right
80+-- side will take precedence/, which is counter to how monoid works
81+-- for 'Map' and 'HashMap', so bear that in mind. The reasoning is
82+-- that if the user is /sequentially/ adding contexts to the right
83+-- side of the sequence, on conflict the intent is to overwrite with
84+-- the newer value (i.e. the rightmost value).
86+-- Additional note: you should not mappend LogContexts in any sort of
87+-- infinite loop, as it retains all data, so that would be a memory
76−newtype LogContexts = LogContexts [AnyLogContext] deriving (Monoid)
89+newtype LogContexts = LogContexts (Seq AnyLogContext) deriving (Monoid) --TODO: could we pre-encode this and also capture payloadKeys
78instance ToJSON LogContexts where
91instance ToJSON LogContexts where
79 toJSON (LogContexts cs) =
92 toJSON (LogContexts cs) =
93+ -- flip mappend to get right-biased merge
80− Object $ mconcat $ map (\(AnyLogContext v) -> toObject v) cs
94+ Object $ FT.foldr (flip mappend) mempty $ fmap (\(AnyLogContext v) -> toObject v) cs
82instance ToObject LogContexts
96instance ToObject LogContexts
84instance LogItem LogContexts where
98instance LogItem LogContexts where
85− payloadKeys verb (LogContexts vs) = mconcat $ map payloadKeys' vs
99+ payloadKeys verb (LogContexts vs) = FT.foldr (flip mappend) mempty $ fmap payloadKeys' vs
87 -- To ensure AllKeys doesn't leak keys from other values when
101 -- To ensure AllKeys doesn't leak keys from other values when
88 -- combined, we resolve AllKeys to its equivalent SomeKeys
102 -- combined, we resolve AllKeys to its equivalent SomeKeys
@@ -96,7 +110,7 @@ instance LogItem LogContexts where
96-- | Lift a log context into the generic wrapper so that it can
110-- | Lift a log context into the generic wrapper so that it can
97-- combine with the existing log context.
111-- combine with the existing log context.
98liftPayload :: (LogItem a) => a -> LogContexts
112liftPayload :: (LogItem a) => a -> LogContexts
99−liftPayload = LogContexts . (:[]) . AnyLogContext
113+liftPayload = LogContexts . Seq.singleton . AnyLogContext
102-------------------------------------------------------------------------------
116-------------------------------------------------------------------------------