summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAravind Vasudevan <aravindvasudev@google.com>2023-09-06 17:25:58 +0000
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-09-06 18:07:55 +0000
commit712e62b9b07f690abbb40e089a17f4ddec6ba952 (patch)
treecac20eaeb631df384031f42797d33189de70465d
parentdaf2ad38eb62fb990fadc3fc872f303113ac4768 (diff)
downloadgit-repo-712e62b9b07f690abbb40e089a17f4ddec6ba952.tar.gz
logging: Use log.formatter for coloring logs
Bug: b/292704435 Change-Id: Iebdf8fb7666592dc5df2b36aae3185d1fc71bd66 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/385514 Tested-by: Aravind Vasudevan <aravindvasudev@google.com> Commit-Queue: Aravind Vasudevan <aravindvasudev@google.com> Reviewed-by: Mike Frysinger <vapier@google.com>
-rw-r--r--repo_logging.py58
-rw-r--r--tests/test_repo_logging.py36
2 files changed, 33 insertions, 61 deletions
diff --git a/repo_logging.py b/repo_logging.py
index e94af7df..7d050555 100644
--- a/repo_logging.py
+++ b/repo_logging.py
@@ -15,7 +15,7 @@
15"""Logic for printing user-friendly logs in repo.""" 15"""Logic for printing user-friendly logs in repo."""
16 16
17import logging 17import logging
18from typing import Any, List 18from typing import List
19 19
20from color import Coloring 20from color import Coloring
21 21
@@ -23,23 +23,42 @@ from color import Coloring
23SEPARATOR = "=" * 80 23SEPARATOR = "=" * 80
24 24
25 25
26class LogColoring(Coloring): 26class _ConfigMock:
27 """Default coloring config to use when Logging.config is not set."""
28
29 def __init__(self):
30 self.default_values = {"color.ui": "auto"}
31
32 def GetString(self, x):
33 return self.default_values.get(x, None)
34
35
36class _LogColoring(Coloring):
27 """Coloring outstream for logging.""" 37 """Coloring outstream for logging."""
28 38
29 def __init__(self, config): 39 def __init__(self, config):
30 super().__init__(config, "logs") 40 super().__init__(config, "logs")
31 self.error = self.colorer("error", fg="red") 41 self.error = self.colorer("error", fg="red")
32 self.warning = self.colorer("warn", fg="yellow") 42 self.warning = self.colorer("warn", fg="yellow")
43 self.levelMap = {
44 "WARNING": self.warning,
45 "ERROR": self.error,
46 }
33 47
34 48
35class ConfigMock: 49class _LogColoringFormatter(logging.Formatter):
36 """Default coloring config to use when Logging.config is not set.""" 50 """Coloring formatter for logging."""
37 51
38 def __init__(self): 52 def __init__(self, config=None, *args, **kwargs):
39 self.default_values = {"color.ui": "auto"} 53 self.config = config if config else _ConfigMock()
54 self.colorer = _LogColoring(self.config)
55 super().__init__(*args, **kwargs)
40 56
41 def GetString(self, x): 57 def format(self, record):
42 return self.default_values.get(x, None) 58 """Formats |record| with color."""
59 msg = super().format(record)
60 colorer = self.colorer.levelMap.get(record.levelname)
61 return msg if not colorer else colorer(msg)
43 62
44 63
45class RepoLogger(logging.Logger): 64class RepoLogger(logging.Logger):
@@ -47,23 +66,12 @@ class RepoLogger(logging.Logger):
47 66
48 def __init__(self, name: str, config=None, **kwargs): 67 def __init__(self, name: str, config=None, **kwargs):
49 super().__init__(name, **kwargs) 68 super().__init__(name, **kwargs)
50 self.config = config if config else ConfigMock() 69 handler = logging.StreamHandler()
51 self.colorer = LogColoring(self.config) 70 handler.setFormatter(_LogColoringFormatter(config))
52 71 self.addHandler(handler)
53 def error(self, msg: Any, *args, **kwargs):
54 """Print and aggregate error-level logs."""
55 colored_error = self.colorer.error(str(msg), *args)
56 super().error(colored_error, **kwargs)
57
58 def warning(self, msg: Any, *args, **kwargs):
59 """Print warning-level logs with coloring."""
60 colored_warning = self.colorer.warning(str(msg), *args)
61 super().warning(colored_warning, **kwargs)
62 72
63 def log_aggregated_errors(self, errors: List[Exception]): 73 def log_aggregated_errors(self, errors: List[Exception]):
64 """Print all aggregated logs.""" 74 """Print all aggregated logs."""
65 super().error(self.colorer.error(SEPARATOR)) 75 super().error(SEPARATOR)
66 super().error( 76 super().error("Repo command failed due to following errors:")
67 self.colorer.error("Repo command failed due to following errors:") 77 super().error("\n".join(str(e) for e in errors))
68 )
69 super().error("\n".join(map(str, errors)))
diff --git a/tests/test_repo_logging.py b/tests/test_repo_logging.py
index b51e6270..52f251a7 100644
--- a/tests/test_repo_logging.py
+++ b/tests/test_repo_logging.py
@@ -20,42 +20,6 @@ from repo_logging import RepoLogger
20 20
21 21
22class TestRepoLogger(unittest.TestCase): 22class TestRepoLogger(unittest.TestCase):
23 def test_error_logs_error(self):
24 """Test if error fn outputs logs."""
25 logger = RepoLogger(__name__)
26 result = None
27
28 def mock_handler(log):
29 nonlocal result
30 result = log.getMessage()
31
32 mock_out = mock.MagicMock()
33 mock_out.level = 0
34 mock_out.handle = mock_handler
35 logger.addHandler(mock_out)
36
37 logger.error("We're no strangers to love")
38
39 self.assertEqual(result, "We're no strangers to love")
40
41 def test_warning_logs_error(self):
42 """Test if warning fn outputs logs."""
43 logger = RepoLogger(__name__)
44 result = None
45
46 def mock_handler(log):
47 nonlocal result
48 result = log.getMessage()
49
50 mock_out = mock.MagicMock()
51 mock_out.level = 0
52 mock_out.handle = mock_handler
53 logger.addHandler(mock_out)
54
55 logger.warning("You know the rules and so do I (do I)")
56
57 self.assertEqual(result, "You know the rules and so do I (do I)")
58
59 def test_log_aggregated_errors_logs_aggregated_errors(self): 23 def test_log_aggregated_errors_logs_aggregated_errors(self):
60 """Test if log_aggregated_errors outputs aggregated errors.""" 24 """Test if log_aggregated_errors outputs aggregated errors."""
61 logger = RepoLogger(__name__) 25 logger = RepoLogger(__name__)