Skip to content

Commit 0e52bc7

Browse files
authored
[#176] Add an action to flush a handle (#194)
* [#176] Add an action to flush a handle Problem: during logging one often wants data to reach destination immediately, but logging actions provided out of the box do not flush handles they log to. Solution: we add a new action to `co-log-core`. This action does not _write_ anything, but flushes the handler. We call it `logFlush` for consistency with other `log*` actions. It can be used as `logPrintStderr <> logFlush stderr`. * fixup! [#191] Export showTime (#192) * [#176] Automatically flush file handles Problem: if we use builtin logging actions that log to files, you will often notice that your messages appear in files with delay due to buffering. File handles are usually block-buffered and do not flush on newlines. We find that behavior confusing to end users and think that it's better to flush file handles on each log action by default. Note that the same problem may occur with other handles (e. g. stderr) as well, but it's less likely because they normally have line buffering (but it's not guaranteed and is not always the case). Solution: first of all we checked performance overhead of additional `hFlush` call. For vanilla `putStrLn` it varies from "more than 4x" for short messages to negligible for long messages. On average we roughly assess it as 2x (that really depends on your use case). Based on that we make the following trade-off: 1. Actions that write to files now also automatically flush output buffers using `logFlush`. That's more expensive, but without flushing behavior is quite confusing for the library users. 2. Actions that write to an arbitrary `Handle` or standard handles do not flush by default to avoid overhead. Usually flushing will happen automatically because of line bufferring. * [#176] Clarify lack of flushing in default actions * fixup! [#176] Automatically flush file handles
1 parent 14c1257 commit 0e52bc7

File tree

1 file changed

+30
-4
lines changed
  • co-log-core/src/Colog/Core

1 file changed

+30
-4
lines changed

co-log-core/src/Colog/Core/IO.hs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ module Colog.Core.IO
2727

2828
-- * Various combinators
2929
, liftLogIO
30+
, logFlush
3031
) where
3132

3233
import Colog.Core.Action (LogAction (..))
3334
import Control.Monad.IO.Class (MonadIO, liftIO)
34-
import System.IO (Handle, IOMode (AppendMode), hPrint, hPutStrLn, stderr, withFile)
35+
import Data.Semigroup ((<>))
36+
import System.IO (Handle, IOMode (AppendMode), hFlush, hPrint, hPutStrLn, stderr, withFile)
3537

3638

3739
{- $setup
@@ -43,6 +45,8 @@ import System.IO (Handle, IOMode (AppendMode), hPrint, hPutStrLn, stderr, withFi
4345
----------------------------------------------------------------------------
4446

4547
{- | Action that prints 'String' to stdout.
48+
This action does not flush the output buffer.
49+
If buffering mode is block buffering, the effect of this action can be delayed.
4650
4751
>>> logStringStdout <& "foo"
4852
foo
@@ -53,6 +57,8 @@ logStringStdout = LogAction (liftIO . putStrLn)
5357
{-# SPECIALIZE logStringStdout :: LogAction IO String #-}
5458

5559
{- | Action that prints 'String' to stderr.
60+
This action does not flush the output buffer.
61+
If buffering mode is block buffering, the effect of this action can be delayed.
5662
5763
>>> logStringStderr <& "foo"
5864
foo
@@ -63,6 +69,8 @@ logStringStderr = logStringHandle stderr
6369
{-# SPECIALIZE logStringStderr :: LogAction IO String #-}
6470

6571
{- | Action that prints 'String' to 'Handle'.
72+
This action does not flush the output buffer.
73+
If buffering mode is block buffering, the effect of this action can be delayed.
6674
6775
>>> logStringHandle stderr <& "foo"
6876
foo
@@ -77,7 +85,7 @@ implemented in continuation-passing style because it's more efficient to open
7785
file only once at the start of the application and write to 'Handle' instead of
7886
opening file each time we need to write to it.
7987
80-
Opens file in 'AppendMode'.
88+
Opens file in 'AppendMode'. Automatically flushes the output buffer.
8189
8290
#ifndef mingw32_HOST_OS
8391
@@ -88,7 +96,8 @@ foo
8896
#endif
8997
-}
9098
withLogStringFile :: MonadIO m => FilePath -> (LogAction m String -> IO r) -> IO r
91-
withLogStringFile path action = withFile path AppendMode $ action . logStringHandle
99+
withLogStringFile path action = withFile path AppendMode $ \handle ->
100+
action (logStringHandle handle <> logFlush handle)
92101
{-# INLINE withLogStringFile #-}
93102
{-# SPECIALIZE withLogStringFile :: FilePath -> (LogAction IO String -> IO r) -> IO r #-}
94103

@@ -97,6 +106,8 @@ withLogStringFile path action = withFile path AppendMode $ action . logStringHan
97106
----------------------------------------------------------------------------
98107

99108
{- | Action that prints to stdout using 'Show'.
109+
This action does not flush the output buffer.
110+
If buffering mode is block buffering, the effect of this action can be delayed.
100111
101112
>>> logPrint <& 5
102113
5
@@ -107,6 +118,8 @@ logPrint = LogAction $ liftIO . print
107118
{-# SPECIALIZE logPrint :: Show a => LogAction IO a #-}
108119

109120
{- | Action that prints to stderr using 'Show'.
121+
This action does not flush the output buffer.
122+
If buffering mode is block buffering, the effect of this action can be delayed.
110123
111124
>>> logPrintStderr <& 5
112125
5
@@ -117,6 +130,8 @@ logPrintStderr = logPrintHandle stderr
117130
{-# SPECIALIZE logPrintStderr :: Show a => LogAction IO a #-}
118131

119132
{- | Action that prints to a 'Handle' using 'Show'.
133+
This action does not flush the output buffer.
134+
If buffering mode is block buffering, the effect of this action can be delayed.
120135
121136
>>> logPrintHandle stderr <& 5
122137
5
@@ -133,7 +148,8 @@ withLogPrintFile
133148
=> FilePath
134149
-> (LogAction m a -> IO r)
135150
-> IO r
136-
withLogPrintFile path action = withFile path AppendMode $ action . logPrintHandle
151+
withLogPrintFile path action = withFile path AppendMode $ \handle ->
152+
action (logPrintHandle handle <> logFlush handle)
137153
{-# INLINE withLogPrintFile #-}
138154
{-# SPECIALIZE withLogPrintFile :: Show a => FilePath -> (LogAction IO a -> IO r) -> IO r #-}
139155

@@ -150,3 +166,13 @@ foo
150166
liftLogIO :: MonadIO m => LogAction IO msg -> LogAction m msg
151167
liftLogIO (LogAction action) = LogAction (liftIO . action)
152168
{-# INLINE liftLogIO #-}
169+
170+
{- | This action can be used in combination with other actions to flush
171+
a handle every time you log anything.
172+
173+
@since x.x.x.x
174+
-}
175+
logFlush :: MonadIO m => Handle -> LogAction m a
176+
logFlush handle = LogAction $ const $ liftIO $ hFlush handle
177+
{-# INLINE logFlush #-}
178+
{-# SPECIALIZE logFlush :: Handle -> LogAction IO a #-}

0 commit comments

Comments
 (0)