Skip to content
This repository has been archived by the owner on Sep 2, 2021. It is now read-only.

Commit

Permalink
Forgotten commit of "Support structured logging to stderr" (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
happz committed Jan 31, 2020
1 parent 3529033 commit ad329f5
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 17 deletions.
18 changes: 18 additions & 0 deletions gluetool/glue.py
Original file line number Diff line number Diff line change
Expand Up @@ -1768,12 +1768,26 @@ class Glue(Configurable):
""",
'default': None
},
'json-file-pretty': {
'help': """
When JSON output to file is enable (via ``-j/--json-file``), instead of emmiting one log
entry per line, nicely format the entries for better readability.
""",
'action': 'store_true'
},
('J', 'json-output'): {
'help': """
If set, all log messages sent to the terminal are emitted as JSON structures.
""",
'action': 'store_true'
},
'json-output-pretty': {
'help': """
When JSON output is enable (via ``-J/--json-output``), instead of emmiting one log
entry per line, nicely format the entries for better readability.
""",
'action': 'store_true'
},
('o', 'debug-file', 'output'): {
'help': 'Log messages with at least ``DEBUG`` level are sent to this file.'
},
Expand Down Expand Up @@ -2474,7 +2488,9 @@ def parse_args(self, args):
debug_file = self.option('debug-file')
verbose_file = self.option('verbose-file')
json_file = self.option('json-file')
json_file_pretty = normalize_bool_option(self.option('json-file-pretty'))
json_output = normalize_bool_option(self.option('json-output'))
json_output_pretty = normalize_bool_option(self.option('json-output-pretty'))

if debug_file and not verbose_file:
verbose_file = '{}.verbose'.format(debug_file)
Expand All @@ -2484,7 +2500,9 @@ def parse_args(self, args):
debug_file=debug_file,
verbose_file=verbose_file,
json_file=json_file,
json_file_pretty=json_file_pretty,
json_output=json_output,
json_output_pretty=json_output_pretty,
sentry=self._sentry,
show_traceback=normalize_bool_option(self.option('show-traceback'))
)
Expand Down
58 changes: 41 additions & 17 deletions gluetool/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,22 @@ def print_wrapper(log_fn=None, label='print wrapper'):
log_fn('{} {} disabled'.format(BLOB_FOOTER, label))


def _json_dump(struct, **kwargs):
# type: (Any, **Any) -> str
"""
Dump given data structure as a JSON string. Additional arguments are passed to ``json.dumps``.
"""

# Use custom "default" handler, to at least encode obj's repr() output when
# json encoder does not know how to encode such class
def default(obj):
# type: (Any) -> str

return repr(obj)

return json.dumps(struct, default=default, **kwargs)


def format_blob(blob):
# type: (AnyStr) -> str

Expand All @@ -262,14 +278,7 @@ def format_dict(dictionary):
capabilities to present readable representation of a given structure.
"""

# Use custom "default" handler, to at least encode obj's repr() output when
# json encoder does not know how to encode such class
def default(obj):
# type: (Any) -> str

return repr(obj)

return json.dumps(dictionary, sort_keys=True, indent=4, separators=(',', ': '), default=default)
return _json_dump(dictionary, sort_keys=True, indent=4, separators=(',', ': '))


def format_table(table, **kwargs):
Expand Down Expand Up @@ -794,13 +803,14 @@ class LoggingFormatter(logging.Formatter):
logging.CRITICAL: lambda text: Colors.style(text, fg='red')
} # type: Dict[int, Callable[[str], str]]

def __init__(self, colors=True, log_tracebacks=False):
# type: (Optional[bool], Optional[bool]) -> None
def __init__(self, colors=True, log_tracebacks=False, prettify=False):
# type: (bool, bool, bool) -> None

super(LoggingFormatter, self).__init__()

self.colors = colors
self.log_tracebacks = log_tracebacks
self.prettify = prettify

@staticmethod
def _format_exception_chain(exc_info):
Expand Down Expand Up @@ -912,12 +922,18 @@ class JSONLoggingFormatter(logging.Formatter):
Custom logging formatter producing a JSON dictionary describing the log record.
"""

def __init__(self, **kwargs):
# type: (**Any) -> None
def __init__(self, colors=False, log_tracebacks=False, prettify=False):
# type: (bool, bool, bool) -> None
# pylint: disable=unused-argument

super(JSONLoggingFormatter, self).__init__()

if prettify:
self._emit = format_dict

else:
self._emit = _json_dump # type: ignore

@staticmethod
def _format_exception_chain(serialized, exc_info):
# type: (Dict[str, Any], ExceptionInfoType) -> None
Expand Down Expand Up @@ -1011,7 +1027,7 @@ def format(self, record):
if record.exc_info:
JSONLoggingFormatter._format_exception_chain(serialized, record.exc_info)

return ensure_str(format_dict(serialized))
return ensure_str(self._emit(serialized))


class Logging(object):
Expand Down Expand Up @@ -1141,7 +1157,8 @@ def enable_json_file(logger):
def _setup_log_file(filepath, # type: str
level, # type: int
limit_level=False, # type: bool
formatter_class=LoggingFormatter # type: Type[Union[LoggingFormatter, JSONLoggingFormatter]]
formatter_class=LoggingFormatter, # type: Type[Union[LoggingFormatter, JSONLoggingFormatter]]
prettify=False # type: bool
): # noqa
# type: (...) -> Optional[logging.FileHandler]

Expand All @@ -1156,7 +1173,7 @@ def _setup_log_file(filepath, # type: str

handler.setLevel(level)

formatter = formatter_class(colors=False, log_tracebacks=True)
formatter = formatter_class(colors=False, log_tracebacks=True, prettify=prettify)
handler.setFormatter(formatter)

def _close_log_file():
Expand All @@ -1183,7 +1200,9 @@ def setup_logger(level=DEFAULT_LOG_LEVEL, # type: int
debug_file=None, # type: Optional[str]
verbose_file=None, # type: Optional[str]
json_file=None, # type: Optional[str]
json_file_pretty=False, # type: bool
json_output=False, # type: bool
json_output_pretty=False, # type: bool
sentry=None, # type: Optional[gluetool.sentry.Sentry]
show_traceback=False # type: bool
): # noqa
Expand Down Expand Up @@ -1237,7 +1256,7 @@ def setup_logger(level=DEFAULT_LOG_LEVEL, # type: int

# set formatter
if json_output:
Logging.stderr_handler.setFormatter(JSONLoggingFormatter())
Logging.stderr_handler.setFormatter(JSONLoggingFormatter(prettify=json_output_pretty))

else:
Logging.stderr_handler.setFormatter(LoggingFormatter())
Expand All @@ -1257,7 +1276,12 @@ def setup_logger(level=DEFAULT_LOG_LEVEL, # type: int
Logging.verbose_file_handler = Logging._setup_log_file(verbose_file, VERBOSE, limit_level=True)

if json_file:
Logging.json_file_handler = Logging._setup_log_file(json_file, VERBOSE, formatter_class=JSONLoggingFormatter)
Logging.json_file_handler = Logging._setup_log_file(
json_file,
VERBOSE,
formatter_class=JSONLoggingFormatter,
prettify=json_file_pretty
)

# now our main logger should definitely exist and it should be usable
logger = Logging.get_logger()
Expand Down

0 comments on commit ad329f5

Please sign in to comment.