diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,10 @@
+# log-base-0.7.2 (2017-06-19)
+* mkBulkLogger now uses a bounded queue to interact with the logger thread.
+
+# log-base-0.7.1 (2017-03-16)
+* Added a few MTL class instances (#28).
+
+# log-base-0.7 (2016-11-25)
+* Initial release (split from the log package).
+* Improved documentation (#22).
+* Implement 'toEncoding' directly in 'ToJSON' instances (#21).
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# log-base [![Hackage version](https://img.shields.io/hackage/v/log-base.svg?label=Hackage)](https://hackage.haskell.org/package/log-base) [![Build Status](https://secure.travis-ci.org/scrive/log.svg?branch=master)](http://travis-ci.org/scrive/log)
+
+Base package for the `log` library. Includes only the standard output
+back end. Use this package in conjunction with `log-elasticsearch` or
+`log-postgres`, depending on which back end you need. Use the `log`
+library if you need all back ends.
diff --git a/log-base.cabal b/log-base.cabal
--- a/log-base.cabal
+++ b/log-base.cabal
@@ -1,5 +1,5 @@
 name:                log-base
-version:             0.7.1
+version:             0.7.1.1
 synopsis:            Structured logging solution (base package)
 
 description:         A library that provides a way to record structured log
@@ -21,7 +21,8 @@
 category:            System
 build-type:          Simple
 cabal-version:       >=1.10
-tested-with:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1
+extra-source-files:  CHANGELOG.md, README.md
+tested-with:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2
 
 Source-repository head
   Type:     git
diff --git a/src/Log.hs b/src/Log.hs
--- a/src/Log.hs
+++ b/src/Log.hs
@@ -27,7 +27,7 @@
     module Log.Class
     -- | 'Log.Data.LogMessage' and 'Log.Data.LogLevel' data definitions.
   , module Log.Data
-    -- | 'Log.Logger.Logger' objects used to perform logging operations in ''Log.Monad.LogT'.
+    -- | 'Log.Logger.Logger' objects used to perform logging operations in 'Log.Monad.LogT'.
   , module Log.Logger
     -- | 'Log.Monad.LogT' monad transformer that adds logging capabilities to
     -- the underlying monad.
diff --git a/src/Log/Data.hs b/src/Log/Data.hs
--- a/src/Log/Data.hs
+++ b/src/Log/Data.hs
@@ -55,7 +55,7 @@
 data LogMessage = LogMessage {
   -- | Component of an application.
   lmComponent :: !T.Text
-  -- | Aplication log domain.
+  -- | Application log domain.
 , lmDomain    :: ![T.Text]
   -- | Time of the logged event.
 , lmTime      :: !UTCTime
diff --git a/src/Log/Logger.hs b/src/Log/Logger.hs
--- a/src/Log/Logger.hs
+++ b/src/Log/Logger.hs
@@ -28,11 +28,12 @@
   name exec (return ())
 
 -- | 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 'withLogger', 'Log.Backend.ElasticSearch.withElasticSearchLogger' or
--- 'Log.Backend.StandardOutput.Bulk.withBulkStdOutLogger'
--- instead of this function directly.
+-- messages once per second. Uses a bounded queue internally to avoid
+-- space leaks. To make sure that the messages get written out in the
+-- presence of exceptions, use high-level wrappers 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
@@ -76,32 +77,48 @@
 -- @
 mkBulkLogger :: T.Text -> ([LogMessage] -> IO ()) -> IO () -> IO Logger
 mkBulkLogger = mkLoggerImpl
-  newSQueueIO isEmptySQueue readSQueue writeSQueue (threadDelay 1000000)
+  (newSBQueueIO sbDefaultCapacity) isEmptySBQueue readSBQueue writeSBQueue
+  (threadDelay 1000000)
 
 ----------------------------------------
 
--- | A simple STM based queue.
-newtype SQueue a = SQueue (TVar [a])
+-- | A simple STM based bounded queue.
+data SBQueue a = SBQueue !(TVar [a]) !(TVar Int) !Int
 
--- | Create an instance of 'SQueue'.
-newSQueueIO :: IO (SQueue a)
-newSQueueIO = SQueue <$> newTVarIO []
+-- | Default capacity of a 'SBQueue'. This corresponds to
+-- approximately 200 MiB memory residency when the queue is full.
+sbDefaultCapacity :: Int
+sbDefaultCapacity = 1000000
 
--- | Check if an 'SQueue' is empty.
-isEmptySQueue :: SQueue a -> STM Bool
-isEmptySQueue (SQueue queue) = null <$> readTVar queue
+-- | Create an instance of 'SBQueue' with a given capacity.
+newSBQueueIO :: Int -> IO (SBQueue a)
+newSBQueueIO capacity = SBQueue <$> newTVarIO [] <*> newTVarIO 0 <*> pure capacity
 
--- | Read all the values stored in an 'SQueue'.
-readSQueue :: SQueue a -> STM [a]
-readSQueue (SQueue queue) = do
+-- | Check if an 'SBQueue' is empty.
+isEmptySBQueue :: SBQueue a -> STM Bool
+isEmptySBQueue (SBQueue queue count _capacity) = do
+  isEmpty  <- null <$> readTVar queue
+  numElems <- readTVar count
+  assert (if isEmpty then numElems == 0 else numElems > 0) $
+    return isEmpty
+
+-- | Read all the values stored in an 'SBQueue'.
+readSBQueue :: SBQueue a -> STM [a]
+readSBQueue (SBQueue queue count _capacity) = do
   elems <- readTVar queue
   when (null elems) retry
   writeTVar queue []
+  writeTVar count 0
   return $ reverse elems
 
--- | Write a value to an 'SQueue'.
-writeSQueue :: SQueue a -> a -> STM ()
-writeSQueue (SQueue queue) a = modifyTVar queue (a :)
+-- | Write a value to an 'SBQueue'.
+writeSBQueue :: SBQueue a -> a -> STM ()
+writeSBQueue (SBQueue queue count capacity) a = do
+  numElems <- readTVar count
+  if numElems < capacity
+    then do modifyTVar queue (a :)
+            modifyTVar count (+1)
+    else return ()
 
 ----------------------------------------
 
