From 091122630de8d04dfa43f2808c3ff728fcf243e2 Mon Sep 17 00:00:00 2001 From: aferro Date: Wed, 14 Apr 2021 21:01:28 +0100 Subject: [PATCH 1/2] added optional log functionality --- elsapy/__init__.py | 23 +++++++++++++++- elsapy/log_util.py | 67 +++++++++++++++++++++++++++++++++------------- exampleProg.py | 9 +++++-- 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/elsapy/__init__.py b/elsapy/__init__.py index 5b0c4a4..b8c190a 100644 --- a/elsapy/__init__.py +++ b/elsapy/__init__.py @@ -7,4 +7,25 @@ * https://dev.elsevier.com * https://api.elsevier.com""" -version = '0.5.0' \ No newline at end of file +version = '0.5.0' + + +from .log_util import LOGGERS, add_file_handle_to_logger + + +def log_to_directory(dirpath, logger_names=None): + """ + :param str dirpath: output directory where loggers will write into. + :param logger_names: Optional list of strings to identify the affected + loggers by the name given when created via ``get_logger``. If none + is given, all loggers will be affected. + + When called, existing loggers will start logging their output to the + provided directory. + """ + if logger_names is None: + logger_names = LOGGERS.keys() + assert all(ln in LOGGERS for ln in logger_names), \ + f"Unknown loggers! {logger_names}" + for ln in logger_names: + add_file_handle_to_logger(LOGGERS[ln], dirpath) diff --git a/elsapy/log_util.py b/elsapy/log_util.py index 5645b66..55103ea 100644 --- a/elsapy/log_util.py +++ b/elsapy/log_util.py @@ -12,30 +12,59 @@ ## Following adapted from https://docs.python.org/3/howto/logging-cookbook.html -def get_logger(name): - # TODO: add option to disable logging, without stripping logger out of all modules - # - e.g. by simply not writing to file if logging is disabled. See - # https://github.com/ElsevierDev/elsapy/issues/26 - + +# A collection of singletons. Allows to e.g. manipulate file handles at runtime +LOGGERS = {} + + +def _make_logger(name): + """ + .. note:: + + Do not call this function! To get a logger, use get_logger instead. + """ # create logger with module name logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) + # create console handler with error log level + ch = logging.StreamHandler() + ch.setLevel(logging.ERROR) + # create formatter and add it to the handler + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + ch.setFormatter(formatter) + # add the handler to the logger + logger.addHandler(ch) + logger.info(f"Logger created: {name}") + return logger + + +def get_logger(name): + """ + Returns a logger with the given name. If it doesn't exist, creates, + stores and returns the new logger. + """ + if name not in LOGGERS: + LOGGERS[name] = _make_logger(name) + return LOGGERS[name] + + +def add_file_handle_to_logger(lgr, output_dir): + """ + :returns: None. This function adds a ``FileHandler`` to the given logger, + to write a log file inside the given ``output_dir``. + """ # create log path, if not already there - logPath = Path('logs') + logPath = Path(output_dir) if not logPath.exists(): logPath.mkdir() - # create file handler which logs even debug messages - fh = logging.FileHandler('logs/elsapy-%s.log' % time.strftime('%Y%m%d')) + # create file handler with debug log level + fh = logging.FileHandler(logPath / f'elsapy-{time.strftime("%Y%m%d")}.log') fh.setLevel(logging.DEBUG) - # create console handler with a higher log level - ch = logging.StreamHandler() - ch.setLevel(logging.ERROR) - # create formatter and add it to the handlers - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + # create formatter and add it to the handler + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) - ch.setFormatter(formatter) - # add the handlers to the logger - logger.addHandler(fh) - logger.addHandler(ch) - logger.info("Module loaded.") - return logger \ No newline at end of file + # add the handler to the logger + lgr.addHandler(fh) + diff --git a/exampleProg.py b/exampleProg.py index 2bd56d3..fbe1cd1 100644 --- a/exampleProg.py +++ b/exampleProg.py @@ -5,7 +5,12 @@ from elsapy.elsdoc import FullDoc, AbsDoc from elsapy.elssearch import ElsSearch import json - + +## Comment out if you don't want to log files +import elsapy +elsapy.log_to_directory("logs") + + ## Load configuration con_file = open("config.json") config = json.load(con_file) @@ -103,4 +108,4 @@ # retrieving all results doc_srch = ElsSearch("star trek vs star wars",'sciencedirect') doc_srch.execute(client, get_all = False) -print ("doc_srch has", len(doc_srch.results), "results.") \ No newline at end of file +print ("doc_srch has", len(doc_srch.results), "results.") From 269048f0acd4b6a4ee3fb2389bdff4867e5c0839 Mon Sep 17 00:00:00 2001 From: aferro Date: Wed, 14 Apr 2021 21:30:16 +0100 Subject: [PATCH 2/2] added note about fork on readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index a66641a..d11ff6f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +## NOTE from andres-fr + +This fork was made to fix issue #26 (added optional logging functionality). +The main repo logged by default into a `logs` directory. In this fork, logging is off by default and the user can provide a specific directory if they want to opt-in. + +I'm not sure if the main repo is maintained anymore. You can install this one with the following command: + +``` +pip install git+git://github.com/andres-fr/elsapy.git +``` + + # elsapy A Python module for use with api.elsevier.com. Its aim is to make life easier for people who are not primarily programmers, but need to interact with publication and citation data from Elsevier products in a programmatic manner (e.g. academic researchers). The module consists of the following classes: