* Technically %(class)s can be e.g. A.B not just 'B' so say "name(s)".
* To not confuse EDMCLogging.getLogger() with logging.getLogger() it's
been renamed to get_logger().
* Note how we signal errors with finding class and/or qualname.
* Call out EDMCLogging.py in Contributing.md.
* Added/fleshed out docstrings on file, classes and functions.
* No need to use a function for the stack frame getting.
* Check if LogRecord has class or qualname before setting, allowing
upstream to implement them.
* Use setattr()/getattr() rather than __dict__ fiddling.
* Force an error string into class/qualname if we have issues finding
them, rather than failing silently to ''.
* Also now providers single caller_class_and_qualname() method to get
both strings, returned as a Tuple[str, str]. Either could be empty
if something went wrong.
* Rather than a fragile 'skip' this now:
1. Looks for the first up-stack frame with self of logging.Logger
1. Then looks for the next up-stack frame with self NOT of
logging.Logger. This should be the call site we want.
* Nothing should "from EDMarketConnector import logger" any more, so
we can move this back inside __main__ section
* We shouldn't rely on a magic number of frames to skip. Detect the
proper frame automatically.
Using:
from EDMarketConnector import logger
causes issues if EDMarketConnector is already importing 'this' file.
So just get a logger using logger.getLogger(appname) instead.
`from config import appname` if needs be.
This gets the function that called the logging, and if it's in a class
it looks up the function via getattr(<class>, <func name>) and then
uses __qualname__ to quickly get the fully qualified name.
This relies on the logger.Formatter setup to fill in function name
rather than calling out exactly why we're logging. It should be
obvious, i.e. if you're in plug.py:notify_journal_entry then the named
plugin threw an un-caught exception trying to call its method for this.