diff --git a/Yesod/Content.hs b/Yesod/Content.hs
--- a/Yesod/Content.hs
+++ b/Yesod/Content.hs
@@ -28,6 +28,8 @@
     , typeOctet
       -- * Utilities
     , simpleContentType
+      -- * Evaluation strategy
+    , DontFullyEvaluate (..)
       -- * Representations
     , ChooseRep
     , HasReps (..)
@@ -60,18 +62,15 @@
 import Data.Monoid (mempty)
 
 import Text.Hamlet (Html)
-#if MIN_VERSION_blaze_html(0, 5, 0)
 import Text.Blaze.Html.Renderer.Utf8 (renderHtmlBuilder)
-#else
-import Text.Blaze.Renderer.Utf8 (renderHtmlBuilder)
-#endif
 import Data.String (IsString (fromString))
 import Network.Wai (FilePart)
 import Data.Conduit (Source, ResourceT, Flush)
 
-data Content = ContentBuilder Builder (Maybe Int) -- ^ The content and optional content length.
-             | ContentSource (Source (ResourceT IO) (Flush Builder))
-             | ContentFile FilePath (Maybe FilePart)
+data Content = ContentBuilder !Builder !(Maybe Int) -- ^ The content and optional content length.
+             | ContentSource !(Source (ResourceT IO) (Flush Builder))
+             | ContentFile !FilePath !(Maybe FilePart)
+             | ContentDontEvaluate !Content
 
 -- | Zero-length enumerator.
 emptyContent :: Content
@@ -239,3 +238,15 @@
 -- | Format as per RFC 822.
 formatRFC822 :: UTCTime -> T.Text
 formatRFC822 = T.pack . formatTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S %z"
+
+-- | Prevents a response body from being fully evaluated before sending the
+-- request.
+--
+-- Since 1.1.0
+newtype DontFullyEvaluate a = DontFullyEvaluate a
+
+instance HasReps a => HasReps (DontFullyEvaluate a) where
+    chooseRep (DontFullyEvaluate a) = fmap (fmap (fmap ContentDontEvaluate)) $ chooseRep a
+
+instance ToContent a => ToContent (DontFullyEvaluate a) where
+    toContent (DontFullyEvaluate a) = ContentDontEvaluate $ toContent a
diff --git a/Yesod/Core.hs b/Yesod/Core.hs
--- a/Yesod/Core.hs
+++ b/Yesod/Core.hs
@@ -10,6 +10,7 @@
     , breadcrumbs
       -- * Types
     , Approot (..)
+    , FileUpload (..)
       -- * Utitlities
     , maybeAuthorized
     , widgetToPageContent
@@ -20,8 +21,6 @@
     , unauthorizedI
       -- * Logging
     , LogLevel (..)
-    , formatLogMessage
-    , fileLocationToString
     , logDebug
     , logInfo
     , logWarn
@@ -59,38 +58,7 @@
 import Yesod.Widget
 import Yesod.Message
 
-import Language.Haskell.TH.Syntax
-import qualified Language.Haskell.TH.Syntax as TH
-import Data.Text (Text)
-
-logTH :: LogLevel -> Q Exp
-logTH level =
-    [|messageLoggerHandler $(qLocation >>= liftLoc) $(TH.lift level)|]
-  where
-    liftLoc :: Loc -> Q Exp
-    liftLoc (Loc a b c d e) = [|Loc $(TH.lift a) $(TH.lift b) $(TH.lift c) $(TH.lift d) $(TH.lift e)|]
-
--- | Generates a function that takes a 'Text' and logs a 'LevelDebug' message. Usage:
---
--- > $(logDebug) "This is a debug log message"
-logDebug :: Q Exp
-logDebug = logTH LevelDebug
-
--- | See 'logDebug'
-logInfo :: Q Exp
-logInfo = logTH LevelInfo
--- | See 'logDebug'
-logWarn :: Q Exp
-logWarn = logTH LevelWarn
--- | See 'logDebug'
-logError :: Q Exp
-logError = logTH LevelError
-
--- | Generates a function that takes a 'Text' and logs a 'LevelOther' message. Usage:
---
--- > $(logOther "My new level") "This is a log message"
-logOther :: Text -> Q Exp
-logOther = logTH . LevelOther
+import Control.Monad.Logger
 
 -- | Return an 'Unauthorized' value, with the given i18n message.
 unauthorizedI :: RenderMessage master msg => msg -> GHandler sub master AuthResult
diff --git a/Yesod/Dispatch.hs b/Yesod/Dispatch.hs
--- a/Yesod/Dispatch.hs
+++ b/Yesod/Dispatch.hs
@@ -28,7 +28,7 @@
     , WaiSubsite (..)
     ) where
 
-import Data.Functor   ((<$>))
+import Control.Applicative ((<$>), (<*>))
 import Prelude hiding (exp)
 import Yesod.Internal.Core
 import Yesod.Handler hiding (lift)
@@ -53,6 +53,7 @@
 import Yesod.Routes.TH
 import Yesod.Content (chooseRep)
 import Yesod.Routes.Parse
+import System.Log.FastLogger (Logger)
 
 type Texts = [Text]
 
@@ -60,7 +61,7 @@
 -- is used for creating sites, /not/ subsites. See 'mkYesodSub' for the latter.
 -- Use 'parseRoutes' to create the 'Resource's.
 mkYesod :: String -- ^ name of the argument datatype
-        -> [Resource String]
+        -> [ResourceTree String]
         -> Q [Dec]
 mkYesod name = fmap (uncurry (++)) . mkYesodGeneral name [] [] False
 
@@ -71,7 +72,7 @@
 -- be embedded in other sites.
 mkYesodSub :: String -- ^ name of the argument datatype
            -> Cxt
-           -> [Resource String]
+           -> [ResourceTree String]
            -> Q [Dec]
 mkYesodSub name clazzes =
     fmap (uncurry (++)) . mkYesodGeneral name' rest clazzes True
@@ -82,28 +83,28 @@
 -- your handlers elsewhere. For example, this is the only way to break up a
 -- monolithic file into smaller parts. Use this function, paired with
 -- 'mkYesodDispatch', to do just that.
-mkYesodData :: String -> [Resource String] -> Q [Dec]
+mkYesodData :: String -> [ResourceTree String] -> Q [Dec]
 mkYesodData name res = mkYesodDataGeneral name [] False res
 
-mkYesodSubData :: String -> Cxt -> [Resource String] -> Q [Dec]
+mkYesodSubData :: String -> Cxt -> [ResourceTree String] -> Q [Dec]
 mkYesodSubData name clazzes res = mkYesodDataGeneral name clazzes True res
 
-mkYesodDataGeneral :: String -> Cxt -> Bool -> [Resource String] -> Q [Dec]
+mkYesodDataGeneral :: String -> Cxt -> Bool -> [ResourceTree String] -> Q [Dec]
 mkYesodDataGeneral name clazzes isSub res = do
     let (name':rest) = words name
     (x, _) <- mkYesodGeneral name' rest clazzes isSub res
     let rname = mkName $ "resources" ++ name
     eres <- lift res
-    let y = [ SigD rname $ ListT `AppT` (ConT ''Resource `AppT` ConT ''String)
+    let y = [ SigD rname $ ListT `AppT` (ConT ''ResourceTree `AppT` ConT ''String)
             , FunD rname [Clause [] (NormalB eres) []]
             ]
     return $ x ++ y
 
 -- | See 'mkYesodData'.
-mkYesodDispatch :: String -> [Resource String] -> Q [Dec]
+mkYesodDispatch :: String -> [ResourceTree String] -> Q [Dec]
 mkYesodDispatch name = fmap snd . mkYesodGeneral name [] [] False
 
-mkYesodSubDispatch :: String -> Cxt -> [Resource String] -> Q [Dec]
+mkYesodSubDispatch :: String -> Cxt -> [ResourceTree String] -> Q [Dec]
 mkYesodSubDispatch name clazzes = fmap snd . mkYesodGeneral name' rest clazzes True 
   where (name':rest) = words name
 
@@ -111,7 +112,7 @@
                -> [String]
                -> Cxt -- ^ classes
                -> Bool -- ^ is subsite?
-               -> [Resource String]
+               -> [ResourceTree String]
                -> Q ([Dec], [Dec])
 mkYesodGeneral name args clazzes isSub resS = do
     let args' = map mkName args
@@ -119,7 +120,13 @@
     let res = map (fmap parseType) resS
     renderRouteDec <- mkRenderRouteInstance arg res
 
-    disp <- mkDispatchClause [|yesodRunner|] [|yesodDispatch|] [|fmap chooseRep|] res
+    let logger = mkName "logger"
+    Clause pat body decs <- mkDispatchClause
+        [|yesodRunner $(return $ VarE logger)|]
+        [|yesodDispatch $(return $ VarE logger)|]
+        [|fmap chooseRep|]
+        res
+    let disp = Clause (VarP logger : pat) body decs
     let master = mkName "master"
     let ctx = if isSub
                 then ClassP (mkName "Yesod") [VarT master] : clazzes
@@ -130,7 +137,7 @@
     let yesodDispatch' =
             InstanceD ctx ytyp [FunD (mkName "yesodDispatch") [disp]]
 
-    return (renderRouteDec : masterTypSyns, [yesodDispatch'])
+    return (renderRouteDec ++ masterTypSyns, [yesodDispatch'])
   where
     name' = mkName name
     masterTypSyns
@@ -160,23 +167,24 @@
 toWaiAppPlain :: ( Yesod master
                  , YesodDispatch master master
                  ) => master -> IO W.Application
-toWaiAppPlain a = toWaiApp' a <$> makeSessionBackend a
+toWaiAppPlain a = toWaiApp' a <$> getLogger a <*> makeSessionBackend a
 
 
 toWaiApp' :: ( Yesod master
              , YesodDispatch master master
              )
           => master
+          -> Logger
           -> Maybe (SessionBackend master)
           -> W.Application
-toWaiApp' y sb env =
+toWaiApp' y logger sb env =
     case cleanPath y $ W.pathInfo env of
         Left pieces -> sendRedirect y pieces env
         Right pieces ->
-            yesodDispatch y y id app404 handler405 method pieces sb env
+            yesodDispatch logger y y id app404 handler405 method pieces sb env
   where
-    app404 = yesodRunner notFound y y Nothing id
-    handler405 route = yesodRunner badMethod y y (Just route) id
+    app404 = yesodRunner logger notFound y y Nothing id
+    handler405 route = yesodRunner logger badMethod y y (Just route) id
     method = decodeUtf8With lenientDecode $ W.requestMethod env
 
 sendRedirect :: Yesod master => master -> [Text] -> W.Application
@@ -202,4 +210,4 @@
     renderRoute (WaiSubsiteRoute ps qs) = (ps, qs)
 
 instance YesodDispatch WaiSubsite master where
-    yesodDispatch _master (WaiSubsite app) _tomaster _404 _405 _method _pieces _session = app
+    yesodDispatch _logger _master (WaiSubsite app) _tomaster _404 _405 _method _pieces _session = app
diff --git a/Yesod/Handler.hs b/Yesod/Handler.hs
--- a/Yesod/Handler.hs
+++ b/Yesod/Handler.hs
@@ -138,11 +138,7 @@
 import qualified Network.HTTP.Types as H
 
 import Text.Hamlet
-#if MIN_VERSION_blaze_html(0, 5, 0)
 import qualified Text.Blaze.Html.Renderer.Text as RenderText
-#else
-import qualified Text.Blaze.Renderer.Text as RenderText
-#endif
 import qualified Data.Text as T
 import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
@@ -150,6 +146,7 @@
 
 import qualified Data.Map as Map
 import qualified Data.ByteString as S
+import qualified Data.ByteString.Lazy as L
 import Network.Wai.Parse (parseHttpAccept)
 
 import Yesod.Content
@@ -159,19 +156,19 @@
 import qualified Network.Wai.Parse as NWP
 import Data.Monoid (mappend, mempty, Endo (..))
 import qualified Data.ByteString.Char8 as S8
+import Data.ByteString (ByteString)
 import Data.CaseInsensitive (CI)
 import qualified Data.CaseInsensitive as CI
-import Blaze.ByteString.Builder (toByteString)
+import Blaze.ByteString.Builder (toByteString, toLazyByteString, fromLazyByteString)
 import Data.Text (Text)
 import Yesod.Message (RenderMessage (..))
 
-#if MIN_VERSION_blaze_html(0, 5, 0)
 import Text.Blaze.Html (toHtml, preEscapedToMarkup)
 #define preEscapedText preEscapedToMarkup
-#else
-import Text.Blaze (toHtml, preEscapedText)
-#endif
 
+import System.Log.FastLogger
+import Control.Monad.Logger
+
 import qualified Yesod.Internal.Cache as Cache
 import Yesod.Internal.Cache (mkCacheKey, CacheKey)
 import Data.Typeable (Typeable)
@@ -181,6 +178,8 @@
 import Control.Monad.Trans.Resource
 import Control.Monad.Base
 import Yesod.Routes.Class
+import Data.Word (Word64)
+import Language.Haskell.TH.Syntax (Loc)
 
 class YesodSubRoute s y where
     fromSubRoute :: s -> y -> Route s -> Route y
@@ -193,6 +192,8 @@
     , handlerRender   :: Route master -> [(Text, Text)] -> Text
     , handlerToMaster :: Route sub -> Route master
     , handlerState    :: I.IORef GHState
+    , handlerUpload   :: Word64 -> FileUpload
+    , handlerLog      :: Loc -> LogLevel -> LogStr -> IO ()
     }
 
 handlerSubData :: (Route sub -> Route master)
@@ -322,22 +323,36 @@
 
 runRequestBody :: GHandler s m RequestBodyContents
 runRequestBody = do
+    hd <- ask
+    let getUpload = handlerUpload hd
+        len = reqBodySize $ handlerRequest hd
+        upload = getUpload len
     x <- get
     case ghsRBC x of
         Just rbc -> return rbc
         Nothing -> do
             rr <- waiRequest
-            rbc <- lift $ rbHelper rr
+            rbc <- lift $ rbHelper upload rr
             put x { ghsRBC = Just rbc }
             return rbc
 
-rbHelper :: W.Request -> ResourceT IO RequestBodyContents
-rbHelper req =
-    (map fix1 *** map fix2) <$> (NWP.parseRequestBody NWP.lbsBackEnd req)
+rbHelper :: FileUpload -> W.Request -> ResourceT IO RequestBodyContents
+rbHelper upload =
+    case upload of
+        FileUploadMemory s -> rbHelper' s mkFileInfoLBS
+        FileUploadDisk s -> rbHelper' s mkFileInfoFile
+        FileUploadSource s -> rbHelper' s mkFileInfoSource
+
+rbHelper' :: NWP.BackEnd x
+          -> (Text -> Text -> x -> FileInfo)
+          -> W.Request
+          -> ResourceT IO ([(Text, Text)], [(Text, FileInfo)])
+rbHelper' backend mkFI req =
+    (map fix1 *** map fix2) <$> (NWP.parseRequestBody backend req)
   where
     fix1 = go *** go
     fix2 (x, NWP.FileInfo a b c) =
-        (go x, FileInfo (go a) (go b) c)
+        (go x, mkFI (go a) (go b) c)
     go = decodeUtf8With lenientDecode
 
 -- | Get the sub application argument.
@@ -378,8 +393,10 @@
            -> (Route sub -> Route master)
            -> master
            -> sub
+           -> (Word64 -> FileUpload)
+           -> (Loc -> LogLevel -> LogStr -> IO ())
            -> YesodApp
-runHandler handler mrender sroute tomr master sub =
+runHandler handler mrender sroute tomr master sub upload log' =
   YesodApp $ \eh rr cts initSession -> do
     let toErrorHandler e =
             case fromException e of
@@ -400,6 +417,8 @@
             , handlerRender = mrender
             , handlerToMaster = tomr
             , handlerState = istate
+            , handlerUpload = upload
+            , handlerLog = log'
             }
     contents' <- catch (fmap Right $ unGHandler handler hd)
         (\e -> return $ Left $ maybe (HCError $ toErrorHandler e) id
@@ -420,7 +439,10 @@
     case contents of
         HCContent status a -> do
             (ct, c) <- liftIO $ a cts
-            return $ YARPlain status (appEndo headers []) ct c finalSession
+            ec' <- liftIO $ evaluateContent c
+            case ec' of
+                Left e -> handleError e
+                Right c' -> return $ YARPlain status (appEndo headers []) ct c' finalSession
         HCError e -> handleError e
         HCRedirect status loc -> do
             let hs = Header "Location" (encodeUtf8 loc) : appEndo headers []
@@ -440,6 +462,15 @@
                 finalSession
         HCWai r -> return $ YARWai r
 
+evaluateContent :: Content -> IO (Either ErrorResponse Content)
+evaluateContent (ContentBuilder b mlen) = Control.Exception.handle f $ do
+    let lbs = toLazyByteString b
+    L.length lbs `seq` return (Right $ ContentBuilder (fromLazyByteString lbs) mlen)
+  where
+    f :: SomeException -> IO (Either ErrorResponse Content)
+    f = return . Left . InternalError . T.pack . show
+evaluateContent c = return (Right c)
+
 safeEh :: ErrorResponse -> YesodApp
 safeEh er = YesodApp $ \_ _ _ session -> do
     liftIO $ hPutStrLn stderr $ "Error handler errored out: " ++ show er
@@ -772,6 +803,8 @@
 handlerToYAR :: (HasReps a, HasReps b)
              => master -- ^ master site foundation
              -> sub    -- ^ sub site foundation
+             -> (Word64 -> FileUpload)
+             -> (Loc -> LogLevel -> LogStr -> IO ())
              -> (Route sub -> Route master)
              -> (Route master -> [(Text, Text)] -> Text) -- route renderer
              -> (ErrorResponse -> GHandler sub master a)
@@ -780,28 +813,31 @@
              -> SessionMap
              -> GHandler sub master b
              -> ResourceT IO YesodAppResult
-handlerToYAR y s toMasterRoute render errorHandler rr murl sessionMap h =
+handlerToYAR y s upload log' toMasterRoute render errorHandler rr murl sessionMap h =
     unYesodApp ya eh' rr types sessionMap
   where
-    ya = runHandler h render murl toMasterRoute y s
-    eh' er = runHandler (errorHandler' er) render murl toMasterRoute y s
+    ya = runHandler h render murl toMasterRoute y s upload log'
+    eh' er = runHandler (errorHandler' er) render murl toMasterRoute y s upload log'
     types = httpAccept $ reqWaiRequest rr
     errorHandler' = localNoCurrent . errorHandler
 
-yarToResponse :: YesodAppResult -> [(CI H.Ascii, H.Ascii)] -> W.Response
+yarToResponse :: YesodAppResult -> [(CI ByteString, ByteString)] -> W.Response
 yarToResponse (YARWai a) _ = a
 yarToResponse (YARPlain s hs _ c _) extraHeaders =
-    case c of
-        ContentBuilder b mlen ->
-            let hs' = maybe finalHeaders finalHeaders' mlen
-             in W.ResponseBuilder s hs' b
-        ContentFile fp p -> W.ResponseFile s finalHeaders fp p
-        ContentSource body -> W.ResponseSource s finalHeaders body
+    go c
   where
     finalHeaders = extraHeaders ++ map headerToPair hs
     finalHeaders' len = ("Content-Length", S8.pack $ show len)
                       : finalHeaders
 
+    go (ContentBuilder b mlen) =
+        W.ResponseBuilder s hs' b
+      where
+        hs' = maybe finalHeaders finalHeaders' mlen
+    go (ContentFile fp p) = W.ResponseFile s finalHeaders fp p
+    go (ContentSource body) = W.ResponseSource s finalHeaders body
+    go (ContentDontEvaluate c') = go c'
+
 httpAccept :: W.Request -> [ContentType]
 httpAccept = parseHttpAccept
            . fromMaybe mempty
@@ -810,7 +846,7 @@
 
 -- | Convert Header to a key/value pair.
 headerToPair :: Header
-             -> (CI H.Ascii, H.Ascii)
+             -> (CI ByteString, ByteString)
 headerToPair (AddCookie sc) =
     ("Set-Cookie", toByteString $ renderSetCookie $ sc)
 headerToPair (DeleteCookie key path) =
@@ -842,6 +878,7 @@
 redirectToPost url = do
     urlText <- toTextUrl url
     hamletToRepHtml [hamlet|
+$newline never
 $doctype 5
 
 <html>
@@ -936,3 +973,8 @@
     register = lift . register
     release = lift . release
     resourceMask = lift . resourceMask
+
+instance MonadLogger (GHandler sub master) where
+    monadLoggerLog a b c = do
+        hd <- ask
+        liftIO $ handlerLog hd a b (toLogStr c)
diff --git a/Yesod/Internal.hs b/Yesod/Internal.hs
--- a/Yesod/Internal.hs
+++ b/Yesod/Internal.hs
@@ -27,7 +27,8 @@
     , tokenKey
     ) where
 
-import Text.Hamlet (HtmlUrl, hamlet, Html)
+import Text.Hamlet (HtmlUrl, Html)
+import Text.Blaze.Html (toHtml)
 import Text.Julius (JavascriptUrl)
 import Data.Monoid (Monoid (..), Last)
 import Data.List (nub)
@@ -41,8 +42,8 @@
 import Data.String (IsString)
 import qualified Data.Map as Map
 import Data.Text.Lazy.Builder (Builder)
-import Network.HTTP.Types (Ascii)
 import Web.Cookie (SetCookie (..))
+import Data.ByteString (ByteString)
 
 -- | Responses to indicate some form of an error occurred. These are different
 -- from 'SpecialResponse' in that they allow for custom error pages.
@@ -59,8 +60,8 @@
 -- | Headers to be added to a 'Result'.
 data Header =
     AddCookie SetCookie
-    | DeleteCookie Ascii Ascii
-    | Header Ascii Ascii
+    | DeleteCookie ByteString ByteString
+    | Header ByteString ByteString
     deriving (Eq, Show)
 
 langKey :: IsString a => a
@@ -69,10 +70,8 @@
 data Location url = Local url | Remote Text
     deriving (Show, Eq)
 locationToHtmlUrl :: Location url -> HtmlUrl url
-locationToHtmlUrl (Local url) = [hamlet|\@{url}
-|]
-locationToHtmlUrl (Remote s) = [hamlet|\#{s}
-|]
+locationToHtmlUrl (Local url) render = toHtml $ render url []
+locationToHtmlUrl (Remote s) _ = toHtml s
 
 newtype UniqueList x = UniqueList ([x] -> [x])
 instance Monoid (UniqueList x) where
@@ -100,13 +99,14 @@
 type CssBuilderUrl a = (a -> [(Text, Text)] -> Text) -> Builder
 
 data GWData a = GWData
-    !(Body a)
-    !(Last Title)
-    !(UniqueList (Script a))
-    !(UniqueList (Stylesheet a))
-    !(Map.Map (Maybe Text) (CssBuilderUrl a)) -- media type
-    !(Maybe (JavascriptUrl a))
-    !(Head a)
+    { gwdBody :: !(Body a)
+    , gwdTitle :: !(Last Title)
+    , gwdScripts :: !(UniqueList (Script a))
+    , gwdStylesheets :: !(UniqueList (Stylesheet a))
+    , gwdCss :: !(Map.Map (Maybe Text) (CssBuilderUrl a)) -- media type
+    , gwdJavascript :: !(Maybe (JavascriptUrl a))
+    , gwdHead :: !(Head a)
+    }
 instance Monoid (GWData a) where
     mempty = GWData mempty mempty mempty mempty mempty mempty mempty
     mappend (GWData a1 a2 a3 a4 a5 a6 a7)
diff --git a/Yesod/Internal/Core.hs b/Yesod/Internal/Core.hs
--- a/Yesod/Internal/Core.hs
+++ b/Yesod/Internal/Core.hs
@@ -20,11 +20,6 @@
     , defaultErrorHandler
       -- * Data types
     , AuthResult (..)
-      -- * Logging
-    , LogLevel (..)
-    , formatLogMessage
-    , fileLocationToString
-    , messageLoggerHandler
       -- * Sessions
     , SessionBackend (..)
     , defaultClientSessionBackend
@@ -40,6 +35,7 @@
     , yesodRender
     , resolveApproot
     , Approot (..)
+    , FileUpload (..)
     ) where
 
 import Yesod.Content
@@ -47,6 +43,7 @@
 
 import Yesod.Routes.Class
 
+import Data.Word (Word64)
 import Control.Arrow ((***))
 import Control.Monad (forM)
 import Yesod.Widget
@@ -80,26 +77,19 @@
 import Data.List (foldl')
 import qualified Network.HTTP.Types as H
 import Web.Cookie (SetCookie (..))
-import qualified Data.Text.Lazy as TL
-import qualified Data.Text.Lazy.IO
-import qualified Data.Text.Lazy.Builder as TB
-import Language.Haskell.TH.Syntax (Loc (..), Lift (..))
-#if MIN_VERSION_blaze_html(0, 5, 0)
+import Language.Haskell.TH.Syntax (Loc (..))
 import Text.Blaze (preEscapedToMarkup)
-#else
-import Text.Blaze (preEscapedLazyText)
-#endif
 import Data.Aeson (Value (Array, String))
 import Data.Aeson.Encode (encode)
 import qualified Data.Vector as Vector
 import Network.Wai.Middleware.Gzip (GzipSettings, def)
+import Network.Wai.Parse (tempFileBackEnd, lbsBackEnd)
 import qualified Paths_yesod_core
 import Data.Version (showVersion)
-
-#if MIN_VERSION_blaze_html(0, 5, 0)
-preEscapedLazyText :: TL.Text -> Html
-preEscapedLazyText = preEscapedToMarkup
-#endif
+import System.Log.FastLogger (Logger, mkLogger, loggerDate, LogStr (..), loggerPutStr)
+import Control.Monad.Logger (LogLevel (LevelInfo, LevelOther))
+import System.Log.FastLogger.Date (ZonedDate)
+import System.IO (stdout)
 
 yesodVersion :: String
 yesodVersion = showVersion Paths_yesod_core.version
@@ -109,7 +99,8 @@
 class YesodDispatch sub master where
     yesodDispatch
         :: Yesod master
-        => master
+        => Logger
+        -> master
         -> sub
         -> (Route sub -> Route master)
         -> (Maybe (SessionBackend master) -> W.Application) -- ^ 404 handler
@@ -120,7 +111,8 @@
         -> W.Application
 
     yesodRunner :: Yesod master
-                => GHandler sub master ChooseRep
+                => Logger
+                -> GHandler sub master ChooseRep
                 -> master
                 -> sub
                 -> Maybe (Route sub)
@@ -170,6 +162,7 @@
         p <- widgetToPageContent w
         mmsg <- getMessage
         hamletToRepHtml [hamlet|
+$newline never
 $doctype 5
 
 <html>
@@ -290,21 +283,28 @@
     cookieDomain _ = Nothing
 
     -- | Maximum allowed length of the request body, in bytes.
-    maximumContentLength :: a -> Maybe (Route a) -> Int
+    --
+    -- Default: 2 megabytes.
+    maximumContentLength :: a -> Maybe (Route a) -> Word64
     maximumContentLength _ _ = 2 * 1024 * 1024 -- 2 megabytes
 
-    -- | Send a message to the log. By default, prints to stdout.
+    -- | Returns a @Logger@ to use for log messages.
+    --
+    -- Default: Sends to stdout and automatically flushes on each write.
+    getLogger :: a -> IO Logger
+    getLogger _ = mkLogger True stdout
+
+    -- | Send a message to the @Logger@ provided by @getLogger@.
     messageLogger :: a
+                  -> Logger
                   -> Loc -- ^ position in source code
                   -> LogLevel
-                  -> Text -- ^ message
+                  -> LogStr -- ^ message
                   -> IO ()
-    messageLogger a loc level msg =
+    messageLogger a logger loc level msg =
         if level < logLevel a
             then return ()
-            else
-                formatLogMessage loc level msg >>=
-                Data.Text.Lazy.IO.putStrLn
+            else formatLogMessage (loggerDate logger) loc level msg >>= loggerPutStr logger
 
     -- | The logging level in place for this application. Any messages below
     -- this level will simply be ignored.
@@ -332,38 +332,37 @@
         key <- CS.getKey CS.defaultKeyFile
         return $ Just $ clientSessionBackend key 120
 
-
-messageLoggerHandler :: Yesod m
-                     => Loc -> LogLevel -> Text -> GHandler s m ()
-messageLoggerHandler loc level msg = do
-    y <- getYesod
-    liftIO $ messageLogger y loc level msg
-
-data LogLevel = LevelDebug | LevelInfo | LevelWarn | LevelError | LevelOther Text
-    deriving (Eq, Show, Read, Ord)
-
-instance Lift LogLevel where
-    lift LevelDebug = [|LevelDebug|]
-    lift LevelInfo = [|LevelInfo|]
-    lift LevelWarn = [|LevelWarn|]
-    lift LevelError = [|LevelError|]
-    lift (LevelOther x) = [|LevelOther $ T.pack $(lift $ T.unpack x)|]
+    -- | How to store uploaded files.
+    --
+    -- Default: Whe nthe request body is greater than 50kb, store in a temp
+    -- file. Otherwise, store in memory.
+    fileUpload :: a
+               -> Word64 -- ^ request body size
+               -> FileUpload
+    fileUpload _ size
+        | size > 50000 = FileUploadDisk tempFileBackEnd
+        | otherwise = FileUploadMemory lbsBackEnd
 
-formatLogMessage :: Loc
+formatLogMessage :: IO ZonedDate
+                 -> Loc
                  -> LogLevel
-                 -> Text -- ^ message
-                 -> IO TL.Text
-formatLogMessage loc level msg = do
-    now <- getCurrentTime
-    return $ TB.toLazyText $
-        TB.fromText (T.pack $ show now)
-        `mappend` TB.fromText " ["
-        `mappend` TB.fromText (T.pack $ drop 5 $ show level)
-        `mappend` TB.fromText "] "
-        `mappend` TB.fromText msg
-        `mappend` TB.fromText " @("
-        `mappend` TB.fromText (T.pack $ fileLocationToString loc)
-        `mappend` TB.fromText ") "
+                 -> LogStr -- ^ message
+                 -> IO [LogStr]
+formatLogMessage getdate loc level msg = do
+    now <- getdate
+    return
+        [ LB now
+        , LB " ["
+        , LS $
+            case level of
+                LevelOther t -> T.unpack t
+                _ -> drop 5 $ show level
+        , LB "] "
+        , msg
+        , LB " @("
+        , LS $ fileLocationToString loc
+        , LB ")\n"
+        ]
 
 -- taken from file-location package
 -- turn the TH Loc loaction information into a human readable string
@@ -376,31 +375,26 @@
     char = show . snd . loc_start
 
 defaultYesodRunner :: Yesod master
-                   => GHandler sub master ChooseRep
+                   => Logger
+                   -> GHandler sub master ChooseRep
                    -> master
                    -> sub
                    -> Maybe (Route sub)
                    -> (Route sub -> Route master)
                    -> Maybe (SessionBackend master)
                    -> W.Application
-defaultYesodRunner _ master _ murl toMaster _ req
-    | maximumContentLength master (fmap toMaster murl) < len =
+defaultYesodRunner logger handler master sub murl toMasterRoute msb req
+  | maximumContentLength master (fmap toMasterRoute murl) < len =
         return $ W.responseLBS
             (H.Status 413 "Too Large")
             [("Content-Type", "text/plain")]
             "Request body too large to be processed."
-  where
-    len = fromMaybe 0 $ lookup "content-length" (W.requestHeaders req) >>= readMay
-    readMay s =
-        case reads $ S8.unpack s of
-            [] -> Nothing
-            (x, _):_ -> Just x
-defaultYesodRunner handler master sub murl toMasterRoute msb req = do
+  | otherwise = do
     now <- liftIO getCurrentTime
     let dontSaveSession _ _ = return []
     (session, saveSession) <- liftIO $
         maybe (return ([], dontSaveSession)) (\sb -> sbLoadSession sb master req now) msb
-    rr <- liftIO $ parseWaiRequest req session (isJust msb)
+    rr <- liftIO $ parseWaiRequest req session (isJust msb) len
     let h = {-# SCC "h" #-} do
           case murl of
             Nothing -> handler
@@ -420,7 +414,8 @@
                 handler
     let sessionMap = Map.fromList . filter ((/=) tokenKey . fst) $ session
     let ra = resolveApproot master req
-    yar <- handlerToYAR master sub toMasterRoute
+    let log' = messageLogger master logger
+    yar <- handlerToYAR master sub (fileUpload master) log' toMasterRoute
         (yesodRender master ra) errorHandler rr murl sessionMap h
     extraHeaders <- case yar of
         (YARPlain _ _ ct _ newSess) -> do
@@ -432,6 +427,12 @@
             return $ ("Content-Type", ct) : map headerToPair sessionHeaders
         _ -> return []
     return $ yarToResponse yar extraHeaders
+  where
+    len = fromMaybe 0 $ lookup "content-length" (W.requestHeaders req) >>= readMay
+    readMay s =
+        case reads $ S8.unpack s of
+            [] -> Nothing
+            (x, _):_ -> Just x
 
 data AuthResult = Authorized | AuthenticationRequired | Unauthorized Text
     deriving (Eq, Show, Read)
@@ -478,18 +479,21 @@
     let path' = TE.decodeUtf8With TEE.lenientDecode $ W.rawPathInfo r
     applyLayout' "Not Found"
         [hamlet|
+$newline never
 <h1>Not Found
 <p>#{path'}
 |]
 defaultErrorHandler (PermissionDenied msg) =
     applyLayout' "Permission Denied"
         [hamlet|
+$newline never
 <h1>Permission denied
 <p>#{msg}
 |]
 defaultErrorHandler (InvalidArgs ia) =
     applyLayout' "Invalid Arguments"
         [hamlet|
+$newline never
 <h1>Invalid Arguments
 <ul>
     $forall msg <- ia
@@ -498,12 +502,14 @@
 defaultErrorHandler (InternalError e) =
     applyLayout' "Internal Server Error"
         [hamlet|
+$newline never
 <h1>Internal Server Error
 <p>#{e}
 |]
 defaultErrorHandler (BadMethod m) =
     applyLayout' "Bad Method"
         [hamlet|
+$newline never
 <h1>Method Not Supported
 <p>Method "#{S8.unpack m}" not supported
 |]
@@ -521,7 +527,7 @@
     return $ if x == Authorized then Just r else Nothing
 
 jsToHtml :: Javascript -> Html
-jsToHtml (Javascript b) = preEscapedLazyText $ toLazyText b
+jsToHtml (Javascript b) = preEscapedToMarkup $ toLazyText b
 
 jelper :: JavascriptUrl url -> HtmlUrl url
 jelper = fmap jsToHtml
@@ -549,7 +555,7 @@
            $ encodeUtf8 rendered
         return (mmedia,
             case x of
-                Nothing -> Left $ preEscapedLazyText rendered
+                Nothing -> Left $ preEscapedToMarkup rendered
                 Just y -> Right $ either id (uncurry render) y)
     jsLoc <-
         case jscript of
@@ -563,6 +569,7 @@
     -- the asynchronous loader means your page doesn't have to wait for all the js to load
     let (mcomplete, asyncScripts) = asyncHelper render scripts jscript jsLoc
         regularScriptLoad = [hamlet|
+$newline never
 $forall s <- scripts
     ^{mkScriptTag s}
 $maybe j <- jscript
@@ -573,6 +580,7 @@
 |]
 
         headAll = [hamlet|
+$newline never
 \^{head'}
 $forall s <- stylesheets
     ^{mkLinkTag s}
@@ -595,6 +603,7 @@
       ^{regularScriptLoad}
 |]
     let bodyScript = [hamlet|
+$newline never
 ^{body}
 ^{regularScriptLoad}
 |]
@@ -641,6 +650,7 @@
 loadJsYepnope :: Yesod master => Either Text (Route master) -> [Text] -> Maybe (HtmlUrl (Route master)) -> (HtmlUrl (Route master))
 loadJsYepnope eyn scripts mcomplete =
   [hamlet|
+$newline never
     $maybe yn <- left eyn
         <script src=#{yn}>
     $maybe yn <- right eyn
diff --git a/Yesod/Internal/Request.hs b/Yesod/Internal/Request.hs
--- a/Yesod/Internal/Request.hs
+++ b/Yesod/Internal/Request.hs
@@ -4,7 +4,15 @@
     ( parseWaiRequest
     , Request (..)
     , RequestBodyContents
-    , FileInfo (..)
+    , FileInfo
+    , fileName
+    , fileContentType
+    , fileSource
+    , fileMove
+    , mkFileInfoLBS
+    , mkFileInfoFile
+    , mkFileInfoSource
+    , FileUpload (..)
     -- The below are exported for testing.
     , randomString
     , parseWaiRequest'
@@ -28,6 +36,10 @@
 import qualified Data.Text as T
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
+import Data.Conduit
+import Data.Conduit.List (sourceList)
+import Data.Conduit.Binary (sourceFile, sinkFile)
+import Data.Word (Word64)
 
 -- | The parsed request information.
 data Request = Request
@@ -38,23 +50,27 @@
     , reqLangs :: [Text]
       -- | A random, session-specific token used to prevent CSRF attacks.
     , reqToken :: Maybe Text
+      -- | Size of the request body.
+    , reqBodySize :: Word64
     }
 
 parseWaiRequest :: W.Request
                 -> [(Text, ByteString)] -- ^ session
                 -> Bool
+                -> Word64
                 -> IO Request
-parseWaiRequest env session' useToken =
-    parseWaiRequest' env session' useToken <$> newStdGen
+parseWaiRequest env session' useToken bodySize =
+    parseWaiRequest' env session' useToken bodySize <$> newStdGen
 
 parseWaiRequest' :: RandomGen g
                  => W.Request
                  -> [(Text, ByteString)] -- ^ session
                  -> Bool
+                 -> Word64
                  -> g
                  -> Request
-parseWaiRequest' env session' useToken gen = 
-    Request gets'' cookies' env langs'' token
+parseWaiRequest' env session' useToken bodySize gen =
+    Request gets'' cookies' env langs'' token bodySize
   where
     gets' = queryToQueryText $ W.queryString env
     gets'' = map (second $ fromMaybe "") gets'
@@ -116,6 +132,19 @@
 data FileInfo = FileInfo
     { fileName :: Text
     , fileContentType :: Text
-    , fileContent :: L.ByteString
+    , fileSource :: Source (ResourceT IO) ByteString
+    , fileMove :: FilePath -> IO ()
     }
-    deriving (Eq, Show)
+
+mkFileInfoLBS :: Text -> Text -> L.ByteString -> FileInfo
+mkFileInfoLBS name ct lbs = FileInfo name ct (sourceList $ L.toChunks lbs) (\fp -> L.writeFile fp lbs)
+
+mkFileInfoFile :: Text -> Text -> FilePath -> FileInfo
+mkFileInfoFile name ct fp = FileInfo name ct (sourceFile fp) (\dst -> runResourceT $ sourceFile fp $$ sinkFile dst)
+
+mkFileInfoSource :: Text -> Text -> Source (ResourceT IO) ByteString -> FileInfo
+mkFileInfoSource name ct src = FileInfo name ct src (\dst -> runResourceT $ src $$ sinkFile dst)
+
+data FileUpload = FileUploadMemory (NWP.BackEnd L.ByteString)
+                | FileUploadDisk (NWP.BackEnd FilePath)
+                | FileUploadSource (NWP.BackEnd (Source (ResourceT IO) ByteString))
diff --git a/Yesod/Logger.hs b/Yesod/Logger.hs
deleted file mode 100644
--- a/Yesod/Logger.hs
+++ /dev/null
@@ -1,138 +0,0 @@
-{-# LANGUAGE BangPatterns #-}
-module Yesod.Logger
-    ( Logger
-    , handle
-    , developmentLogger, productionLogger
-    , defaultDevelopmentLogger, defaultProductionLogger
-    , toProduction
-    , flushLogger
-    , logText
-    , logLazyText
-    , logString
-    , logBS
-    , logMsg
-    , formatLogText
-    , timed
-    -- * Deprecated
-    , makeLoggerWithHandle
-    , makeDefaultLogger
-    ) where
-
-import System.IO (Handle, stdout, hFlush)
-import Data.ByteString (ByteString)
-import Data.ByteString.Char8 (pack)
-import Data.ByteString.Lazy (toChunks)
-import qualified Data.Text.Lazy as TL
-import Data.Text (Text)
-import Data.Text.Encoding (encodeUtf8)
-import qualified Data.Text.Lazy.Encoding as TLE
-import System.Log.FastLogger
-import Network.Wai.Logger.Date (DateRef, dateInit, getDate)
-
--- for timed logging
-import Data.Time (getCurrentTime, diffUTCTime)
-import Control.Monad.IO.Class (MonadIO, liftIO)
-import Text.Printf (printf)
-import Data.Text (unpack)
-
--- for formatter
-import Language.Haskell.TH.Syntax (Loc)
-import Yesod.Core (LogLevel, fileLocationToString)
-
-data Logger = Logger {
-    loggerLogFun   :: [LogStr] -> IO ()
-  , loggerHandle   :: Handle
-  , loggerDateRef  :: DateRef
-  }
-
-handle :: Logger -> Handle
-handle = loggerHandle
-
-flushLogger :: Logger -> IO ()
-flushLogger = hFlush . loggerHandle
-
-makeDefaultLogger :: IO Logger
-makeDefaultLogger = defaultDevelopmentLogger
-{-# DEPRECATED makeDefaultLogger "Use defaultProductionLogger or defaultDevelopmentLogger instead" #-}
-
-makeLoggerWithHandle, developmentLogger, productionLogger :: Handle -> IO Logger
-makeLoggerWithHandle = productionLogger
-{-# DEPRECATED makeLoggerWithHandle "Use productionLogger or developmentLogger instead" #-}
-
--- | uses stdout handle
-defaultProductionLogger, defaultDevelopmentLogger :: IO Logger
-defaultProductionLogger = productionLogger stdout
-defaultDevelopmentLogger = developmentLogger stdout
-
-
-productionLogger h = mkLogger h (handleToLogFun h)
--- | a development logger gets automatically flushed
-developmentLogger h = mkLogger h (\bs -> (handleToLogFun h) bs >> hFlush h)
-
-mkLogger :: Handle -> ([LogStr] -> IO ()) -> IO Logger
-mkLogger h logFun = do
-    initHandle h
-    dateInit >>= return . Logger logFun h
-
--- convert (a development) logger to production settings
-toProduction :: Logger -> Logger
-toProduction (Logger _ h d) = Logger (handleToLogFun h) h d
-
-handleToLogFun :: Handle -> ([LogStr] -> IO ())
-handleToLogFun = hPutLogStr
-
-logMsg :: Logger -> [LogStr] -> IO ()
-logMsg = hPutLogStr . handle
-
-logLazyText :: Logger -> TL.Text -> IO ()
-logLazyText logger msg = loggerLogFun logger $
-  map LB (toChunks $ TLE.encodeUtf8 msg) ++ [newLine]
-
-logText :: Logger -> Text -> IO ()
-logText logger = logBS logger . encodeUtf8
-
-logBS :: Logger -> ByteString -> IO ()
-logBS logger msg = loggerLogFun logger $ [LB msg, newLine]
-
-logString :: Logger -> String -> IO ()
-logString logger msg = loggerLogFun logger $ [LS msg, newLine]
-
-formatLogText :: Logger -> Loc -> LogLevel -> Text -> IO [LogStr]
-formatLogText logger loc level msg = formatLogMsg logger loc level (toLB msg)
-
-toLB :: Text -> LogStr
-toLB = LB . encodeUtf8
-
-formatLogMsg :: Logger -> Loc -> LogLevel -> LogStr -> IO [LogStr]
-formatLogMsg logger loc level msg = do
-    date <- liftIO $ getDate $ loggerDateRef logger
-    return
-        [ LB date
-        , LB $ pack" ["
-        , LS (drop 5 $ show level)
-        , LB $ pack "] "
-        , msg
-        , LB $ pack " @("
-        , LS (fileLocationToString loc)
-        , LB $ pack ") "
-        ]
-
-newLine :: LogStr
-newLine = LB $ pack "\n"
-
--- | Execute a monadic action and log the duration
---
-timed :: MonadIO m
-      => Logger  -- ^ Logger
-      -> Text  -- ^ Message
-      -> m a     -- ^ Action
-      -> m a     -- ^ Timed and logged action
-timed logger msg action = do
-    start <- liftIO getCurrentTime
-    !result <- action
-    stop <- liftIO getCurrentTime
-    let diff = fromEnum $ diffUTCTime stop start
-        ms = diff `div` 10 ^ (9 :: Int)
-        formatted = printf "  [%4dms] %s" ms (unpack msg)
-    liftIO $ logString logger formatted
-    return result
diff --git a/Yesod/Request.hs b/Yesod/Request.hs
--- a/Yesod/Request.hs
+++ b/Yesod/Request.hs
@@ -16,7 +16,11 @@
       -- * Request datatype
       RequestBodyContents
     , Request (..)
-    , FileInfo (..)
+    , FileInfo
+    , fileName
+    , fileContentType
+    , fileSource
+    , fileMove
       -- * Convenience functions
     , languages
       -- * Lookup parameters
diff --git a/Yesod/Widget.hs b/Yesod/Widget.hs
--- a/Yesod/Widget.hs
+++ b/Yesod/Widget.hs
@@ -53,6 +53,7 @@
     , addScriptEither
       -- * Internal
     , unGWidget
+    , whamletFileWithSettings
     ) where
 
 import Data.Monoid
@@ -80,20 +81,16 @@
 import Control.Exception (throwIO)
 import qualified Text.Hamlet as NP
 import Data.Text.Lazy.Builder (fromLazyText)
-#if MIN_VERSION_blaze_html(0, 5, 0)
 import Text.Blaze.Html (toHtml, preEscapedToMarkup)
 import qualified Data.Text.Lazy as TL
-#else
-import Text.Blaze (toHtml, preEscapedLazyText)
-#endif
 import Control.Monad.Base (MonadBase (liftBase))
 import Control.Arrow (first)
 import Control.Monad.Trans.Resource
 
-#if MIN_VERSION_blaze_html(0, 5, 0)
+import Control.Monad.Logger
+
 preEscapedLazyText :: TL.Text -> Html
 preEscapedLazyText = preEscapedToMarkup
-#endif
 
 -- | A generic widget, allowing specification of both the subsite and master
 -- site datatypes. While this is simply a @WriterT@, we define a newtype for
@@ -171,6 +168,7 @@
 {-# DEPRECATED addHamletHead, addHtmlHead "Use toWidgetHead instead" #-}
 {-# DEPRECATED addHamlet, addHtml, addCassius, addLucius, addJulius "Use toWidget instead" #-}
 {-# DEPRECATED addJuliusBody "Use toWidgetBody instead" #-}
+{-# DEPRECATED addWidget "addWidget can be omitted" #-}
 
 -- | Add a 'Hamlet' to the head tag.
 addHamletHead :: HtmlUrl (Route master) -> GWidget sub master ()
@@ -272,6 +270,9 @@
 whamletFile :: FilePath -> Q Exp
 whamletFile = NP.hamletFileWithSettings rules NP.defaultHamletSettings
 
+whamletFileWithSettings :: NP.HamletSettings -> FilePath -> Q Exp
+whamletFileWithSettings = NP.hamletFileWithSettings rules
+
 rules :: Q NP.HamletRules
 rules = do
     ah <- [|toWidget|]
@@ -344,3 +345,6 @@
     register = lift . register
     release = lift . release
     resourceMask = lift . resourceMask
+
+instance MonadLogger (GWidget sub master) where
+    monadLoggerLog a b = lift . monadLoggerLog a b
diff --git a/test.hs b/test.hs
--- a/test.hs
+++ b/test.hs
@@ -2,4 +2,4 @@
 import qualified YesodCoreTest
 
 main :: IO ()
-main = hspecX $ YesodCoreTest.specs
+main = hspec YesodCoreTest.specs
diff --git a/test/YesodCoreTest.hs b/test/YesodCoreTest.hs
--- a/test/YesodCoreTest.hs
+++ b/test/YesodCoreTest.hs
@@ -15,18 +15,17 @@
 
 import Test.Hspec
 
-specs :: [Spec]
-specs = 
-    [ cleanPathTest
-    , exceptionsTest
-    , widgetTest
-    , mediaTest
-    , linksTest
-    , noOverloadedTest
-    , internalRequestTest
-    , errorHandlingTest
-    , cacheTest
-    , WaiSubsite.specs
-    , Redirect.specs
-    , JsLoader.specs
-    ]
+specs :: Spec
+specs = do
+      cleanPathTest
+      exceptionsTest
+      widgetTest
+      mediaTest
+      linksTest
+      noOverloadedTest
+      internalRequestTest
+      errorHandlingTest
+      cacheTest
+      WaiSubsite.specs
+      Redirect.specs
+      JsLoader.specs
diff --git a/test/YesodCoreTest/Cache.hs b/test/YesodCoreTest/Cache.hs
--- a/test/YesodCoreTest/Cache.hs
+++ b/test/YesodCoreTest/Cache.hs
@@ -37,9 +37,8 @@
 
 cacheTest :: Spec
 cacheTest =
-  describe "Test.Cache"
-    [ it "works" works
-    ]
+  describe "Test.Cache" $ do
+      it "works" works
 
 runner :: Session () -> IO ()
 runner f = toWaiApp C >>= runSession f
diff --git a/test/YesodCoreTest/CleanPath.hs b/test/YesodCoreTest/CleanPath.hs
--- a/test/YesodCoreTest/CleanPath.hs
+++ b/test/YesodCoreTest/CleanPath.hs
@@ -26,7 +26,7 @@
     renderRoute (SubsiteRoute x) = (x, [])
 
 instance YesodDispatch Subsite master where
-    yesodDispatch _ _ _ _ _ _ pieces _ _ = return $ responseLBS
+    yesodDispatch _ _ _ _ _ _ _ pieces _ _ = return $ responseLBS
         status200
         [ ("Content-Type", "SUBSITE")
         ] $ L8.pack $ show pieces
@@ -64,15 +64,14 @@
 
 cleanPathTest :: Spec
 cleanPathTest =
-  describe "Test.CleanPath"
-    [ it "remove trailing slash" removeTrailingSlash
-    , it "noTrailingSlash" noTrailingSlash
-    , it "add trailing slash" addTrailingSlash
-    , it "has trailing slash" hasTrailingSlash
-    , it "/foo/something" fooSomething
-    , it "subsite dispatch" subsiteDispatch
-    , it "redirect with query string" redQueryString
-    ]
+  describe "Test.CleanPath" $ do
+      it "remove trailing slash" removeTrailingSlash
+      it "noTrailingSlash" noTrailingSlash
+      it "add trailing slash" addTrailingSlash
+      it "has trailing slash" hasTrailingSlash
+      it "/foo/something" fooSomething
+      it "subsite dispatch" subsiteDispatch
+      it "redirect with query string" redQueryString
 
 runner :: Session () -> IO ()
 runner f = toWaiApp Y >>= runSession f
diff --git a/test/YesodCoreTest/ErrorHandling.hs b/test/YesodCoreTest/ErrorHandling.hs
--- a/test/YesodCoreTest/ErrorHandling.hs
+++ b/test/YesodCoreTest/ErrorHandling.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 module YesodCoreTest.ErrorHandling
     ( errorHandlingTest
     , Widget
@@ -11,6 +12,7 @@
 import Text.Hamlet (hamlet)
 import qualified Data.ByteString.Lazy as L
 import qualified Data.ByteString.Char8 as S8
+import Control.Exception (SomeException, try)
 
 data App = App
 
@@ -19,12 +21,16 @@
 /not_found      NotFoundR POST
 /first_thing    FirstThingR POST
 /after_runRequestBody AfterRunRequestBodyR POST
+/error-in-body ErrorInBodyR GET
+/error-in-body-noeval ErrorInBodyNoEvalR GET
 |]
 
 instance Yesod App
 
 getHomeR :: Handler RepHtml
-getHomeR = defaultLayout $ toWidget [hamlet|
+getHomeR = do
+    $logDebug "Testing logging"
+    defaultLayout $ toWidget [hamlet|
 $doctype 5
 
 <html>
@@ -49,15 +55,24 @@
 
 postAfterRunRequestBodyR = do
    x <- runRequestBody
-   _ <- error $ show x
+   _ <- error $ show $ fst x
    getHomeR
 
+getErrorInBodyR :: Handler RepHtml
+getErrorInBodyR = do
+    let foo = error "error in body 19328" :: String
+    defaultLayout [whamlet|#{foo}|]
+
+getErrorInBodyNoEvalR :: Handler (DontFullyEvaluate RepHtml)
+getErrorInBodyNoEvalR = fmap DontFullyEvaluate getErrorInBodyR
+
 errorHandlingTest :: Spec
-errorHandlingTest = describe "Test.ErrorHandling"
-    [ it "says not found" caseNotFound
-    , it "says 'There was an error' before runRequestBody" caseBefore
-    , it "says 'There was an error' after runRequestBody" caseAfter
-    ]
+errorHandlingTest = describe "Test.ErrorHandling" $ do
+      it "says not found" caseNotFound
+      it "says 'There was an error' before runRequestBody" caseBefore
+      it "says 'There was an error' after runRequestBody" caseAfter
+      it "error in body == 500" caseErrorInBody
+      it "error in body, no eval == 200" caseErrorInBodyNoEval
 
 runner :: Session () -> IO ()
 runner f = toWaiApp App >>= runSession f
@@ -96,3 +111,18 @@
         }
     assertStatus 500 res
     assertBodyContains "bin12345" res
+
+caseErrorInBody :: IO ()
+caseErrorInBody = runner $ do
+    res <- request defaultRequest { pathInfo = ["error-in-body"] }
+    assertStatus 500 res
+    assertBodyContains "error in body 19328" res
+
+caseErrorInBodyNoEval :: IO ()
+caseErrorInBodyNoEval = do
+    eres <- try $ runner $ do
+        _ <- request defaultRequest { pathInfo = ["error-in-body-noeval"] }
+        return ()
+    case eres of
+        Left (_ :: SomeException) -> return ()
+        Right _ -> error "Expected an exception"
diff --git a/test/YesodCoreTest/Exceptions.hs b/test/YesodCoreTest/Exceptions.hs
--- a/test/YesodCoreTest/Exceptions.hs
+++ b/test/YesodCoreTest/Exceptions.hs
@@ -31,10 +31,9 @@
     redirectWith status301 RootR
 
 exceptionsTest :: Spec
-exceptionsTest = describe "Test.Exceptions"
-    [ it "500" case500
-    , it "redirect keeps headers" caseRedirect
-    ]
+exceptionsTest = describe "Test.Exceptions" $ do
+      it "500" case500
+      it "redirect keeps headers" caseRedirect
 
 runner :: Session () -> IO ()
 runner f = toWaiApp Y >>= runSession f
diff --git a/test/YesodCoreTest/InternalRequest.hs b/test/YesodCoreTest/InternalRequest.hs
--- a/test/YesodCoreTest/InternalRequest.hs
+++ b/test/YesodCoreTest/InternalRequest.hs
@@ -11,10 +11,9 @@
 import Test.Hspec
 
 randomStringSpecs :: Spec
-randomStringSpecs = describe "Yesod.Internal.Request.randomString"
-  [ it "looks reasonably random" looksRandom
-  , it "does not repeat itself" $ noRepeat 10 100
-  ]
+randomStringSpecs = describe "Yesod.Internal.Request.randomString" $ do
+    it "looks reasonably random" looksRandom
+    it "does not repeat itself" $ noRepeat 10 100
 
 -- NOTE: this testcase may break on other systems/architectures if
 -- mkStdGen is not identical everywhere (is it?).
@@ -31,57 +30,55 @@
 
 
 tokenSpecs :: Spec
-tokenSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqToken)"
-  [ it "is Nothing if sessions are disabled" noDisabledToken
-  , it "ignores pre-existing token if sessions are disabled" ignoreDisabledToken
-  , it "uses preexisting token in session" useOldToken
-  , it "generates a new token for sessions without token" generateToken
-  ]
+tokenSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqToken)" $ do
+    it "is Nothing if sessions are disabled" noDisabledToken
+    it "ignores pre-existing token if sessions are disabled" ignoreDisabledToken
+    it "uses preexisting token in session" useOldToken
+    it "generates a new token for sessions without token" generateToken
 
 noDisabledToken :: Bool
 noDisabledToken = reqToken r == Nothing where
-  r = parseWaiRequest' defaultRequest [] False g
+  r = parseWaiRequest' defaultRequest [] False 0 g
 
 ignoreDisabledToken :: Bool
 ignoreDisabledToken = reqToken r == Nothing where
-  r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] False g
+  r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] False 0 g
 
 useOldToken :: Bool
 useOldToken = reqToken r == Just "old" where
-  r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True g
+  r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True 0 g
 
 generateToken :: Bool
 generateToken = reqToken r /= Nothing where
-  r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True g
+  r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True 0 g
 
 
 langSpecs :: Spec
-langSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqLangs)"
-  [ it "respects Accept-Language" respectAcceptLangs
-  , it "respects sessions" respectSessionLang
-  , it "respects cookies" respectCookieLang
-  , it "respects queries" respectQueryLang
-  , it "prioritizes correctly" prioritizeLangs
-  ]
+langSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqLangs)" $ do
+    it "respects Accept-Language" respectAcceptLangs
+    it "respects sessions" respectSessionLang
+    it "respects cookies" respectCookieLang
+    it "respects queries" respectQueryLang
+    it "prioritizes correctly" prioritizeLangs
 
 respectAcceptLangs :: Bool
 respectAcceptLangs = reqLangs r == ["en-US", "es", "en"] where
   r = parseWaiRequest' defaultRequest
-        { requestHeaders = [("Accept-Language", "en-US, es")] } [] False g
+        { requestHeaders = [("Accept-Language", "en-US, es")] } [] False 0 g
 
 respectSessionLang :: Bool
 respectSessionLang = reqLangs r == ["en"] where
-  r = parseWaiRequest' defaultRequest [("_LANG", "en")] False g
+  r = parseWaiRequest' defaultRequest [("_LANG", "en")] False 0 g
 
 respectCookieLang :: Bool
 respectCookieLang = reqLangs r == ["en"] where
   r = parseWaiRequest' defaultRequest
         { requestHeaders = [("Cookie", "_LANG=en")]
-        } [] False g
+        } [] False 0 g
 
 respectQueryLang :: Bool
 respectQueryLang = reqLangs r == ["en-US", "en"] where
-  r = parseWaiRequest' defaultRequest { queryString = [("_LANG", Just "en-US")] } [] False g
+  r = parseWaiRequest' defaultRequest { queryString = [("_LANG", Just "en-US")] } [] False 0 g
 
 prioritizeLangs :: Bool
 prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "es"] where
@@ -90,12 +87,11 @@
                            , ("Cookie", "_LANG=en-COOKIE")
                            ]
         , queryString = [("_LANG", Just "en-QUERY")]
-        } [("_LANG", "en-SESSION")] False g
+        } [("_LANG", "en-SESSION")] False 0 g
 
 
 internalRequestTest :: Spec
-internalRequestTest = describe "Test.InternalRequestTest" 
-    [ randomStringSpecs
-    , tokenSpecs
-    , langSpecs
-    ]
+internalRequestTest = describe "Test.InternalRequestTest" $ do
+      randomStringSpecs
+      tokenSpecs
+      langSpecs
diff --git a/test/YesodCoreTest/JsLoader.hs b/test/YesodCoreTest/JsLoader.hs
--- a/test/YesodCoreTest/JsLoader.hs
+++ b/test/YesodCoreTest/JsLoader.hs
@@ -23,19 +23,18 @@
 getHeadR = defaultLayout $ addScriptRemote "load.js"
 
 specs :: Spec
-specs = describe "Test.JsLoader" [
+specs = describe "Test.JsLoader" $ do
     it "link from head" $ runner H $ do
       res <- request defaultRequest
       assertBody "<!DOCTYPE html>\n<html><head><title></title><script src=\"load.js\"></script></head><body></body></html>" res
 
-  , it "link from head async" $ runner HA $ do
+    it "link from head async" $ runner HA $ do
       res <- request defaultRequest
       assertBody "<!DOCTYPE html>\n<html><head><title></title><script src=\"yepnope.js\"></script><script>yepnope({load:[\"load.js\"]});</script></head><body></body></html>" res
 
-  , it "link from bottom" $ runner B $ do
+    it "link from bottom" $ runner B $ do
       res <- request defaultRequest
       assertBody "<!DOCTYPE html>\n<html><head><title></title></head><body><script src=\"load.js\"></script></body></html>" res
-  ]
 
 runner :: (YesodDispatch master master, Yesod master) => master -> Session () -> IO ()
 runner app f = toWaiApp app >>= runSession f
diff --git a/test/YesodCoreTest/Links.hs b/test/YesodCoreTest/Links.hs
--- a/test/YesodCoreTest/Links.hs
+++ b/test/YesodCoreTest/Links.hs
@@ -21,9 +21,8 @@
 getRootR = defaultLayout $ toWidget [hamlet|<a href=@{RootR}>|]
 
 linksTest :: Spec
-linksTest = describe "Test.Links"
-    [ it "linkToHome" case_linkToHome
-    ]
+linksTest = describe "Test.Links" $ do
+      it "linkToHome" case_linkToHome
 
 runner :: Session () -> IO ()
 runner f = toWaiApp Y >>= runSession f
@@ -31,4 +30,4 @@
 case_linkToHome :: IO ()
 case_linkToHome = runner $ do
     res <- request defaultRequest
-    assertBody "<!DOCTYPE html>\n<html><head><title></title></head><body><a href=\"/\"></a></body></html>" res
+    assertBody "<!DOCTYPE html>\n<html><head><title></title></head><body><a href=\"/\"></a>\n</body></html>" res
diff --git a/test/YesodCoreTest/Media.hs b/test/YesodCoreTest/Media.hs
--- a/test/YesodCoreTest/Media.hs
+++ b/test/YesodCoreTest/Media.hs
@@ -50,7 +50,6 @@
     flip assertBody res "<!DOCTYPE html>\n<html><head><title></title><link rel=\"stylesheet\" href=\"all.css\"><link rel=\"stylesheet\" media=\"screen\" href=\"screen.css\"></head><body></body></html>"
 
 mediaTest :: Spec
-mediaTest = describe "Test.Media"
-    [ it "media" caseMedia
-    , it "media link" caseMediaLink
-    ]
+mediaTest = describe "Test.Media" $ do
+      it "media" caseMedia
+      it "media link" caseMediaLink
diff --git a/test/YesodCoreTest/NoOverloadedStrings.hs b/test/YesodCoreTest/NoOverloadedStrings.hs
--- a/test/YesodCoreTest/NoOverloadedStrings.hs
+++ b/test/YesodCoreTest/NoOverloadedStrings.hs
@@ -45,6 +45,5 @@
     assertBody mempty res
 
 noOverloadedTest :: Spec
-noOverloadedTest = describe "Test.NoOverloadedStrings"
-    [ it "sanity" case_sanity
-    ]
+noOverloadedTest = describe "Test.NoOverloadedStrings" $ do
+      it "sanity" case_sanity
diff --git a/test/YesodCoreTest/Redirect.hs b/test/YesodCoreTest/Redirect.hs
--- a/test/YesodCoreTest/Redirect.hs
+++ b/test/YesodCoreTest/Redirect.hs
@@ -27,33 +27,32 @@
 getRRegular = redirect RootR
 
 specs :: Spec
-specs = describe "Redirect" [
+specs = describe "Redirect" $ do
     it "301 redirect" $ app $ do
       res <- request defaultRequest { pathInfo = ["r301"] }
       assertStatus 301 res
       assertBodyContains "" res
 
-  , it "303 redirect" $ app $ do
+    it "303 redirect" $ app $ do
       res <- request defaultRequest { pathInfo = ["r303"] }
       assertStatus 303 res
       assertBodyContains "" res
 
-  , it "307 redirect" $ app $ do
+    it "307 redirect" $ app $ do
       res <- request defaultRequest { pathInfo = ["r307"] }
       assertStatus 307 res
       assertBodyContains "" res
 
-  , it "303 redirect for regular, HTTP 1.1" $ app $ do
+    it "303 redirect for regular, HTTP 1.1" $ app $ do
       res <- request defaultRequest {
         pathInfo = ["rregular"]
       }
       assertStatus 303 res
       assertBodyContains "" res
-  , it "302 redirect for regular, HTTP 1.0" $ app $ do
+    it "302 redirect for regular, HTTP 1.0" $ app $ do
       res <- request defaultRequest {
         pathInfo = ["rregular"]
       , httpVersion = H.http10
       }
       assertStatus 302 res
       assertBodyContains "" res
-  ]
diff --git a/test/YesodCoreTest/WaiSubsite.hs b/test/YesodCoreTest/WaiSubsite.hs
--- a/test/YesodCoreTest/WaiSubsite.hs
+++ b/test/YesodCoreTest/WaiSubsite.hs
@@ -26,14 +26,13 @@
 getRootR = return ()
 
 specs :: Spec
-specs = describe "WaiSubsite" [
+specs = describe "WaiSubsite" $ do
     it "root" $ app $ do
       res <- request defaultRequest { pathInfo = [] }
       assertStatus 200 res
       assertBodyContains "" res
 
-  , it "subsite" $ app $ do
+    it "subsite" $ app $ do
       res <- request defaultRequest { pathInfo = ["sub", "foo"] }
       assertStatus 200 res
       assertBodyContains "WAI" res
-  ]
diff --git a/test/YesodCoreTest/Widget.hs b/test/YesodCoreTest/Widget.hs
--- a/test/YesodCoreTest/Widget.hs
+++ b/test/YesodCoreTest/Widget.hs
@@ -56,12 +56,13 @@
     toWidget [lucius|foo{bar:baz}|]
     toWidgetHead [lucius|foo{bar:baz}|]
 
-    toWidget [hamlet|<foo>|] :: Widget
+    toWidget [hamlet|<foo>|]
     toWidgetHead [hamlet|<foo>|]
     toWidgetBody [hamlet|<foo>|]
 
 getWhamletR :: Handler RepHtml
 getWhamletR = defaultLayout [whamlet|
+$newline never
 <h1>Test
 <h2>@{WhamletR}
 <h3>_{Goodbye}
@@ -69,10 +70,14 @@
 ^{embed}
 |]
   where
-    embed = [whamlet|<h4>Embed|]
+    embed = [whamlet|
+$newline never
+<h4>Embed
+|]
 
 getAutoR :: Handler RepHtml
 getAutoR = defaultLayout [whamlet|
+$newline never
 ^{someHtml}
 |]
   where
@@ -82,13 +87,12 @@
 getJSHeadR = defaultLayout $ toWidgetHead [julius|alert("hello");|]
 
 widgetTest :: Spec
-widgetTest = describe "Test.Widget"
-    [ it "addJuliusBody" case_addJuliusBody
-    , it "whamlet" case_whamlet
-    , it "two letter lang codes" case_two_letter_lang
-    , it "automatically applies toWidget" case_auto
-    , it "toWidgetHead puts JS in head" case_jshead
-    ]
+widgetTest = describe "Test.Widget" $ do
+      it "addJuliusBody" case_addJuliusBody
+      it "whamlet" case_whamlet
+      it "two letter lang codes" case_two_letter_lang
+      it "automatically applies toWidget" case_auto
+      it "toWidgetHead puts JS in head" case_jshead
 
 runner :: Session () -> IO ()
 runner f = toWaiApp Y >>= runSession f
diff --git a/test/test.hs b/test/test.hs
--- a/test/test.hs
+++ b/test/test.hs
@@ -2,4 +2,4 @@
 import qualified YesodCoreTest
 
 main :: IO ()
-main = hspecX $ YesodCoreTest.specs
+main = hspec YesodCoreTest.specs
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.0.1.3
+version:         1.1.0
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -40,10 +40,6 @@
   description: Build the executable to run unit tests
   default: False
 
-flag blaze_html_0_5
-    description: use blaze-html 0.5 and blaze-markup 0.5
-    default: True
-
 library
     -- Work around a bug in cabal. Without this, wai-test doesn't get built and
     -- we have a missing dependency during --enable-tests builds.
@@ -52,21 +48,21 @@
 
     build-depends:   base                  >= 4.3      && < 5
                    , time                  >= 1.1.4
-                   , yesod-routes          >= 1.0      && < 1.1
-                   , wai                   >= 1.2      && < 1.3
-                   , wai-extra             >= 1.2      && < 1.3
+                   , yesod-routes          >= 1.1      && < 1.2
+                   , wai                   >= 1.3      && < 1.4
+                   , wai-extra             >= 1.3      && < 1.4
                    , bytestring            >= 0.9.1.4
                    , text                  >= 0.7      && < 0.12
                    , template-haskell
                    , path-pieces           >= 0.1      && < 0.2
-                   , hamlet                >= 1.0      && < 1.1
+                   , hamlet                >= 1.1      && < 1.2
                    , shakespeare           >= 1.0      && < 1.1
                    , shakespeare-js        >= 1.0      && < 1.1
                    , shakespeare-css       >= 1.0      && < 1.1
                    , shakespeare-i18n      >= 1.0      && < 1.1
                    , blaze-builder         >= 0.2.1.4  && < 0.4
                    , transformers          >= 0.2.2    && < 0.4
-                   , clientsession         >= 0.7.3.1  && < 0.8
+                   , clientsession         >= 0.8      && < 0.9
                    , random                >= 1.0.0.2  && < 1.1
                    , cereal                >= 0.3      && < 0.4
                    , old-locale            >= 1.0.0.2  && < 1.1
@@ -75,31 +71,24 @@
                    , monad-control         >= 0.3      && < 0.4
                    , transformers-base     >= 0.4
                    , cookie                >= 0.4      && < 0.5
-                   , http-types            >= 0.6.5    && < 0.7
+                   , http-types            >= 0.7      && < 0.8
                    , case-insensitive      >= 0.2
                    , parsec                >= 2        && < 3.2
                    , directory             >= 1        && < 1.2
                    , vector                >= 0.9      && < 0.10
                    , aeson                 >= 0.5
-                   , fast-logger           >= 0.0.2
-                   , wai-logger            >= 0.0.1
-                   , conduit               >= 0.4      && < 0.5
+                   , fast-logger           >= 0.2      && < 0.3
+                   , monad-logger          >= 0.2      && < 0.3
+                   , conduit               >= 0.5      && < 0.6
                    , resourcet             >= 0.3      && < 0.4
                    , lifted-base           >= 0.1      && < 0.2
-
-    if flag(blaze_html_0_5)
-        build-depends:
-                     blaze-html               >= 0.5     && < 0.6
-                   , blaze-markup             >= 0.5.1   && < 0.6
-    else
-        build-depends:
-                     blaze-html               >= 0.4     && < 0.5
+                   , blaze-html            >= 0.5      && < 0.6
+                   , blaze-markup          >= 0.5.1    && < 0.6
 
     exposed-modules: Yesod.Content
                      Yesod.Core
                      Yesod.Dispatch
                      Yesod.Handler
-                     Yesod.Logger
                      Yesod.Request
                      Yesod.Widget
                      Yesod.Message
@@ -119,7 +108,7 @@
 
     cpp-options:   -DTEST
     build-depends: base
-                  ,hspec >= 1.1 && < 1.2
+                  ,hspec >= 1.3 && < 1.4
                   ,wai-test
                   ,wai
                   ,yesod-core
