fast-logger-3.2.4/0000755000000000000000000000000007346545000012123 5ustar0000000000000000fast-logger-3.2.4/ChangeLog.md0000644000000000000000000000773307346545000014306 0ustar0000000000000000## 3.2.4 * Avoid unnecessary copy for Text values with text-2.0 [#219](https://github.com/kazu-yamamoto/logger/pull/219) ## 3.2.3 * Ensuring flush for single logger. [#214](https://github.com/kazu-yamamoto/logger/pull/214) ## 3.2.2 * Corrected handling of messages at the buffer boundary in the SingleLogger [#211](https://github.com/kazu-yamamoto/logger/pull/211) ## 3.2.1 * Fixing a bug where a single logger is not killed ## 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 [#206](https://github.com/kazu-yamamoto/logger/pull/206) * Remove Safe if directory >= 1.3.8 [#199](https://github.com/kazu-yamamoto/logger/pull/199) ## 3.1.1 * More time-ordered logging functions [#199](https://github.com/kazu-yamamoto/logger/pull/199) ## 3.1.0 * Having a single Buffer in LoggerSet for locking [#197](https://github.com/kazu-yamamoto/logger/pull/197. This would have performance penalty. So, the major version bumps up. If you see performance regression, please register an issue on github. ## 3.0.5 * recovering backward compatibility for newFileLoggerSet. ## 3.0.4 * New API: `newFastLogger1` which use only one capability. * Making `FD` safer with `invalidFD`. ## 3.0.3 * Dropping support of GHC 7.x. * Add `ToLogStr` instance for `ShortByteString`. Add lower bound on `bytestring` dependency to ensure that `bytestring` exports `Data.ByteString.Short`. ## 3.0.2 * Fixing documentation. ## 3.0.1 * Creating the `Internal` module. [#185](https://github.com/kazu-yamamoto/logger/pull/185) ## 3.0.0 * Allowing the callback logger to be generic. [#182](https://github.com/kazu-yamamoto/logger/pull/180) This is a BREAKING CHANGE. Users should do: 1. Importing `LogType'` and related constructors because `LogType` is now a type alias. 2. Using `{-# LANGUAGE GADTs #-}`, even if you aren't using anything new, any time you try and `case` over values of type `LogType'`. ## 2.4.17 * Obtaining a fresh fd from IORef just before writing. [#180](https://github.com/kazu-yamamoto/logger/pull/180) ## 2.4.16 * Using strict language extensions. ## 2.4.15 * Rescuing GHC 7.8. ## 2.4.14 * Add `ToLogStr` instances for the following types: signed integers, unsigned integers, floating-point numbers. These instances all use decimal encodings. [#177](https://github.com/kazu-yamamoto/logger/pull/177) ## 2.4.11 * Give an explicit definition for (<>) in LogStr's Semigroup instance. [#155](https://github.com/kazu-yamamoto/logger/pull/155) ## 2.4.10 * Fix Windows build on GHC 7.8. [#121](https://github.com/kazu-yamamoto/logger/pull/121) ## 2.4.9 * Fixing build on Windows. [#118](https://github.com/kazu-yamamoto/logger/pull/118) ## 2.4.8 * Add Semigroup instance to LogStr [#115](https://github.com/kazu-yamamoto/logger/pull/115) * Added note on log message ordering [#116](https://github.com/kazu-yamamoto/logger/pull/116) ## 2.4.7 * Fixing interleaved log output when messages are larger than buffer size. [#103](https://github.com/kazu-yamamoto/logger/pull/103) ## 2.4.6 * Ensuring that stdio is flushed. [#92](https://github.com/kazu-yamamoto/logger/pull/92) ## 2.4.5 * Bringing backward compatibility back. ## 2.4.4 * New API: newFastLogger and newTimedFastLogger. * LogType and date cache are transferred from wai-logger. ## 2.4.3 * Opening files in the append mode on Windows. ## 2.4.2 * Fixing a buf of long log messages [#80](https://github.com/kazu-yamamoto/logger/pull/80) * Log rotation support for Windows [#79](https://github.com/kazu-yamamoto/logger/pull/79) * Unsupporting GHC 7.4. ## 2.4.1 * Restore compatibility with bytestring < 0.10 * Mark fast-logger modules as Safe/Trustworth [#68](https://github.com/kazu-yamamoto/logger/pull/68) ## 2.4.0 * Providing pushLogStrLn. [#64](https://github.com/kazu-yamamoto/logger/pull/64) ## 2.3.1 * No changes. ## 2.3.0 * Move from blaze-builder to `Data.ByteString.Builder` [#55](https://github.com/kazu-yamamoto/logger/pull/55) fast-logger-3.2.4/LICENSE0000644000000000000000000000276507346545000013142 0ustar0000000000000000Copyright (c) 2009, IIJ Innovation Institute Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. fast-logger-3.2.4/README.md0000644000000000000000000000004607346545000013402 0ustar0000000000000000## fast-logger A fast logging system fast-logger-3.2.4/Setup.hs0000644000000000000000000000005607346545000013560 0ustar0000000000000000import Distribution.Simple main = defaultMain fast-logger-3.2.4/System/Log/0000755000000000000000000000000007346545000014130 5ustar0000000000000000fast-logger-3.2.4/System/Log/FastLogger.hs0000644000000000000000000002555507346545000016535 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GADTs #-} -- | This module provides a fast logging system which -- scales on multicore environments (i.e. +RTS -N\). -- -- Note: This library does not guarantee correct ordering of log messages -- when program is run on more than one core thus users -- should rely more on message timestamps than on their order in the -- log. module System.Log.FastLogger ( -- * FastLogger FastLogger , LogType , LogType'(..) , newFastLogger , newFastLogger1 , withFastLogger -- * Timed FastLogger , TimedFastLogger , newTimedFastLogger , withTimedFastLogger -- * Log messages , LogStr , ToLogStr(..) , fromLogStr , logStrLength -- * Buffer size , BufSize , defaultBufSize -- * LoggerSet , module System.Log.FastLogger.LoggerSet -- * Date cache , module System.Log.FastLogger.Date -- * File rotation , module System.Log.FastLogger.File -- * Types , module System.Log.FastLogger.Types ) where import Control.Concurrent (MVar, newMVar, tryTakeMVar, putMVar) import Control.Exception (handle, SomeException(..), bracket) import System.EasyFile (getFileSize) import System.Log.FastLogger.Date import System.Log.FastLogger.File import System.Log.FastLogger.IO import System.Log.FastLogger.Imports import System.Log.FastLogger.LogStr import System.Log.FastLogger.LoggerSet import System.Log.FastLogger.Types ---------------------------------------------------------------- -- | 'FastLogger' simply log 'logStr'. type FastLogger = LogStr -> IO () -- | 'TimedFastLogger' pass 'FormattedTime' to callback and simply log its result. -- this can be used to customize how to log timestamp. -- -- Usually, one would write a wrapper on top of 'TimedFastLogger', for example: -- -- > {-# LANGUAGE OverloadedStrings #-} -- > -- > log :: TimedFastLogger -> LogStr -> IO () -- > log logger msg = logger (\time -> toLogStr (show time) <> " " <> msg <> "\n") type TimedFastLogger = (FormattedTime -> LogStr) -> IO () type LogType = LogType' LogStr -- | Logger Type. data LogType' a where LogNone :: LogType' LogStr -- ^ No logging. LogStdout :: BufSize -> LogType' LogStr -- ^ Logging to stdout. -- 'BufSize' is a buffer size -- for each capability. LogStderr :: BufSize -> LogType' LogStr -- ^ Logging to stderr. -- 'BufSize' is a buffer size -- for each capability. LogFileNoRotate :: FilePath -> BufSize -> LogType' LogStr -- ^ Logging to a file. -- 'BufSize' is a buffer size -- for each capability. LogFile :: FileLogSpec -> BufSize -> LogType' LogStr -- ^ Logging to a file. -- 'BufSize' is a buffer size -- for each capability. -- File rotation is done on-demand. LogFileTimedRotate :: TimedFileLogSpec -> BufSize -> LogType' LogStr -- ^ Logging to a file. -- 'BufSize' is a buffer size -- for each capability. -- Rotation happens based on check specified -- in 'TimedFileLogSpec'. LogCallback :: (v -> IO ()) -> IO () -> LogType' v -- ^ Logging with a log and flush action. -- run flush after log each message. -- | Initialize a 'FastLogger' without attaching timestamp -- a tuple of logger and clean up action are returned. -- This type signature should be read as: -- -- > newFastLogger :: LogType -> IO (FastLogger, IO ()) -- -- This logger uses `numCapabilities` many buffers, and thus -- does not provide time-ordered output. -- For time-ordered output, use `newFastLogger1`. newFastLogger :: LogType' v -> IO (v -> IO (), IO ()) newFastLogger typ = newFastLoggerCore Nothing typ -- | Like `newFastLogger`, but creating a logger that uses only 1 -- 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 newFastLoggerCore :: Maybe Int -> LogType' v -> IO (v -> IO (), IO ()) newFastLoggerCore mn typ = case typ of LogNone -> return (const noOp, noOp) LogStdout bsize -> newStdoutLoggerSetN bsize mn >>= stdLoggerInit LogStderr bsize -> newStderrLoggerSetN bsize mn >>= stdLoggerInit LogFileNoRotate fp bsize -> newFileLoggerSetN bsize mn fp >>= fileLoggerInit LogFile fspec bsize -> rotateLoggerInit fspec bsize LogFileTimedRotate fspec bsize -> timedRotateLoggerInit fspec bsize LogCallback cb flush -> return (\str -> cb str >> flush, noOp) where stdLoggerInit lgrset = return (pushLogStr lgrset, rmLoggerSet lgrset) fileLoggerInit lgrset = return (pushLogStr lgrset, rmLoggerSet lgrset) rotateLoggerInit fspec bsize = do lgrset <- newFileLoggerSetN bsize mn $ log_file fspec ref <- newIORef (0 :: Int) mvar <- newMVar () let logger str = do cnt <- decrease ref pushLogStr lgrset str when (cnt <= 0) $ tryRotate lgrset fspec ref mvar return (logger, rmLoggerSet lgrset) timedRotateLoggerInit fspec bsize = do cache <- newTimeCache $ timed_timefmt fspec now <- cache lgrset <- newFileLoggerSetN bsize mn $ prefixTime now $ timed_log_file fspec ref <- newIORef now mvar <- newMVar lgrset let logger str = do ct <- cache updated <- updateTime (timed_same_timeframe fspec) ref ct when updated $ tryTimedRotate fspec ct mvar pushLogStr lgrset str return (logger, rmLoggerSet lgrset) -- | 'bracket' version of 'newFastLogger' withFastLogger :: LogType -> (FastLogger -> IO a) -> IO a withFastLogger typ log' = bracket (newFastLogger typ) snd (log' . fst) -- | Initialize a 'FastLogger' with timestamp attached to each message. -- a tuple of logger and clean up action are returned. newTimedFastLogger :: IO FormattedTime -- ^ How do we get 'FormattedTime'? -- "System.Log.FastLogger.Date" provide cached formatted time. -> LogType -> IO (TimedFastLogger, IO ()) newTimedFastLogger tgetter typ = case typ of LogNone -> return (const noOp, noOp) LogStdout bsize -> newStdoutLoggerSet bsize >>= stdLoggerInit LogStderr bsize -> newStderrLoggerSet bsize >>= stdLoggerInit LogFileNoRotate fp bsize -> newFileLoggerSet bsize fp >>= fileLoggerInit LogFile fspec bsize -> rotateLoggerInit fspec bsize LogFileTimedRotate fspec bsize -> timedRotateLoggerInit fspec bsize LogCallback cb flush -> return (\f -> tgetter >>= cb . f >> flush, noOp) where stdLoggerInit lgrset = return ( \f -> tgetter >>= pushLogStr lgrset . f, rmLoggerSet lgrset) fileLoggerInit lgrset = return (\f -> tgetter >>= pushLogStr lgrset . f, rmLoggerSet lgrset) rotateLoggerInit fspec bsize = do lgrset <- newFileLoggerSet bsize $ log_file fspec ref <- newIORef (0 :: Int) mvar <- newMVar () let logger f = do cnt <- decrease ref t <- tgetter pushLogStr lgrset (f t) when (cnt <= 0) $ tryRotate lgrset fspec ref mvar return (logger, rmLoggerSet lgrset) timedRotateLoggerInit fspec bsize = do cache <- newTimeCache $ timed_timefmt fspec now <- cache lgrset <- newFileLoggerSet bsize $ prefixTime now $ timed_log_file fspec ref <- newIORef now mvar <- newMVar lgrset let logger f = do ct <- cache updated <- updateTime (timed_same_timeframe fspec) ref ct when updated $ tryTimedRotate fspec ct mvar t <- tgetter pushLogStr lgrset (f t) return (logger, rmLoggerSet lgrset) -- | 'bracket' version of 'newTimeFastLogger' withTimedFastLogger :: IO FormattedTime -> LogType -> (TimedFastLogger -> IO a) -> IO a withTimedFastLogger tgetter typ log' = bracket (newTimedFastLogger tgetter typ) snd (log' . fst) ---------------------------------------------------------------- noOp :: IO () noOp = return () decrease :: IORef Int -> IO Int decrease ref = atomicModifyIORef' ref (\x -> (x - 1, x - 1)) -- updateTime returns whether the timeframe has changed updateTime :: (FormattedTime -> FormattedTime -> Bool) -> IORef FormattedTime -> FormattedTime -> IO Bool updateTime cmp ref newTime = atomicModifyIORef' ref (\x -> (newTime, not $ cmp x newTime)) tryRotate :: LoggerSet -> FileLogSpec -> IORef Int -> MVar () -> IO () tryRotate lgrset spec ref mvar = bracket lock unlock rotateFiles where lock = tryTakeMVar mvar unlock Nothing = return () unlock _ = putMVar mvar () rotateFiles Nothing = return () rotateFiles _ = do msiz <- getSize case msiz of -- A file is not available. -- So, let's set a big value to the counter so that -- this function is not called frequently. Nothing -> writeIORef ref 1000000 Just siz | siz > limit -> do rotate spec renewLoggerSet lgrset writeIORef ref $ estimate limit | otherwise -> writeIORef ref $ estimate (limit - siz) file = log_file spec limit = log_file_size spec getSize = handle (\(SomeException _) -> return Nothing) $ -- The log file is locked by GHC. -- We need to get its file size by the way not using locks. Just . fromIntegral <$> getFileSize file -- 200 is an ad-hoc value for the length of log line. estimate x = fromInteger (x `div` 200) tryTimedRotate :: TimedFileLogSpec -> FormattedTime -> MVar LoggerSet -> IO () tryTimedRotate spec now mvar = bracket lock unlock rotateFiles where lock = tryTakeMVar mvar unlock Nothing = return () unlock (Just lgrset) = do let (newlgrset, current_path) = replaceLoggerSet lgrset new_file_path putMVar mvar newlgrset case current_path of Nothing -> return () Just path -> timed_post_process spec path rotateFiles Nothing = return () rotateFiles (Just lgrset) = do let (newlgrset, _) = replaceLoggerSet lgrset new_file_path renewLoggerSet newlgrset new_file_path = prefixTime now $ timed_log_file spec fast-logger-3.2.4/System/Log/FastLogger/0000755000000000000000000000000007346545000016165 5ustar0000000000000000fast-logger-3.2.4/System/Log/FastLogger/Date.hs0000644000000000000000000000303007346545000017372 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} -- | -- Formatting time is slow. -- This package provides mechanisms to cache formatted date. module System.Log.FastLogger.Date ( -- * Date cacher newTimeCache , simpleTimeFormat , simpleTimeFormat' ) where import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction) import System.Log.FastLogger.Types (TimeFormat, FormattedTime) import Data.UnixTime (formatUnixTime, fromEpochTime) import System.PosixCompat.Types (EpochTime) import System.PosixCompat.Time (epochTime) ---------------------------------------------------------------- -- | Get date using UnixTime. getTime :: IO EpochTime getTime = epochTime -- | Format unix EpochTime date. formatDate :: TimeFormat -> EpochTime -> IO FormattedTime formatDate fmt = formatUnixTime fmt . fromEpochTime ---------------------------------------------------------------- -- | Make 'IO' action which get cached formatted local time. -- Use this to avoid the cost of frequently time formatting by caching an -- auto updating formatted time, this cache update every 1 second. -- more detail in "Control.AutoUpdate" newTimeCache :: TimeFormat -> IO (IO FormattedTime) newTimeCache fmt = mkAutoUpdate defaultUpdateSettings{ updateAction = getTime >>= formatDate fmt } -- | A simple time cache using format @"%d/%b/%Y:%T %z"@ simpleTimeFormat :: TimeFormat simpleTimeFormat = "%d/%b/%Y:%T %z" -- | A simple time cache using format @"%d-%b-%Y %T"@ simpleTimeFormat' :: TimeFormat simpleTimeFormat' = "%d-%b-%Y %T" fast-logger-3.2.4/System/Log/FastLogger/File.hs0000644000000000000000000000612607346545000017405 0ustar0000000000000000{-# LANGUAGE CPP #-} #if !MIN_VERSION_directory(1,3,8) {-# LANGUAGE Safe #-} #endif module System.Log.FastLogger.File ( FileLogSpec(..) , TimedFileLogSpec (..) , check , rotate , prefixTime ) where import Data.ByteString.Char8 (unpack) import System.Directory (doesFileExist, doesDirectoryExist, getPermissions, writable, renameFile) import System.FilePath (takeDirectory, dropFileName, takeFileName, ()) import System.Log.FastLogger.Imports import System.Log.FastLogger.Types (TimeFormat, FormattedTime) -- | The spec for logging files data FileLogSpec = FileLogSpec { log_file :: FilePath , log_file_size :: Integer -- ^ Max log file size (in bytes) before requiring rotation. , log_backup_number :: Int -- ^ Max number of rotated log files to keep around before overwriting the oldest one. } -- | The spec for time based rotation. It supports post processing of log files. Does -- not delete any logs. Example: -- -- @ -- timeRotate fname = LogFileTimedRotate -- (TimedFileLogSpec fname timeFormat sametime compressFile) -- defaultBufSize -- where -- timeFormat = "%FT%H%M%S" -- sametime = (==) `on` C8.takeWhile (/='T') -- compressFile fp = void . forkIO $ -- callProcess "tar" [ "--remove-files", "-caf", fp <> ".gz", fp ] -- @ data TimedFileLogSpec = TimedFileLogSpec { timed_log_file :: FilePath -- ^ base file path , timed_timefmt :: TimeFormat -- ^ time format to prepend , timed_same_timeframe :: FormattedTime -> FormattedTime -> Bool -- ^ function that compares two -- formatted times as specified by -- timed_timefmt and decides if a -- new rotation is supposed to -- begin , timed_post_process :: FilePath -> IO () -- ^ processing function called asynchronously after a file is added to the rotation } -- | Checking if a log file can be written. check :: FilePath -> IO () check file = do dirExist <- doesDirectoryExist dir unless dirExist $ fail $ dir ++ " does not exist or is not a directory." dirPerm <- getPermissions dir unless (writable dirPerm) $ fail $ dir ++ " is not writable." exist <- doesFileExist file when exist $ do perm <- getPermissions file unless (writable perm) $ fail $ file ++ " is not writable." where dir = takeDirectory file -- | Rotating log files. rotate :: FileLogSpec -> IO () rotate spec = mapM_ move srcdsts where path = log_file spec n = log_backup_number spec dsts' = reverse . ("":) . map (('.':). show) $ [0..n-1] dsts = map (path++) dsts' srcs = tail dsts srcdsts = zip srcs dsts move (src,dst) = do exist <- doesFileExist src when exist $ renameFile src dst -- | Prefix file name with formatted time prefixTime :: FormattedTime -> FilePath -> FilePath prefixTime time path = dropFileName path unpack time ++ "-" ++ takeFileName path fast-logger-3.2.4/System/Log/FastLogger/FileIO.hs0000644000000000000000000000162107346545000017630 0ustar0000000000000000module System.Log.FastLogger.FileIO where import Foreign.Ptr (Ptr) import GHC.IO.Device (close) import GHC.IO.FD (openFile, stderr, stdout, writeRawBufferPtr) import qualified GHC.IO.FD as POSIX (FD(..)) import GHC.IO.IOMode (IOMode(..)) import System.Log.FastLogger.Imports type FD = POSIX.FD closeFD :: FD -> IO () closeFD = close openFileFD :: FilePath -> IO FD openFileFD f = fst <$> openFile f AppendMode False getStderrFD :: IO FD getStderrFD = return stderr getStdoutFD :: IO FD getStdoutFD = return stdout writeRawBufferPtr2FD :: IORef FD -> Ptr Word8 -> Int -> IO Int writeRawBufferPtr2FD fdref bf len = do fd <- readIORef fdref if isFDValid fd then fromIntegral <$> writeRawBufferPtr "write" fd bf 0 (fromIntegral len) else return (-1) invalidFD :: POSIX.FD invalidFD = stdout { POSIX.fdFD = -1 } isFDValid :: POSIX.FD -> Bool isFDValid fd = POSIX.fdFD fd /= -1 fast-logger-3.2.4/System/Log/FastLogger/IO.hs0000644000000000000000000000245207346545000017033 0ustar0000000000000000{-# LANGUAGE CPP #-} #if __GLASGOW_HASKELL__ <= 708 {-# LANGUAGE Trustworthy #-} #else {-# LANGUAGE Safe #-} #endif module System.Log.FastLogger.IO where import Data.ByteString.Builder.Extra (Next(..)) import qualified Data.ByteString.Builder.Extra as BBE import Foreign.ForeignPtr (withForeignPtr) import Foreign.Marshal.Alloc (mallocBytes, free) import Foreign.Ptr (Ptr, plusPtr) import System.Log.FastLogger.Imports import System.Log.FastLogger.LogStr type Buffer = Ptr Word8 -- | The type for buffer size of each core. type BufSize = Int -- | The default buffer size (4,096 bytes). defaultBufSize :: BufSize defaultBufSize = 4096 getBuffer :: BufSize -> IO Buffer getBuffer = mallocBytes freeBuffer :: Buffer -> IO () freeBuffer = free toBufIOWith :: Buffer -> BufSize -> (Buffer -> Int -> IO ()) -> Builder -> IO () toBufIOWith buf size io builder = loop $ BBE.runBuilder builder where loop writer = do (len, next) <- writer buf size io buf len case next of Done -> return () More minSize writer' | size < minSize -> error "toBufIOWith: More: minSize" | otherwise -> loop writer' Chunk (PS fptr off siz) writer' -> withForeignPtr fptr $ \ptr -> io (ptr `plusPtr` off) siz >> loop writer' fast-logger-3.2.4/System/Log/FastLogger/Imports.hs0000644000000000000000000000105407346545000020156 0ustar0000000000000000{-# LANGUAGE Trustworthy #-} module System.Log.FastLogger.Imports ( ByteString(..) , module Control.Applicative , module Control.Monad , module Data.IORef , module Data.List , module Data.Int , module Data.Monoid , module Data.Ord , module Data.Word , module Data.Maybe , module Numeric ) where import Control.Applicative import Control.Monad import Data.ByteString.Internal (ByteString(..)) import Data.IORef import Data.Int import Data.List import Data.Maybe import Data.Monoid import Data.Ord import Data.Word import Numeric fast-logger-3.2.4/System/Log/FastLogger/Internal.hs0000644000000000000000000000124307346545000020275 0ustar0000000000000000-- | -- The contents of this module can change at any time without warning. module System.Log.FastLogger.Internal ( module System.Log.FastLogger.IO , module System.Log.FastLogger.FileIO , module System.Log.FastLogger.LogStr , 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.SingleLogger import System.Log.FastLogger.MultiLogger import System.Log.FastLogger.Write import System.Log.FastLogger.LoggerSet fast-logger-3.2.4/System/Log/FastLogger/LogStr.hs0000644000000000000000000001202507346545000017733 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE Trustworthy #-} module System.Log.FastLogger.LogStr ( Builder , LogStr(..) , logStrLength , fromLogStr , ToLogStr(..) , mempty , (<>) ) where import qualified Data.ByteString as BS import Data.ByteString.Builder (Builder) import qualified Data.ByteString.Builder as B import qualified Data.ByteString.Char8 as S8 import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Short as SBS #if MIN_VERSION_base(4,9,0) import qualified Data.Semigroup as Semi (Semigroup(..)) #endif import Data.String (IsString(..)) import qualified Data.Text as T import qualified Data.Text.Encoding as T #if MIN_VERSION_text(2,0,0) import qualified Data.Text.Foreign as T #endif import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TL import System.Log.FastLogger.Imports ---------------------------------------------------------------- toBuilder :: ByteString -> Builder toBuilder = B.byteString fromBuilder :: Builder -> ByteString #if MIN_VERSION_bytestring(0,10,0) fromBuilder = BL.toStrict . B.toLazyByteString #else fromBuilder = BS.concat . BL.toChunks . B.toLazyByteString #endif ---------------------------------------------------------------- -- | Log message builder. Use ('<>') to append two LogStr in O(1). data LogStr = LogStr !Int Builder #if MIN_VERSION_base(4,9,0) instance Semi.Semigroup LogStr where {-# INLINE (<>) #-} LogStr s1 b1 <> LogStr s2 b2 = LogStr (s1 + s2) (b1 <> b2) 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 #-} fromString = toLogStr . TL.pack -- | Types that can be converted to a 'LogStr'. Instances for -- types from the @text@ library use a UTF-8 encoding. Instances -- for numerical types use a decimal encoding. class ToLogStr msg where toLogStr :: msg -> LogStr instance ToLogStr LogStr where {-# INLINE toLogStr #-} toLogStr = id instance ToLogStr S8.ByteString where {-# INLINE toLogStr #-} toLogStr bs = LogStr (BS.length bs) (toBuilder bs) instance ToLogStr BL.ByteString where {-# INLINE toLogStr #-} toLogStr b = LogStr (fromIntegral (BL.length b)) (B.lazyByteString b) instance ToLogStr Builder where {-# INLINE toLogStr #-} toLogStr x = let b = B.toLazyByteString x in LogStr (fromIntegral (BL.length b)) (B.lazyByteString b) instance ToLogStr SBS.ShortByteString where {-# INLINE toLogStr #-} toLogStr b = LogStr (SBS.length b) (B.shortByteString b) instance ToLogStr String where {-# INLINE toLogStr #-} toLogStr = toLogStr . TL.pack instance ToLogStr T.Text where {-# INLINE toLogStr #-} #if MIN_VERSION_text(2,0,0) toLogStr t = LogStr (T.lengthWord8 t) (T.encodeUtf8Builder t) #else toLogStr = toLogStr . T.encodeUtf8 #endif instance ToLogStr TL.Text where {-# INLINE toLogStr #-} #if MIN_VERSION_text(2,0,0) toLogStr t = LogStr (TL.foldlChunks (\n c -> T.lengthWord8 c + n) 0 t) (TL.encodeUtf8Builder t) #else toLogStr = toLogStr . TL.encodeUtf8 #endif -- | @since 2.4.14 instance ToLogStr Int where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.intDec -- | @since 2.4.14 instance ToLogStr Int8 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.int8Dec -- | @since 2.4.14 instance ToLogStr Int16 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.int16Dec -- | @since 2.4.14 instance ToLogStr Int32 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.int32Dec -- | @since 2.4.14 instance ToLogStr Int64 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.int64Dec -- | @since 2.4.14 instance ToLogStr Word where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.wordDec -- | @since 2.4.14 instance ToLogStr Word8 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.word8Dec -- | @since 2.4.14 instance ToLogStr Word16 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.word16Dec -- | @since 2.4.14 instance ToLogStr Word32 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.word32Dec -- | @since 2.4.14 instance ToLogStr Word64 where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.word64Dec -- | @since 2.4.14 instance ToLogStr Integer where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.integerDec -- | @since 2.4.14 instance ToLogStr Float where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.floatDec -- | @since 2.4.14 instance ToLogStr Double where {-# INLINE toLogStr #-} toLogStr = toLogStr . B.doubleDec instance Show LogStr where show = show . T.decodeUtf8 . fromLogStr instance Eq LogStr where a == b = fromLogStr a == fromLogStr b -- | Obtaining the length of 'LogStr'. logStrLength :: LogStr -> Int logStrLength (LogStr n _) = n -- | Converting 'LogStr' to 'ByteString'. fromLogStr :: LogStr -> ByteString fromLogStr (LogStr _ builder) = fromBuilder builder fast-logger-3.2.4/System/Log/FastLogger/LoggerSet.hs0000644000000000000000000001455507346545000020426 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module System.Log.FastLogger.LoggerSet ( -- * Creating a logger set LoggerSet , newFileLoggerSet , newFileLoggerSetN , newStdoutLoggerSet , newStdoutLoggerSetN , newStderrLoggerSet , newStderrLoggerSetN , newLoggerSet , newFDLoggerSet -- * Renewing and removing a logger set , renewLoggerSet , rmLoggerSet -- * Writing a log message , pushLogStr , pushLogStrLn -- * Flushing buffered log messages , flushLogStr -- * Misc , replaceLoggerSet ) where import Control.Concurrent (getNumCapabilities) import Control.Debounce (mkDebounce, defaultDebounceSettings, debounceAction) import System.Log.FastLogger.FileIO import System.Log.FastLogger.IO import System.Log.FastLogger.Imports import System.Log.FastLogger.LogStr 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\\". -- A buffer is prepared for each capability. data LoggerSet = LoggerSet { lgrsetFilePath :: Maybe FilePath , lgrsetFdRef :: IORef FD , lgrsetLogger :: Logger , lgrsetDebounce :: IO () } -- | Creating a new 'LoggerSet' using a file. -- -- Uses `numCapabilties` many buffers, which will result in log -- output that is not ordered by time (see `newFileLoggerSetN`). newFileLoggerSet :: BufSize -> FilePath -> IO LoggerSet newFileLoggerSet size file = openFileFD file >>= newFDLoggerSet size Nothing (Just file) -- | Creating a new 'LoggerSet' using a file, using only the given number of capabilites. -- -- Giving @mn = Just 1@ scales less well on multi-core machines, -- but provides time-ordered output. newFileLoggerSetN :: BufSize -> Maybe Int -> FilePath -> IO LoggerSet newFileLoggerSetN size mn file = openFileFD file >>= newFDLoggerSet size mn (Just file) -- | Creating a new 'LoggerSet' using stdout. newStdoutLoggerSet :: BufSize -> IO LoggerSet newStdoutLoggerSet size = getStdoutFD >>= newFDLoggerSet size Nothing Nothing -- | Creating a new 'LoggerSet' using stdout, with the given number of buffers -- (see `newFileLoggerSetN`). newStdoutLoggerSetN :: BufSize -> Maybe Int -> IO LoggerSet newStdoutLoggerSetN size mn = getStdoutFD >>= newFDLoggerSet size mn Nothing -- | Creating a new 'LoggerSet' using stderr. newStderrLoggerSet :: BufSize -> IO LoggerSet newStderrLoggerSet size = getStderrFD >>= newFDLoggerSet size Nothing Nothing -- | Creating a new 'LoggerSet' using stderr, with the given number of buffers -- (see `newFileLoggerSetN`). newStderrLoggerSetN :: BufSize -> Maybe Int -> IO LoggerSet newStderrLoggerSetN size mn = getStderrFD >>= newFDLoggerSet size mn Nothing {-# DEPRECATED newLoggerSet "Use newFileLoggerSet etc instead" #-} -- | Creating a new 'LoggerSet'. -- If 'Nothing' is specified to the second argument, -- stdout is used. -- Please note that the minimum 'BufSize' is 1. newLoggerSet :: BufSize -> Maybe Int -> Maybe FilePath -> IO LoggerSet newLoggerSet size mn = maybe (newStdoutLoggerSet size) (newFileLoggerSetN size mn) -- | Creating a new 'LoggerSet' using a FD. newFDLoggerSet :: BufSize -> Maybe Int -> Maybe FilePath -> FD -> IO LoggerSet newFDLoggerSet size mn mfile fd = do n <- case mn of Just n' -> return n' Nothing -> getNumCapabilities fdref <- newIORef fd let bufsiz = max 1 size 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 logger } 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{..} 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 () pushLogStrLn loggerSet logStr = pushLogStr loggerSet (logStr <> "\n") -- | Flushing log messages in buffers. -- This function must be called explicitly when the program is -- being terminated. -- -- Note: Since version 2.1.6, this function does not need to be -- explicitly called, as every push includes an auto-debounced flush -- courtesy of the auto-update package. Since version 2.2.2, this -- function can be used to force flushing outside of the debounced -- flush calls. flushLogStr :: LoggerSet -> IO () flushLogStr LoggerSet{..} = flushLogStrRaw lgrsetLogger 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{..} = 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{..} = do fd <- readIORef lgrsetFdRef when (isFDValid fd) $ do 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 lgrset@LoggerSet{..} new_file_path = (lgrset { lgrsetFilePath = Just new_file_path }, lgrsetFilePath) fast-logger-3.2.4/System/Log/FastLogger/MultiLogger.hs0000644000000000000000000001013307346545000020751 0ustar0000000000000000{-# 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 fast-logger-3.2.4/System/Log/FastLogger/SingleLogger.hs0000644000000000000000000000671207346545000021110 0ustar0000000000000000{-# LANGUAGE RecordWildCards #-} module System.Log.FastLogger.SingleLogger ( SingleLogger, newSingleLogger, ) where import Control.Concurrent (MVar, forkIO, newEmptyMVar, putMVar, takeMVar) 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 ---------------------------------------------------------------- data Ent = F (MVar ()) Bool | L LogStr type Q = [Ent] -- writer queue -- | A non-scale but time-ordered logger. data SingleLogger = SingleLogger { slgrRef :: IORef (LogStr, Q) , slgrFlush :: Bool -> IO () -- teminate if False , 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, Q) -> IO () writer bufsize buf fdref tvar ref = 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 when cont $ loop cnt' go [] = return True go (F mvar cont : msgs) = do putMVar mvar () if cont then go msgs else return False go (L msg@(LogStr len _) : msgs) | 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, []) buf <- getBuffer bufsize _ <- forkIO $ writer bufsize buf fdref tvar ref let wakeup = atomically $ modifyTVar' tvar (+ 1) flush cont = do mvar <- newEmptyMVar let fin = F mvar cont atomicModifyIORef' ref (\(old, q) -> ((mempty, fin : L old : q), ())) wakeup takeMVar mvar return $ SingleLogger { slgrRef = ref , slgrFlush = flush , 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, L nlogmsg : L old : q), ())) slgrWakeup | otherwise = do wake <- atomicModifyIORef' slgrRef checkBuf when wake slgrWakeup where checkBuf (ologmsg@(LogStr olen _), q) | slgrBufSize < olen + nlen = ((nlogmsg, L ologmsg : q), True) | otherwise = ((ologmsg <> nlogmsg, q), False) flushAllLog :: SingleLogger -> IO () flushAllLog SingleLogger{..} = do atomicModifyIORef' slgrRef (\(old, q) -> ((mempty, L old : q), ())) slgrFlush True stopLoggers :: SingleLogger -> IO () stopLoggers SingleLogger{..} = do slgrFlush False freeBuffer slgrBuffer fast-logger-3.2.4/System/Log/FastLogger/Types.hs0000644000000000000000000000047007346545000017626 0ustar0000000000000000module System.Log.FastLogger.Types ( -- * Types TimeFormat , FormattedTime ) where import System.Log.FastLogger.Imports ---------------------------------------------------------------- -- | Type aliaes for date format and formatted date. type FormattedTime = ByteString type TimeFormat = ByteString fast-logger-3.2.4/System/Log/FastLogger/Write.hs0000644000000000000000000000262107346545000017614 0ustar0000000000000000{-# 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 () fast-logger-3.2.4/fast-logger.cabal0000644000000000000000000000420007346545000015315 0ustar0000000000000000cabal-version: >=1.10 name: fast-logger version: 3.2.4 license: BSD3 license-file: LICENSE maintainer: Kazu Yamamoto author: Kazu Yamamoto tested-with: ghc ==7.8.4 || ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.3 homepage: https://github.com/kazu-yamamoto/logger synopsis: A fast logging system description: A fast logging system for Haskell category: System build-type: Simple extra-source-files: README.md ChangeLog.md source-repository head type: git location: https://github.com/kazu-yamamoto/logger.git library exposed-modules: System.Log.FastLogger System.Log.FastLogger.Date System.Log.FastLogger.File System.Log.FastLogger.Internal System.Log.FastLogger.LoggerSet System.Log.FastLogger.Types other-modules: System.Log.FastLogger.Imports System.Log.FastLogger.FileIO System.Log.FastLogger.IO System.Log.FastLogger.LogStr System.Log.FastLogger.MultiLogger System.Log.FastLogger.SingleLogger System.Log.FastLogger.Write default-language: Haskell2010 ghc-options: -Wall build-depends: base >=4.9 && <5, array, auto-update >=0.1.2, easy-file >=0.2, bytestring >=0.10.4, directory, filepath, stm, text, unix-time >=0.4.4, unix-compat >=0.2 if impl(ghc <7.8) build-depends: bytestring-builder if impl(ghc >=8) default-extensions: Strict StrictData test-suite spec type: exitcode-stdio-1.0 main-is: Spec.hs build-tools: hspec-discover >=2.6 hs-source-dirs: test other-modules: FastLoggerSpec default-language: Haskell2010 ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4 && <5, async, bytestring >=0.10.4, directory, fast-logger, hspec if impl(ghc >=8) default-extensions: Strict StrictData fast-logger-3.2.4/test/0000755000000000000000000000000007346545000013102 5ustar0000000000000000fast-logger-3.2.4/test/FastLoggerSpec.hs0000644000000000000000000000657007346545000016316 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module FastLoggerSpec (spec) where #if __GLASGOW_HASKELL__ < 709 import Control.Applicative ((<$>)) #endif import Control.Exception (finally) 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 Text.Printf (printf) import Test.Hspec import Test.Hspec.QuickCheck (prop) import System.Log.FastLogger spec :: Spec spec = do describe "instance Show LogStr" $ do prop "it should be consistent with instance IsString" $ \str -> 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 , 1000 , 10000 , 100000 , 1000000 ] it "logs all messages" logAllMsgs describe "fastlogger 1" $ do it "maintains the ordering of log messages" logOrdering tempFile :: FilePath tempFile = "test/temp.txt" safeForLarge :: [Int] -> IO () safeForLarge = mapM_ safeForLarge' safeForLarge' :: Int -> IO () 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 tempFile bs `shouldBe` BS.pack (take (abs n) (cycle ['a'..'z']) <> "x") cleanup :: FilePath -> IO () cleanup file = do exist <- doesFileExist file when exist $ removeFile file logAllMsgs :: IO () logAllMsgs = logAll "LICENSE" `finally` cleanup tempFile where logAll file = do 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 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 <> " " fast-logger-3.2.4/test/Spec.hs0000644000000000000000000000005407346545000014327 0ustar0000000000000000{-# OPTIONS_GHC -F -pgmF hspec-discover #-}