mirror of
https://github.com/snoyberg/keter.git
synced 2024-12-14 17:12:46 +03:00
117 lines
3.2 KiB
Haskell
117 lines
3.2 KiB
Haskell
{-# LANGUAGE NoImplicitPrelude #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
module Keter.Logger
|
|
( Logger
|
|
, start
|
|
, attach
|
|
, detach
|
|
, LogPipes (..)
|
|
, LogPipe
|
|
, mkLogPipe
|
|
, dummy
|
|
) where
|
|
|
|
import Keter.Prelude
|
|
import qualified Prelude as P
|
|
import qualified Keter.LogFile as LogFile
|
|
import Control.Concurrent (killThread)
|
|
import qualified Data.ByteString as S
|
|
import Data.Conduit (Sink, await)
|
|
import qualified Control.Concurrent.MVar as M
|
|
import Control.Monad.Trans.Class (lift)
|
|
|
|
data LogPipes = LogPipes
|
|
{ stdOut :: LogPipe
|
|
, stdErr :: LogPipe
|
|
}
|
|
|
|
data LogPipe = LogPipe
|
|
{ readLogPipe :: KIO (Maybe S.ByteString)
|
|
, closeLogPipe :: KIO ()
|
|
}
|
|
|
|
mkLogPipe :: KIO (LogPipe, Sink S.ByteString P.IO ())
|
|
mkLogPipe = do
|
|
toSink <- newEmptyMVar
|
|
fromSink <- newEmptyMVar
|
|
let pipe = LogPipe
|
|
{ readLogPipe = do
|
|
putMVar toSink True
|
|
takeMVar fromSink
|
|
, closeLogPipe = do
|
|
_ <- tryTakeMVar toSink
|
|
putMVar toSink False
|
|
}
|
|
sink = do
|
|
toCont <- lift $ M.takeMVar toSink
|
|
if toCont
|
|
then do
|
|
mbs <- await
|
|
lift $ M.putMVar fromSink mbs
|
|
maybe (return ()) (P.const sink) mbs
|
|
else return ()
|
|
return (pipe, sink)
|
|
|
|
newtype Logger = Logger (Command -> KIO ())
|
|
|
|
data Command = Attach LogPipes | Detach
|
|
|
|
start :: LogFile.LogFile -- ^ stdout
|
|
-> LogFile.LogFile -- ^ stderr
|
|
-> KIO Logger
|
|
start lfout lferr = do
|
|
chan <- newChan
|
|
forkKIO $ loop chan Nothing Nothing
|
|
return $ Logger $ writeChan chan
|
|
where
|
|
killOld tid = do
|
|
res <- liftIO $ killThread tid
|
|
case res of
|
|
Left e -> $logEx e
|
|
Right () -> return ()
|
|
|
|
loop chan moldout molderr = do
|
|
c <- readChan chan
|
|
maybe (return ()) killOld moldout
|
|
maybe (return ()) killOld molderr
|
|
case c of
|
|
Detach -> do
|
|
LogFile.close lfout
|
|
LogFile.close lferr
|
|
Attach (LogPipes out err) -> do
|
|
LogFile.addChunk lfout "\n\nAttaching new process\n\n"
|
|
LogFile.addChunk lferr "\n\nAttaching new process\n\n"
|
|
let go logpipe lf = do
|
|
etid <- forkKIO' $ listener logpipe lf
|
|
case etid of
|
|
Left e -> do
|
|
$logEx e
|
|
closeLogPipe logpipe
|
|
return Nothing
|
|
Right tid -> return $ Just tid
|
|
newout <- go out lfout
|
|
newerr <- go err lferr
|
|
loop chan newout newerr
|
|
|
|
listener :: LogPipe -> LogFile.LogFile -> KIO ()
|
|
listener out lf =
|
|
loop
|
|
where
|
|
loop = do
|
|
mbs <- readLogPipe out
|
|
case mbs of
|
|
Nothing -> return ()
|
|
Just bs -> do
|
|
LogFile.addChunk lf bs
|
|
loop
|
|
|
|
attach :: Logger -> LogPipes -> KIO ()
|
|
attach (Logger f) h = f (Attach h)
|
|
|
|
detach :: Logger -> KIO ()
|
|
detach (Logger f) = f Detach
|
|
|
|
dummy :: Logger
|
|
dummy = Logger $ P.const $ return ()
|