Skip to content

Commit 557cd34

Browse files
feat: Add check50-aligned logging system
- Add LogLevel enum (DEBUG, INFO, WARNING, ERROR) - Add ColoredFormatter for colored log output to stderr - Add --log-level flag with choices: debug, info, warning, error - Dev mode (-d/--dev) automatically sets log level to 'info' - Default log level is 'warning' for normal operation - Log format: (LEVEL) message with colors (magenta for INFO, etc.) Aligned with check50's logging behavior: - Info level shows: commands run, config details, file patterns - Debug level shows: command output (future enhancement) - Warning level (default): only shows warnings - Deprecate --log flag in favor of --log-level info All 129 tests passing
1 parent e5f5553 commit 557cd34

File tree

1 file changed

+83
-1
lines changed

1 file changed

+83
-1
lines changed

bootcs/__main__.py

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"""
88

99
import argparse
10+
import enum
1011
import json
12+
import logging
1113
import os
1214
import sys
1315
import time
@@ -21,6 +23,53 @@
2123
from . import lib50
2224

2325

26+
LOGGER = logging.getLogger(__name__)
27+
28+
29+
class LogLevel(enum.IntEnum):
30+
"""Log levels aligned with check50."""
31+
DEBUG = logging.DEBUG
32+
INFO = logging.INFO
33+
WARNING = logging.WARNING
34+
ERROR = logging.ERROR
35+
36+
37+
class ColoredFormatter(logging.Formatter):
38+
"""Colored log formatter aligned with check50."""
39+
COLORS = {
40+
"ERROR": "red",
41+
"WARNING": "yellow",
42+
"DEBUG": "cyan",
43+
"INFO": "magenta",
44+
}
45+
46+
def __init__(self, fmt, use_color=True):
47+
super().__init__(fmt=fmt)
48+
self.use_color = use_color
49+
50+
def format(self, record):
51+
msg = super().format(record)
52+
return msg if not self.use_color else termcolor.colored(
53+
msg, getattr(record, "color", self.COLORS.get(record.levelname))
54+
)
55+
56+
57+
def setup_logging(level):
58+
"""
59+
Setup logging system aligned with check50.
60+
61+
Args:
62+
level: Log level string (debug, info, warning, error)
63+
"""
64+
for logger in (logging.getLogger("lib50"), LOGGER):
65+
logger.setLevel(level.upper())
66+
handler = logging.StreamHandler(sys.stderr)
67+
handler.setFormatter(
68+
ColoredFormatter("(%(levelname)s) %(message)s", use_color=sys.stderr.isatty())
69+
)
70+
logger.addHandler(handler)
71+
72+
2473
def main():
2574
"""Main entry point for bootcs CLI."""
2675
parser = argparse.ArgumentParser(
@@ -36,7 +85,14 @@ def main():
3685
check_parser.add_argument("slug", help="The check slug (e.g., cs50/mario-less)")
3786
check_parser.add_argument("-o", "--output", choices=["ansi", "json"], default="ansi",
3887
help="Output format (default: ansi)")
39-
check_parser.add_argument("--log", action="store_true", help="Show detailed log")
88+
check_parser.add_argument("--log", action="store_true", help="Show detailed log (deprecated, use --log-level info)")
89+
check_parser.add_argument("--log-level",
90+
action="store",
91+
choices=[level.name.lower() for level in LogLevel],
92+
type=str.lower,
93+
help="warning: displays usage warnings.\n"
94+
"info: adds all commands run and log messages.\n"
95+
"debug: adds output of all commands run.")
4096
check_parser.add_argument("--target", action="append", metavar="check",
4197
help="Run only the specified check(s)")
4298
check_parser.add_argument("-L", "--language",
@@ -175,6 +231,21 @@ def run_check(args):
175231
slug = args.slug
176232
force_update = getattr(args, 'update', False)
177233

234+
# Dev mode implies log level "info" if not overwritten (like check50)
235+
if args.dev:
236+
if not args.log_level:
237+
args.log_level = "info"
238+
239+
# Setup logging
240+
if not args.log_level:
241+
args.log_level = "warning"
242+
setup_logging(args.log_level)
243+
244+
# Legacy --log flag support
245+
if args.log and not args.log_level:
246+
args.log_level = "info"
247+
setup_logging(args.log_level)
248+
178249
# Parse slug (MVP: only 2-part format)
179250
course_slug, stage_slug = parse_slug(slug)
180251

@@ -195,6 +266,12 @@ def run_check(args):
195266
termcolor.cprint("Use --dev to specify a checks directory for development.", "yellow", file=sys.stderr)
196267
return 1
197268

269+
# Debug info in dev mode
270+
if args.dev:
271+
LOGGER.info(f"Dev mode enabled")
272+
LOGGER.info(f"Check directory: {check_dir}")
273+
LOGGER.info(f"Language: {language}")
274+
198275
# Set internal state
199276
internal.check_dir = check_dir
200277
internal.slug = slug
@@ -211,6 +288,11 @@ def run_check(args):
211288
termcolor.cprint(f"Error: Checks file '{config['checks']}' not found in {check_dir}", "red", file=sys.stderr)
212289
return 1
213290

291+
# Dev mode: show config details
292+
if args.dev:
293+
LOGGER.info(f"Config file: {config.get('checks', '__init__.py')}")
294+
LOGGER.info(f"Files pattern: {config.get('files', [])}")
295+
214296
# Get list of files to include
215297
cwd = Path.cwd()
216298
try:

0 commit comments

Comments
 (0)