From 74653c48daea4a8af4609a9769e3f2654bbe8bdd Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Tue, 15 Sep 2020 16:31:49 -0400 Subject: [PATCH] Add run_logged_shell_subprocess to track subprocess output/errors --- utils/venv-manager.py | 76 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/utils/venv-manager.py b/utils/venv-manager.py index f8f8c99..ec2f452 100644 --- a/utils/venv-manager.py +++ b/utils/venv-manager.py @@ -11,7 +11,8 @@ $ venv-manager.py reset """ -import os, logging, argparse, pathlib +from pathlib import Path +import os, logging, argparse, subprocess ################################################################################ @@ -19,9 +20,10 @@ ################################################################################ # Set virtual environment path -WEBQUEUE2_DIR = pathlib.Path(__file__).parent.parent -API_DIR = pathlib.Path(WEBQUEUE2_DIR, "api") -VENV_DIR = pathlib.Path(API_DIR, 'venv') +WEBQUEUE2_DIR = Path(__file__).parent.parent +API_DIR = Path(WEBQUEUE2_DIR, "api") +VENV_NAME = "venv" +VENV_DIR = Path(API_DIR, VENV_NAME) # Configure the logger @@ -38,7 +40,7 @@ stream_handler = logging.StreamHandler() stream_handler.setFormatter(log_formatter) -log_file_path = pathlib.Path(WEBQUEUE2_DIR, "utils", f'{logger_name}.log') +log_file_path = Path(WEBQUEUE2_DIR, "utils", f'{logger_name}.log') file_handler = logging.FileHandler(log_file_path) file_handler.setFormatter(log_formatter) @@ -65,7 +67,71 @@ def get_args() -> argparse.Namespace: ) return parser.parse_args() +command = f"cd {API_DIR} && python3 -m venv {VENV_NAME}" +def run_logged_shell_subprocess(command: str, timeout: int = 10) -> int: + """Executes a shell command using subprocess with logging. + + stderr is redirected to stdout and stdout is pipped to logger. + If the subprocess raises an exception, the exception is logged as critical. + + Example: + Running a successful command: + run_logged_shell_subprocess(command="pwd") + Returns 0 + + Running an unsuccessful command with a 20 second timeout: + run_logged_shell_subprocess(command="pwd", timeout=20) + Returns 1 + + Args: + command (str): The command to run + timeout (int): The number of seconds to wait for a program before killing it + + Returns: + int: The return code of the subprocess + """ + logger.debug(f"Entering subprocess for '{command}'") + with subprocess.Popen(command,\ + stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True, text=True)\ + as logged_shell_process: + + subprocess_log_prefix = f"(PID: {logged_shell_process.pid})" + + try: + # Convert combined stdout and stderr stream to list of strings + process_output_stream, _ = logged_shell_process.communicate(timeout=timeout) + process_output_lines = process_output_stream.split("\n") + # Remove last entry in process_output_lines because it is always empty + process_output_lines.pop(-1) + + for line in process_output_lines: + logger.debug(f"{subprocess_log_prefix}: {line}") + except Exception as exception: + logger.critical(str(exception)) + return logged_shell_process.returncode + else: + if logged_shell_process.returncode != 0: + logger.debug(f"Subprocess exitied with a return code of {logged_shell_process.returncode}") + return logged_shell_process.returncode + elif logged_shell_process.returncode == 0: + logger.info(f"Subprocess for '{command}' completed successfuly") + return logged_shell_process.returncode + finally: + logger.debug(f"Exiting subprocess for '{command}''") + + + + if __name__ == "__main__": args = get_args() action = args.action + + if action == "create": + exit(create_environment()) + elif action == "delete": + exit(delete_environment()) + elif action == "reset": + exit(reset_environment()) + else: + logger.critical(f'Invalid argument {action}') \ No newline at end of file