From e42840a41fef273525abea06fb858eb530765b7e Mon Sep 17 00:00:00 2001 From: Yuanle Song Date: Mon, 7 Sep 2015 20:52:08 +0800 Subject: [PATCH] support print out slow requests using -g param --- src/Main.hs | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index 8358dd8..258a3c1 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -15,13 +15,18 @@ import qualified Options.Applicative as O import Data.Time.Clock (getCurrentTime, diffUTCTime) import Text.Printf (printf) +import Data.ByteString.Char8 (pack, unpack) + data AccessLog = AccessLog { clientIp :: ByteString , datetime :: ByteString , requestVerb :: ByteString , requestPath :: ByteString , statusCode :: Int , browser :: ByteString - , responseTime :: Int} deriving (Show) + , responseTime :: Int} + +instance Show AccessLog where + show (AccessLog clientIp datetime requestVerb requestPath statusCode browser responseTime) = printf "%s [%s] \"%s %s\" %s %s %s" (unpack clientIp) (unpack datetime) (unpack requestVerb) (unpack requestPath) (show statusCode) (unpack browser) (show responseTime) --------------- -- log parsing @@ -92,7 +97,8 @@ logParser = many $ parseLine <* endOfLine -- command line argument handling ---------------------------------- -data AppArguments = AppArguments { inputFile :: String } +data AppArguments = AppArguments { inputFile :: String + , slowRequestDuration :: Maybe String} argumentParser :: O.Parser AppArguments argumentParser = AppArguments @@ -100,7 +106,12 @@ argumentParser = AppArguments (long "input" <> short 'i' <> metavar "ACCESS_LOG_FILE" - <> help "the access filename to parse") + <> help "The access log filename to parse") + <*> (optional $ strOption + (long "gt" + <> short 'g' + <> metavar "DURATION" + <> help "Print requests that take longer than DURATION, duration could have a suffix of us, ms, s. Default suffix is s.")) parseArguments :: IO AppArguments parseArguments = execParser opts @@ -110,6 +121,29 @@ parseArguments = execParser opts <> progDesc "parse apache2 access log" <> header "a2p - parse apache2 access log") +durationParser :: Parser (Int, String) +durationParser = do + number <- decimal + suffix <- nonEmpty + return (number, unpack suffix) + +parseDuration :: ByteString -> Either String (Int, String) +parseDuration = parseOnly durationParser + +-- | convert a duration string to microseconds. +-- if there is no suffix, default is seconds. +-- supported suffix are us (microseconds), ms (milliseconds) and s (seconds) +toMicroseconds :: String -> Int +toMicroseconds duration = + case parseDuration (pack duration) of + Left msg -> error ("Invalid duration: " ++ duration) + Right (number, unit) -> number * unitValue unit where + -- convert it to microseconds + unitValue "us" = 1 + unitValue "ms" = 1000 + unitValue "s" = 1000000 + unitValue "" = 1000000 + parseFile :: AppArguments -> IO () parseFile args = do let filename = inputFile args @@ -128,6 +162,15 @@ parseFile args = do else printf "parsed %d lines in %d seconds. (%d line/s)\n" linesParsed duration (linesParsed `div` duration) + -- print slow requests if -g is used. + case slowRequestDuration args of + Nothing -> return () + Just str -> do + let durationMicroseconds = toMicroseconds str + printf "== requests that take longer than %dus ==\n" + durationMicroseconds + mapM_ print + (filter (\log -> responseTime log >= durationMicroseconds) logs) main :: IO () main = parseArguments >>= parseFile -- GitLab