diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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`
diff --git a/library/Control/Monad/Logger/Aeson.hs b/library/Control/Monad/Logger/Aeson.hs
--- a/library/Control/Monad/Logger/Aeson.hs
+++ b/library/Control/Monad/Logger/Aeson.hs
@@ -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'
 --
diff --git a/monad-logger-aeson.cabal b/monad-logger-aeson.cabal
--- a/monad-logger-aeson.cabal
+++ b/monad-logger-aeson.cabal
@@ -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
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -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
diff --git a/test-suite/Test/Control/Monad/Logger/AesonSpec.hs b/test-suite/Test/Control/Monad/Logger/AesonSpec.hs
--- a/test-suite/Test/Control/Monad/Logger/AesonSpec.hs
+++ b/test-suite/Test/Control/Monad/Logger/AesonSpec.hs
@@ -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
diff --git a/test-suite/TestCase/LogDebugN/MetadataNoThreadContextNo.hs b/test-suite/TestCase/LogDebugN/MetadataNoThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogDebugN/MetadataNoThreadContextNo.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogDebugN/MetadataNoThreadContextYes.hs b/test-suite/TestCase/LogDebugN/MetadataNoThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogDebugN/MetadataNoThreadContextYes.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogDebugN/MetadataYesThreadContextNo.hs b/test-suite/TestCase/LogDebugN/MetadataYesThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogDebugN/MetadataYesThreadContextNo.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogDebugN/MetadataYesThreadContextYes.hs b/test-suite/TestCase/LogDebugN/MetadataYesThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogDebugN/MetadataYesThreadContextYes.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogErrorN/MetadataNoThreadContextNo.hs b/test-suite/TestCase/LogErrorN/MetadataNoThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogErrorN/MetadataNoThreadContextNo.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogErrorN/MetadataNoThreadContextYes.hs b/test-suite/TestCase/LogErrorN/MetadataNoThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogErrorN/MetadataNoThreadContextYes.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogErrorN/MetadataYesThreadContextNo.hs b/test-suite/TestCase/LogErrorN/MetadataYesThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogErrorN/MetadataYesThreadContextNo.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogErrorN/MetadataYesThreadContextYes.hs b/test-suite/TestCase/LogErrorN/MetadataYesThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogErrorN/MetadataYesThreadContextYes.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogInfoN/MetadataNoThreadContextNo.hs b/test-suite/TestCase/LogInfoN/MetadataNoThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogInfoN/MetadataNoThreadContextNo.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogInfoN/MetadataNoThreadContextYes.hs b/test-suite/TestCase/LogInfoN/MetadataNoThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogInfoN/MetadataNoThreadContextYes.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogInfoN/MetadataYesThreadContextNo.hs b/test-suite/TestCase/LogInfoN/MetadataYesThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogInfoN/MetadataYesThreadContextNo.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogInfoN/MetadataYesThreadContextYes.hs b/test-suite/TestCase/LogInfoN/MetadataYesThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogInfoN/MetadataYesThreadContextYes.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogOtherN/MetadataNoThreadContextNo.hs b/test-suite/TestCase/LogOtherN/MetadataNoThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogOtherN/MetadataNoThreadContextNo.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogOtherN/MetadataNoThreadContextYes.hs b/test-suite/TestCase/LogOtherN/MetadataNoThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogOtherN/MetadataNoThreadContextYes.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogOtherN/MetadataYesThreadContextNo.hs b/test-suite/TestCase/LogOtherN/MetadataYesThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogOtherN/MetadataYesThreadContextNo.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogOtherN/MetadataYesThreadContextYes.hs b/test-suite/TestCase/LogOtherN/MetadataYesThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogOtherN/MetadataYesThreadContextYes.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogWarnN/MetadataNoThreadContextNo.hs b/test-suite/TestCase/LogWarnN/MetadataNoThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogWarnN/MetadataNoThreadContextNo.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogWarnN/MetadataNoThreadContextYes.hs b/test-suite/TestCase/LogWarnN/MetadataNoThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogWarnN/MetadataNoThreadContextYes.hs
@@ -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
+          }
+    }
diff --git a/test-suite/TestCase/LogWarnN/MetadataYesThreadContextNo.hs b/test-suite/TestCase/LogWarnN/MetadataYesThreadContextNo.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogWarnN/MetadataYesThreadContextNo.hs
@@ -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)
+                ]
+          }
+    }
diff --git a/test-suite/TestCase/LogWarnN/MetadataYesThreadContextYes.hs b/test-suite/TestCase/LogWarnN/MetadataYesThreadContextYes.hs
new file mode 100644
--- /dev/null
+++ b/test-suite/TestCase/LogWarnN/MetadataYesThreadContextYes.hs
@@ -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)
+                ]
+          }
+    }
