Skip to content

Commit 39649b6

Browse files
authored
Améliore la gestion des logs (#76)
2 parents c837a03 + 6952e50 commit 39649b6

File tree

4 files changed

+190
-19
lines changed

4 files changed

+190
-19
lines changed

docs/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
description = __about__.__summary__
2424
project = __about__.__title__
2525
version = release = __about__.__version__
26+
github_doc_root = f"{__about__.__uri__}/tree/main/docs/"
2627

2728
# -- General configuration ---------------------------------------------------
2829

@@ -170,7 +171,7 @@
170171
"deflist",
171172
"html_admonition",
172173
"html_image",
173-
# "linkify",
174+
"linkify",
174175
"replacements",
175176
"smartquotes",
176177
"strikethrough",

geotribu_cli/cli.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
parser_search_image,
3939
parser_upgrade,
4040
)
41+
from geotribu_cli.utils.journalizer import configure_logger
4142

4243
# #############################################################################
4344
# ########## Globals ###############
@@ -144,11 +145,18 @@ def main(args: list[str] = None):
144145
action="count",
145146
default=1,
146147
dest="verbosity",
147-
# metavar="GEOTRIBU_LOGS_LEVEL",
148148
help="Niveau de verbosité : None = WARNING, -v = INFO, -vv = DEBUG. Réglable "
149149
"avec la variable d'environnement GEOTRIBU_LOGS_LEVEL.",
150150
)
151151

152+
main_parser.add_argument(
153+
"--no-logfile",
154+
default=True,
155+
action="store_false",
156+
dest="opt_logfile_disabled",
157+
help="Désactiver les fichiers de journalisation (logs).",
158+
)
159+
152160
main_parser.add_argument(
153161
"-h",
154162
"--help",
@@ -351,23 +359,13 @@ def main(args: list[str] = None):
351359
# just get passed args
352360
args = main_parser.parse_args(args)
353361

354-
# set log level depending on verbosity argument
355-
if 0 < args.verbosity < 4:
356-
args.verbosity = 40 - (10 * args.verbosity)
357-
elif args.verbosity >= 4:
358-
# debug is the limit
359-
args.verbosity = 40 - (10 * 3)
362+
# log configuration
363+
if args.opt_logfile_disabled:
364+
configure_logger(
365+
verbosity=args.verbosity, logfile=f"{__title_clean__}_{__version__}.log"
366+
)
360367
else:
361-
args.verbosity = 0
362-
363-
logging.basicConfig(
364-
level=args.verbosity,
365-
format="%(asctime)s||%(levelname)s||%(module)s||%(message)s",
366-
datefmt="%Y-%m-%d %H:%M:%S",
367-
)
368-
369-
console = logging.StreamHandler()
370-
console.setLevel(args.verbosity)
368+
configure_logger(verbosity=args.verbosity)
371369

372370
# add the handler to the root logger
373371
logger = logging.getLogger(__title_clean__)

geotribu_cli/utils/journalizer.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#! python3 # noqa: E265
2+
3+
"""Helper to configure logging depending on CLI options."""
4+
5+
# ############################################################################
6+
# ########## IMPORTS #############
7+
# ################################
8+
9+
# standard library
10+
import logging
11+
from getpass import getuser
12+
from logging.handlers import RotatingFileHandler
13+
from os import environ, getenv
14+
from os.path import expanduser, expandvars
15+
from pathlib import Path
16+
from platform import architecture, platform
17+
from socket import gethostname
18+
from typing import Optional
19+
20+
# package
21+
from geotribu_cli.__about__ import __title__, __version__
22+
from geotribu_cli.constants import GeotribuDefaults
23+
from geotribu_cli.utils.check_path import check_path
24+
from geotribu_cli.utils.proxies import get_proxy_settings
25+
from geotribu_cli.utils.str2bool import str2bool
26+
27+
# ############################################################################
28+
# ########## GLOBALS #############
29+
# ################################
30+
31+
# logs
32+
logger = logging.getLogger(__name__)
33+
default_settings = GeotribuDefaults()
34+
35+
# ############################################################################
36+
# ########## FUNCTIONS ###########
37+
# ################################
38+
39+
40+
def configure_logger(verbosity: int = 1, logfile: Optional[Path] = None):
41+
"""Configure logging according to verbosity from CLI.
42+
43+
Args:
44+
verbosity (int): verbosity level
45+
logfile (Path, optional): file where to store log. Defaults to None.
46+
"""
47+
# handle log level overridden by environment variable
48+
verbosity = getenv("GEOTRIBU_LOGS_LEVEL", verbosity)
49+
try:
50+
verbosity = int(verbosity)
51+
except ValueError as err:
52+
logger.error(f"Bad verbosity value type: {err}. Fallback to 1.")
53+
verbosity = 1
54+
55+
# set log level depending on verbosity argument
56+
if 0 < verbosity < 4:
57+
verbosity = 40 - (10 * verbosity)
58+
elif verbosity >= 4:
59+
# debug is the limit
60+
verbosity = 40 - (10 * 3)
61+
else:
62+
verbosity = 0
63+
64+
# set console handler
65+
log_console_handler = logging.StreamHandler()
66+
log_console_handler.setLevel(verbosity)
67+
68+
# set log file
69+
if not logfile:
70+
logging.basicConfig(
71+
level=verbosity,
72+
format="%(asctime)s||%(levelname)s||%(module)s||%(funcName)s||%(lineno)d||%(message)s",
73+
datefmt="%Y-%m-%d %H:%M:%S",
74+
handlers=[log_console_handler],
75+
)
76+
77+
else:
78+
if getenv("GEOTRIBU_LOGS_DIR") and check_path(
79+
input_path=Path(expandvars(expanduser(getenv("GEOTRIBU_LOGS_DIR")))),
80+
must_be_a_file=False,
81+
must_be_a_folder=True,
82+
must_be_writable=True,
83+
raise_error=False,
84+
):
85+
logs_folder = Path(expandvars(expanduser(getenv("GEOTRIBU_LOGS_DIR"))))
86+
logger.debug(
87+
f"Logs folder set with GEOTRIBU_LOGS_DIR environment variable: {logs_folder}"
88+
)
89+
else:
90+
logs_folder: Path = default_settings.geotribu_working_folder.joinpath(
91+
"logs"
92+
)
93+
logger.debug(
94+
"Logs folder specified in GEOTRIBU_LOGS_DIR environment variable "
95+
f"{getenv('GEOTRIBU_LOGS_DIR')} can't be used (see logs above). Fallback on "
96+
f"default folder: {logs_folder}"
97+
)
98+
99+
# make sure folder exists
100+
logs_folder.mkdir(exist_ok=True, parents=True)
101+
logs_filepath = Path(logs_folder, logfile)
102+
103+
log_file_handler = RotatingFileHandler(
104+
backupCount=10,
105+
delay=True,
106+
encoding="UTF-8",
107+
filename=logs_filepath,
108+
maxBytes=3000000,
109+
mode="a",
110+
)
111+
# force new file by execution
112+
if logs_filepath.is_file():
113+
log_file_handler.doRollover()
114+
115+
logging.basicConfig(
116+
level=verbosity,
117+
format="%(asctime)s||%(levelname)s||%(module)s||%(funcName)s||%(lineno)d||%(message)s",
118+
datefmt="%Y-%m-%d %H:%M:%S",
119+
handlers=[log_console_handler, log_file_handler],
120+
)
121+
122+
logger.info(f"Log file: {logs_filepath}")
123+
124+
headers()
125+
126+
127+
def headers():
128+
"""Basic information to log before other message."""
129+
# initialize the log
130+
logger.info(f"{'='*10} {__title__} - {__version__} {'='*10}")
131+
logger.debug(f"Operating System: {platform()}")
132+
logger.debug(f"Architecture: {architecture()[0]}")
133+
logger.debug(f"Computer: {gethostname()}")
134+
logger.debug(f"Launched by user: {getuser()}")
135+
136+
if getenv("userdomain"):
137+
logger.debug(f"OS Domain: {getenv('userdomain')}")
138+
139+
if get_proxy_settings():
140+
logger.debug(f"Network proxies detected: {get_proxy_settings()}")
141+
else:
142+
logger.debug("No network proxies detected")
143+
144+
if str2bool(getenv("QDT_SSL_USE_SYSTEM_STORES", False)):
145+
logger.debug("Option to use native system certificates stores is enabled.")
146+
if "REQUESTS_CA_BUNDLE" in environ:
147+
environ.pop("REQUESTS_CA_BUNDLE")
148+
logger.debug(
149+
"Custom path to CA Bundle (REQUESTS_CA_BUNDLE) has been removed from "
150+
"environment variables."
151+
)
152+
if "CURL_CA_BUNDLE" in environ:
153+
environ.pop("CURL_CA_BUNDLE")
154+
logger.debug(
155+
"Custom path to CA Bundle (CURL_CA_BUNDLE) has been removed from "
156+
"environment variables."
157+
)
158+
159+
160+
def get_logger_filepath() -> Optional[Path]:
161+
"""Retrieve log filepath within logger handlers.
162+
163+
Returns:
164+
Path | None: path to the logfile or None if no handler has baseFilename attr.
165+
"""
166+
if logger.root.hasHandlers():
167+
for handler in logger.root.handlers:
168+
if hasattr(handler, "baseFilename"):
169+
return Path(handler.baseFilename)
170+
171+
logger.warning("No file found in ay log handlers.")
172+
return None

requirements/base.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ markdownify>=0.11,<0.13
44
Mastodon.py>=1.8.1,<1.9
55
orjson>=3.8,<3.11
66
packaging>=20,<25
7-
rich_argparse>=0.6,<1.5
7+
rich_argparse>=1,<1.5
88
python-frontmatter>=1,<2
99
requests>=2.31,<3
1010
typing-extensions>=4,<5 ; python_version < '3.11'

0 commit comments

Comments
 (0)