packages feed

monad-logger-aeson 0.3.1.0 → 0.4.0.0

raw patch · 25 files changed

+1641/−2 lines, 25 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Control.Monad.Logger.Aeson: logDebugN :: MonadLogger m => Text -> m ()
+ Control.Monad.Logger.Aeson: logDebugN :: (HasCallStack, MonadLogger m) => Message -> m ()
- Control.Monad.Logger.Aeson: logErrorN :: MonadLogger m => Text -> m ()
+ Control.Monad.Logger.Aeson: logErrorN :: (HasCallStack, MonadLogger m) => Message -> m ()
- Control.Monad.Logger.Aeson: logInfoN :: MonadLogger m => Text -> m ()
+ Control.Monad.Logger.Aeson: logInfoN :: (HasCallStack, MonadLogger m) => Message -> m ()
- Control.Monad.Logger.Aeson: logOtherN :: MonadLogger m => LogLevel -> Text -> m ()
+ Control.Monad.Logger.Aeson: logOtherN :: (HasCallStack, MonadLogger m) => LogLevel -> Message -> m ()
- Control.Monad.Logger.Aeson: logWarnN :: MonadLogger m => Text -> m ()
+ Control.Monad.Logger.Aeson: logWarnN :: (HasCallStack, MonadLogger m) => Message -> m ()

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Change log +## 0.4.0.0++* Replace `log*N` variants reexported from `monad-logger` with custom variants+  that accept `Message` instead of `Text`+ ## 0.3.1.0  * Add `Semigroup` instance for `SeriesElem`
library/Control/Monad/Logger/Aeson.hs view
@@ -31,6 +31,12 @@   , logWarnNS   , logErrorNS   , logOtherNS+    -- ** Convenience aliases+  , logDebugN+  , logInfoN+  , logWarnN+  , logErrorN+  , logOtherN      -- ** Thread context   , withThreadContext@@ -76,6 +82,11 @@   , logErrorCS   , logOtherCS   , logWithoutLoc -- No re-export, as the 'log*NS' here use call stack for loc+  , logDebugN+  , logInfoN+  , logWarnN+  , logErrorN+  , logOtherN   , logDebugNS   , logInfoNS   , logWarnNS@@ -173,6 +184,39 @@ -- @since 0.1.0.0 logErrorCS :: (MonadLogger m) => CallStack -> Message -> m () logErrorCS cs msg = Internal.logCS cs "" LevelError msg++-- | See 'logDebug'.+--+-- This is an alias for 'logDebug' and is provided mainly for symmetry with+-- @monad-logger@.+--+-- @since 0.4.0.0+logDebugN :: (HasCallStack, MonadLogger m) => Message -> m ()+logDebugN = logDebugCS callStack++-- | See 'logDebugN'+--+-- @since 0.4.0.0+logInfoN :: (HasCallStack, MonadLogger m) => Message -> m ()+logInfoN = logInfoCS callStack++-- | See 'logDebugN'+--+-- @since 0.4.0.0+logWarnN :: (HasCallStack, MonadLogger m) => Message -> m ()+logWarnN = logWarnCS callStack++-- | See 'logDebugN'+--+-- @since 0.4.0.0+logErrorN :: (HasCallStack, MonadLogger m) => Message -> m ()+logErrorN = logErrorCS callStack++-- | See 'logDebugN'+--+-- @since 0.4.0.0+logOtherN :: (HasCallStack, MonadLogger m) => LogLevel -> Message -> m ()+logOtherN = logOtherCS callStack  -- | See 'logDebugCS' --
monad-logger-aeson.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           monad-logger-aeson-version:        0.3.1.0+version:        0.4.0.0 synopsis:       JSON logging using monad-logger interface description:    @monad-logger-aeson@ provides structured JSON logging using @monad-logger@'s                 interface.@@ -78,6 +78,10 @@       TestCase.LogDebug.MetadataNoThreadContextYes       TestCase.LogDebug.MetadataYesThreadContextNo       TestCase.LogDebug.MetadataYesThreadContextYes+      TestCase.LogDebugN.MetadataNoThreadContextNo+      TestCase.LogDebugN.MetadataNoThreadContextYes+      TestCase.LogDebugN.MetadataYesThreadContextNo+      TestCase.LogDebugN.MetadataYesThreadContextYes       TestCase.LogDebugNS.MetadataNoThreadContextNo       TestCase.LogDebugNS.MetadataNoThreadContextYes       TestCase.LogDebugNS.MetadataYesThreadContextNo@@ -86,6 +90,10 @@       TestCase.LogError.MetadataNoThreadContextYes       TestCase.LogError.MetadataYesThreadContextNo       TestCase.LogError.MetadataYesThreadContextYes+      TestCase.LogErrorN.MetadataNoThreadContextNo+      TestCase.LogErrorN.MetadataNoThreadContextYes+      TestCase.LogErrorN.MetadataYesThreadContextNo+      TestCase.LogErrorN.MetadataYesThreadContextYes       TestCase.LogErrorNS.MetadataNoThreadContextNo       TestCase.LogErrorNS.MetadataNoThreadContextYes       TestCase.LogErrorNS.MetadataYesThreadContextNo@@ -94,6 +102,10 @@       TestCase.LogInfo.MetadataNoThreadContextYes       TestCase.LogInfo.MetadataYesThreadContextNo       TestCase.LogInfo.MetadataYesThreadContextYes+      TestCase.LogInfoN.MetadataNoThreadContextNo+      TestCase.LogInfoN.MetadataNoThreadContextYes+      TestCase.LogInfoN.MetadataYesThreadContextNo+      TestCase.LogInfoN.MetadataYesThreadContextYes       TestCase.LogInfoNS.MetadataNoThreadContextNo       TestCase.LogInfoNS.MetadataNoThreadContextYes       TestCase.LogInfoNS.MetadataYesThreadContextNo@@ -102,6 +114,10 @@       TestCase.LogOther.MetadataNoThreadContextYes       TestCase.LogOther.MetadataYesThreadContextNo       TestCase.LogOther.MetadataYesThreadContextYes+      TestCase.LogOtherN.MetadataNoThreadContextNo+      TestCase.LogOtherN.MetadataNoThreadContextYes+      TestCase.LogOtherN.MetadataYesThreadContextNo+      TestCase.LogOtherN.MetadataYesThreadContextYes       TestCase.LogOtherNS.MetadataNoThreadContextNo       TestCase.LogOtherNS.MetadataNoThreadContextYes       TestCase.LogOtherNS.MetadataYesThreadContextNo@@ -110,6 +126,10 @@       TestCase.LogWarn.MetadataNoThreadContextYes       TestCase.LogWarn.MetadataYesThreadContextNo       TestCase.LogWarn.MetadataYesThreadContextYes+      TestCase.LogWarnN.MetadataNoThreadContextNo+      TestCase.LogWarnN.MetadataNoThreadContextYes+      TestCase.LogWarnN.MetadataYesThreadContextNo+      TestCase.LogWarnN.MetadataYesThreadContextYes       TestCase.LogWarnNS.MetadataNoThreadContextNo       TestCase.LogWarnNS.MetadataNoThreadContextYes       TestCase.LogWarnNS.MetadataYesThreadContextNo
package.yaml view
@@ -1,5 +1,5 @@ name: monad-logger-aeson-version: '0.3.1.0'+version: '0.4.0.0' github: "jship/monad-logger-aeson" license: MIT license-file: LICENSE.md
test-suite/Test/Control/Monad/Logger/AesonSpec.hs view
@@ -12,6 +12,10 @@ import qualified TestCase.LogDebug.MetadataNoThreadContextYes import qualified TestCase.LogDebug.MetadataYesThreadContextNo import qualified TestCase.LogDebug.MetadataYesThreadContextYes+import qualified TestCase.LogDebugN.MetadataNoThreadContextNo+import qualified TestCase.LogDebugN.MetadataNoThreadContextYes+import qualified TestCase.LogDebugN.MetadataYesThreadContextNo+import qualified TestCase.LogDebugN.MetadataYesThreadContextYes import qualified TestCase.LogDebugNS.MetadataNoThreadContextNo import qualified TestCase.LogDebugNS.MetadataNoThreadContextYes import qualified TestCase.LogDebugNS.MetadataYesThreadContextNo@@ -20,6 +24,10 @@ import qualified TestCase.LogError.MetadataNoThreadContextYes import qualified TestCase.LogError.MetadataYesThreadContextNo import qualified TestCase.LogError.MetadataYesThreadContextYes+import qualified TestCase.LogErrorN.MetadataNoThreadContextNo+import qualified TestCase.LogErrorN.MetadataNoThreadContextYes+import qualified TestCase.LogErrorN.MetadataYesThreadContextNo+import qualified TestCase.LogErrorN.MetadataYesThreadContextYes import qualified TestCase.LogErrorNS.MetadataNoThreadContextNo import qualified TestCase.LogErrorNS.MetadataNoThreadContextYes import qualified TestCase.LogErrorNS.MetadataYesThreadContextNo@@ -28,6 +36,10 @@ import qualified TestCase.LogInfo.MetadataNoThreadContextYes import qualified TestCase.LogInfo.MetadataYesThreadContextNo import qualified TestCase.LogInfo.MetadataYesThreadContextYes+import qualified TestCase.LogInfoN.MetadataNoThreadContextNo+import qualified TestCase.LogInfoN.MetadataNoThreadContextYes+import qualified TestCase.LogInfoN.MetadataYesThreadContextNo+import qualified TestCase.LogInfoN.MetadataYesThreadContextYes import qualified TestCase.LogInfoNS.MetadataNoThreadContextNo import qualified TestCase.LogInfoNS.MetadataNoThreadContextYes import qualified TestCase.LogInfoNS.MetadataYesThreadContextNo@@ -36,6 +48,10 @@ import qualified TestCase.LogOther.MetadataNoThreadContextYes import qualified TestCase.LogOther.MetadataYesThreadContextNo import qualified TestCase.LogOther.MetadataYesThreadContextYes+import qualified TestCase.LogOtherN.MetadataNoThreadContextNo+import qualified TestCase.LogOtherN.MetadataNoThreadContextYes+import qualified TestCase.LogOtherN.MetadataYesThreadContextNo+import qualified TestCase.LogOtherN.MetadataYesThreadContextYes import qualified TestCase.LogOtherNS.MetadataNoThreadContextNo import qualified TestCase.LogOtherNS.MetadataNoThreadContextYes import qualified TestCase.LogOtherNS.MetadataYesThreadContextNo@@ -44,6 +60,10 @@ import qualified TestCase.LogWarn.MetadataNoThreadContextYes import qualified TestCase.LogWarn.MetadataYesThreadContextNo import qualified TestCase.LogWarn.MetadataYesThreadContextYes+import qualified TestCase.LogWarnN.MetadataNoThreadContextNo+import qualified TestCase.LogWarnN.MetadataNoThreadContextYes+import qualified TestCase.LogWarnN.MetadataYesThreadContextNo+import qualified TestCase.LogWarnN.MetadataYesThreadContextYes import qualified TestCase.LogWarnNS.MetadataNoThreadContextNo import qualified TestCase.LogWarnNS.MetadataNoThreadContextYes import qualified TestCase.LogWarnNS.MetadataYesThreadContextNo@@ -106,6 +126,56 @@           runTest $ TestCase.LogOther.MetadataYesThreadContextNo.testCase logFilePath         it "metadata + thread context" $ \logFilePath -> do           runTest $ TestCase.LogOther.MetadataYesThreadContextYes.testCase logFilePath++      describe "logDebugN" $ do+        it "no metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogDebugN.MetadataNoThreadContextNo.testCase logFilePath+        it "no metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogDebugN.MetadataNoThreadContextYes.testCase logFilePath+        it "metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogDebugN.MetadataYesThreadContextNo.testCase logFilePath+        it "metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogDebugN.MetadataYesThreadContextYes.testCase logFilePath++      describe "logInfoN" $ do+        it "no metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogInfoN.MetadataNoThreadContextNo.testCase logFilePath+        it "no metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogInfoN.MetadataNoThreadContextYes.testCase logFilePath+        it "metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogInfoN.MetadataYesThreadContextNo.testCase logFilePath+        it "metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogInfoN.MetadataYesThreadContextYes.testCase logFilePath++      describe "logWarnN" $ do+        it "no metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogWarnN.MetadataNoThreadContextNo.testCase logFilePath+        it "no metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogWarnN.MetadataNoThreadContextYes.testCase logFilePath+        it "metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogWarnN.MetadataYesThreadContextNo.testCase logFilePath+        it "metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogWarnN.MetadataYesThreadContextYes.testCase logFilePath++      describe "logErrorN" $ do+        it "no metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogErrorN.MetadataNoThreadContextNo.testCase logFilePath+        it "no metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogErrorN.MetadataNoThreadContextYes.testCase logFilePath+        it "metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogErrorN.MetadataYesThreadContextNo.testCase logFilePath+        it "metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogErrorN.MetadataYesThreadContextYes.testCase logFilePath++      describe "logOtherN" $ do+        it "no metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogOtherN.MetadataNoThreadContextNo.testCase logFilePath+        it "no metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogOtherN.MetadataNoThreadContextYes.testCase logFilePath+        it "metadata + no thread context" $ \logFilePath -> do+          runTest $ TestCase.LogOtherN.MetadataYesThreadContextNo.testCase logFilePath+        it "metadata + thread context" $ \logFilePath -> do+          runTest $ TestCase.LogOtherN.MetadataYesThreadContextYes.testCase logFilePath        describe "logDebugNS" $ do         it "no metadata + no thread context" $ \logFilePath -> do
+ test-suite/TestCase/LogDebugN/MetadataNoThreadContextNo.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogDebugN.MetadataNoThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson (Loc(..), LogLevel(..), LoggedMessage(..), logDebugN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logDebugN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "debug",+            "location": {+              "package": "main",+              "module": "TestCase.LogDebugN.MetadataNoThreadContextNo",+              "file": "test-suite/TestCase/LogDebugN/MetadataNoThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelDebug+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogDebugN.MetadataNoThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogDebugN/MetadataNoThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogDebugN/MetadataNoThreadContextYes.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogDebugN.MetadataNoThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), logDebugN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logDebugN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "debug",+            "location": {+              "package": "main",+              "module": "TestCase.LogDebugN.MetadataNoThreadContextYes",+              "file": "test-suite/TestCase/LogDebugN/MetadataNoThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelDebug+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogDebugN.MetadataNoThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogDebugN/MetadataNoThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogDebugN/MetadataYesThreadContextNo.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogDebugN.MetadataYesThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson ((.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logDebugN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logDebugN $ "With metadata" :#+          [ "a" .= (42 :: Int)+          , "b" .= ("x" :: String)+          ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "debug",+            "location": {+              "package": "main",+              "module": "TestCase.LogDebugN.MetadataYesThreadContextNo",+              "file": "test-suite/TestCase/LogDebugN/MetadataYesThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelDebug+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogDebugN.MetadataYesThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogDebugN/MetadataYesThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogDebugN/MetadataYesThreadContextYes.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogDebugN.MetadataYesThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logDebugN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logDebugN $ "With metadata" :#+            [ "a" .= (42 :: Int)+            , "b" .= ("x" :: String)+            ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "debug",+            "location": {+              "package": "main",+              "module": "TestCase.LogDebugN.MetadataYesThreadContextYes",+              "file": "test-suite/TestCase/LogDebugN/MetadataYesThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelDebug+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogDebugN.MetadataYesThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogDebugN/MetadataYesThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogErrorN/MetadataNoThreadContextNo.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogErrorN.MetadataNoThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson (Loc(..), LogLevel(..), LoggedMessage(..), logErrorN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logErrorN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "error",+            "location": {+              "package": "main",+              "module": "TestCase.LogErrorN.MetadataNoThreadContextNo",+              "file": "test-suite/TestCase/LogErrorN/MetadataNoThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelError+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogErrorN.MetadataNoThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogErrorN/MetadataNoThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogErrorN/MetadataNoThreadContextYes.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogErrorN.MetadataNoThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), logErrorN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logErrorN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "error",+            "location": {+              "package": "main",+              "module": "TestCase.LogErrorN.MetadataNoThreadContextYes",+              "file": "test-suite/TestCase/LogErrorN/MetadataNoThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelError+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogErrorN.MetadataNoThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogErrorN/MetadataNoThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogErrorN/MetadataYesThreadContextNo.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogErrorN.MetadataYesThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson ((.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logErrorN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logErrorN $ "With metadata" :#+          [ "a" .= (42 :: Int)+          , "b" .= ("x" :: String)+          ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "error",+            "location": {+              "package": "main",+              "module": "TestCase.LogErrorN.MetadataYesThreadContextNo",+              "file": "test-suite/TestCase/LogErrorN/MetadataYesThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelError+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogErrorN.MetadataYesThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogErrorN/MetadataYesThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogErrorN/MetadataYesThreadContextYes.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogErrorN.MetadataYesThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logErrorN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logErrorN $ "With metadata" :#+            [ "a" .= (42 :: Int)+            , "b" .= ("x" :: String)+            ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "error",+            "location": {+              "package": "main",+              "module": "TestCase.LogErrorN.MetadataYesThreadContextYes",+              "file": "test-suite/TestCase/LogErrorN/MetadataYesThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelError+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogErrorN.MetadataYesThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogErrorN/MetadataYesThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogInfoN/MetadataNoThreadContextNo.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogInfoN.MetadataNoThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson (Loc(..), LogLevel(..), LoggedMessage(..), logInfoN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logInfoN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "info",+            "location": {+              "package": "main",+              "module": "TestCase.LogInfoN.MetadataNoThreadContextNo",+              "file": "test-suite/TestCase/LogInfoN/MetadataNoThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelInfo+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogInfoN.MetadataNoThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogInfoN/MetadataNoThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogInfoN/MetadataNoThreadContextYes.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogInfoN.MetadataNoThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), logInfoN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logInfoN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "info",+            "location": {+              "package": "main",+              "module": "TestCase.LogInfoN.MetadataNoThreadContextYes",+              "file": "test-suite/TestCase/LogInfoN/MetadataNoThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelInfo+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogInfoN.MetadataNoThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogInfoN/MetadataNoThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogInfoN/MetadataYesThreadContextNo.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogInfoN.MetadataYesThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson ((.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logInfoN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logInfoN $ "With metadata" :#+          [ "a" .= (42 :: Int)+          , "b" .= ("x" :: String)+          ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "info",+            "location": {+              "package": "main",+              "module": "TestCase.LogInfoN.MetadataYesThreadContextNo",+              "file": "test-suite/TestCase/LogInfoN/MetadataYesThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelInfo+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogInfoN.MetadataYesThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogInfoN/MetadataYesThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogInfoN/MetadataYesThreadContextYes.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogInfoN.MetadataYesThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logInfoN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logInfoN $ "With metadata" :#+            [ "a" .= (42 :: Int)+            , "b" .= ("x" :: String)+            ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "info",+            "location": {+              "package": "main",+              "module": "TestCase.LogInfoN.MetadataYesThreadContextYes",+              "file": "test-suite/TestCase/LogInfoN/MetadataYesThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelInfo+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogInfoN.MetadataYesThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogInfoN/MetadataYesThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogOtherN/MetadataNoThreadContextNo.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogOtherN.MetadataNoThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson (Loc(..), LogLevel(..), LoggedMessage(..), logOtherN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logOtherN (LevelOther "foo") "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "foo",+            "location": {+              "package": "main",+              "module": "TestCase.LogOtherN.MetadataNoThreadContextNo",+              "file": "test-suite/TestCase/LogOtherN/MetadataNoThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelOther "foo"+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogOtherN.MetadataNoThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogOtherN/MetadataNoThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogOtherN/MetadataNoThreadContextYes.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogOtherN.MetadataNoThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), logOtherN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logOtherN (LevelOther "foo") "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "foo",+            "location": {+              "package": "main",+              "module": "TestCase.LogOtherN.MetadataNoThreadContextYes",+              "file": "test-suite/TestCase/LogOtherN/MetadataNoThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelOther "foo"+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogOtherN.MetadataNoThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogOtherN/MetadataNoThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogOtherN/MetadataYesThreadContextNo.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogOtherN.MetadataYesThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson ((.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logOtherN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logOtherN (LevelOther "foo") $ "With metadata" :#+          [ "a" .= (42 :: Int)+          , "b" .= ("x" :: String)+          ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "foo",+            "location": {+              "package": "main",+              "module": "TestCase.LogOtherN.MetadataYesThreadContextNo",+              "file": "test-suite/TestCase/LogOtherN/MetadataYesThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelOther "foo"+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogOtherN.MetadataYesThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogOtherN/MetadataYesThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogOtherN/MetadataYesThreadContextYes.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogOtherN.MetadataYesThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logOtherN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logOtherN (LevelOther "foo") $ "With metadata" :#+            [ "a" .= (42 :: Int)+            , "b" .= ("x" :: String)+            ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "foo",+            "location": {+              "package": "main",+              "module": "TestCase.LogOtherN.MetadataYesThreadContextYes",+              "file": "test-suite/TestCase/LogOtherN/MetadataYesThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelOther "foo"+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogOtherN.MetadataYesThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogOtherN/MetadataYesThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogWarnN/MetadataNoThreadContextNo.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogWarnN.MetadataNoThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson (Loc(..), LogLevel(..), LoggedMessage(..), logWarnN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logWarnN "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "warn",+            "location": {+              "package": "main",+              "module": "TestCase.LogWarnN.MetadataNoThreadContextNo",+              "file": "test-suite/TestCase/LogWarnN/MetadataNoThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelWarn+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogWarnN.MetadataNoThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogWarnN/MetadataNoThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogWarnN/MetadataNoThreadContextYes.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogWarnN.MetadataNoThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), logWarnN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logWarnN $ "No metadata"+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "warn",+            "location": {+              "package": "main",+              "module": "TestCase.LogWarnN.MetadataNoThreadContextYes",+              "file": "test-suite/TestCase/LogWarnN/MetadataNoThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "No metadata"+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelWarn+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogWarnN.MetadataNoThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogWarnN/MetadataNoThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "No metadata"+          , loggedMessageMeta = Internal.emptyKeyMap+          }+    }
+ test-suite/TestCase/LogWarnN/MetadataYesThreadContextNo.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogWarnN.MetadataYesThreadContextNo+  ( testCase+  ) where++import Control.Monad.Logger.Aeson ((.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logWarnN)+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        logWarnN $ "With metadata" :#+          [ "a" .= (42 :: Int)+          , "b" .= ("x" :: String)+          ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "warn",+            "location": {+              "package": "main",+              "module": "TestCase.LogWarnN.MetadataYesThreadContextNo",+              "file": "test-suite/TestCase/LogWarnN/MetadataYesThreadContextNo.hs",+              "line": 19,+              "char": 9+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelWarn+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogWarnN.MetadataYesThreadContextNo"+                , loc_filename = "test-suite/TestCase/LogWarnN/MetadataYesThreadContextNo.hs"+                , loc_start = (19, 9)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext = Internal.emptyKeyMap+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }
+ test-suite/TestCase/LogWarnN/MetadataYesThreadContextYes.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+module TestCase.LogWarnN.MetadataYesThreadContextYes+  ( testCase+  ) where++import Control.Monad.Logger.Aeson+  ( (.=), Loc(..), LogLevel(..), LoggedMessage(..), Message(..), logWarnN, withThreadContext+  )+import Data.Aeson.QQ.Simple (aesonQQ)+import Data.Time (UTCTime(..))+import TestCase (TestCase(..))+import qualified Control.Monad.Logger.Aeson.Internal as Internal+import qualified Data.Time as Time++testCase :: FilePath -> TestCase+testCase logFilePath =+  TestCase+    { actionUnderTest = do+        withThreadContext ["reqId" .= ("74ec1d0b" :: String)] $ do+          logWarnN $ "With metadata" :#+            [ "a" .= (42 :: Int)+            , "b" .= ("x" :: String)+            ]+    , logFilePath+    , expectedValue =+        [aesonQQ|+          {+            "time": "2022-05-07T20:03:54.0000000Z",+            "level": "warn",+            "location": {+              "package": "main",+              "module": "TestCase.LogWarnN.MetadataYesThreadContextYes",+              "file": "test-suite/TestCase/LogWarnN/MetadataYesThreadContextYes.hs",+              "line": 22,+              "char": 11+            },+            "context": {+              "reqId": "74ec1d0b"+            },+            "message": {+              "text": "With metadata",+              "meta": {+                "a": 42,+                "b": "x"+              }+            }+          }+        |]+    , expectedPatch =+        [aesonQQ|+          [+            { "op": "replace", "path": "/time", "value": "2022-05-07T20:03:54.0000000Z" }+          ]+        |]+    , expectedLoggedMessage =+        LoggedMessage+          { loggedMessageTimestamp =+              UTCTime+                { utctDay = Time.fromGregorian 2022 05 07+                , utctDayTime = 72234+                }+          , loggedMessageLevel = LevelWarn+          , loggedMessageLoc =+              Just Loc+                { loc_package = "main"+                , loc_module = "TestCase.LogWarnN.MetadataYesThreadContextYes"+                , loc_filename = "test-suite/TestCase/LogWarnN/MetadataYesThreadContextYes.hs"+                , loc_start = (22, 11)+                , loc_end = (0, 0)+                }+          , loggedMessageLogSource = Nothing+          , loggedMessageThreadContext =+              Internal.keyMapFromList+                [ "reqId" .= ("74ec1d0b" :: String)+                ]+          , loggedMessageText = "With metadata"+          , loggedMessageMeta =+              Internal.keyMapFromList+                [ "a" .= (42 :: Int)+                , "b" .= ("x" :: String)+                ]+          }+    }