time-warp-1.1.1.0: bench/Network/LogReader/Main.hs
{-# LANGUAGE TypeApplications #-}
import Control.Applicative (empty, (<|>))
import Control.Exception (Exception)
import Control.Lens (at, (%=), (^.), _2,
_Just)
import Control.Monad (forM_)
import Control.Monad.Catch (handle)
import Control.Monad.State (StateT (..), evalStateT, execStateT, get,
modify)
import Control.Monad.Trans (lift, liftIO)
import Control.Monad.Trans.Resource (runResourceT)
import Data.Conduit (Source, yield, ($$), (=$=))
import Data.Conduit.Binary (sinkFile, sourceFile)
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import Data.Conduit.Text (decode, encode, utf8)
import Data.List (intersperse)
import qualified Data.Map as M
import Data.Text (Text)
import Data.Text.Buildable (Buildable (..))
import Data.Typeable (Typeable)
import Formatting (bprint, int, right, sformat, (%))
import qualified Formatting as F
import System.IO (FilePath)
import Data.Attoparsec.Text (parseOnly)
import Options.Applicative.Simple (simpleOptions)
import Bench.Network.Commons (LogMessage (..), MeasureEvent (..),
MeasureInfo (..), MsgId, Payload (..),
Timestamp, logMessageParser,
measureInfoParser)
import LogReaderOptions (Args (..), argsParser)
import System.Wlog (LoggerNameBox, Severity (Info),
initLogging, logError, logWarning,
usingLoggerName, usingLoggerName)
type Measures = M.Map MsgId (Payload, [(MeasureEvent, Timestamp)])
newtype MeasureInfoDuplicateError = MeasureInfoDuplicateError (Timestamp, MeasureInfo)
deriving (Typeable)
instance Show MeasureInfoDuplicateError where
show = F.formatToString F.build . build
instance Buildable MeasureInfoDuplicateError where
build (MeasureInfoDuplicateError (was, new)) = mconcat
["Duplicate measure: was "
, build was
, " but meet "
, build new
]
instance Exception MeasureInfoDuplicateError
type RowId = Int
analyze :: FilePath -> StateT Measures (LoggerNameBox IO) ()
analyze file =
catchE . flip evalStateT 0 . runResourceT $
sourceFile file =$= CB.lines =$= CL.iterM (const $ modify succ)
=$= decode utf8 $$ CL.mapM_ (lift . saveMeasure)
where
saveMeasure :: Text -> StateT RowId (StateT Measures (LoggerNameBox IO)) ()
saveMeasure row = do
case parseOnly (logMessageParser measureInfoParser) row of
Left err -> do
rowNo <- get
logWarning $
sformat ("Parse error at file "%F.build%" (line "%F.int%"): "%F.build)
file rowNo err
Right (Just (LogMessage MeasureInfo{..})) -> lift $ do
at miId %= (<|> Just (miPayload, mempty))
at miId . _Just . _2 %= ((miEvent, miTime):)
--mwas <- singular (at miId . _Just . _2) . at miEvent <<.= Just miTime
--forM_ mwas $ \was -> throwM $ MeasureInfoDuplicateError (was, mi)
Right _ -> return ()
catchE = handle @_ @MeasureInfoDuplicateError $ logError . sformat F.build
printMeasures :: FilePath -> Measures -> LoggerNameBox IO ()
printMeasures file measures = runResourceT $
source $$ encode utf8 =$= sinkFile file
where
source = printHeader >> mapM_ printMeasure (M.toList measures)
printHeader = printRow $ "MsgId" : "Size" : map (sformat F.build) eventsUniverse
printMeasure :: Monad m
=> (MsgId, (Payload, [(MeasureEvent, Timestamp)])) -> Source m Text
printMeasure (mid, (Payload p, mm)) = do
case uniqMap mm of
Just mm' -> printRow $ sformat int mid
: sformat int p
: [ maybe "-" (sformat int) $ mm' ^. at ev | ev <- eventsUniverse ]
_ -> return ()
printRow :: Monad m => [Text] -> Source m Text
printRow = yield
. sformat (F.build%"\n")
. mconcat
. intersperse ","
. alignColumns
uniqMap = foldl upd (Just mempty)
where
upd m (ev, ts) = m >>= \m' ->
case ev `M.lookup` m' of
Nothing -> return $ M.insert ev ts m'
_ -> fail ""
alignColumns = map (\(s, m) -> bprint (right s ' ') m)
. zip (7 : 7 : (18 <$ eventsUniverse))
eventsUniverse = [minBound .. maxBound]
getOptions :: IO Args
getOptions = (\(a, ()) -> a) <$> simpleOptions
"bench-log-reader"
"Utility to extract measures from logs into csv file"
"Use it!"
argsParser
empty
main :: IO ()
main = usingLoggerName mempty $ do
initLogging Info
Args{..} <- liftIO getOptions
measures <- flip execStateT M.empty $
forM_ inputFiles analyze
printMeasures resultFile measures