packages feed

eventlog-live-0.4.0.0: src/GHC/Eventlog/Live/Options.hs

{- |
Module      : GHC.Eventlog.Live.Options
Description : Command-line option parsers for eventlog machines.
Stability   : experimental
Portability : portable
-}
module GHC.Eventlog.Live.Options (
  EventlogSource (..),
  eventlogSourceParser,
  eventlogSocketTimeoutParser,
  eventlogSocketTimeoutExponentParser,
  heapProfBreakdownParser,
  eventlogLogFileParser,
  batchIntervalParser,
  verbosityParser,
  statsParser,
) where

import Control.Applicative (asum)
import Data.Char (toLower)
import GHC.Eventlog.Live.Machine.Analysis.Heap (heapProfBreakdownEitherReader)
import GHC.Eventlog.Live.Verbosity (Verbosity, verbosityDebug, verbosityError, verbosityInfo, verbosityQuiet, verbosityWarning)
import GHC.RTS.Events (HeapProfBreakdown (..))
import Options.Applicative qualified as O
import Text.Read (readEither)

--------------------------------------------------------------------------------
-- Eventlog Source

{- |
The type of eventlog sockets.
-}
data EventlogSource
  = EventlogStdin
  | EventlogFile FilePath
  | EventlogSocketUnix FilePath

{- |
Parser for the eventlog socket.
-}
eventlogSourceParser :: O.Parser EventlogSource
eventlogSourceParser =
  asum
    [ stdinParser
    , fileParser
    , socketUnixParser
    ]
 where
  stdinParser =
    EventlogStdin
      <$ O.flag'
        ()
        ( O.long "eventlog-stdin"
            <> O.help "Read the eventlog from stdin."
        )
  fileParser =
    EventlogFile
      <$> O.strOption
        ( O.long "eventlog-file"
            <> O.metavar "FILE"
            <> O.help "Read the eventlog from a file."
        )
  socketUnixParser =
    EventlogSocketUnix
      <$> O.strOption
        ( O.long "eventlog-socket"
            <> O.metavar "SOCKET"
            <> O.help "Read the eventlog from a Unix socket."
        )

{- |
Parser for the intial timeout for exponential backoff.
-}
eventlogSocketTimeoutParser :: O.Parser Double
eventlogSocketTimeoutParser =
  O.option
    O.auto
    ( O.long "eventlog-socket-timeout"
        <> O.metavar "NUM"
        <> O.help "Eventlog socket connection retry timeout in microseconds."
        <> O.value 1
    )

{- |
Parser for the exponent for exponential backoff.
-}
eventlogSocketTimeoutExponentParser :: O.Parser Double
eventlogSocketTimeoutExponentParser =
  O.option
    O.auto
    ( O.long "eventlog-socket-exponent"
        <> O.metavar "NUM"
        <> O.help "Eventlog socket connection retry timeout exponent."
        <> O.value 1
    )

--------------------------------------------------------------------------------
-- Heap Profile Breakdown

{- |
Parser for the heap profile breakdown.
-}
heapProfBreakdownParser :: O.Parser HeapProfBreakdown
heapProfBreakdownParser =
  O.option
    (O.eitherReader heapProfBreakdownEitherReader)
    ( O.short 'h'
        <> O.metavar "Tcmdyrbi"
        <> O.help "Heap profile breakdown."
    )

--------------------------------------------------------------------------------
-- Eventlog Log File

{- |
Parser for the eventlog log file.
-}
eventlogLogFileParser :: O.Parser FilePath
eventlogLogFileParser =
  O.strOption
    ( O.long "eventlog-log-file"
        <> O.metavar "FILE"
        <> O.help "Use file to log binary eventlog data."
    )

--------------------------------------------------------------------------------
-- Batch Interval

{- |
Parser for the batch interval.
-}
batchIntervalParser :: O.Parser Int
batchIntervalParser =
  O.option
    O.auto
    ( O.long "batch-interval"
        <> O.metavar "NUM"
        <> O.help "Batch interval in milliseconds."
        <> O.value defaultBatchIntervalMs
    )

{- |
Internal helper.
The default batch interval in milliseconds.
-}
defaultBatchIntervalMs :: Int
defaultBatchIntervalMs = 1_000

--------------------------------------------------------------------------------
-- Verbosity

{- |
Parser for verbosities.
The default verbosity is `verbosityWarning`.
-}
verbosityParser :: O.Parser Verbosity
verbosityParser =
  O.option
    (O.eitherReader readEitherVerbosity)
    ( O.short 'v'
        <> O.long "verbosity"
        <> O.metavar "quiet|error|warning|info|debug|0-4"
        <> O.help "The verbosity threshold for logging."
        <> O.value verbosityWarning
    )

{- |
Internal helper.
Parser for verbosities by number or name.
Case insensitive.
-}
readEitherVerbosity :: String -> Either String Verbosity
readEitherVerbosity rawVerbosity =
  -- try to parse the verbosity as a number...
  case readEither @Word rawVerbosity of
    -- if the verbosity string is a number, map it to a verbosity...
    Right verbosityThreshold
      | verbosityThreshold <= 0 -> Right verbosityQuiet
      | verbosityThreshold == 1 -> Right verbosityError
      | verbosityThreshold == 2 -> Right verbosityWarning
      | verbosityThreshold == 3 -> Right verbosityInfo
      | otherwise -> Right verbosityDebug
    -- otherwise, match it against the literal names of the levels...
    Left _parseError -> case toLower <$> rawVerbosity of
      "quiet" -> Right verbosityQuiet
      "error" -> Right verbosityError
      "warning" -> Right verbosityWarning
      "info" -> Right verbosityInfo
      "debug" -> Right verbosityDebug
      _otherwise -> Left $ "Could not parse verbosity '" <> rawVerbosity <> "'."

--------------------------------------------------------------------------------
-- Statistics

statsParser :: O.Parser Bool
statsParser =
  O.flag False True $
    ( O.short 's'
        <> O.long "stats"
        <> O.help "Display runtime statistics."
    )