Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions better_exceptions/log.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,36 @@
from __future__ import absolute_import

import logging
import sys

from logging import Logger, StreamHandler


def _uncolored_format_exception(exc_info):
"""Format exception without ANSI color codes."""
from .formatter import ExceptionFormatter, THEME, MAX_LENGTH, PIPE_CHAR, CAP_CHAR

formatter = ExceptionFormatter(
colored=False, theme=THEME, max_length=MAX_LENGTH,
pipe_char=PIPE_CHAR, cap_char=CAP_CHAR
)
return u''.join(formatter.format_exception(*exc_info))


def patch():
import logging
from . import format_exception

logging_format_exception = lambda exc_info: u''.join(format_exception(*exc_info))
colored_fn = lambda exc_info: u''.join(format_exception(*exc_info))

if hasattr(logging, '_defaultFormatter'):
logging._defaultFormatter.format_exception = logging_format_exception
for handler_ref in logging._handlerList:
handler = handler_ref()
if handler is None or handler.formatter is None:
continue

patchables = [handler() for handler in logging._handlerList if isinstance(handler(), StreamHandler)]
patchables = [handler for handler in patchables if handler.stream == sys.stderr]
patchables = [handler for handler in patchables if handler.formatter is not None]
for handler in patchables:
handler.formatter.formatException = logging_format_exception
if isinstance(handler, StreamHandler) and handler.stream is sys.stderr:
handler.formatter.formatException = colored_fn
else:
handler.formatter.formatException = _uncolored_format_exception


class BetExcLogger(Logger):
Expand Down
59 changes: 59 additions & 0 deletions test/test_file_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Test that file logs are uncolored but stderr keeps colors."""
import io
import logging
import os
import re
import sys
import tempfile

import better_exceptions

ANSI_ESCAPE = re.compile(r'\x1b\[[0-9;]*m')

def main():
better_exceptions.SUPPORTS_COLOR = True
better_exceptions.hook()

fd, path = tempfile.mkstemp()
os.close(fd)

original_stderr = sys.stderr
terminal = io.StringIO()
root = logging.getLogger()

try:
sys.stderr = terminal
file_handler = logging.FileHandler(path)
stream_handler = logging.StreamHandler(sys.stderr)
logging.basicConfig(level=logging.DEBUG, handlers=[file_handler, stream_handler])

logger = logging.getLogger(__name__)
try:
x = 52
assert x == 90
except AssertionError:
logger.exception("test failed")

for h in root.handlers:
h.flush()

with open(path) as f:
file_output = f.read()
terminal_output = terminal.getvalue()

# File output should NOT have ANSI codes
assert not ANSI_ESCAPE.search(file_output), f"File output should not contain ANSI codes: {file_output[:100]}"
print("PASS: file output has no ANSI codes")

# stderr output SHOULD have ANSI codes
assert ANSI_ESCAPE.search(terminal_output), f"Terminal output should contain ANSI codes: {terminal_output[:100]}"
print("PASS: terminal output has ANSI codes")
finally:
sys.stderr = original_stderr
for h in root.handlers[:]:
h.close()
root.removeHandler(h)
os.remove(path)

if __name__ == "__main__":
main()