diff --git a/shelly.cabal b/shelly.cabal
--- a/shelly.cabal
+++ b/shelly.cabal
@@ -1,6 +1,6 @@
 Name:       shelly
 
-Version:     1.5.4.1
+Version:     1.5.5
 Synopsis:    shell-like (systems) programming in Haskell
 
 Description: Shelly provides convenient systems programming in Haskell,
diff --git a/src/Shelly.hs b/src/Shelly.hs
--- a/src/Shelly.hs
+++ b/src/Shelly.hs
@@ -26,6 +26,7 @@
          Sh, ShIO, shelly, shellyNoDir, shellyFailDir, asyncSh, sub
          , silently, verbosely, escaping, print_stdout, print_stderr, print_commands
          , tracing, errExit
+         , log_stdout_with, log_stderr_with
 
          -- * Running external commands.
          , run, run_, runFoldLines, cmd, FoldCallback
@@ -249,8 +250,9 @@
 -- does not close the write handle.
 --
 -- Also, return the complete contents being streamed line by line.
-transferLinesAndCombine :: Handle -> Handle -> IO Text
-transferLinesAndCombine h1 h2 = transferFoldHandleLines mempty (|>) h1 h2 >>=
+transferLinesAndCombine :: Handle -> (Text -> IO ()) -> IO Text
+transferLinesAndCombine readHandle putWrite =
+  transferFoldHandleLines mempty (|>) readHandle putWrite >>=
     return . lineSeqToText
 
 lineSeqToText :: Seq Text -> Text
@@ -264,14 +266,14 @@
 -- does not close the write handle.
 --
 -- Also, fold over the contents being streamed line by line
-transferFoldHandleLines :: a -> FoldCallback a -> Handle -> Handle -> IO a
-transferFoldHandleLines start foldLine readHandle writeHandle = go start
+transferFoldHandleLines :: a -> FoldCallback a -> Handle -> (Text -> IO ()) -> IO a
+transferFoldHandleLines start foldLine readHandle putWrite = go start
   where
     go acc = do
         mLine <- filterIOErrors $ TIO.hGetLine readHandle
         case mLine of
             Nothing -> return acc
-            Just line -> TIO.hPutStrLn writeHandle line >> go (foldLine acc line)
+            Just line -> putWrite line >> go (foldLine acc line)
 
 filterIOErrors :: IO a -> IO (Maybe a)
 filterIOErrors action = catchIOError
@@ -771,6 +773,16 @@
                                  , sPrintCommands = True
                                  }) >> a
 
+-- | Create a sub-Sh in which stdout is sent to the user-defined logger
+log_stdout_with :: (Text -> IO ()) -> Sh a -> Sh a
+log_stdout_with logger a = sub $ modify (\s -> s { sPutStdout = logger })
+                                 >> a
+
+-- | Create a sub-Sh in which stderr is sent to the user-defined logger
+log_stderr_with :: (Text -> IO ()) -> Sh a -> Sh a
+log_stderr_with logger a = sub $ modify (\s -> s { sPutStderr = logger })
+                                 >> a
+
 -- | Create a sub-Sh with stdout printing on or off
 -- Defaults to True.
 print_stdout :: Bool -> Sh a -> Sh a
@@ -869,6 +881,8 @@
   let def  = State { sCode = 0
                    , sStdin = Nothing
                    , sStderr = T.empty
+                   , sPutStdout = TIO.hPutStrLn stdout
+                   , sPutStderr = TIO.hPutStrLn stderr
                    , sPrintStdout = True
                    , sPrintStderr = True
                    , sPrintCommands = False
@@ -1058,7 +1072,8 @@
           -> (Handle -> Sh a) -- ^ stdout handle
           -> Sh a
 runHandle exe args withHandle = runHandles exe args [] $ \_ outH errH -> do
-    errPromise <- liftIO $ async $ transferLinesAndCombine errH stderr
+    putStderr <- gets sPutStderr
+    errPromise <- liftIO $ async $ transferLinesAndCombine errH putStderr
     res <- withHandle outH
     errs <- liftIO $ wait errPromise
 
@@ -1127,8 +1142,8 @@
     (errVar, outVar) <- liftIO $ do
       hClose inH -- setStdin was taken care of before the process even ran
       liftM2 (,)
-          (putHandleIntoMVar mempty (|>) errH stderr (sPrintStderr state))
-          (putHandleIntoMVar start cb outH stdout (sPrintStdout state))
+          (putHandleIntoMVar mempty (|>) errH (sPutStderr state) (sPrintStderr state))
+          (putHandleIntoMVar start cb outH (sPutStdout state) (sPrintStdout state))
     errs <- liftIO $ lineSeqToText `fmap` wait errVar
     modify $ \state' -> state' { sStderr = errs }
     liftIO $ wait outVar
@@ -1136,12 +1151,12 @@
 
 putHandleIntoMVar :: a -> FoldCallback a
                   -> Handle -- ^ out handle
-                  -> Handle -- ^ in handle
+                  -> (Text -> IO ()) -- ^ in handle
                   -> Bool  -- ^ should it be printed while transfered?
                   -> IO (Async a)
-putHandleIntoMVar start cb outH inHandle shouldPrint = liftIO $ async $ do
+putHandleIntoMVar start cb outH putWrite shouldPrint = liftIO $ async $ do
   if shouldPrint
-    then transferFoldHandleLines start cb outH inHandle
+    then transferFoldHandleLines start cb outH putWrite
     else foldHandleLines start cb outH
 
 
diff --git a/src/Shelly/Base.hs b/src/Shelly/Base.hs
--- a/src/Shelly/Base.hs
+++ b/src/Shelly/Base.hs
@@ -100,7 +100,9 @@
    , sStdin :: Maybe Text -- ^ stdin for the command to be run
    , sStderr :: Text -- ^ stderr for command that ran
    , sDirectory :: FilePath -- ^ working directory
+   , sPutStdout :: Text -> IO ()   -- ^ by default, hPutStrLn stdout
    , sPrintStdout :: Bool   -- ^ print stdout of command that is executed
+   , sPutStderr :: Text -> IO ()   -- ^ by default, hPutStrLn stderr
    , sPrintStderr :: Bool   -- ^ print stderr of command that is executed
    , sPrintCommands :: Bool -- ^ print command that is executed
    , sRun :: [StdHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle) -- ^ command runner, a different runner is used when escaping, probably better to just hold the escaping flag
diff --git a/src/Shelly/Lifted.hs b/src/Shelly/Lifted.hs
--- a/src/Shelly/Lifted.hs
+++ b/src/Shelly/Lifted.hs
@@ -35,6 +35,7 @@
          Sh, ShIO, S.shelly, S.shellyNoDir, S.shellyFailDir, sub
          , silently, verbosely, escaping, print_stdout, print_stderr, print_commands
          , tracing, errExit
+         , log_stdout_with, log_stderr_with
 
          -- * Running external commands.
          , run, run_, runFoldLines, S.cmd, S.FoldCallback
@@ -313,6 +314,12 @@
 verbosely :: MonadShControl m => m a -> m a
 verbosely a = controlSh $ \runInSh -> S.verbosely (runInSh a)
 
+log_stdout_with :: MonadShControl m => (Text -> IO ()) -> m a -> m a
+log_stdout_with logger a = controlSh $ \runInSh -> S.log_stdout_with logger (runInSh a)
+
+log_stderr_with :: MonadShControl m => (Text -> IO ()) -> m a -> m a
+log_stderr_with logger a = controlSh $ \runInSh -> S.log_stderr_with logger (runInSh a)
+
 print_stdout :: MonadShControl m => Bool -> m a -> m a
 print_stdout shouldPrint a = controlSh $ \runInSh -> S.print_stdout shouldPrint (runInSh a)
 
@@ -354,7 +361,7 @@
 toTextWarn :: MonadSh m => FilePath -> m Text
 toTextWarn = liftSh . toTextWarn
 
-transferLinesAndCombine :: MonadIO m => Handle -> Handle -> m Text
+transferLinesAndCombine :: MonadIO m => Handle -> (Text -> IO ()) -> m Text
 transferLinesAndCombine = (liftIO .) . S.transferLinesAndCombine
 
 get :: MonadSh m => m S.State
diff --git a/src/Shelly/Pipe.hs b/src/Shelly/Pipe.hs
--- a/src/Shelly/Pipe.hs
+++ b/src/Shelly/Pipe.hs
@@ -36,7 +36,7 @@
 module Shelly.Pipe
        (
          -- * Entering Sh.
-         Sh, shs, shelly, shellyFailDir, shsFailDir, sub, silently, verbosely, escaping, print_stdout, print_commands, tracing, errExit
+         Sh, shs, shelly, shellyFailDir, shsFailDir, sub, silently, verbosely, escaping, print_stdout, print_commands, tracing, errExit, log_stdout_with, log_stderr_with
          -- * List functions
          , roll, unroll, liftSh
          -- * Running external commands.
@@ -235,6 +235,14 @@
 -- | see 'S.escaping'
 escaping :: Bool -> Sh a -> Sh a
 escaping b = lift1 (S.escaping b)
+
+-- | see 'S.log_stdout_with'
+log_stdout_with :: (Text -> IO ()) -> Sh a -> Sh a
+log_stdout_with logger = lift1 (S.log_stdout_with logger)
+
+-- | see 'S.log_stderr_with'
+log_stderr_with :: (Text -> IO ()) -> Sh a -> Sh a
+log_stderr_with logger = lift1 (S.log_stdout_with logger)
 
 -- | see 'S.print_stdout'
 print_stdout :: Bool -> Sh a -> Sh a
