From 3d37cf6f86eb6c48a3a0a094c42ade6d7aed1daf Mon Sep 17 00:00:00 2001 From: Cody Logan Date: Fri, 20 Oct 2023 16:31:56 -0700 Subject: Move logging configuration to new file Also, use a LoggerAdapter to add contextual info (such as filenames) to log messages when downloading, especially useful with threaded batch processing. --- src/wikiget/dl.py | 29 +++++++++++++------------ src/wikiget/logging.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/wikiget/wikiget.py | 31 +-------------------------- 3 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 src/wikiget/logging.py diff --git a/src/wikiget/dl.py b/src/wikiget/dl.py index 83aef9f..5491378 100644 --- a/src/wikiget/dl.py +++ b/src/wikiget/dl.py @@ -27,6 +27,7 @@ from tqdm import tqdm import wikiget from wikiget.exceptions import ParseError from wikiget.file import File +from wikiget.logging import FileLogAdapter from wikiget.parse import get_dest from wikiget.validations import verify_hash @@ -136,6 +137,9 @@ def download(f, args): errors = 0 + logger = logging.getLogger("") + adapter = FileLogAdapter(logger, {"filename": filename}) + if file.exists: # file exists either locally or at a common repository, like Wikimedia Commons file_url = file.imageinfo["url"] @@ -145,22 +149,17 @@ def download(f, args): filename_log = f"Downloading '{filename}' ({file_size} bytes) from {site.host}" if args.output: filename_log += f" to '{dest}'" - logging.info(filename_log) - logging.info(f"{file_url}") + adapter.info(filename_log) + adapter.info(f"{file_url}") if os.path.isfile(dest) and not args.force: - logging.warning( - f"File '{dest}' already exists, skipping download (use -f to force)" - ) + adapter.warning("File already exists, skipping download (use -f to force)") errors += 1 else: try: fd = open(dest, "wb") except OSError as e: - logging.error( - "File could not be written. The following error was encountered:" - ) - logging.error(e) + adapter.error(f"File could not be written. {e}") errors += 1 else: # download the file(s) @@ -185,22 +184,22 @@ def download(f, args): # verify file integrity and log details dl_sha1 = verify_hash(dest) - logging.info(f"Remote file SHA1 is {file_sha1}") - logging.info(f"Local file SHA1 is {dl_sha1}") + adapter.info(f"Remote file SHA1 is {file_sha1}") + adapter.info(f"Local file SHA1 is {dl_sha1}") if dl_sha1 == file_sha1: - logging.info("Hashes match!") + adapter.info("Hashes match!") # at this point, we've successfully downloaded the file success_log = f"'{filename}' downloaded" if args.output: success_log += f" to '{dest}'" - logging.info(success_log) + adapter.info(success_log) else: - logging.error("Hash mismatch! Downloaded file may be corrupt.") + adapter.error("Hash mismatch! Downloaded file may be corrupt.") errors += 1 else: # no file information returned - logging.error(f"Target '{filename}' does not appear to be a valid file") + adapter.warning("Target does not appear to be a valid file") errors += 1 return errors diff --git a/src/wikiget/logging.py b/src/wikiget/logging.py new file mode 100644 index 0000000..1536156 --- /dev/null +++ b/src/wikiget/logging.py @@ -0,0 +1,57 @@ +# wikiget - CLI tool for downloading files from Wikimedia sites +# Copyright (C) 2023 Cody Logan +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Wikiget is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Wikiget is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Wikiget. If not, see . + +import logging + +import wikiget + + +class FileLogAdapter(logging.LoggerAdapter): + def process(self, msg, kwargs): + return f"[{self.extra['filename']}] {msg}", kwargs + + +def configure_logging(args): + loglevel = logging.WARNING + if args.verbose >= wikiget.VERY_VERBOSE: + # this includes API and library messages + loglevel = logging.DEBUG + elif args.verbose >= wikiget.STD_VERBOSE: + loglevel = logging.INFO + elif args.quiet: + loglevel = logging.ERROR + + # configure logging: + # console log level is set via -v, -vv, and -q options; + # file log level is always debug (TODO: make this user configurable) + base_format = "%(message)s" + log_format = "[%(levelname)s] " + base_format + if args.logfile: + # log to console and file + logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s [%(levelname)-7s] " + base_format, + filename=args.logfile, + ) + + console = logging.StreamHandler() + console.setLevel(loglevel) + console.setFormatter(logging.Formatter(log_format)) + logging.getLogger("").addHandler(console) + else: + # log only to console + logging.basicConfig(level=loglevel, format=log_format) diff --git a/src/wikiget/wikiget.py b/src/wikiget/wikiget.py index e9a1147..5b84dac 100644 --- a/src/wikiget/wikiget.py +++ b/src/wikiget/wikiget.py @@ -25,6 +25,7 @@ from requests import ConnectionError, HTTPError import wikiget from wikiget.dl import batch_download, download, prep_download from wikiget.exceptions import ParseError +from wikiget.logging import configure_logging def construct_parser(): @@ -114,36 +115,6 @@ def construct_parser(): return parser -def configure_logging(args): - loglevel = logging.WARNING - if args.verbose >= wikiget.VERY_VERBOSE: - # this includes API and library messages - loglevel = logging.DEBUG - elif args.verbose >= wikiget.STD_VERBOSE: - loglevel = logging.INFO - elif args.quiet: - loglevel = logging.ERROR - - # configure logging: - # console log level is set via -v, -vv, and -q options; - # file log level is always debug (TODO: make this user configurable) - base_format = "%(threadName)s - %(message)s" - log_format = "[%(levelname)s] " + base_format - if args.logfile: - # log to console and file - logging.basicConfig( - level=logging.DEBUG, - format="%(asctime)s [%(levelname)-7s] " + base_format, - filename=args.logfile, - ) - - console = logging.StreamHandler() - console.setLevel(loglevel) - console.setFormatter(logging.Formatter(log_format)) - logging.getLogger("").addHandler(console) - else: - # log only to console - logging.basicConfig(level=loglevel, format=log_format) def main(): # setup our environment parser = construct_parser() -- cgit v1.2.3