diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,8 @@
+## 3.2.0
+
+* newFastLogger1 ensures the ordering of logs
+  [#207](https://github.com/kazu-yamamoto/logger/pull/207)
+
 ## 3.1.2
 
 * Require unix-compat >= 0.2
diff --git a/System/Log/FastLogger.hs b/System/Log/FastLogger.hs
--- a/System/Log/FastLogger.hs
+++ b/System/Log/FastLogger.hs
@@ -109,8 +109,9 @@
 newFastLogger typ = newFastLoggerCore Nothing typ
 
 -- | Like `newFastLogger`, but creating a logger that uses only 1
--- capability. This scales less well on multi-core machines,
--- but provides time-ordered output.
+-- internal builder. This scales less on multi-core machines and
+-- consumes more memory because of an internal queue but provides
+-- time-ordered output.
 newFastLogger1 :: LogType' v -> IO (v -> IO (), IO ())
 newFastLogger1 typ = newFastLoggerCore (Just 1) typ
 
diff --git a/System/Log/FastLogger/Internal.hs b/System/Log/FastLogger/Internal.hs
--- a/System/Log/FastLogger/Internal.hs
+++ b/System/Log/FastLogger/Internal.hs
@@ -4,12 +4,16 @@
   ( module System.Log.FastLogger.IO
   , module System.Log.FastLogger.FileIO
   , module System.Log.FastLogger.LogStr
-  , module System.Log.FastLogger.Logger
+  , module System.Log.FastLogger.SingleLogger
+  , module System.Log.FastLogger.MultiLogger
+  , module System.Log.FastLogger.Write
   , module System.Log.FastLogger.LoggerSet
   ) where
 
 import System.Log.FastLogger.IO
 import System.Log.FastLogger.FileIO
 import System.Log.FastLogger.LogStr
-import System.Log.FastLogger.Logger
+import System.Log.FastLogger.SingleLogger
+import System.Log.FastLogger.MultiLogger
+import System.Log.FastLogger.Write
 import System.Log.FastLogger.LoggerSet
diff --git a/System/Log/FastLogger/LogStr.hs b/System/Log/FastLogger/LogStr.hs
--- a/System/Log/FastLogger/LogStr.hs
+++ b/System/Log/FastLogger/LogStr.hs
@@ -50,12 +50,14 @@
 instance Semi.Semigroup LogStr where
     {-# INLINE (<>) #-}
     LogStr s1 b1 <> LogStr s2 b2 = LogStr (s1 + s2) (b1 <> b2)
-#endif
-
 instance Monoid LogStr where
     mempty = LogStr 0 (toBuilder BS.empty)
+#else
+instance Monoid LogStr where
+    mempty = LogStr 0 (toBuilder BS.empty)
     {-# INLINE mappend #-}
     LogStr s1 b1 `mappend` LogStr s2 b2 = LogStr (s1 + s2) (b1 <> b2)
+#endif
 
 instance IsString LogStr where
     {-# INLINE fromString #-}
diff --git a/System/Log/FastLogger/Logger.hs b/System/Log/FastLogger/Logger.hs
deleted file mode 100644
--- a/System/Log/FastLogger/Logger.hs
+++ /dev/null
@@ -1,85 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE Safe #-}
-
-module System.Log.FastLogger.Logger (
-    Logger(..)
-  , newLogger
-  , pushLog
-  , flushLog
-  ) where
-
-
-import Control.Concurrent (MVar, withMVar)
-import Foreign.Marshal.Alloc (allocaBytes)
-import Foreign.Ptr (plusPtr)
-
-import System.Log.FastLogger.FileIO
-import System.Log.FastLogger.IO
-import System.Log.FastLogger.Imports
-import System.Log.FastLogger.LogStr
-
-----------------------------------------------------------------
-
-newtype Logger = Logger (IORef LogStr)
-
-----------------------------------------------------------------
-
-newLogger :: IO Logger
-newLogger = Logger <$> newIORef mempty
-
-----------------------------------------------------------------
-
-pushLog :: IORef FD -> BufSize -> MVar Buffer -> Logger -> LogStr -> IO ()
-pushLog fdref size mbuf logger@(Logger ref) nlogmsg@(LogStr nlen nbuilder)
-  | nlen > size = do
-      flushLog fdref size mbuf logger
-      -- Make sure we have a large enough buffer to hold the entire
-      -- contents, thereby allowing for a single write system call and
-      -- avoiding interleaving. This does not address the possibility
-      -- of write not writing the entire buffer at once.
-      allocaBytes nlen $ \buf -> withMVar mbuf $ \_ ->
-        toBufIOWith buf nlen (write fdref) nbuilder
-  | otherwise = do
-    mmsg <- atomicModifyIORef' ref checkBuf
-    case mmsg of
-        Nothing  -> return ()
-        Just msg -> withMVar mbuf $ \buf -> writeLogStr fdref buf size msg
-  where
-    checkBuf ologmsg@(LogStr olen _)
-      | size < olen + nlen = (nlogmsg, Just ologmsg)
-      | otherwise          = (ologmsg <> nlogmsg, Nothing)
-
-----------------------------------------------------------------
-
-flushLog :: IORef FD -> BufSize -> MVar Buffer -> Logger -> IO ()
-flushLog fdref size mbuf (Logger lref) = do
-    logmsg <- atomicModifyIORef' lref (\old -> (mempty, old))
-    -- If a special buffer is prepared for flusher, this MVar could
-    -- be removed. But such a code does not contribute logging speed
-    -- according to experiment. And even with the special buffer,
-    -- there is no grantee that this function is exclusively called
-    -- for a buffer. So, we use MVar here.
-    -- This is safe and speed penalty can be ignored.
-    withMVar mbuf $ \buf -> writeLogStr fdref buf size logmsg
-
-----------------------------------------------------------------
-
--- | Writting 'LogStr' using a buffer in blocking mode.
---   The size of 'LogStr' must be smaller or equal to
---   the size of buffer.
-writeLogStr :: IORef FD
-            -> Buffer
-            -> BufSize
-            -> LogStr
-            -> IO ()
-writeLogStr fdref buf size (LogStr len builder)
-  | size < len = error "writeLogStr"
-  | otherwise  = toBufIOWith buf size (write fdref) builder
-
-write :: IORef FD -> Buffer -> Int -> IO ()
-write fdref buf len' = loop buf (fromIntegral len')
-  where
-    loop bf len = do
-        written <- writeRawBufferPtr2FD fdref bf len
-        when (0 <= written && written < len) $
-            loop (bf `plusPtr` fromIntegral written) (len - written)
diff --git a/System/Log/FastLogger/LoggerSet.hs b/System/Log/FastLogger/LoggerSet.hs
--- a/System/Log/FastLogger/LoggerSet.hs
+++ b/System/Log/FastLogger/LoggerSet.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
 
 module System.Log.FastLogger.LoggerSet (
   -- * Creating a logger set
@@ -23,26 +24,35 @@
   , replaceLoggerSet
   ) where
 
-import Control.Concurrent (MVar, getNumCapabilities, myThreadId, threadCapability, takeMVar, newMVar)
+import Control.Concurrent (getNumCapabilities)
 import Control.Debounce (mkDebounce, defaultDebounceSettings, debounceAction)
-import Data.Array (Array, listArray, (!), bounds)
 
 import System.Log.FastLogger.FileIO
 import System.Log.FastLogger.IO
 import System.Log.FastLogger.Imports
 import System.Log.FastLogger.LogStr
-import System.Log.FastLogger.Logger
+import System.Log.FastLogger.MultiLogger (MultiLogger)
+import qualified System.Log.FastLogger.MultiLogger as M
+import System.Log.FastLogger.SingleLogger (SingleLogger)
+import qualified System.Log.FastLogger.SingleLogger as S
+import System.Log.FastLogger.Write
 
 ----------------------------------------------------------------
 
+data Logger = SL SingleLogger | ML MultiLogger
+
+----------------------------------------------------------------
+
 -- | A set of loggers.
 --   The number of loggers is the capabilities of GHC RTS.
 --   You can specify it with \"+RTS -N\<x\>\".
 --   A buffer is prepared for each capability.
-data LoggerSet = LoggerSet (Maybe FilePath) (IORef FD)
-                           BufSize (MVar Buffer)
-                           (Array Int Logger)
-                           (IO ())
+data LoggerSet = LoggerSet {
+    lgrsetFilePath :: Maybe FilePath
+  , lgrsetFdRef    :: IORef FD
+  , lgrsetLogger   :: Logger
+  , lgrsetDebounce :: IO ()
+  }
 
 -- | Creating a new 'LoggerSet' using a file.
 --
@@ -90,31 +100,33 @@
     n <- case mn of
       Just n' -> return n'
       Nothing -> getNumCapabilities
-    loggers <- replicateM n newLogger
-    let arr = listArray (0,n-1) loggers
-    fref <- newIORef fd
+    fdref <- newIORef fd
     let bufsiz = max 1 size
-    mbuf <- getBuffer bufsiz >>= newMVar
+    logger <- if n == 1 && mn == Just 1 then
+                  SL <$> S.newSingleLogger bufsiz fdref
+                else do
+                  ML <$> M.newMultiLogger n bufsiz fdref
     flush <- mkDebounce defaultDebounceSettings
-        { debounceAction = flushLogStrRaw fref bufsiz mbuf arr
+        { debounceAction = flushLogStrRaw logger
         }
-    return $ LoggerSet mfile fref bufsiz mbuf arr flush
+    return $ LoggerSet {
+        lgrsetFilePath = mfile
+      , lgrsetFdRef    = fdref
+      , lgrsetLogger   = logger
+      , lgrsetDebounce = flush
+      }
 
 -- | Writing a log message to the corresponding buffer.
 --   If the buffer becomes full, the log messages in the buffer
 --   are written to its corresponding file, stdout, or stderr.
 pushLogStr :: LoggerSet -> LogStr -> IO ()
-pushLogStr (LoggerSet _ fdref size mbuf arr flush) logmsg = do
-    (i, _) <- myThreadId >>= threadCapability
-    -- The number of capability could be dynamically changed.
-    -- So, let's check the upper boundary of the array.
-    let u = snd $ bounds arr
-        lim = u + 1
-        j | i < lim   = i
-          | otherwise = i `mod` lim
-    let logger = arr ! j
-    pushLog fdref size mbuf logger logmsg
-    flush
+pushLogStr LoggerSet{..} logmsg = case lgrsetLogger of
+  SL sl -> do
+      pushLog sl logmsg
+      lgrsetDebounce
+  ML ml -> do
+      pushLog ml logmsg
+      lgrsetDebounce
 
 -- | Same as 'pushLogStr' but also appends a newline.
 pushLogStrLn :: LoggerSet -> LogStr -> IO ()
@@ -130,41 +142,36 @@
 --   function can be used to force flushing outside of the debounced
 --   flush calls.
 flushLogStr :: LoggerSet -> IO ()
-flushLogStr (LoggerSet _ fref size mbuf arr _) = flushLogStrRaw fref size mbuf arr
+flushLogStr LoggerSet{..} = flushLogStrRaw lgrsetLogger
 
-flushLogStrRaw :: IORef FD -> BufSize -> MVar Buffer -> Array Int Logger -> IO ()
-flushLogStrRaw fdref size mbuf arr = do
-    let (l,u) = bounds arr
-    mapM_ flushIt [l .. u]
-  where
-    flushIt i = flushLog fdref size mbuf (arr ! i)
+flushLogStrRaw :: Logger -> IO ()
+flushLogStrRaw (SL sl) = flushAllLog sl
+flushLogStrRaw (ML ml) = flushAllLog ml
 
 -- | Renewing the internal file information in 'LoggerSet'.
 --   This does nothing for stdout and stderr.
 renewLoggerSet :: LoggerSet -> IO ()
-renewLoggerSet (LoggerSet Nothing     _    _ _ _ _) = return ()
-renewLoggerSet (LoggerSet (Just file) fref _ _ _ _) = do
-    newfd <- openFileFD file
-    oldfd <- atomicModifyIORef' fref (\fd -> (newfd, fd))
-    closeFD oldfd
+renewLoggerSet LoggerSet{..} = case lgrsetFilePath of
+  Nothing -> return ()
+  Just file -> do
+      newfd <- openFileFD file
+      oldfd <- atomicModifyIORef' lgrsetFdRef (\fd -> (newfd, fd))
+      closeFD oldfd
 
 -- | Flushing the buffers, closing the internal file information
 --   and freeing the buffers.
 rmLoggerSet :: LoggerSet -> IO ()
-rmLoggerSet (LoggerSet mfile fdref size mbuf arr _) = do
-    fd <- readIORef fdref
+rmLoggerSet LoggerSet{..} = do
+    fd <- readIORef lgrsetFdRef
     when (isFDValid fd) $ do
-        let (l,u) = bounds arr
-        let nums = [l .. u]
-        mapM_ flushIt nums
-        takeMVar mbuf >>= freeBuffer
-        when (isJust mfile) $ closeFD fd
-        writeIORef fdref invalidFD
-  where
-    flushIt i = flushLog fdref size mbuf(arr ! i)
+        case lgrsetLogger of
+          SL sl -> stopLoggers sl
+          ML ml -> stopLoggers ml
+        when (isJust lgrsetFilePath) $ closeFD fd
+        writeIORef lgrsetFdRef invalidFD
 
 -- | Replacing the file path in 'LoggerSet' and returning a new
 --   'LoggerSet' and the old file path.
 replaceLoggerSet :: LoggerSet -> FilePath -> (LoggerSet, Maybe FilePath)
-replaceLoggerSet (LoggerSet current_path a b c d e) new_file_path =
-    (LoggerSet (Just new_file_path) a b c d e, current_path)
+replaceLoggerSet lgrset@LoggerSet{..} new_file_path =
+    (lgrset { lgrsetFilePath = Just new_file_path }, lgrsetFilePath)
diff --git a/System/Log/FastLogger/MultiLogger.hs b/System/Log/FastLogger/MultiLogger.hs
new file mode 100644
--- /dev/null
+++ b/System/Log/FastLogger/MultiLogger.hs
@@ -0,0 +1,120 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module System.Log.FastLogger.MultiLogger (
+    MultiLogger
+  , newMultiLogger
+  ) where
+
+
+import Control.Concurrent (myThreadId, threadCapability, MVar, newMVar, withMVar, takeMVar)
+import Data.Array (Array, listArray, (!), bounds)
+
+import System.Log.FastLogger.FileIO
+import System.Log.FastLogger.IO
+import System.Log.FastLogger.Imports
+import System.Log.FastLogger.LogStr
+import System.Log.FastLogger.Write
+
+----------------------------------------------------------------
+
+newtype MLogger = MLogger {
+    lgrRef :: IORef LogStr
+  }
+
+-- | A scale but non-time-ordered logger.
+data MultiLogger = MultiLogger {
+    mlgrArray   :: Array Int MLogger
+  , mlgrMBuffer :: MVar Buffer
+  , mlgrBufSize :: BufSize
+  , mlgrFdRef   :: IORef FD
+  }
+
+instance Loggers MultiLogger where
+    stopLoggers = System.Log.FastLogger.MultiLogger.stopLoggers
+    pushLog     = System.Log.FastLogger.MultiLogger.pushLog
+    flushAllLog = System.Log.FastLogger.MultiLogger.flushAllLog
+
+----------------------------------------------------------------
+
+newMLogger :: IO MLogger
+newMLogger = MLogger <$> newIORef mempty
+
+-- | Creating `MultiLogger`.
+--   The first argument is the number of the internal builders.
+newMultiLogger :: Int -> BufSize -> IORef FD -> IO MultiLogger
+newMultiLogger n bufsize fdref= do
+    mbuf <- getBuffer bufsize >>= newMVar
+    arr <- listArray (0,n-1) <$> replicateM n newMLogger
+    return $ MultiLogger {
+        mlgrArray   = arr
+      , mlgrMBuffer = mbuf
+      , mlgrBufSize = bufsize
+      , mlgrFdRef   = fdref
+      }
+
+----------------------------------------------------------------
+
+pushLog :: MultiLogger -> LogStr -> IO ()
+pushLog ml@MultiLogger{..} logmsg = do
+    (i, _) <- myThreadId >>= threadCapability
+    -- The number of capability could be dynamically changed.
+    -- So, let's check the upper boundary of the array.
+    let u = snd $ bounds mlgrArray
+        lim = u + 1
+        j | i < lim   = i
+          | otherwise = i `mod` lim
+    let logger = mlgrArray ! j
+    pushLog' logger logmsg
+  where
+    pushLog' logger@MLogger{..} nlogmsg@(LogStr nlen _)
+      | nlen > mlgrBufSize = do
+          flushLog ml logger
+          -- Make sure we have a large enough buffer to hold the entire
+          -- contents, thereby allowing for a single write system call and
+          -- avoiding interleaving. This does not address the possibility
+          -- of write not writing the entire buffer at once.
+          writeBigLogStr' ml nlogmsg
+      | otherwise = do
+        action <- atomicModifyIORef' lgrRef checkBuf
+        action
+      where
+        checkBuf ologmsg@(LogStr olen _)
+          | mlgrBufSize < olen + nlen = (nlogmsg, writeLogStr' ml ologmsg)
+          | otherwise                 = (ologmsg <> nlogmsg, return ())
+
+----------------------------------------------------------------
+
+flushAllLog :: MultiLogger -> IO ()
+flushAllLog ml@MultiLogger{..} = do
+    let flushIt i = flushLog ml (mlgrArray ! i)
+        (l,u) = bounds mlgrArray
+        nums = [l .. u]
+    mapM_ flushIt nums
+
+flushLog :: MultiLogger -> MLogger -> IO ()
+flushLog ml MLogger{..} = do
+    -- If a special buffer is prepared for flusher, this MVar could
+    -- be removed. But such a code does not contribute logging speed
+    -- according to experiment. And even with the special buffer,
+    -- there is no grantee that this function is exclusively called
+    -- for a buffer. So, we use MVar here.
+    -- This is safe and speed penalty can be ignored.
+    old <- atomicModifyIORef' lgrRef (\old -> (mempty, old))
+    writeLogStr' ml old
+
+----------------------------------------------------------------
+
+stopLoggers :: MultiLogger -> IO ()
+stopLoggers ml@MultiLogger{..} = do
+  System.Log.FastLogger.MultiLogger.flushAllLog ml
+  takeMVar mlgrMBuffer >>= freeBuffer
+
+----------------------------------------------------------------
+
+writeLogStr' :: MultiLogger -> LogStr -> IO ()
+writeLogStr' MultiLogger{..} logstr =
+    withMVar mlgrMBuffer $ \buf -> writeLogStr buf mlgrFdRef logstr
+
+writeBigLogStr' :: MultiLogger -> LogStr -> IO ()
+writeBigLogStr' MultiLogger{..} logstr =
+    withMVar mlgrMBuffer $ \_ -> writeBigLogStr mlgrFdRef logstr
diff --git a/System/Log/FastLogger/SingleLogger.hs b/System/Log/FastLogger/SingleLogger.hs
new file mode 100644
--- /dev/null
+++ b/System/Log/FastLogger/SingleLogger.hs
@@ -0,0 +1,104 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module System.Log.FastLogger.SingleLogger (
+    SingleLogger
+  , newSingleLogger
+  ) where
+
+import Control.Concurrent (forkIO, newEmptyMVar, MVar, takeMVar, putMVar)
+import Control.Concurrent.STM
+
+import System.Log.FastLogger.FileIO
+import System.Log.FastLogger.IO
+import System.Log.FastLogger.Imports
+import System.Log.FastLogger.LogStr
+import System.Log.FastLogger.Write
+
+----------------------------------------------------------------
+
+-- | A non-scale but time-ordered logger.
+data SingleLogger = SingleLogger {
+    slgrRef     :: IORef (LogStr
+                         ,[LogStr])-- writer queue
+  , slgrKill    :: IO ()
+  , slgrWakeup  :: IO ()
+  , slgrBuffer  :: Buffer
+  , slgrBufSize :: BufSize
+  , slgrFdRef   :: IORef FD
+  }
+
+instance Loggers SingleLogger where
+    stopLoggers = System.Log.FastLogger.SingleLogger.stopLoggers
+    pushLog     = System.Log.FastLogger.SingleLogger.pushLog
+    flushAllLog = System.Log.FastLogger.SingleLogger.flushAllLog
+
+----------------------------------------------------------------
+
+writer :: BufSize -> Buffer -> IORef FD -> TVar Int -> IORef (LogStr, [LogStr]) -> MVar () -> IO ()
+writer bufsize buf fdref tvar ref mvar = loop (0 :: Int)
+  where
+    loop cnt = do
+        cnt' <- atomically $ do
+            n <- readTVar tvar
+            check (n /= cnt)
+            return n
+        msgs <- reverse <$> atomicModifyIORef' ref (\(msg,q) -> ((msg,[]),q))
+        cont <- go msgs
+        if cont then
+            loop cnt'
+          else
+            putMVar mvar ()
+    go [] = return True
+    go (msg@(LogStr len _):msgs)
+      | len <  0       = return False
+      | len <= bufsize = writeLogStr buf fdref msg >> go msgs
+      | otherwise      = writeBigLogStr  fdref msg >> go msgs
+
+----------------------------------------------------------------
+
+-- | Creating `SingleLogger`.
+newSingleLogger :: BufSize -> IORef FD -> IO SingleLogger
+newSingleLogger bufsize fdref = do
+    tvar <- newTVarIO 0
+    ref <- newIORef (mempty,[])
+    mvar <- newEmptyMVar
+    buf <- getBuffer bufsize
+    _ <- forkIO $ writer bufsize buf fdref tvar ref mvar
+    let kill = do
+            let fin = LogStr (-1) mempty
+            atomicModifyIORef' ref (\(old,q) -> ((mempty,fin:old:q),()))
+            takeMVar mvar
+        wakeup = atomically $ modifyTVar' tvar (+ 1)
+    return $ SingleLogger {
+        slgrRef     = ref
+      , slgrKill    = kill
+      , slgrWakeup  = wakeup
+      , slgrBuffer  = buf
+      , slgrBufSize = bufsize
+      , slgrFdRef   = fdref
+      }
+
+----------------------------------------------------------------
+
+pushLog :: SingleLogger -> LogStr -> IO ()
+pushLog SingleLogger{..} nlogmsg@(LogStr nlen _)
+  | nlen > slgrBufSize = do
+        atomicModifyIORef' slgrRef (\(old,q) -> ((mempty,nlogmsg:old:q),()))
+        slgrWakeup
+  | otherwise = do
+        wake <- atomicModifyIORef' slgrRef checkBuf
+        when wake slgrWakeup
+  where
+    checkBuf (ologmsg@(LogStr olen _),q)
+      | slgrBufSize < olen + nlen = ((nlogmsg, nlogmsg:q), True)
+      | otherwise                 = ((ologmsg <> nlogmsg, q), False)
+
+flushAllLog :: SingleLogger -> IO ()
+flushAllLog SingleLogger{..} = do
+    atomicModifyIORef' slgrRef (\(old,q) -> ((mempty,old:q),()))
+    slgrWakeup
+
+stopLoggers :: SingleLogger -> IO ()
+stopLoggers SingleLogger{..} = do
+    slgrKill
+    freeBuffer slgrBuffer
diff --git a/System/Log/FastLogger/Write.hs b/System/Log/FastLogger/Write.hs
new file mode 100644
--- /dev/null
+++ b/System/Log/FastLogger/Write.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module System.Log.FastLogger.Write (
+    writeLogStr
+  , writeBigLogStr
+  , Loggers(..)
+  ) where
+
+import Foreign.Marshal.Alloc (allocaBytes)
+import Foreign.Ptr (plusPtr)
+
+import System.Log.FastLogger.FileIO
+import System.Log.FastLogger.IO
+import System.Log.FastLogger.Imports
+import System.Log.FastLogger.LogStr
+
+----------------------------------------------------------------
+
+-- | Writting 'LogStr' using a buffer in blocking mode.
+--   The size of 'LogStr' must be smaller or equal to
+--   the size of buffer.
+writeLogStr :: Buffer -> IORef FD -> LogStr -> IO ()
+writeLogStr buf fdref (LogStr len builder) =
+    toBufIOWith buf len (write fdref) builder
+
+-- | Writting 'LogStr' using a temporary buffer.
+writeBigLogStr :: IORef FD -> LogStr -> IO ()
+writeBigLogStr fdref (LogStr len builder) =  allocaBytes len $ \buf ->
+    toBufIOWith buf len (write fdref) builder
+
+write :: IORef FD -> Buffer -> Int -> IO ()
+write fdref buf len' = loop buf (fromIntegral len')
+  where
+    loop bf len = do
+        written <- writeRawBufferPtr2FD fdref bf len
+        when (0 <= written && written < len) $
+            loop (bf `plusPtr` fromIntegral written) (len - written)
+
+----------------------------------------------------------------
+
+-- | A class for internal loggers.
+class Loggers a where
+    stopLoggers :: a -> IO ()
+    pushLog     :: a -> LogStr -> IO ()
+    flushAllLog :: a -> IO ()
diff --git a/fast-logger.cabal b/fast-logger.cabal
--- a/fast-logger.cabal
+++ b/fast-logger.cabal
@@ -1,5 +1,5 @@
 Name:                   fast-logger
-Version:                3.1.2
+Version:                3.2.0
 Author:                 Kazu Yamamoto <kazu@iij.ad.jp>
 Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>
 License:                BSD3
@@ -23,10 +23,12 @@
                         System.Log.FastLogger.LoggerSet
                         System.Log.FastLogger.Types
   Other-Modules:        System.Log.FastLogger.Imports
-                        System.Log.FastLogger.IO
                         System.Log.FastLogger.FileIO
+                        System.Log.FastLogger.IO
                         System.Log.FastLogger.LogStr
-                        System.Log.FastLogger.Logger
+                        System.Log.FastLogger.MultiLogger
+                        System.Log.FastLogger.SingleLogger
+                        System.Log.FastLogger.Write
   Build-Depends:        base >= 4.9 && < 5
                       , array
                       , auto-update >= 0.1.2
@@ -34,6 +36,7 @@
                       , bytestring >= 0.10.4
                       , directory
                       , filepath
+                      , stm
                       , text
                       , unix-time >= 0.4.4
                       , unix-compat >= 0.2
@@ -48,10 +51,11 @@
   Default-Language: Haskell2010
   Type:             exitcode-stdio-1.0
 
-  Ghc-Options:      -Wall -threaded
+  Ghc-Options:      -Wall -threaded -rtsopts -with-rtsopts=-N
   Other-Modules:    FastLoggerSpec
   Build-Tools:      hspec-discover >= 2.6
   Build-Depends:    base >= 4 && < 5
+                  , async
                   , bytestring >= 0.10.4
                   , directory
                   , fast-logger
diff --git a/test/FastLoggerSpec.hs b/test/FastLoggerSpec.hs
--- a/test/FastLoggerSpec.hs
+++ b/test/FastLoggerSpec.hs
@@ -1,20 +1,29 @@
-{-# LANGUAGE OverloadedStrings, BangPatterns, CPP #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
 
-module FastLoggerSpec where
+module FastLoggerSpec (spec) where
 
 #if __GLASGOW_HASKELL__ < 709
 import Control.Applicative ((<$>))
 #endif
 import Control.Exception (finally)
-import Control.Monad (when)
+import Control.Concurrent (getNumCapabilities)
+import Control.Concurrent.Async (forConcurrently_)
+import Control.Monad (when, forM_)
 import qualified Data.ByteString.Char8 as BS
+import Data.List (sort)
+#if !MIN_VERSION_base(4,11,0)
 import Data.Monoid ((<>))
+#endif
 import Data.String (IsString(fromString))
 import System.Directory (doesFileExist, removeFile)
-import System.Log.FastLogger
+import Text.Printf (printf)
+
 import Test.Hspec
 import Test.Hspec.QuickCheck (prop)
 
+import System.Log.FastLogger
+
 spec :: Spec
 spec = do
   describe "instance Show LogStr" $ do
@@ -22,12 +31,14 @@
       let logstr :: LogStr
           logstr = fromString str
       in show logstr == show str
+
   describe "instance Eq LogStr" $ do
     prop "it should be consistent with instance IsString" $ \str1 str2 ->
       let logstr1, logstr2 :: LogStr
           logstr1 = fromString str1
           logstr2 = fromString str2
       in (logstr1 == logstr2) == (str1 == str2)
+
   describe "pushLogMsg" $ do
     it "is safe for a large message" $ safeForLarge [
         100
@@ -38,29 +49,26 @@
       ]
     it "logs all messages" logAllMsgs
 
-nullLogger :: IO LoggerSet
-#ifdef mingw32_HOST_OS
-nullLogger = newFileLoggerSet 4096 "nul"
-#else
-nullLogger = newFileLoggerSet 4096 "/dev/null"
-#endif
+  describe "fastlogger 1" $ do
+    it "maintains the ordering of log messages" logOrdering
 
+tempFile :: FilePath
+tempFile = "test/temp.txt"
+
 safeForLarge :: [Int] -> IO ()
-safeForLarge ns = mapM_ safeForLarge' ns
+safeForLarge = mapM_ safeForLarge'
 
 safeForLarge' :: Int -> IO ()
-safeForLarge' n = flip finally (cleanup tmpfile) $ do
-    cleanup tmpfile
-    lgrset <- newFileLoggerSet defaultBufSize tmpfile
+safeForLarge' n = flip finally (cleanup tempFile) $ do
+    cleanup tempFile
+    lgrset <- newFileLoggerSet defaultBufSize tempFile
     let xs = toLogStr $ BS.pack $ take (abs n) (cycle ['a'..'z'])
         lf = "x"
     pushLogStr lgrset $ xs <> lf
     flushLogStr lgrset
     rmLoggerSet lgrset
-    bs <- BS.readFile tmpfile
+    bs <- BS.readFile tempFile
     bs `shouldBe` BS.pack (take (abs n) (cycle ['a'..'z']) <> "x")
-    where
-        tmpfile = "test/temp"
 
 cleanup :: FilePath -> IO ()
 cleanup file = do
@@ -68,16 +76,39 @@
     when exist $ removeFile file
 
 logAllMsgs :: IO ()
-logAllMsgs = logAll "LICENSE" `finally` cleanup tmpfile
+logAllMsgs = logAll "LICENSE" `finally` cleanup tempFile
   where
-    tmpfile = "test/temp"
     logAll file = do
-        cleanup tmpfile
-        lgrset <- newFileLoggerSet 512 tmpfile
+        cleanup tempFile
+        lgrset <- newFileLoggerSet 512 tempFile
         src <- BS.readFile file
         let bs = (<> "\n") . toLogStr <$> BS.lines src
         mapM_ (pushLogStr lgrset) bs
         flushLogStr lgrset
         rmLoggerSet lgrset
-        dst <- BS.readFile tmpfile
+        dst <- BS.readFile tempFile
         dst `shouldBe` src
+
+logOrdering :: IO ()
+logOrdering = flip finally (cleanup tempFile) $ do
+    cleanup tempFile
+    -- 128 is small enough for out-of-ordering
+    (pushlog, teardown) <- newFastLogger1 $ LogFileNoRotate tempFile 128
+    numCapabilities <- getNumCapabilities
+    let concurrency = numCapabilities * 200 :: Int
+        logEntriesCount = 100 :: Int
+    forConcurrently_ [0 .. concurrency - 1] $ \t ->
+      forM_ [0 .. logEntriesCount - 1] $ \i -> do
+        let tag = mktag t
+            cnt = printf "%02d" i :: String
+            logmsg = toLogStr tag <> "log line nr: " <> toLogStr cnt <> "\n"
+        pushlog logmsg
+    teardown
+    xs <- BS.lines <$> BS.readFile tempFile
+    forM_ [0 .. concurrency - 1] $ \t -> do
+        let tag = BS.pack $ mktag t
+            msgs = filter (tag `BS.isPrefixOf`) xs
+        sort msgs `shouldBe` msgs
+  where
+    mktag :: Int -> String
+    mktag t = "thread id: " <> show t <> " "
