Skip to content

Commit 3b86542

Browse files
committed
Logger handler manages exception info using the existing ExceptionTrace class
1 parent bd98a59 commit 3b86542

File tree

3 files changed

+91
-9
lines changed

3 files changed

+91
-9
lines changed

src/cleo/logging/cleo_handler.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
from logging import LogRecord
66
from typing import TYPE_CHECKING
77
from typing import ClassVar
8+
from typing import cast
89

10+
from cleo.exceptions import CleoUserError
911
from cleo.io.outputs.output import Verbosity
12+
from cleo.ui.exception_trace.component import ExceptionTrace
1013

1114

1215
if TYPE_CHECKING:
@@ -65,6 +68,14 @@ def emit(self, record: logging.LogRecord) -> None:
6568
try:
6669
msg = self.tags.get(record.levelname, "") + self.format(record) + "</>"
6770
self.output.write(msg, new_line=True)
71+
if record.exc_info:
72+
_type, error, traceback = record.exc_info
73+
simple = not self.output.is_verbose() or isinstance(
74+
error, CleoUserError
75+
)
76+
error = cast(Exception, error)
77+
trace = ExceptionTrace(error)
78+
trace.render(self.output, simple)
6879

6980
except Exception:
7081
self.handleError(record)

tests/fixtures/foo4_command.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
import logging
44

5+
from typing import TYPE_CHECKING
56
from typing import ClassVar
67

78
from cleo.commands.command import Command
9+
from cleo.helpers import option
10+
11+
12+
if TYPE_CHECKING:
13+
from cleo.io.inputs.option import Option
814

915

1016
_logger = logging.getLogger(__file__)
@@ -17,13 +23,26 @@ def log_stuff() -> None:
1723
_logger.error("This is an error log record")
1824

1925

26+
def log_exception() -> None:
27+
try:
28+
raise RuntimeError("This is an exception that I raised")
29+
except RuntimeError as e:
30+
_logger.exception(e)
31+
32+
2033
class Foo4Command(Command):
2134
name = "foo4"
2235

2336
description = "The foo4 bar command"
2437

2538
aliases: ClassVar[list[str]] = ["foo4"]
2639

40+
options: ClassVar[list[Option]] = [option("exception")]
41+
2742
def handle(self) -> int:
28-
log_stuff()
43+
if self.option("exception"):
44+
log_exception()
45+
else:
46+
log_stuff()
47+
2948
return 0

tests/test_logging.py

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ def root_logger() -> logging.Logger:
3232
return root
3333

3434

35-
def test_run_with_logging_integration_normal(
36-
tester: ApplicationTester, root_logger: logging.Logger
35+
def test_cleohandler_normal(
36+
tester: ApplicationTester,
37+
root_logger: logging.Logger,
3738
) -> None:
3839
handler = CleoHandler(tester.io.output)
3940
root_logger.addHandler(handler)
@@ -46,8 +47,9 @@ def test_run_with_logging_integration_normal(
4647
assert tester.io.fetch_output() == expected
4748

4849

49-
def test_run_with_logging_integration_quiet(
50-
tester: ApplicationTester, root_logger: logging.Logger
50+
def test_cleohandler_quiet(
51+
tester: ApplicationTester,
52+
root_logger: logging.Logger,
5153
) -> None:
5254
handler = CleoHandler(tester.io.output)
5355
root_logger.addHandler(handler)
@@ -58,8 +60,9 @@ def test_run_with_logging_integration_quiet(
5860
assert tester.io.fetch_output() == ""
5961

6062

61-
def test_run_with_logging_integration_verbose(
62-
tester: ApplicationTester, root_logger: logging.Logger
63+
def test_cleohandler_verbose(
64+
tester: ApplicationTester,
65+
root_logger: logging.Logger,
6366
) -> None:
6467
handler = CleoHandler(tester.io.output)
6568
root_logger.addHandler(handler)
@@ -76,8 +79,9 @@ def test_run_with_logging_integration_verbose(
7679
assert tester.io.fetch_output() == expected
7780

7881

79-
def test_run_with_logging_integration_very_verbose(
80-
tester: ApplicationTester, root_logger: logging.Logger
82+
def test_cleohandler_very_verbose(
83+
tester: ApplicationTester,
84+
root_logger: logging.Logger,
8185
) -> None:
8286
handler = CleoHandler(tester.io.output)
8387
root_logger.addHandler(handler)
@@ -93,3 +97,51 @@ def test_run_with_logging_integration_very_verbose(
9397

9498
assert status_code == 0
9599
assert tester.io.fetch_output() == expected
100+
101+
102+
def test_cleohandler_exception_normal(
103+
tester: ApplicationTester,
104+
root_logger: logging.Logger,
105+
) -> None:
106+
handler = CleoHandler(tester.io.output)
107+
root_logger.addHandler(handler)
108+
109+
status_code = tester.execute("--exception")
110+
111+
assert status_code == 0
112+
lines = tester.io.fetch_output().splitlines()
113+
114+
assert len(lines) == 7
115+
assert lines[0] == "This is an exception that I raised"
116+
117+
118+
def test_cleohandler_exception_verbose(
119+
tester: ApplicationTester,
120+
root_logger: logging.Logger,
121+
) -> None:
122+
handler = CleoHandler(tester.io.output)
123+
root_logger.addHandler(handler)
124+
125+
status_code = tester.execute("-v --exception")
126+
127+
assert status_code == 0
128+
lines = tester.io.fetch_output().splitlines()
129+
130+
assert len(lines) == 20
131+
assert lines[0] == "This is an exception that I raised"
132+
133+
134+
def test_cleohandler_exception_very_verbose(
135+
tester: ApplicationTester,
136+
root_logger: logging.Logger,
137+
) -> None:
138+
handler = CleoHandler(tester.io.output)
139+
root_logger.addHandler(handler)
140+
141+
status_code = tester.execute("-vv --exception")
142+
143+
assert status_code == 0
144+
lines = tester.io.fetch_output().splitlines()
145+
146+
assert len(lines) == 20
147+
assert lines[0] == "This is an exception that I raised"

0 commit comments

Comments
 (0)