-
Notifications
You must be signed in to change notification settings - Fork 10
Add file logger #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add file logger #218
Conversation
news/218-add-logger
Outdated
| ### Enhancements | ||
|
|
||
| * <news item> | ||
| * Add option to log `conda-standalone` and `conda` outputs into log file. (#218) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * Add option to log `conda-standalone` and `conda` outputs into log file. (#218) | |
| * Add option to log `conda-standalone` and `conda` outputs into a log file. (#218) |
Optional: I think it could be good also to mention how to enable it. I'm thinking in the perspective of the user, help them know what to look for in order to enable it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Good call on adding the CLI option
src/conda_constructor/uninstall.py
Outdated
| f"Failed to unprotect {env_prefix}. Try to re-run the uninstallation with " | ||
| f"elevated privileges or remove the file {frozen_file} manually." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we wrap the {...} in simple quotes like '{...}'? Just thinking if this is something that could contain spaces etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's a good idea - done
src/conda_constructor/uninstall.py
Outdated
| conda_main("remove", "-y", "-p", str(env_prefix), "--all") | ||
| return_code = conda_main("remove", "-y", "-p", str(env_prefix), "--all") | ||
| if return_code != 0: | ||
| raise RuntimeError(f"Failed to remove environment {env_prefix}.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is necessary, but would help with debugging. Feel free to just ignore.
| raise RuntimeError(f"Failed to remove environment {env_prefix}.") | |
| raise RuntimeError(f"Failed to remove environment {env_prefix}. Return code: {return_code}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realistically, the return code won't tell us more than the preceding error messages, so I don't think the return code is necessary.
| conda_main("clean", "--all", "-y") | ||
| return_code = conda_main("clean", "--all", "-y") | ||
| if return_code != 0: | ||
| logger.warning("Failed to remove all cache files.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here (if we should add the return_code to the warning). Perhaps even less important since it's just a warning.
| def flush(self): | ||
| pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious why this is needed. Maybe a docstring could help here understand why we just implement it as empty. I assume it's not necessary to flush since we the write methods write directly via self.logger.log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your guess is correct. We must have a flush method implemented, but the log call does all that work already. I added a comment.
lrandersson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you need a secondary review but LGTM, well done!
| logger = logging.getLogger() | ||
| # On Windows, these warnings are expected because the uninstaller may still be | ||
| # accessing files (like install.log) that conda cannot rename. | ||
| if sys.platform == "win32": | ||
| conda_logger = logging.getLogger("conda.gateways.disk.delete") | ||
| conda_logger.addFilter(lambda record: "Could not remove or rename" not in record.getMessage()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this as import-time logic, or should it be a separate function we call as part of the CLI initialization? Is that too late in the process?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it for the visibility, but you are right that it isn't strictly needed at import time. So, I went a third way: importing these main execution function isn't needed until we actually use it. So, I just moved the import statements into the execute function.
src/entry_point.py
Outdated
| logger_parser = argparse.ArgumentParser(add_help=False) | ||
| logger_parser.add_argument("--log-file", type=Path) | ||
| args, remaining = logger_parser.parse_known_args() | ||
| if args.log_file: | ||
| sys.argv[1:] = remaining | ||
| setup_logger(args.log_file.resolve()) | ||
|
|
||
| return main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you turn setup_logger into a context manager, then change this to:
| logger_parser = argparse.ArgumentParser(add_help=False) | |
| logger_parser.add_argument("--log-file", type=Path) | |
| args, remaining = logger_parser.parse_known_args() | |
| if args.log_file: | |
| sys.argv[1:] = remaining | |
| setup_logger(args.log_file.resolve()) | |
| return main() | |
| logger_parser = argparse.ArgumentParser(add_help=False) | |
| logger_parser.add_argument("--log-file", type=Path) | |
| args, remaining = logger_parser.parse_known_args() | |
| if args.log_file: | |
| sys.argv[1:] = remaining | |
| maybe_setup_logger = setup_logger(args.log_file.resolve()) | |
| else: | |
| maybe_setup_logger = nullcontext() # this comes from contextlib | |
| with maybe_setup_logger: | |
| return main() |
| # on CI, setup-miniconda registers `test` as auto-activate for every CMD | ||
| # which adds some unnecessary stderr output; so, only read the first line | ||
| log_text = log_text.split("\n")[0] | ||
| assert os.path.realpath(log_text.strip()) == os.path.realpath(CONDA_EXE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to assert whether the process had any output? All of it should have technically gone to the logfile, right?
| assert os.path.realpath(log_text.strip()) == os.path.realpath(CONDA_EXE) | |
| assert os.path.realpath(log_text.strip()) == os.path.realpath(CONDA_EXE) | |
| assert not process.stdout + process.stderr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Output goes to both the screen and the log file, so we should still check the output.
Description
Add the ability to log
condaandconda-standaloneoutput into a file. This feature essentially simulates theteecommand.This can be used to populate log files for installations, especially on systems where
teeis not an option (Windows, in particular). One caveat of capturing output streams this way is that carriage returns are not properly handled and are output into new lines. This means thatbecomes
I also added some more logging and error handling output to the uninstallation process.
Xref: conda/constructor#1108
Checklist - did you ...
newsdirectory (using the template) for the next release's release notes?