-
Notifications
You must be signed in to change notification settings - Fork 7
Centralize results directories and files and revamp logging #289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
41f1220
65a5d10
11900c7
064c35e
b44e78d
a00af10
477a3cb
c34bc08
0b69b1a
a4b9f45
4b4c788
6ca5b1c
ae373fa
bcfa40a
7564099
66abd3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,68 +1,121 @@ | ||
| """Logger module for setting up a custom logger.""" | ||
|
|
||
| import logging | ||
| import os | ||
| import time | ||
| from datetime import datetime | ||
|
|
||
| from pytz import UTC | ||
| LOG_FORMAT = ( | ||
| "%(asctime)s [%(levelname)s]: %(filename)s(%(funcName)s:%(lineno)s) >> %(message)s" | ||
| ) | ||
| LOG_FILEMODE = "w" | ||
| LOG_LEVEL = logging.INFO | ||
|
|
||
|
|
||
| def _setup_root_logger() -> str: # pragma: no cover | ||
| """Sets up the root logger. | ||
| class CustomFormatter(logging.Formatter): | ||
| def formatTime(self, record, datefmt=None): | ||
| # Includes microseconds up to 6 digits | ||
| dt = datetime.fromtimestamp(record.created) | ||
|
|
||
| The logger module will write to a log file and stream the console | ||
| simultaneously. | ||
| return dt.strftime("%Y-%m-%d %H:%M:%S.%f") | ||
|
|
||
| The log files are saved in a `/logs` directory relative to where | ||
| `e3sm_to_cmip` is executed. | ||
|
|
||
| Returns | ||
| ------- | ||
| str | ||
| The name of the logfile. | ||
| def _setup_root_logger(): | ||
| """Configures the root logger. | ||
|
|
||
| This function sets up the root logger with a predefined format and log level. | ||
| It also enables capturing of warnings issued by the `warnings` module and | ||
| redirects them to the logging system. | ||
|
|
||
| Notes | ||
| ----- | ||
| - The `force=True` parameter ensures that any existing logging configuration | ||
| is overridden. | ||
| - The file handler is added dynamically to the root logger later in the | ||
| E3SMtoCMIP class once the log file path is known. | ||
| """ | ||
| os.makedirs("logs", exist_ok=True) | ||
| filename = f'logs/{UTC.localize(datetime.utcnow()).strftime("%Y%m%d_%H%M%S_%f")}' | ||
| log_format = "%(asctime)s_%(msecs)03d:%(levelname)s:%(funcName)s:%(message)s" | ||
| custom_formatter = CustomFormatter(LOG_FORMAT) | ||
| console_handler = logging.StreamHandler() | ||
| console_handler.setFormatter(custom_formatter) | ||
|
|
||
| # Setup the logging module. | ||
| logging.basicConfig( | ||
| filename=filename, | ||
| format=log_format, | ||
| datefmt="%Y%m%d_%H%M%S", | ||
| level=logging.DEBUG, | ||
| level=LOG_LEVEL, | ||
| force=True, | ||
| handlers=[console_handler], | ||
| ) | ||
| logging.captureWarnings(True) | ||
| logging.Formatter.converter = time.gmtime | ||
|
|
||
| # Configure and add a console stream handler. | ||
| console_handler = logging.StreamHandler() | ||
| console_handler.setLevel(logging.INFO) | ||
| log_formatter = logging.Formatter(log_format) | ||
| console_handler.setFormatter(log_formatter) | ||
| logging.getLogger().addHandler(console_handler) | ||
|
|
||
| return filename | ||
| logging.captureWarnings(True) | ||
|
|
||
|
|
||
| def _setup_logger(name, propagate=True) -> logging.Logger: | ||
| """Sets up a logger object. | ||
| def _setup_child_logger(name: str, propagate: bool = True) -> logging.Logger: | ||
| """Sets up a logger that is a child of the root logger. | ||
|
|
||
| This function is intended to be used at the top-level of a module. | ||
| This child logger inherits the root logger's handlers. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : str | ||
| Name of the file where this function is called. | ||
| propagate : bool, optional | ||
| Propogate this logger module's messages to the root logger or not, by | ||
| default True. | ||
| Whether to propagate logger messages or not, by default True. | ||
|
|
||
| Returns | ||
| ------- | ||
| logging.Logger | ||
| The logger. | ||
| The child logger. | ||
|
|
||
| Examples | ||
| --------- | ||
| Detailed information, typically of interest only when diagnosing problems: | ||
|
|
||
| >>> logger.debug("") | ||
|
|
||
| Confirmation that things are working as expected: | ||
|
|
||
| >>> logger.info("") | ||
|
|
||
| An indication that something unexpected happened, or indicative of some | ||
| problem in the near future: | ||
|
|
||
| >>> logger.warning("") | ||
|
|
||
| The software has not been able to perform some function due to a more | ||
| serious problem: | ||
|
|
||
| >>> logger.error("") | ||
|
|
||
| Similar to ``logger.error()``, but also outputs stack trace: | ||
|
|
||
| >>> logger.exception("", exc_info=True) | ||
|
|
||
| A serious error, indicating that the program itself may be unable to | ||
| continue running: | ||
|
|
||
| >>> logger.critical("") | ||
| """ | ||
| logger = logging.getLogger(name) | ||
| logger.propagate = propagate | ||
|
|
||
| return logger | ||
|
|
||
|
|
||
| def _add_filehandler(log_path: str): | ||
| """Adds a file handler to the root logger dynamically. | ||
|
|
||
| Adding the file handler will also create the log file automatically. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| log_path : str | ||
| The path to the log file. | ||
|
|
||
| Notes | ||
| ----- | ||
| Any warnings that appear before the log filehandler is instantiated will not | ||
| be captured (e.g,. esmpy VersionWarning). However, they will still be | ||
| captured by the console via the default StreamHandler. | ||
| """ | ||
| file_handler = logging.FileHandler(log_path, mode=LOG_FILEMODE) | ||
|
|
||
| custom_formatter = CustomFormatter(LOG_FORMAT) | ||
| file_handler.setFormatter(custom_formatter) | ||
|
|
||
| logging.root.addHandler(file_handler) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,4 +6,3 @@ to `handlers.yaml`. | |
| For example: | ||
|
|
||
| - Some contain legacy `handle_simple()` functions that have since been refactored as a single `handle_simple()` function | ||
| - `phalf.py` and `pfull.py` still use `cdms2` and `cdutil` | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These variables have been refactored. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,9 +14,9 @@ | |
| import yaml | ||
| from tqdm import tqdm | ||
|
|
||
| from e3sm_to_cmip._logger import _setup_logger | ||
| from e3sm_to_cmip._logger import _setup_child_logger | ||
|
|
||
| logger = _setup_logger(__name__) | ||
| logger = _setup_child_logger(__name__) | ||
|
|
||
|
|
||
| ATMOS_TABLES = [ | ||
|
|
@@ -50,6 +50,7 @@ | |
|
|
||
|
|
||
| def print_debug(e): | ||
| # TODO: Deprecate this function. We use Python logger now. | ||
|
Comment on lines
52
to
+53
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI |
||
| _, _, tb = sys.exc_info() | ||
| traceback.print_tb(tb) | ||
| print(e) | ||
|
|
@@ -73,11 +74,16 @@ def print_message(message, status="error"): | |
| """ | ||
| Prints a message with either a green + or a red - | ||
|
|
||
| # TODO: Deprecate this function. We use Python logger now. Colors can't | ||
| # be captured in log files. | ||
|
|
||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI deprecate |
||
| Parameters: | ||
| message (str): the message to print | ||
| status (str): th""" | ||
| status (str): the status message. | ||
| """ | ||
|
|
||
| if status == "error": | ||
| print( | ||
| logger.error( | ||
| colors.FAIL | ||
| + "[-] " | ||
| + colors.ENDC | ||
|
|
@@ -86,11 +92,11 @@ def print_message(message, status="error"): | |
| + colors.ENDC | ||
| ) | ||
| elif status == "ok": | ||
| print(colors.OKGREEN + "[+] " + colors.ENDC + str(message)) | ||
| logger.info(colors.OKGREEN + "[+] " + colors.ENDC + str(message)) | ||
| elif status == "info": | ||
| print(str(message)) | ||
| logger.info(str(message)) | ||
| elif status == "debug": | ||
| print( | ||
| logger.info( | ||
| colors.OKBLUE | ||
| + "[*] " | ||
| + colors.ENDC | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.