diff --git a/Log.hs b/Log.hs
new file mode 100644
--- /dev/null
+++ b/Log.hs
@@ -0,0 +1,22 @@
+module Log (
+    Logger
+  , initLogger
+  , apatcheLogger
+  , finLogger
+  ) where
+
+import Network.Wai.Logger
+import Network.Wai.Logger.Prefork
+
+data Logger = Logger ApacheLogger LogFlusher
+
+initLogger :: IPAddrSource -> LogType -> IO Logger
+initLogger ipsrc logtyp = do
+    (aplgr, flusher) <- logInit ipsrc logtyp
+    return $ Logger aplgr flusher
+
+finLogger :: Logger -> LogFlusher
+finLogger (Logger _ flusher) = flusher
+
+apatcheLogger :: Logger -> ApacheLogger
+apatcheLogger (Logger aplogr _) = aplogr
diff --git a/Mighty.hs b/Mighty.hs
--- a/Mighty.hs
+++ b/Mighty.hs
@@ -11,12 +11,15 @@
 import Data.Conduit.Network
 import FileCGIApp
 import FileCache
+import GHC.IO.Exception (IOErrorType(ResourceVanished))
+import Log
 import Network
 import qualified Network.HTTP.Conduit as H
 import Network.Wai.Application.Classic hiding ((</>), (+++))
 import Network.Wai.Handler.Warp
 import Network.Wai.Logger
 import Network.Wai.Logger.Prefork
+import Prelude hiding (catch)
 import Process
 import Report
 import Route
@@ -27,7 +30,7 @@
 import System.Exit
 import System.FilePath
 import System.IO
-import System.IO.Error (ioeGetErrorString)
+import System.IO.Error (ioeGetErrorString, ioeGetErrorType)
 import System.Posix
 import Types
 import Utils
@@ -37,14 +40,11 @@
 main :: IO ()
 main = do
     (opt,route) <- getOptRoute
+    rpt <- initReporter >>= checkReporter
     if opt_debug_mode opt then
-        server opt route
-      else do
-        let port = opt_port opt
-        putStrLn $ "Serving on port " ++ show port ++ " and detaching this terminal..."
-        putStrLn $ "(If errors occur, they will be written in \"" ++ reportFile ++ "\".)"
-        hFlush stdout
-        daemonize $ server opt route
+        server opt route rpt
+      else
+        background opt $ server opt route rpt
   where
     getOptRoute = getArgs >>= eachCase
     eachCase args
@@ -74,11 +74,17 @@
         | otherwise       = do
             dir <- getCurrentDirectory
             return $ dir </> normalise file
+    checkReporter (Right rpt) = return rpt
+    checkReporter (Left e)    = do
+        hPutStrLn stderr $ reportFile ++ " is not writable"
+        hPrint stderr e
+        exitFailure
 
 ----------------------------------------------------------------
 
-server :: Option -> RouteDB -> IO ()
-server opt route = safeDo $ do
+server :: Option -> RouteDB -> Reporter -> IO ()
+server opt route rpt = reportDo rpt $ do
+    unlimit
     s <- sOpen
     if debug then do
         putStrLn $ "Serving on port " ++ show port ++ "."
@@ -86,16 +92,18 @@
       else
         writePidFile
     logCheck logtype
-    sref <- initState
     myid <- getProcessID
+    stt <- initStater
     if workers == 1 then do
-        void . forkIO $ single opt route s logtype sref -- killed by signal
+        lgr <- initLogger FromSocket logtype
+         -- killed by signal
+        void . forkIO $ single opt route s rpt stt lgr
         void . forkIO $ logController logtype [myid]
-        slaveMainLoop sref
+        slaveMainLoop rpt stt lgr
       else do
-        cids <- multi opt route s logtype sref
+        cids <- multi opt route s logtype stt rpt
         void . forkIO $ logController logtype cids
-        masterMainLoop myid
+        masterMainLoop rpt myid
   where
     debug = opt_debug_mode opt
     port = opt_port opt
@@ -114,48 +122,65 @@
     logtype
       | not (opt_logging opt) = LogNone
       | debug                 = LogStdout
-      | otherwise             = LogFile logspec
+      | otherwise             = LogFile logspec sigLogCtl
 
 ----------------------------------------------------------------
 
-masterMainLoop :: ProcessID -> IO ()
-masterMainLoop myid = do
+masterMainLoop :: Reporter -> ProcessID -> IO ()
+masterMainLoop rpt myid = do
     threadDelay 10000000
     cs <- findChildren myid
-    if null cs then -- FIXME serverStatus st == Retiring
-        report "Master Mighty retired"
+    if null cs then do -- FIXME serverStatus st == Retiring
+        report rpt "Master Mighty retired"
+        finReporter rpt
+        -- No logging
+        exitSuccess
       else
-        masterMainLoop myid
+        masterMainLoop rpt myid
 
-slaveMainLoop :: StateRef -> IO ()
-slaveMainLoop sref = do
+slaveMainLoop :: Reporter -> Stater -> Logger -> IO ()
+slaveMainLoop rpt stt lgr = do
     threadDelay 1000000
-    st <- getState sref
-    if serverStatus st == Retiring && connectionCounter st == 0 then
-        report "Worker Mighty retired"
+    retiring <- isRetiring stt
+    counter <- getConnectionCounter stt
+    if retiring && counter == 0 then do
+        report rpt "Worker Mighty retired"
+        finReporter rpt
+        finLogger lgr
+        exitSuccess
       else
-        slaveMainLoop sref
+        slaveMainLoop rpt stt lgr
 
 ----------------------------------------------------------------
 
-safeDo :: IO () -> IO ()
-safeDo act = act `catches` [Handler asynHandler, Handler someHandler]
-
-asynHandler :: AsyncException -> IO ()
-asynHandler e
-  | e == ThreadKilled = return ()
-  | otherwise         = report $ bshow e
+reportDo :: Reporter -> IO () -> IO ()
+reportDo rpt act = act `catch` warpHandler rpt
 
-someHandler :: SomeException -> IO ()
-someHandler e = report $ bshow e
+warpHandler :: Reporter -> SomeException -> IO ()
+warpHandler rpt e = throwIO e `catches` handlers
+  where
+    handlers = [Handler ah, Handler ih, Handler oh, Handler sh]
+    ah :: AsyncException -> IO ()
+    ah ThreadKilled = norecode
+    ah x            = recode x
+    ih :: InvalidRequest -> IO ()
+    ih _ = norecode
+    oh :: IOException -> IO ()
+    oh x
+      | ioeGetErrorType x == ResourceVanished = norecode
+      | otherwise                             = recode x
+    sh :: SomeException -> IO ()
+    sh x = recode x
+    norecode = return ()
+    recode :: Exception e => e -> IO ()
+    recode   = report rpt . bshow
 
 ----------------------------------------------------------------
 
-single :: Option -> RouteDB -> Socket -> LogType -> StateRef -> IO ()
-single opt route s logtype sref = safeDo $ do
+single :: Option -> RouteDB -> Socket -> Reporter -> Stater -> Logger -> IO ()
+single opt route s rpt stt lgr = reportDo rpt $ do
     setGroupUser opt -- don't change the user of the master process
     ignoreSigChild
-    lgr <- logInit FromSocket logtype
     getInfo <- fileCacheInit
     mgr <- H.newManager H.def {
             -- FIXME
@@ -165,52 +190,54 @@
     setHandler sigRetire retireHandler
     setHandler sigReload (reloadHandler lgr getInfo mgr)
     setHandler sigInfo   infoHandler
-    report "Worker Mighty started"
-    single' opt route s sref lgr getInfo mgr
+    report rpt "Worker Mighty started"
+    single' opt route s rpt stt lgr getInfo mgr
   where
     stopHandler = Catch $ do
-        report "Worker Mighty finished"
+        report rpt "Worker Mighty finished"
+        finReporter rpt
+        finLogger lgr
         sClose s
         exitImmediately ExitSuccess
     retireHandler = Catch $
-        warpThreadId <$> getState sref >>>= \tid -> do
-            report "Worker Mighty retiring"
+        getWarpThreadId stt >>>= \tid -> do
+            report rpt "Worker Mighty retiring"
             killThread tid
             sClose s
-            retireStatus sref
-    reloadHandler lgr getInfo mgr = Catch $
-        warpThreadId <$> getState sref >>>= \tid ->
-        ifRouteFileIsValid opt $ \newroute -> do
-            report "Worker Mighty reloaded"
+            goRetiring stt
+    reloadHandler lggr getInfo mgr = Catch $
+        getWarpThreadId stt >>>= \tid ->
+        ifRouteFileIsValid rpt opt $ \newroute -> do
+            report rpt "Worker Mighty reloaded"
             killThread tid
-            void . forkIO $ single' opt newroute s sref lgr getInfo mgr
+            void . forkIO $ single' opt newroute s rpt stt lggr getInfo mgr
     infoHandler = Catch $ do
-        st <- getState sref
-        let i =  bshow $ connectionCounter st
-            status = bshow $ serverStatus st
-        report $ status +++ ": # of connections = " +++ i
+        i <- bshow <$> getConnectionCounter stt
+        status <- bshow <$> getServerStatus stt
+        report rpt $ status +++ ": # of connections = " +++ i
 
 single' :: Option -> RouteDB -> Socket
-        -> StateRef -> ApacheLogger -> (Path -> IO FileInfo) -> H.Manager
+        -> Reporter -> Stater -> Logger
+        -> (Path -> IO FileInfo) -> H.Manager
         -> IO ()
-single' opt route s sref lgr getInfo mgr = safeDo $ do
-    myThreadId >>= setWarpThreadId sref
+single' opt route s rpt stt lgr getInfo mgr = reportDo rpt $ do
+    myThreadId >>= setWarpThreadId stt
     runSettingsSocket setting s $ \req ->
         fileCgiApp cspec filespec cgispec revproxyspec route req
   where
     debug = opt_debug_mode opt
     setting = defaultSettings {
         settingsPort        = opt_port opt
-      , settingsOnException = if debug then printStdout else ignore
-      , settingsOnOpen      = increment sref
-      , settingsOnClose     = decrement sref
+      , settingsOnException = if debug then printStdout else warpHandler rpt
+      , settingsOnOpen      = increment stt
+      , settingsOnClose     = decrement stt
       , settingsTimeout     = opt_connection_timeout opt
       , settingsHost        = HostAny
       }
     serverName = BS.pack $ opt_server_name opt
     cspec = ClassicAppSpec {
         softwareName = serverName
-      , logger = lgr
+      , logger = apatcheLogger lgr
       , statusFileDir = fromString $ opt_status_file_dir opt
       }
     filespec = FileAppSpec {
@@ -227,13 +254,15 @@
 
 ----------------------------------------------------------------
 
-multi :: Option -> RouteDB -> Socket -> LogType -> StateRef -> IO [ProcessID]
-multi opt route s logtype sref = do
-    report "Master Mighty started"
+multi :: Option -> RouteDB -> Socket -> LogType -> Stater -> Reporter -> IO [ProcessID]
+multi opt route s logtype stt rpt = do
+    report rpt "Master Mighty started"
     ignoreSigChild
     cids <- replicateM workers $ forkProcess $ do
-        void . forkIO $ single opt route s logtype sref -- killed by signal
-        slaveMainLoop sref
+        lgr <- initLogger FromSocket logtype
+        -- killed by signal
+        void . forkIO $ single opt route s rpt stt lgr
+        slaveMainLoop rpt stt lgr
     sClose s
     setHandler sigStop   $ stopHandler cids
     setHandler sigINT    $ stopHandler cids -- C-c from keyboard when debugging
@@ -244,26 +273,28 @@
   where
     workers = opt_worker_processes opt
     stopHandler cids   = Catch $ do
-        report "Master Mighty finished"
+        report rpt "Master Mighty finished"
+        finReporter rpt
+        -- No logging
         mapM_ (sendSignal sigStop) cids
         exitImmediately ExitSuccess
     retireHandler cids = Catch $ do
-        report "Master Mighty retiring"
-        retireStatus sref
+        report rpt "Master Mighty retiring"
+        goRetiring stt
         mapM_ (sendSignal sigRetire) cids
-    reloadHandler cids = Catch $ ifRouteFileIsValid opt $ \_ -> do
-        report "Master Mighty reloaded"
+    reloadHandler cids = Catch $ ifRouteFileIsValid rpt opt $ \_ -> do
+        report rpt "Master Mighty reloaded"
         mapM_ (sendSignal sigReload) cids
     infoHandler cids   = Catch $ mapM_ (sendSignal sigInfo) cids
 
 ----------------------------------------------------------------
 
-ifRouteFileIsValid :: Option -> (RouteDB -> IO ()) -> IO ()
-ifRouteFileIsValid opt act =
+ifRouteFileIsValid :: Reporter -> Option -> (RouteDB -> IO ()) -> IO ()
+ifRouteFileIsValid rpt opt act =
     return (opt_routing_file opt) >>>= \rfile ->
     try (parseRoute rfile) >>= either reportError act
   where
-    reportError = report . BS.pack . ioeGetErrorString
+    reportError = report rpt . BS.pack . ioeGetErrorString
 
 ----------------------------------------------------------------
 
@@ -279,6 +310,26 @@
 
 ----------------------------------------------------------------
 
+unlimit :: IO ()
+unlimit = handle ignore $ do
+    hard <- hardLimit <$> getResourceLimit ResourceOpenFiles
+    let lim = if hard == ResourceLimitInfinity then
+                  ResourceLimits (ResourceLimit 10000) hard
+                else
+                  ResourceLimits hard hard
+    setResourceLimit ResourceOpenFiles lim
+
+----------------------------------------------------------------
+
+background :: Option -> IO () -> IO ()
+background opt svr = do
+    putStrLn $ "Serving on port " ++ show port ++ " and detaching this terminal..."
+    putStrLn $ "(If errors occur, they will be written in \"" ++ reportFile ++ "\".)"
+    hFlush stdout
+    daemonize svr
+  where
+    port = opt_port opt
+
 daemonize :: IO () -> IO ()
 daemonize program = ensureDetachTerminalCanWork $ do
     detachTerminal
@@ -290,8 +341,8 @@
   where
     ensureDetachTerminalCanWork p = do
         void $ forkProcess p
-        exitImmediately ExitSuccess
+        exitSuccess
     ensureNeverAttachTerminal p = do
         void $ forkProcess p
-        exitImmediately ExitSuccess
+        exitSuccess
     detachTerminal = void createSession
diff --git a/Report.hs b/Report.hs
--- a/Report.hs
+++ b/Report.hs
@@ -1,22 +1,37 @@
 {-# LANGUAGE OverloadedStrings #-}
 
-module Report where
+module Report (
+    Reporter
+  , initReporter
+  , finReporter
+  , report
+  , reportFile
+  ) where
 
 import Control.Applicative
 import Control.Exception
 import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as BS
 import Data.UnixTime
+import System.IO
 import System.Posix
 import Utils
 
 reportFile :: FilePath
 reportFile = "/tmp/mighty_report"
 
-report :: ByteString -> IO ()
-report msg = handle ignore $ do
-    pid <- BS.pack . show <$> getProcessID
-    tm <- formatUnixTime "%d %b %Y %H:%M:%S" <$> getUnixTime
-    BS.appendFile reportFile $ BS.concat [tm, ": pid = ", pid, ": ", msg, "\n"]
+newtype Reporter = Reporter Handle
 
+initReporter :: IO (Either SomeException Reporter)
+initReporter = try $ Reporter <$> openFile reportFile AppendMode
 
+finReporter :: Reporter -> IO ()
+finReporter (Reporter rpthdl) = hClose rpthdl
+
+report :: Reporter -> ByteString -> IO ()
+report (Reporter rpthdl) msg = handle ignore $ do
+    pid <- BS.pack . show <$> getProcessID
+    tm <- formatUnixTime "%d %b %Y %H:%M:%S" <$> getUnixTime
+    let logmsg = BS.concat [tm, ": pid = ", pid, ": ", msg, "\n"]
+    BS.hPutStr rpthdl logmsg
+    hFlush rpthdl
diff --git a/Signal.hs b/Signal.hs
--- a/Signal.hs
+++ b/Signal.hs
@@ -18,7 +18,10 @@
 sigRetire = sigQUIT
 
 sigInfo :: Signal
-sigInfo   = sigUSR2 -- sigUSR1 does not work on Linux
+sigInfo   = sigUSR2
+
+sigLogCtl :: Signal
+sigLogCtl = sigUSR1
 
 ----------------------------------------------------------------
 
diff --git a/State.hs b/State.hs
--- a/State.hs
+++ b/State.hs
@@ -1,49 +1,81 @@
-module State where
+module State (
+    Status(..)
+  , Stater
+  , initStater
+  , getConnectionCounter
+  , increment
+  , decrement
+  , isRetiring
+  , goRetiring
+  , getServerStatus
+  , getWarpThreadId
+  , setWarpThreadId
+  ) where
 
+import Control.Applicative
 import Control.Concurrent
 import Data.IORef
 import Utils
 
+----------------------------------------------------------------
+
 data Status = Serving | Retiring deriving (Eq, Show)
 
 data State = State {
     connectionCounter :: !Int
-  , serverStatus :: !Status
-  , warpThreadId :: !(Maybe ThreadId)
+  , serverStatus      :: !Status
+  , warpThreadId      :: !(Maybe ThreadId)
   }
 
 initialState :: State
 initialState = State 0 Serving Nothing
 
-type StateRef = IORef State
+----------------------------------------------------------------
 
-initState :: IO (IORef State)
-initState = newIORef initialState
+newtype Stater = Stater (IORef State)
 
-getState :: IORef State -> IO State
-getState = readIORef
+initStater :: IO Stater
+initStater = Stater <$> newIORef initialState
 
-retireStatus :: StateRef -> IO ()
-retireStatus sref =
-    strictAtomicModifyIORef sref $ \st -> st {
-        serverStatus = Retiring
-      , warpThreadId = Nothing
-      }
+----------------------------------------------------------------
 
-increment :: IORef State -> IO ()
-increment sref =
+getConnectionCounter :: Stater -> IO Int
+getConnectionCounter (Stater sref) = connectionCounter <$> readIORef sref
+
+increment :: Stater -> IO ()
+increment (Stater sref) =
     strictAtomicModifyIORef sref $ \st -> st {
         connectionCounter = connectionCounter st + 1
       }
 
-decrement :: IORef State -> IO ()
-decrement sref =
+decrement :: Stater -> IO ()
+decrement (Stater sref) =
     strictAtomicModifyIORef sref $ \st -> st {
         connectionCounter = connectionCounter st - 1
       }
 
-setWarpThreadId :: IORef State -> ThreadId -> IO ()
-setWarpThreadId sref tid =
+----------------------------------------------------------------
+
+getServerStatus :: Stater -> IO Status
+getServerStatus (Stater sref) = serverStatus <$> readIORef sref
+
+isRetiring :: Stater -> IO Bool
+isRetiring stt = (== Retiring) <$> getServerStatus stt
+
+goRetiring :: Stater -> IO ()
+goRetiring (Stater sref) =
+    strictAtomicModifyIORef sref $ \st -> st {
+        serverStatus = Retiring
+      , warpThreadId = Nothing
+      }
+
+----------------------------------------------------------------
+
+getWarpThreadId :: Stater -> IO (Maybe ThreadId)
+getWarpThreadId (Stater sref) = warpThreadId <$> readIORef sref
+
+setWarpThreadId :: Stater -> ThreadId -> IO ()
+setWarpThreadId (Stater sref) tid =
     strictAtomicModifyIORef sref $ \st -> st {
         warpThreadId = Just tid
       }
diff --git a/mighttpd2.cabal b/mighttpd2.cabal
--- a/mighttpd2.cabal
+++ b/mighttpd2.cabal
@@ -1,5 +1,5 @@
 Name:                   mighttpd2
-Version:                2.7.0
+Version:                2.7.1
 Author:                 Kazu Yamamoto <kazu@iij.ad.jp>
 Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>
 License:                BSD3
@@ -17,7 +17,7 @@
 
 Executable mighty
   Main-Is:              Mighty.hs
-  GHC-Options:          -Wall -threaded -eventlog
+  GHC-Options:          -Wall -threaded
   Build-Depends:        base >= 4.0 && < 5
                       -- should be removed someday
                       , blaze-html >= 0.5
@@ -50,6 +50,7 @@
                         Config.Internal
                         FileCGIApp
                         FileCache
+                        Log
                         Mighty
                         Parser
                         Process
