diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+# Version 1.3
+
+* The `MonadThrow` instance for `DiT` doesn't log exceptions automatically any
+  more. This is because otherwise catching and re-throwing exceptions in
+  downstream code, by default, ends up logging the same exception more than
+  once.
+
+* A `throw` function behaving as the previous `MonadThrow` instance for `DiT`
+  was introduced.
+
+* Documentation improvements.
+
+
 # Version 1.2
 
 * The `MonadMask` constraint added in Version 1.1 is gone, effecively
diff --git a/di-monad.cabal b/di-monad.cabal
--- a/di-monad.cabal
+++ b/di-monad.cabal
@@ -1,5 +1,5 @@
 name: di-monad
-version: 1.2
+version: 1.3
 author: Renzo Carbonara
 maintainer: renλren.zone
 copyright: Renzo Carbonara 2017-2018
diff --git a/lib/Di/Monad.hs b/lib/Di/Monad.hs
--- a/lib/Di/Monad.hs
+++ b/lib/Di/Monad.hs
@@ -47,6 +47,7 @@
  , flush
  , push
  , filter
+ , throw
  , onException
 
  , -- * DiT
@@ -237,7 +238,7 @@
 {-# INLINE runDiT' #-}
 
 -- | Lift a monad morphism from @m@ to @n@ to a monad morphism from
--- @'DiT' level path msg m@ to @'DiT' n@.
+-- @'DiT' level path msg m@ to @'DiT' level path msg n@.
 --
 -- Notice that 'DiT' itself is not a functor in the category of monads,
 -- so it can't be an instance of 'Control.Monad.Morph.MFunctor' from the
@@ -321,15 +322,11 @@
     DiT (ReaderT (\di -> Reader.local f (gma di)))
   {-# INLINE local #-}
 
--- | Throw an 'Ex.Exception', but not without logging it first according to the
--- rules established by 'onException', and further restricted by the rules
--- established by 'filter'.
+-- | Throw an 'Ex.Exception' from the underlying @m@, without logging it.
 --
--- WARNING: Note that when `m` is `STM`, or ultimately runs on 'STM', then
--- 'Ex.throwM' *will not log* the exception, just throw it. This might change in
--- the future if we figure out how to make it work safely.
+-- If you want to log the 'Ex.Exception' as you throw it, use 'throw' instead.
 instance Ex.MonadThrow m => Ex.MonadThrow (DiT level path msg m) where
-  throwM e = ask >>= \di -> Di.throw' natSTM di e
+  throwM e = DiT (ReaderT (\_ -> Ex.throwM e))
   {-# INLINE throwM #-}
 
 instance Ex.MonadCatch m => Ex.MonadCatch (DiT level path msg m) where
@@ -516,15 +513,15 @@
 -- time when this 'log' function was called, rather than the time when the log
 -- message is printed in the future.
 --
--- /Note regarding exceptions:/ Any exception thrown by 'natSTM' will be
--- thrown here. /Synchronous/ exceptions that happen due to failures in the
--- actual committing of the log message are handled by attempting to log the
--- message to 'IO.stderr' as a fallback if possible. /Asynchronous/ exceptions
--- happening as part of the committing process will be thrown in a different
--- thread, and are not not explicitly handled. /Pure/ exceptions originating
--- from the 'filter' function will be thrown here. In practical terms, this
--- means that unless you know what you are doing, you should just call 'log''
--- without worrying about it ever throwing exceptions.
+-- /Note regarding exceptions:/ Any exception thrown by 'natSTM' will be thrown
+-- here. /Synchronous/ exceptions that happen due to failures in the actual
+-- committing of the log message, which itself is performed in a different
+-- thread, are ignored (they should be handled in the function passed to 'new'
+-- instead). If an asynchronous exception kills the logging thread, then you
+-- will synchronously get 'Di.ExceptionInLoggingWorker' here, but by the time
+-- that happens, that same exception will have already already been thrown
+-- asynchronously to this same thread anyway, so unless you did something funny
+-- to recover from that exception, you will have died already.
 log :: MonadDi level path msg m => level -> msg -> m ()
 log l = \m -> ask >>= \di -> Di.log' natSTM di l m
 {-# INLINE log #-}
@@ -537,6 +534,9 @@
 -- asynchronously, meaning there might be log messages still waiting to be
 -- processed. A call to 'flush' will block until all pending log messages have
 -- been processed.
+--
+-- Please see 'log' to understand how exceptions behave in this function (hint:
+-- they behave unsurprisingly).
 flush :: MonadDi level path msg m => m ()
 flush = Di.flush' natSTM =<< ask
 {-# INLINABLE flush #-}
@@ -583,9 +583,9 @@
 push p = local (Di.push p)
 {-# INLINE push #-}
 
--- | Within the passed given @m a@, exceptions thrown with 'Ex.throwM' could
--- could be logged as a @msg@ with a particular @level@ if both the passed in
--- function returns 'Just', and 'filter' so allows it afterwards.
+-- | Within the passed given @m a@, exceptions thrown with 'throw' could could
+-- be logged as a @msg@ with a particular @level@ if both the passed in function
+-- returns 'Just', and 'filter' so allows it afterwards.
 --
 -- If the given function returns 'Nothing', then no logging is performed.
 --
@@ -609,4 +609,19 @@
   -> m a
   -> m a
 onException f = local (Di.onException f)
+
+-- | Throw an 'Ex.Exception', but not without logging it first according to the
+-- rules established by 'onException', and further restricted by the rules
+-- established by 'filter'.
+--
+-- If the exception doesn't need to be logged, according to the policy set with
+-- 'onException', then this function behaves just as
+-- 'Control.Concurrent.STM.throwSTM'.
+--
+-- WARNING: Note that when `m` is `STM`, or ultimately runs on 'STM', then
+-- 'throw' /will not log/ the exception, just throw it. This might change in
+-- the future if we figure out how to make it work safely.
+throw :: (MonadDi level path msg m, Ex.Exception e) => e -> m a
+throw e = ask >>= \di -> Di.throw' natSTM di e
+{-# INLINE throw #-}
 
