From c0ac550fc8b3d8e10b896b15d499f9446cd3c739 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Tue, 23 May 2023 16:14:46 +0200 Subject: [PATCH 1/5] Add journalizer module --- geotribu_cli/cli.py | 32 ++++--- geotribu_cli/utils/journalizer.py | 137 ++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 geotribu_cli/utils/journalizer.py diff --git a/geotribu_cli/cli.py b/geotribu_cli/cli.py index a26b0a1..dff08db 100644 --- a/geotribu_cli/cli.py +++ b/geotribu_cli/cli.py @@ -38,6 +38,7 @@ parser_search_image, parser_upgrade, ) +from geotribu_cli.utils.journalizer import configure_logger # ############################################################################# # ########## Globals ############### @@ -144,11 +145,18 @@ def main(args: list[str] = None): action="count", default=1, dest="verbosity", - # metavar="GEOTRIBU_LOGS_LEVEL", help="Niveau de verbosité : None = WARNING, -v = INFO, -vv = DEBUG. Réglable " "avec la variable d'environnement GEOTRIBU_LOGS_LEVEL.", ) + main_parser.add_argument( + "--no-logfile", + default=True, + action="store_false", + dest="opt_logfile_disabled", + help="Désactiver les fichiers de journalisation (logs).", + ) + main_parser.add_argument( "-h", "--help", @@ -351,23 +359,13 @@ def main(args: list[str] = None): # just get passed args args = main_parser.parse_args(args) - # set log level depending on verbosity argument - if 0 < args.verbosity < 4: - args.verbosity = 40 - (10 * args.verbosity) - elif args.verbosity >= 4: - # debug is the limit - args.verbosity = 40 - (10 * 3) + # log configuration + if args.opt_logfile_disabled: + configure_logger( + verbosity=args.verbosity, logfile=f"{__title_clean__}_{__version__}.log" + ) else: - args.verbosity = 0 - - logging.basicConfig( - level=args.verbosity, - format="%(asctime)s||%(levelname)s||%(module)s||%(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - - console = logging.StreamHandler() - console.setLevel(args.verbosity) + configure_logger(verbosity=args.verbosity) # add the handler to the root logger logger = logging.getLogger(__title_clean__) diff --git a/geotribu_cli/utils/journalizer.py b/geotribu_cli/utils/journalizer.py new file mode 100644 index 0000000..b008238 --- /dev/null +++ b/geotribu_cli/utils/journalizer.py @@ -0,0 +1,137 @@ +#! python3 # noqa: E265 + +"""Helper to configure logging depending on CLI options.""" + +# ############################################################################ +# ########## IMPORTS ############# +# ################################ + +# standard library +import logging +from getpass import getuser +from logging.handlers import RotatingFileHandler +from os import getenv +from os.path import expanduser, expandvars +from pathlib import Path +from platform import architecture +from platform import platform as opersys +from socket import gethostname + +# package +from geotribu_cli.__about__ import __title__, __version__ +from geotribu_cli.utils.check_path import check_path +from geotribu_cli.utils.proxies import get_proxy_settings + +# ############################################################################ +# ########## GLOBALS ############# +# ################################ + +# logs +logger = logging.getLogger(__name__) + +# ############################################################################ +# ########## FUNCTIONS ########### +# ################################ + + +def configure_logger(verbosity: int = 1, logfile: Path = None): + """Configure logging according to verbosity from CLI. + + Args: + verbosity (int): verbosity level + logfile (Path, optional): file where to store log. Defaults to None. + """ + # handle log level overridden by environment variable + verbosity = getenv("GEOTRIBU_LOGS_LEVEL", verbosity) + try: + verbosity = int(verbosity) + except ValueError as err: + logger.error(f"Bad verbosity value type: {err}. Fallback to 1.") + verbosity = 1 + + # set log level depending on verbosity argument + if 0 < verbosity < 4: + verbosity = 40 - (10 * verbosity) + elif verbosity >= 4: + # debug is the limit + verbosity = 40 - (10 * 3) + else: + verbosity = 0 + + # set console handler + log_console_handler = logging.StreamHandler() + log_console_handler.setLevel(verbosity) + + # set log file + if not logfile: + logging.basicConfig( + level=verbosity, + format="%(asctime)s||%(levelname)s||%(module)s||%(lineno)d||%(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + handlers=[log_console_handler], + ) + + else: + if getenv("GEOTRIBU_LOGS_DIR") and check_path( + input_path=Path(expandvars(expanduser(getenv("GEOTRIBU_LOGS_DIR")))), + must_be_a_file=False, + must_be_a_folder=True, + must_be_writable=True, + raise_error=False, + ): + logs_folder = Path(expandvars(expanduser(getenv("GEOTRIBU_LOGS_DIR")))) + logger.debug( + f"Logs folder set with GEOTRIBU_LOGS_DIR environment variable: {logs_folder}" + ) + else: + logs_folder: Path = Path().home() / ".geotribu/logs/" + logger.debug( + "Logs folder specified in GEOTRIBU_LOGS_DIR environment variable " + f"{getenv('GEOTRIBU_LOGS_DIR')} can't be used (see logs above). Fallback on " + f"default folder: {logs_folder}" + ) + + # make sure folder exists + logs_folder.mkdir(exist_ok=True, parents=True) + logs_filepath = Path(logs_folder, logfile) + + log_file_handler = RotatingFileHandler( + backupCount=10, + delay=True, + encoding="UTF-8", + filename=logs_filepath, + maxBytes=3000000, + mode="a", + ) + # force new file by execution + if logs_filepath.is_file(): + log_file_handler.doRollover() + + logging.basicConfig( + level=verbosity, + format="%(asctime)s||%(levelname)s||%(module)s||%(lineno)d||%(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + handlers=[log_console_handler, log_file_handler], + ) + + logger.info(f"Log file: {logs_filepath}") + + headers() + + +def headers(): + """Basic information to log before other message.""" + # initialize the log + logger.info(f"{'='*10} {__title__} - {__version__} {'='*10}") + logger.debug(f"Operating System: {opersys()}") + logger.debug(f"Architecture: {architecture()[0]}") + logger.debug(f"Computer: {gethostname()}") + logger.debug(f"Launched by user: {getuser()}") + + if getenv("userdomain"): + logger.debug(f"OS Domain: {getenv('userdomain')}") + + if get_proxy_settings(): + logger.debug(f"Network proxies detected: {get_proxy_settings()}") + else: + logger.debug("No network proxies detected") From 65011fc64d3ebe68be2448ada1776ed0bc7cad78 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Thu, 2 May 2024 10:29:40 +0200 Subject: [PATCH 2/5] feature(logs): improve journalization by adding a rotated log file --- geotribu_cli/utils/journalizer.py | 51 ++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/geotribu_cli/utils/journalizer.py b/geotribu_cli/utils/journalizer.py index b008238..ed51d97 100644 --- a/geotribu_cli/utils/journalizer.py +++ b/geotribu_cli/utils/journalizer.py @@ -10,17 +10,19 @@ import logging from getpass import getuser from logging.handlers import RotatingFileHandler -from os import getenv +from os import environ, getenv from os.path import expanduser, expandvars from pathlib import Path -from platform import architecture -from platform import platform as opersys +from platform import architecture, platform from socket import gethostname +from typing import Optional # package from geotribu_cli.__about__ import __title__, __version__ +from geotribu_cli.constants import GeotribuDefaults from geotribu_cli.utils.check_path import check_path from geotribu_cli.utils.proxies import get_proxy_settings +from geotribu_cli.utils.str2bool import str2bool # ############################################################################ # ########## GLOBALS ############# @@ -28,13 +30,14 @@ # logs logger = logging.getLogger(__name__) +default_settings = GeotribuDefaults() # ############################################################################ # ########## FUNCTIONS ########### # ################################ -def configure_logger(verbosity: int = 1, logfile: Path = None): +def configure_logger(verbosity: int = 1, logfile: Optional[Path] = None): """Configure logging according to verbosity from CLI. Args: @@ -66,7 +69,7 @@ def configure_logger(verbosity: int = 1, logfile: Path = None): if not logfile: logging.basicConfig( level=verbosity, - format="%(asctime)s||%(levelname)s||%(module)s||%(lineno)d||%(message)s", + format="%(asctime)s||%(levelname)s||%(module)s||%(funcName)s||%(lineno)d||%(message)s", datefmt="%Y-%m-%d %H:%M:%S", handlers=[log_console_handler], ) @@ -84,7 +87,9 @@ def configure_logger(verbosity: int = 1, logfile: Path = None): f"Logs folder set with GEOTRIBU_LOGS_DIR environment variable: {logs_folder}" ) else: - logs_folder: Path = Path().home() / ".geotribu/logs/" + logs_folder: Path = default_settings.geotribu_working_folder.joinpath( + "logs" + ) logger.debug( "Logs folder specified in GEOTRIBU_LOGS_DIR environment variable " f"{getenv('GEOTRIBU_LOGS_DIR')} can't be used (see logs above). Fallback on " @@ -109,7 +114,7 @@ def configure_logger(verbosity: int = 1, logfile: Path = None): logging.basicConfig( level=verbosity, - format="%(asctime)s||%(levelname)s||%(module)s||%(lineno)d||%(message)s", + format="%(asctime)s||%(levelname)s||%(module)s||%(funcName)s||%(lineno)d||%(message)s", datefmt="%Y-%m-%d %H:%M:%S", handlers=[log_console_handler, log_file_handler], ) @@ -123,7 +128,7 @@ def headers(): """Basic information to log before other message.""" # initialize the log logger.info(f"{'='*10} {__title__} - {__version__} {'='*10}") - logger.debug(f"Operating System: {opersys()}") + logger.debug(f"Operating System: {platform()}") logger.debug(f"Architecture: {architecture()[0]}") logger.debug(f"Computer: {gethostname()}") logger.debug(f"Launched by user: {getuser()}") @@ -135,3 +140,33 @@ def headers(): logger.debug(f"Network proxies detected: {get_proxy_settings()}") else: logger.debug("No network proxies detected") + + if str2bool(getenv("QDT_SSL_USE_SYSTEM_STORES", False)): + logger.debug("Option to use native system certificates stores is enabled.") + if "REQUESTS_CA_BUNDLE" in environ: + environ.pop("REQUESTS_CA_BUNDLE") + logger.debug( + "Custom path to CA Bundle (REQUESTS_CA_BUNDLE) has been removed from " + "environment variables." + ) + if "CURL_CA_BUNDLE" in environ: + environ.pop("CURL_CA_BUNDLE") + logger.debug( + "Custom path to CA Bundle (CURL_CA_BUNDLE) has been removed from " + "environment variables." + ) + + +def get_logger_filepath() -> Path | None: + """Retrieve log filepath within logger handlers. + + Returns: + Path | None: path to the logfile or None if no handler has baseFilename attr. + """ + if logger.root.hasHandlers(): + for handler in logger.root.handlers: + if hasattr(handler, "baseFilename"): + return Path(handler.baseFilename) + + logger.warning("No file found in ay log handlers.") + return None From d6613939ebf116e61f5e8862bab8bb0ddadd2be0 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Thu, 2 May 2024 10:29:57 +0200 Subject: [PATCH 3/5] chore(deps): bump min version of rich argparse --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index c06d3f8..30f99dd 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,7 +4,7 @@ markdownify>=0.11,<0.13 Mastodon.py>=1.8.1,<1.9 orjson>=3.8,<3.11 packaging>=20,<25 -rich_argparse>=0.6,<1.5 +rich_argparse>=1,<1.5 python-frontmatter>=1,<2 requests>=2.31,<3 typing-extensions>=4,<5 ; python_version < '3.11' From aafc09b299f97db1ec3c75af6af6a4abc43100e5 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Thu, 2 May 2024 10:50:09 +0200 Subject: [PATCH 4/5] fix(python): pipe is not supported in 3.9 --- geotribu_cli/utils/journalizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geotribu_cli/utils/journalizer.py b/geotribu_cli/utils/journalizer.py index ed51d97..3347f87 100644 --- a/geotribu_cli/utils/journalizer.py +++ b/geotribu_cli/utils/journalizer.py @@ -157,7 +157,7 @@ def headers(): ) -def get_logger_filepath() -> Path | None: +def get_logger_filepath() -> Optional[Path]: """Retrieve log filepath within logger handlers. Returns: From 6952e50ae7dcae2f17b258d0a4d88e862e2f7549 Mon Sep 17 00:00:00 2001 From: GeoJulien Date: Thu, 2 May 2024 10:50:17 +0200 Subject: [PATCH 5/5] docs: minor changes --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 4263ea4..e362445 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,6 +23,7 @@ description = __about__.__summary__ project = __about__.__title__ version = release = __about__.__version__ +github_doc_root = f"{__about__.__uri__}/tree/main/docs/" # -- General configuration --------------------------------------------------- @@ -170,7 +171,7 @@ "deflist", "html_admonition", "html_image", - # "linkify", + "linkify", "replacements", "smartquotes", "strikethrough",