diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+## 1.4.10
+
+* Export log formatting [#1001](https://github.com/yesodweb/yesod/pull/1001)
+
 ## 1.4.9.1
 
 * Deal better with multiple cookie headers
diff --git a/Yesod/Core.hs b/Yesod/Core.hs
--- a/Yesod/Core.hs
+++ b/Yesod/Core.hs
@@ -30,6 +30,11 @@
     , AuthResult (..)
     , unauthorizedI
       -- * Logging
+    , defaultMakeLogger
+    , defaultMessageLoggerSource
+    , defaultShouldLog
+    , defaultShouldLogIO
+    , formatLogMessage
     , LogLevel (..)
     , logDebug
     , logInfo
diff --git a/Yesod/Core/Class/Yesod.hs b/Yesod/Core/Class/Yesod.hs
--- a/Yesod/Core/Class/Yesod.hs
+++ b/Yesod/Core/Class/Yesod.hs
@@ -26,7 +26,6 @@
 import           Data.List                          (foldl')
 import           Data.List                          (nub)
 import qualified Data.Map                           as Map
-import           Data.Maybe                         (fromMaybe)
 import           Data.Monoid
 import           Data.Text                          (Text)
 import qualified Data.Text                          as T
@@ -207,17 +206,14 @@
     -- method return that already created value. That way, you can use that
     -- same @Logger@ for printing messages during app initialization.
     --
-    -- Default: Sends to stdout and automatically flushes on each write.
+    -- Default: the 'defaultMakeLogger' function.
     makeLogger :: site -> IO Logger
-    makeLogger _ = do
-        loggerSet' <- newStdoutLoggerSet defaultBufSize
-        (getter, _) <- clockDateCacher
-        return $! Logger loggerSet' getter
+    makeLogger _ = defaultMakeLogger
 
     -- | Send a message to the @Logger@ provided by @getLogger@.
     --
-    -- Default implementation: checks if the message should be logged using
-    -- 'shouldLog' and, if so, formats using 'formatLogMessage'.
+    -- Default: the 'defaultMessageLoggerSource' function, using
+    -- 'shouldLogIO' to check whether we should log.
     messageLoggerSource :: site
                         -> Logger
                         -> Loc -- ^ position in source code
@@ -225,10 +221,7 @@
                         -> LogLevel
                         -> LogStr -- ^ message
                         -> IO ()
-    messageLoggerSource a logger loc source level msg = do
-        sl <- shouldLogIO a source level
-        when sl $
-            formatLogMessage (loggerDate logger) loc source level msg >>= loggerPutStr logger
+    messageLoggerSource site = defaultMessageLoggerSource $ shouldLogIO site
 
     -- | Where to Load sripts from. We recommend the default value,
     -- 'BottomOfBody'.  Alternatively use the built in async yepnope loader:
@@ -260,9 +253,9 @@
 
     -- | Should we log the given log source/level combination.
     --
-    -- Default: Logs everything at or above 'logLevel'
+    -- Default: the 'defaultShouldLog' function.
     shouldLog :: site -> LogSource -> LogLevel -> Bool
-    shouldLog _ _ level = level >= LevelInfo
+    shouldLog _ = defaultShouldLog
 
     -- | Should we log the given log source/level combination.
     --
@@ -300,6 +293,51 @@
     yesodWithInternalState _ _ = bracket createInternalState closeInternalState
     {-# INLINE yesodWithInternalState #-}
 
+-- | Default implementation of 'makeLogger'. Sends to stdout and
+-- automatically flushes on each write.
+--
+-- Since 1.4.10
+defaultMakeLogger :: IO Logger
+defaultMakeLogger = do
+    loggerSet' <- newStdoutLoggerSet defaultBufSize
+    (getter, _) <- clockDateCacher
+    return $! Logger loggerSet' getter
+
+-- | Default implementation of 'messageLoggerSource'. Checks if the
+-- message should be logged using the provided function, and if so,
+-- formats using 'formatLogMessage'. You can use 'defaultShouldLogIO'
+-- as the provided function.
+--
+-- Since 1.4.10
+defaultMessageLoggerSource ::
+       (LogSource -> LogLevel -> IO Bool) -- ^ Check whether we should
+                                          -- log this
+    -> Logger
+    -> Loc -- ^ position in source code
+    -> LogSource
+    -> LogLevel
+    -> LogStr -- ^ message
+    -> IO ()
+defaultMessageLoggerSource ckLoggable logger loc source level msg = do
+    loggable <- ckLoggable source level
+    when loggable $
+        formatLogMessage (loggerDate logger) loc source level msg >>=
+        loggerPutStr logger
+
+-- | Default implementation of 'shouldLog'. Logs everything at or
+-- above 'LevelInfo'.
+--
+-- Since 1.4.10
+defaultShouldLog :: LogSource -> LogLevel -> Bool
+defaultShouldLog _ level = level >= LevelInfo
+
+-- | A default implementation of 'shouldLogIO' that can be used with
+-- 'defaultMessageLoggerSource'. Just uses 'defaultShouldLog'.
+--
+-- Since 1.4.10
+defaultShouldLogIO :: LogSource -> LogLevel -> IO Bool
+defaultShouldLogIO a b = return $ defaultShouldLog a b
+
 -- | Default implementation of 'yesodMiddleware'. Adds the response header
 -- \"Vary: Accept, Accept-Language\" and performs authorization checks.
 --
@@ -577,6 +615,9 @@
                     Nothing -> Nothing
                     Just j -> Just $ jelper j
 
+-- | Default formatting for log messages.
+--
+-- Since 1.4.10
 formatLogMessage :: IO ZonedDate
                  -> Loc
                  -> LogSource
diff --git a/yesod-core.cabal b/yesod-core.cabal
--- a/yesod-core.cabal
+++ b/yesod-core.cabal
@@ -1,5 +1,5 @@
 name:            yesod-core
-version:         1.4.9.1
+version:         1.4.10
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
