diff --git a/docs/cli.md b/docs/cli.md index 8a769cfe..db089710 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -68,6 +68,7 @@ prek install [OPTIONS] [HOOK|PROJECT]...
--config, -c config

Path to alternate config file

--help, -h

Display the concise help for this command

--hook-type, -t hook-type
--install-hooks

Create hook environments for all hooks used in the config file

+
--log-file log-file

Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

--no-progress

Hide all progress outputs.

For example, spinners or progress bars.

--overwrite, -f

Overwrite existing hooks

@@ -133,6 +134,7 @@ prek install-hooks [OPTIONS] [HOOK|PROJECT]...
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -216,6 +218,7 @@ prek run [OPTIONS] [HOOK|PROJECT]...
  • pre-rebase
  • prepare-commit-msg
  • --last-commit

    Run hooks against the last commit. Equivalent to --from-ref HEAD~1 --to-ref HEAD

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -317,7 +320,8 @@ prek list [OPTIONS] [HOOK|PROJECT]...
  • pygrep
  • script
  • system
  • -
    --no-progress

    Hide all progress outputs.

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    +
    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --output-format output-format

    The output format

    [default: text]

    Possible values:

    @@ -366,7 +370,8 @@ prek uninstall [OPTIONS]
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    -
    --hook-type, -t hook-type
    --no-progress

    Hide all progress outputs.

    +
    --hook-type, -t hook-type
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    +
    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    Repeating this option, e.g., -qq, will enable a silent mode in which prek will write no output to stdout.

    @@ -401,6 +406,7 @@ prek validate-config [OPTIONS] [CONFIG]...
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -436,6 +442,7 @@ prek validate-manifest [OPTIONS] [MANIFEST]...
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -467,6 +474,7 @@ prek sample-config [OPTIONS]
    --config, -c config

    Path to alternate config file

    --file, -f file

    Write the sample config to a file (.pre-commit-config.yaml by default)

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -501,7 +509,8 @@ prek auto-update [OPTIONS]
    --freeze

    Store "frozen" hashes in rev instead of tag names

    --help, -h

    Display the concise help for this command

    --jobs, -j jobs

    Number of threads to use

    -

    [default: 3]

    --no-progress

    Hide all progress outputs.

    +

    [default: 3]

    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    +
    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    Repeating this option, e.g., -qq, will enable a silent mode in which prek will write no output to stdout.

    @@ -549,6 +558,7 @@ prek cache dir [OPTIONS]
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -579,6 +589,7 @@ prek cache gc [OPTIONS]
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -609,6 +620,7 @@ prek cache clean [OPTIONS]
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -657,7 +669,8 @@ prek init-template-dir [OPTIONS]
  • pre-push
  • pre-rebase
  • prepare-commit-msg
  • -
    --no-allow-missing-config

    Assume cloned repos should have a pre-commit config

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    +
    --no-allow-missing-config

    Assume cloned repos should have a pre-commit config

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -727,6 +740,7 @@ prek try-repo [OPTIONS] [HOOK|PROJECT]...
  • pre-rebase
  • prepare-commit-msg
  • --last-commit

    Run hooks against the last commit. Equivalent to --from-ref HEAD~1 --to-ref HEAD

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    @@ -793,6 +807,7 @@ prek self update [OPTIONS] [TARGET_VERSION]
  • never: Disables colored output
  • --config, -c config

    Path to alternate config file

    --help, -h

    Display the concise help for this command

    +
    --log-file log-file

    Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

    --no-progress

    Hide all progress outputs.

    For example, spinners or progress bars.

    --quiet, -q

    Use quiet output.

    diff --git a/src/cli/mod.rs b/src/cli/mod.rs index c549ab18..ec2dbb34 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -174,6 +174,15 @@ pub(crate) struct GlobalArgs { #[arg(global = true, short, long, action = ArgAction::Count)] pub(crate) verbose: u8, + /// Write trace logs to the specified file. + /// If not specified, trace logs will be written to `$PREK_HOME/prek.log`. + #[arg(global = true, long, value_name = "LOG_FILE", value_hint = ValueHint::FilePath)] + pub(crate) log_file: Option, + + /// Do not write trace logs to a log file. + #[arg(global = true, long, overrides_with = "log_file", hide = true)] + pub(crate) no_log_file: bool, + /// Display the prek version. #[arg(global = true, short = 'V', long, action = ArgAction::Version)] version: (), diff --git a/src/main.rs b/src/main.rs index 3ca43e15..456dd401 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::path::PathBuf; use std::process::ExitCode; use std::str::FromStr; use std::sync::Mutex; @@ -59,7 +60,29 @@ pub(crate) enum Level { TraceAll, } -fn setup_logging(level: Level) -> Result<()> { +enum LogFile { + Default, + Path(PathBuf), + Disabled, +} + +impl LogFile { + fn from_args(log_file: Option, no_log_file: bool) -> Self { + if no_log_file { + Self::Disabled + } else if let Some(path) = log_file { + Self::Path(path) + } else { + Self::Default + } + } + + fn is_disabled(&self) -> bool { + matches!(self, Self::Disabled) + } +} + +fn setup_logging(level: Level, log_file: LogFile) -> Result<()> { let directive = match level { Level::Default | Level::Verbose => LevelFilter::OFF.into(), Level::Debug => Directive::from_str("prek=debug")?, @@ -81,28 +104,35 @@ fn setup_logging(level: Level) -> Result<()> { .with_writer(anstream::stderr) .with_filter(stderr_filter); - let log_file_path = STORE.as_ref()?.log_file(); - let log_file = fs_err::OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(log_file_path) - .context("Failed to open log file")?; - let log_file = Mutex::new(StripStream::new(log_file.into_file())); + let registry = tracing_subscriber::registry().with(stderr_layer); - let file_format = tracing_subscriber::fmt::format() - .with_target(false) - .with_ansi(false); - let file_layer = tracing_subscriber::fmt::layer() - .with_span_events(FmtSpan::CLOSE) - .event_format(file_format) - .with_writer(log_file) - .with_filter(EnvFilter::new("prek=trace")); + if log_file.is_disabled() { + registry.init(); + } else { + let log_file_path = match log_file { + LogFile::Default => STORE.as_ref()?.log_file(), + LogFile::Path(path) => path, + LogFile::Disabled => unreachable!(), + }; + let log_file = fs_err::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(log_file_path) + .context("Failed to open log file")?; + let log_file = Mutex::new(StripStream::new(log_file.into_file())); - tracing_subscriber::registry() - .with(stderr_layer) - .with(file_layer) - .init(); + let file_format = tracing_subscriber::fmt::format() + .with_target(false) + .with_ansi(false); + let file_layer = tracing_subscriber::fmt::layer() + .with_span_events(FmtSpan::CLOSE) + .event_format(file_format) + .with_writer(log_file) + .with_filter(EnvFilter::new("prek=trace")); + + registry.with(file_layer).init(); + } Ok(()) } @@ -110,13 +140,17 @@ fn setup_logging(level: Level) -> Result<()> { async fn run(mut cli: Cli) -> Result { ColorChoice::write_global(cli.globals.color.into()); - setup_logging(match cli.globals.verbose { - 0 => Level::Default, - 1 => Level::Verbose, - 2 => Level::Debug, - 3 => Level::Trace, - _ => Level::TraceAll, - })?; + let log_file = LogFile::from_args(cli.globals.log_file.clone(), cli.globals.no_log_file); + setup_logging( + match cli.globals.verbose { + 0 => Level::Default, + 1 => Level::Verbose, + 2 => Level::Debug, + 3 => Level::Trace, + _ => Level::TraceAll, + }, + log_file, + )?; let printer = if cli.globals.quiet == 1 { Printer::Quiet diff --git a/tests/run.rs b/tests/run.rs index 7888cccb..a9144d5b 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -8,6 +8,7 @@ use assert_fs::prelude::*; use constants::env_vars::EnvVars; use constants::{ALT_CONFIG_FILE, CONFIG_FILE}; use insta::assert_snapshot; +use predicates::prelude::predicate; mod common; @@ -1736,6 +1737,7 @@ fn selectors_completion() -> Result<()> { --no-progress Hide all progress outputs --quiet Use quiet output --verbose Use verbose output + --log-file Write trace logs to the specified file. If not specified, trace logs will be written to `$PREK_HOME/prek.log` --version Display the prek version ----- stderr ----- @@ -2154,3 +2156,58 @@ fn run_quiet() { ----- stderr ----- "); } + +/// Test `prek run --log-file ` flag. +#[test] +fn run_log_file() { + let context = TestContext::new(); + context.init_project(); + context.write_pre_commit_config(indoc::indoc! {r" + repos: + - repo: local + hooks: + - id: fail + name: fail + entry: fail + language: fail + "}); + context.git_add("."); + + // Run with `--no-log-file`, no `prek.log` is created. + cmd_snapshot!(context.filters(), context.run().arg("--no-log-file"), @r" + success: false + exit_code: 1 + ----- stdout ----- + fail.....................................................................Failed + - hook id: fail + - exit code: 1 + fail + + .pre-commit-config.yaml + + ----- stderr ----- + "); + context + .home_dir() + .child("prek.log") + .assert(predicate::path::missing()); + + // Write log to `log`. + cmd_snapshot!(context.filters(), context.run().arg("--log-file").arg("log"), @r" + success: false + exit_code: 1 + ----- stdout ----- + fail.....................................................................Failed + - hook id: fail + - exit code: 1 + fail + + .pre-commit-config.yaml + + ----- stderr ----- + "); + context + .work_dir() + .child("log") + .assert(predicate::path::exists()); +}