Add configurable log levels and log file support
Build / build (push) Failing after 7s

- New LogLevel type: Debug, Info, Warn, Error with Ord instance
- Logger holds configured level and output handle (stderr or file)
- --log-level and --log-file CLI flags
- log_level and log_file YAML config fields
- Log messages assigned appropriate severity levels
- Test logger updated to new Logger structure
- All 17 tests passing
This commit is contained in:
2026-05-14 10:13:36 -04:00
parent d22ea39cde
commit f66dfcbccb
5 changed files with 152 additions and 45 deletions
+49 -15
View File
@@ -3,6 +3,7 @@ module Main (main) where
import Converge
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import Options.Applicative
import System.IO (hPutStrLn, stderr)
@@ -17,6 +18,10 @@ data CliArgs = CliArgs
-- ^ Branch name (used only in single-repo mode).
, cliDebounce :: !(Maybe Int)
-- ^ Debounce override for both modes.
, cliLogLevel :: !(Maybe LogLevel)
-- ^ Log level override.
, cliLogFile :: !(Maybe FilePath)
-- ^ Log file path.
}
cliParser :: Parser CliArgs
@@ -62,6 +67,26 @@ cliParser =
<> help "Debounce window in milliseconds (overrides config file)"
)
)
<*> optional
( option
(maybeReader (\s -> case T.toLower (T.pack s) of
"debug" -> Just Debug
"info" -> Just Info
"warn" -> Just Warn
"error" -> Just Error
_ -> Nothing))
( long "log-level"
<> metavar "LEVEL"
<> help "Minimum log level: debug, info, warn, or error (default: info)"
)
)
<*> optional
( strOption
( long "log-file"
<> metavar "FILE"
<> help "Write logs to a file instead of stderr"
)
)
parserInfo :: ParserInfo CliArgs
parserInfo =
@@ -79,7 +104,7 @@ main = do
(options, configSource) <- case cliConfig cli of
Just cfgPath -> do
cfg <- loadConfig cfgPath
let opts = applyDebounce mDebounceOverride (configToOptions cfg)
let opts = applyOverrides mDebounceOverride (cliLogLevel cli) (cliLogFile cli) (configToOptions cfg)
pure (opts, Just cfgPath)
Nothing -> do
exists <- configFileExists
@@ -87,20 +112,21 @@ main = do
then do
cfgPath <- defaultConfigPath
cfg <- loadConfig cfgPath
let opts = applyDebounce mDebounceOverride (configToOptions cfg)
let opts = applyOverrides mDebounceOverride (cliLogLevel cli) (cliLogFile cli) (configToOptions cfg)
pure (opts, Just cfgPath)
else
pure
( defaultOptions
{ optRepos =
[ defaultRepoConfig
{ rcPath = fromMaybe "." (cliRepo cli)
, rcRemote = cliRemote cli
, rcBranch = cliBranch cli
}
]
, optDebounceMs = fromMaybe 5000 mDebounceOverride
}
( applyOverrides mDebounceOverride (cliLogLevel cli) (cliLogFile cli)
defaultOptions
{ optRepos =
[ defaultRepoConfig
{ rcPath = fromMaybe "." (cliRepo cli)
, rcRemote = cliRemote cli
, rcBranch = cliBranch cli
}
]
, optDebounceMs = fromMaybe 5000 mDebounceOverride
}
, Nothing
)
case configSource of
@@ -118,6 +144,14 @@ main = do
mapM_ (\r -> hPutStrLn stderr (" - " <> rcPath r)) (optRepos options)
runConverge options
applyDebounce :: Maybe Int -> Options -> Options
applyDebounce (Just ms) opts = opts{optDebounceMs = ms}
applyDebounce Nothing opts = opts
-- | Apply CLI overrides (debounce, log level, log file) to Options.
applyOverrides :: Maybe Int -> Maybe LogLevel -> Maybe FilePath -> Options -> Options
applyOverrides mDebounce mLogLevel mLogFile opts =
opts
{ optDebounceMs = maybe (optDebounceMs opts) id mDebounce
, optLogLevel = fromMaybe (optLogLevel opts) mLogLevel
, optLogFile = case mLogFile of
Just _ -> mLogFile -- CLI --log-file overrides config
Nothing -> optLogFile opts
}