diff --git a/log.cabal b/log.cabal
--- a/log.cabal
+++ b/log.cabal
@@ -1,5 +1,5 @@
 name:                log
-version:             0.5.6
+version:             0.5.7
 synopsis:            Structured logging solution with multiple backends
 
 description:         A library that provides a way to record structured
diff --git a/src/Log/Backend/ElasticSearch.hs b/src/Log/Backend/ElasticSearch.hs
--- a/src/Log/Backend/ElasticSearch.hs
+++ b/src/Log/Backend/ElasticSearch.hs
@@ -62,7 +62,7 @@
                         -> IO r
 withElasticSearchLogger conf randGen act = do
   logger <- elasticSearchLogger conf randGen
-  (act logger) `finally` (do { waitForLogger logger; shutdownLogger logger; })
+  withLogger logger act
 
 {-# DEPRECATED elasticSearchLogger "Use 'withElasticSearchLogger' instead!" #-}
 
diff --git a/src/Log/Backend/PostgreSQL.hs b/src/Log/Backend/PostgreSQL.hs
--- a/src/Log/Backend/PostgreSQL.hs
+++ b/src/Log/Backend/PostgreSQL.hs
@@ -32,7 +32,7 @@
 withPgLogger :: ConnectionSourceM IO -> (Logger -> IO r) -> IO r
 withPgLogger cs act = do
   logger <- pgLogger cs
-  (act logger) `finally` (do { waitForLogger logger; shutdownLogger logger; })
+  withLogger logger act
 
 {-# DEPRECATED pgLogger "Use 'withPgLogger' instead!" #-}
 
diff --git a/src/Log/Backend/StandardOutput.hs b/src/Log/Backend/StandardOutput.hs
--- a/src/Log/Backend/StandardOutput.hs
+++ b/src/Log/Backend/StandardOutput.hs
@@ -6,7 +6,6 @@
   ) where
 
 import Prelude
-import Control.Exception
 import qualified Data.Text.IO as T
 import System.IO
 
@@ -19,7 +18,7 @@
 withSimpleStdOutLogger :: (Logger -> IO r) -> IO r
 withSimpleStdOutLogger act = do
   logger <- stdoutLogger
-  (act logger) `finally` (do { waitForLogger logger; shutdownLogger logger; })
+  withLogger logger act
 
 {-# DEPRECATED simpleStdoutLogger "Use 'withSimpleStdOutLogger'" #-}
 
diff --git a/src/Log/Backend/StandardOutput/Bulk.hs b/src/Log/Backend/StandardOutput/Bulk.hs
--- a/src/Log/Backend/StandardOutput/Bulk.hs
+++ b/src/Log/Backend/StandardOutput/Bulk.hs
@@ -4,7 +4,6 @@
     bulkStdoutLogger
   ) where
 
-import Control.Exception
 import Prelude
 import qualified Data.Text.IO as T
 
@@ -17,7 +16,7 @@
 withBulkStdOutLogger :: (Logger -> IO r) -> IO r
 withBulkStdOutLogger act = do
   logger <- bulkStdoutLogger
-  (act logger) `finally` (do { waitForLogger logger; shutdownLogger logger; })
+  withLogger logger act
 
 {-# DEPRECATED bulkStdoutLogger "Use 'withBulkStdOutLogger' instead!" #-}
 
diff --git a/src/Log/Class.hs b/src/Log/Class.hs
--- a/src/Log/Class.hs
+++ b/src/Log/Class.hs
@@ -58,18 +58,18 @@
 
 -- | Log a message and its associated data using current time as the
 -- event time and the 'LogAttention' log level.
-logAttention :: MonadLog m => T.Text -> Value -> m ()
-logAttention = logNow LogAttention
+logAttention :: (MonadLog m, ToJSON a) => T.Text -> a -> m ()
+logAttention msg = logNow LogAttention msg . toJSON
 
 -- | Log a message and its associated data using current time as the
 -- event time and the 'LogInfo' log level.
-logInfo :: MonadLog m => T.Text -> Value -> m ()
-logInfo = logNow LogInfo
+logInfo :: (MonadLog m, ToJSON a) => T.Text -> a -> m ()
+logInfo msg = logNow LogInfo msg . toJSON
 
 -- | Log a message and its associated data using current time as the
 -- event time and the 'LogTrace' log level.
-logTrace :: MonadLog m => T.Text -> Value -> m ()
-logTrace = logNow LogTrace
+logTrace :: (MonadLog m, ToJSON a) => T.Text -> a -> m ()
+logTrace msg = logNow LogTrace msg . toJSON
 
 -- | Like 'logAttention', but without any additional associated data.
 logAttention_ :: MonadLog m => T.Text -> m ()
diff --git a/src/Log/Logger.hs b/src/Log/Logger.hs
--- a/src/Log/Logger.hs
+++ b/src/Log/Logger.hs
@@ -3,6 +3,7 @@
     Logger
   , mkLogger
   , mkBulkLogger
+  , withLogger
   , execLogger
   , waitForLogger
   , shutdownLogger
@@ -30,8 +31,9 @@
 -- | Start an asynchronous logger thread that consumes all queued
 -- messages once per second. To make sure that the messages get
 -- written out in the presence of exceptions, use high-level wrappers
--- like 'withElasticSearchLogger' or 'withBulkStdOutLogger' instead of
--- this function directly.
+-- like 'withLogger', 'Log.Backend.ElasticSearch.withElasticSearchLogger' or
+-- 'Log.Backend.StandardOutput.Bulk.withBulkStdOutLogger'
+-- instead of this function directly.
 --
 -- Note: some messages can be lost when the main thread shuts down
 -- without making sure that all logger threads have written out all
@@ -50,9 +52,9 @@
 --
 -- main :: IO ()
 -- main = do
---    logger <- elasticSearchLogger
---    a <- async (withElasticSearchLogger $ \logger ->
---                runLogT "main" logger $ logTrace_ "foo")
+--    logger \<- 'Log.Backend.ElasticSearch.elasticSearchLogger'
+--    a \<- 'Control.Concurrent.Async.async' ('Log.Backend.ElasticSearch.withElasticSearchLogger' $ \\logger ->
+--                'Log.Monad.runLogT' "main" logger $ 'Log.Class.logTrace_' "foo")
 --    -- Main thread exits without waiting for the child
 --    -- to finish and without giving the child a chance
 --    -- to do proper cleanup.
@@ -65,10 +67,10 @@
 --
 -- main :: IO ()
 -- main = do
---    logger <- elasticSearchLogger
---    a <- async (withElasticSearchLogger $ \logger ->
---                runLogT "main" logger $ logTrace_ "foo")
---    wait a
+--    logger \<- 'Log.Backend.ElasticSearch.elasticSearchLogger'
+--    a \<- 'Control.Concurrent.Async.async' ('Log.Backend.ElasticSearch.withElasticSearchLogger' $ \\logger ->
+--                'Log.Monad.runLogT' "main" logger $ 'Log.Class.logTrace_' "foo")
+--    'Control.Concurrent.Async.wait' a
 --    -- Main thread waits for the child to finish, giving
 --    -- it a chance to shut down properly. This works even
 --    -- in the presence of exceptions in the child thread.
@@ -76,6 +78,13 @@
 mkBulkLogger :: T.Text -> ([LogMessage] -> IO ()) -> IO () -> IO Logger
 mkBulkLogger = mkLoggerImpl
   newSQueueIO isEmptySQueue readSQueue writeSQueue (threadDelay 1000000)
+
+-- | 'bracket' like execution of an 'IO' action, verifying all messages
+-- are properly logged. See 'mkBulkLogger'.
+withLogger :: Logger -> (Logger -> IO r) -> IO r
+withLogger logger act = act logger `finally` cleanup
+  where
+    cleanup = waitForLogger logger >> shutdownLogger logger
 
 ----------------------------------------
 
