Skip to content

Commit f19785a

Browse files
committed
refactor: improve console logging for commands
Signed-off-by: Demolus13 <parth.govale@oracle.com>
1 parent 9940f19 commit f19785a

File tree

9 files changed

+625
-87
lines changed

9 files changed

+625
-87
lines changed

src/macaron/__main__.py

Lines changed: 120 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
)
2121
from macaron.config.defaults import create_defaults, load_defaults
2222
from macaron.config.global_config import global_config
23+
from macaron.console import access_handler
2324
from macaron.errors import ConfigurationError
2425
from macaron.output_reporter.reporter import HTMLReporter, JSONReporter, PolicyReporter
2526
from macaron.policy_engine.policy_engine import run_policy_engine, show_prelude
@@ -63,7 +64,8 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
6364
if analyzer_single_args.provenance_expectation is not None:
6465
if not os.path.exists(analyzer_single_args.provenance_expectation):
6566
logger.critical(
66-
'The provenance expectation file "%s" does not exist.', analyzer_single_args.provenance_expectation
67+
'The provenance expectation file "%s" does not exist.',
68+
analyzer_single_args.provenance_expectation,
6769
)
6870
sys.exit(os.EX_OSFILE)
6971
global_config.load_expectation_files(analyzer_single_args.provenance_expectation)
@@ -72,7 +74,8 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
7274
if analyzer_single_args.python_venv is not None:
7375
if not os.path.exists(analyzer_single_args.python_venv):
7476
logger.critical(
75-
'The Python virtual environment path "%s" does not exist.', analyzer_single_args.python_venv
77+
'The Python virtual environment path "%s" does not exist.',
78+
analyzer_single_args.python_venv,
7679
)
7780
sys.exit(os.EX_OSFILE)
7881
global_config.load_python_venv(analyzer_single_args.python_venv)
@@ -95,7 +98,10 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
9598
else:
9699
user_provided_local_maven_repo = analyzer_single_args.local_maven_repo
97100
if not os.path.isdir(user_provided_local_maven_repo):
98-
logger.error("The user provided local Maven repo at %s is not valid.", user_provided_local_maven_repo)
101+
logger.error(
102+
"The user provided local Maven repo at %s is not valid.",
103+
user_provided_local_maven_repo,
104+
)
99105
sys.exit(os.EX_USAGE)
100106

101107
global_config.local_maven_repo = user_provided_local_maven_repo
@@ -111,7 +117,8 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
111117
lstrip_blocks=True,
112118
)
113119
html_reporter = HTMLReporter(
114-
env=custom_jinja_env, target_template=os.path.basename(analyzer_single_args.template_path)
120+
env=custom_jinja_env,
121+
target_template=os.path.basename(analyzer_single_args.template_path),
115122
)
116123
if not html_reporter.template:
117124
logger.error("Exiting because the custom template cannot be found.")
@@ -207,8 +214,10 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int:
207214

208215
result = run_policy_engine(verify_policy_args.database, policy_content)
209216
vsa = generate_vsa(policy_content=policy_content, policy_result=result)
217+
rich_handler = access_handler.get_handler()
210218
if vsa is not None:
211219
vsa_filepath = os.path.join(global_config.output_path, "vsa.intoto.jsonl")
220+
rich_handler.update_vsa(vsa_filepath)
212221
logger.info(
213222
"Generating the Verification Summary Attestation (VSA) to %s.",
214223
os.path.relpath(vsa_filepath, os.getcwd()),
@@ -222,8 +231,12 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int:
222231
file.write(json.dumps(vsa))
223232
except OSError as err:
224233
logger.error(
225-
"Could not generate the VSA to %s. Error: %s", os.path.relpath(vsa_filepath, os.getcwd()), err
234+
"Could not generate the VSA to %s. Error: %s",
235+
os.path.relpath(vsa_filepath, os.getcwd()),
236+
err,
226237
)
238+
else:
239+
rich_handler.update_vsa("No VSA generated.")
227240

228241
policy_reporter = PolicyReporter()
229242
policy_reporter.generate(global_config.output_path, result)
@@ -290,16 +303,23 @@ def find_source(find_args: argparse.Namespace) -> int:
290303

291304
def perform_action(action_args: argparse.Namespace) -> None:
292305
"""Perform the indicated action of Macaron."""
306+
rich_handler = access_handler.get_handler()
293307
match action_args.action:
294308
case "dump-defaults":
309+
if not action_args.disable_rich_output:
310+
rich_handler.start("dump-defaults")
295311
# Create the defaults.ini file in the output dir and exit.
296312
create_defaults(action_args.output_dir, os.getcwd())
297313
sys.exit(os.EX_OK)
298314

299315
case "verify-policy":
316+
if not action_args.disable_rich_output:
317+
rich_handler.start("verify-policy")
300318
sys.exit(verify_policy(action_args))
301319

302320
case "analyze":
321+
if not action_args.disable_rich_output:
322+
rich_handler.start("analyze")
303323
if not global_config.gh_token:
304324
logger.error("GitHub access token not set.")
305325
sys.exit(os.EX_USAGE)
@@ -317,6 +337,8 @@ def perform_action(action_args: argparse.Namespace) -> None:
317337
analyze_slsa_levels_single(action_args)
318338

319339
case "find-source":
340+
if not action_args.disable_rich_output:
341+
rich_handler.start("find-source")
320342
try:
321343
for git_service in GIT_SERVICES:
322344
git_service.load_defaults()
@@ -393,6 +415,14 @@ def main(argv: list[str] | None = None) -> None:
393415
action="store_true",
394416
)
395417

418+
main_parser.add_argument(
419+
"-dro",
420+
"--disable-rich-output",
421+
default=False,
422+
help="Disable Rich UI output",
423+
action="store_true",
424+
)
425+
396426
main_parser.add_argument(
397427
"-o",
398428
"--output-dir",
@@ -531,7 +561,10 @@ def main(argv: list[str] | None = None) -> None:
531561
)
532562

533563
# Dump the default values.
534-
sub_parser.add_parser(name="dump-defaults", description="Dumps the defaults.ini file to the output directory.")
564+
sub_parser.add_parser(
565+
name="dump-defaults",
566+
description="Dumps the defaults.ini file to the output directory.",
567+
)
535568

536569
# Verify the Datalog policy.
537570
vp_parser = sub_parser.add_parser(name="verify-policy")
@@ -593,65 +626,94 @@ def main(argv: list[str] | None = None) -> None:
593626
main_parser.print_help()
594627
sys.exit(os.EX_USAGE)
595628

596-
if args.verbose:
597-
log_level = logging.DEBUG
598-
log_format = "%(asctime)s [%(name)s:%(funcName)s:%(lineno)d] [%(levelname)s] %(message)s"
599-
else:
600-
log_level = logging.INFO
601-
log_format = "%(asctime)s [%(levelname)s] %(message)s"
602-
603629
# Set global logging config. We need the stream handler for the initial
604630
# output directory checking log messages.
605-
st_handler = logging.StreamHandler(sys.stdout)
606-
logging.basicConfig(format=log_format, handlers=[st_handler], force=True, level=log_level)
631+
st_handler: logging.StreamHandler = logging.StreamHandler(sys.stdout)
632+
rich_handler: logging.Handler = logging.Handler()
633+
if args.disable_rich_output:
634+
if args.verbose:
635+
log_level = logging.DEBUG
636+
log_format = "%(asctime)s [%(name)s:%(funcName)s:%(lineno)d] [%(levelname)s] %(message)s"
637+
else:
638+
log_level = logging.INFO
639+
log_format = "%(asctime)s [%(levelname)s] %(message)s"
640+
st_handler = logging.StreamHandler(sys.stdout)
641+
logging.basicConfig(format=log_format, handlers=[st_handler], force=True, level=log_level)
642+
else:
643+
if args.verbose:
644+
log_level = logging.DEBUG
645+
log_format = "%(asctime)s [%(name)s:%(funcName)s:%(lineno)d] %(message)s"
646+
else:
647+
log_level = logging.INFO
648+
log_format = "%(asctime)s %(message)s"
649+
rich_handler = access_handler.set_handler(args.verbose)
650+
logging.basicConfig(format=log_format, handlers=[rich_handler], force=True, level=log_level)
607651

608-
# Set the output directory.
609-
if not args.output_dir:
610-
logger.error("The output path cannot be empty. Exiting ...")
611-
sys.exit(os.EX_USAGE)
652+
try:
653+
# Set the output directory.
654+
if not args.output_dir:
655+
logger.error("The output path cannot be empty. Exiting ...")
656+
sys.exit(os.EX_USAGE)
612657

613-
if os.path.isfile(args.output_dir):
614-
logger.error("The output directory already exists. Exiting ...")
615-
sys.exit(os.EX_USAGE)
658+
if os.path.isfile(args.output_dir):
659+
logger.error("The output directory already exists. Exiting ...")
660+
sys.exit(os.EX_USAGE)
616661

617-
if os.path.isdir(args.output_dir):
618-
logger.info("Setting the output directory to %s", os.path.relpath(args.output_dir, os.getcwd()))
619-
else:
620-
logger.info("No directory at %s. Creating one ...", os.path.relpath(args.output_dir, os.getcwd()))
621-
os.makedirs(args.output_dir)
622-
623-
# Add file handler to the root logger. Remove stream handler from the
624-
# root logger to prevent dependencies printing logs to stdout.
625-
debug_log_path = os.path.join(args.output_dir, "debug.log")
626-
log_file_handler = logging.FileHandler(debug_log_path, "w")
627-
log_file_handler.setFormatter(logging.Formatter(log_format))
628-
logging.getLogger().removeHandler(st_handler)
629-
logging.getLogger().addHandler(log_file_handler)
630-
631-
# Add StreamHandler to the Macaron logger only.
632-
mcn_logger = logging.getLogger("macaron")
633-
mcn_logger.addHandler(st_handler)
634-
635-
logger.info("The logs will be stored in debug.log")
636-
637-
# Set Macaron's global configuration.
638-
# The path to provenance expectation files will be updated if
639-
# set through analyze sub-command.
640-
global_config.load(
641-
macaron_path=macaron.MACARON_PATH,
642-
output_path=args.output_dir,
643-
build_log_path=os.path.join(args.output_dir, "build_log"),
644-
debug_level=log_level,
645-
local_repos_path=args.local_repos_path,
646-
resources_path=os.path.join(macaron.MACARON_PATH, "resources"),
647-
)
662+
if os.path.isdir(args.output_dir):
663+
logger.info(
664+
"Setting the output directory to %s",
665+
os.path.relpath(args.output_dir, os.getcwd()),
666+
)
667+
else:
668+
logger.info(
669+
"No directory at %s. Creating one ...",
670+
os.path.relpath(args.output_dir, os.getcwd()),
671+
)
672+
os.makedirs(args.output_dir)
673+
674+
# Add file handler to the root logger. Remove stream handler from the
675+
# root logger to prevent dependencies printing logs to stdout.
676+
debug_log_path = os.path.join(args.output_dir, "debug.log")
677+
log_file_handler = logging.FileHandler(debug_log_path, "w")
678+
log_file_handler.setFormatter(logging.Formatter(log_format))
679+
if args.disable_rich_output:
680+
logging.getLogger().removeHandler(st_handler)
681+
else:
682+
logging.getLogger().removeHandler(rich_handler)
683+
logging.getLogger().addHandler(log_file_handler)
684+
685+
# Add StreamHandler to the Macaron logger only.
686+
mcn_logger = logging.getLogger("macaron")
687+
if args.disable_rich_output:
688+
mcn_logger.addHandler(st_handler)
689+
else:
690+
mcn_logger.addHandler(rich_handler)
691+
692+
logger.info("The logs will be stored in debug.log")
693+
694+
# Set Macaron's global configuration.
695+
# The path to provenance expectation files will be updated if
696+
# set through analyze sub-command.
697+
global_config.load(
698+
macaron_path=macaron.MACARON_PATH,
699+
output_path=args.output_dir,
700+
build_log_path=os.path.join(args.output_dir, "build_log"),
701+
debug_level=log_level,
702+
local_repos_path=args.local_repos_path,
703+
resources_path=os.path.join(macaron.MACARON_PATH, "resources"),
704+
)
648705

649-
# Load the default values from defaults.ini files.
650-
if not load_defaults(args.defaults_path):
651-
logger.error("Exiting because the defaults configuration could not be loaded.")
652-
sys.exit(os.EX_NOINPUT)
706+
# Load the default values from defaults.ini files.
707+
if not load_defaults(args.defaults_path):
708+
logger.error("Exiting because the defaults configuration could not be loaded.")
709+
sys.exit(os.EX_NOINPUT)
653710

654-
perform_action(args)
711+
perform_action(args)
712+
finally:
713+
if args.disable_rich_output:
714+
st_handler.close()
715+
else:
716+
rich_handler.close()
655717

656718

657719
def _get_token_from_dict_or_env(token: str, token_dict: dict[str, str]) -> str:

0 commit comments

Comments
 (0)