From ca95490d7c6022e9910d63ac77ebbe7ba8710d69 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Fri, 9 Oct 2020 14:25:34 -0500 Subject: [PATCH] Add log_json config option with default and validation handling This adds the `NotebookApp.log_json` CLI option which defaults to False. The default value comes from the `JUPYTER_ENABLE_JSON_LOGGING` environment variable. A validation hook is added such that if json logging is enabled but the `json-logging` package is not installed, an error is logged and the effective `log_json` value is False. If enabled and the `json-logging` package is installed, the `init_logging` method will initialize the non-web [1] json logging formatter. [1] https://github.com/bobbui/json-logging-python#21-non-web-application-log --- notebook/notebookapp.py | 62 ++++++++++++++++++++++++++++++----------- notebook/utils.py | 2 +- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py index 45c1cb5eb..7359f23ad 100755 --- a/notebook/notebookapp.py +++ b/notebook/notebookapp.py @@ -139,19 +139,6 @@ try: except ImportError: terminado_available = False -# Tolerate missing json_logging package. -_enable_json_logs = enable_json_logs() -try: - import json_logging -except ImportError: - # If configured for json logs and we can't do it, log a hint. - if _enable_json_logs: - logging.getLogger(__name__).exception( - 'Unable to use json logging due to missing packages. ' - 'Run "pip install notebook[json-logging]" to fix.' - ) - _enable_json_logs = False - #----------------------------------------------------------------------------- # Module globals #----------------------------------------------------------------------------- @@ -716,9 +703,45 @@ class NotebookApp(JupyterApp): password=(NotebookPasswordApp, NotebookPasswordApp.description.splitlines()[0]), ) - # Unless there is a way to re-initialize the log formatter (like with _log_format_changed?) - # we need to load the json logging formatter early here otherwise traitlets complains. - _log_formatter_cls = json_logging.JSONLogFormatter if _enable_json_logs else LogFormatter + _log_formatter_cls = LogFormatter + + _json_logging_import_error_logged = False + + log_json = Bool(False, config=True, + help=_('Set to True to enable JSON formatted logs. ' + 'Run "pip install notebook[json-logging]" to install the ' + 'required dependent packages. Can also be set using the ' + 'environment variable JUPYTER_ENABLE_JSON_LOGGING=true.') + ) + + @default('log_json') + def _default_log_json(self): + """Get the log_json value from the environment.""" + return enable_json_logs() + + @validate('log_json') + def _validate_log_json(self, proposal): + # If log_json=True, see if the json_logging package can be imported and + # override _log_formatter_cls if so. + value = proposal['value'] + if value: + try: + import json_logging + self._log_formatter_cls = json_logging.JSONLogFormatter + # Note that we don't need to trigger _log_format_changed here + # because init_logging will set the JSONLogFormatter on all + # of the registered loggers. + except ImportError: + # If configured for json logs and we can't do it, log a hint. + # Only log the error once though. + if not self._json_logging_import_error_logged: + logging.getLogger(__name__).exception( + 'Unable to use json logging due to missing packages. ' + 'Run "pip install notebook[json-logging]" to fix.' + ) + self._json_logging_import_error_logged = True + value = False + return value @default('log_level') def _default_log_level(self): @@ -1606,6 +1629,13 @@ class NotebookApp(JupyterApp): ) def init_logging(self): + if self.log_json: + self.log.debug('initializing json logging') + # Note that we don't guard against ImportError here because if the + # package is missing then _validate_log_json should have set + # log_json=False. + import json_logging + json_logging.init_non_web(enable_json=True) # This prevents double log messages because tornado use a root logger that # self.log is a child of. The logging module dispatches log messages to a log # and all of its ancenstors until propagate is set to False. diff --git a/notebook/utils.py b/notebook/utils.py index cbb0cf10a..5f3f70164 100644 --- a/notebook/utils.py +++ b/notebook/utils.py @@ -404,4 +404,4 @@ def unix_socket_in_use(socket_path): def enable_json_logs(): - return os.getenv('ENABLE_JSON_LOGGING', 'false').lower() == 'true' + return os.getenv('JUPYTER_ENABLE_JSON_LOGGING', 'false').lower() == 'true'