I guess we're talking about the same thing. My issue now was that I wanted to have precise indication of where each message was being logged (especially in DEBUG mode).
TL;DR: add a simple filter
to your handler:
, through which you can modify the current LogRecord (with the package/module's path, for instance).
Example
My lib is called npt
, and some message is being logged inside the module npt/search/_ode.py
, inside the function "parse" more precisely. I want to have this information in my log messages: the function name, module name, and the (sub)packages where the module is. Something like "INFO: npt.search._ode.parse(): file <whatever> parsed
"
The formatter provides lots of attributes (see LogRecord attributes), of interest here, for instance (from the docs):
- pathname: Full pathname of the source file where the logging call was issued (if available). E.g,
/Users/chbrandt/repos/npt/npt/search/_ode.py
.
- filename: Filename portion of pathname. E.g,
_ode.py
.
- module: Module (name portion of filename). E.g,
_ode
.
There is not a single parameter that provides the relative path of the module inside the library only; pathname
will log the absolute path of the module in your system (we don't wanna do that, right). But it is useful to get what you want, obviously, we just have to cut the absolute part of the path.
So, what I did was: I initialize my log/handler at the very top of my package, inside npt/_log.py
, call it log
, and use from npt import log
everywhere inside npt
. During logging setup, I get the path of npt
inside the system, and then define a filter()
function where an attribute package
is added to the record with the relative path of my module inside my library (npt).
Here is the code I currently have:
import logging
# Get the current path (/path/to/lib/npt/_log.py) from __file__
# and compute the size of the string up-until npt's position
#
from pathlib import Path
_lib_path = Path(__file__).parents[1]
_leng_path = len(_lib_path.as_posix())
def filter(record: logging.LogRecord):
# I'm using the _leng_path because is simple/fast,
# just chop the system path from `pathname`.
# Define `package` attribute with the relative path.
record.package = record.pathname[_leng_path+1:]
return record
# Notice `package` attribute, which is adhoc inside `filter()`
fmt="%(levelname)s: [%(name)s - %(package)s] %(funcName)s: %(message)s"
# Then...proceed as usual...
log = logging.getLogger('npt')
log.setLevel(logging.INFO)
formatter = logging.Formatter(fmt)
handler = logging.StreamHandler()
handler.setFormatter(formatter)
handler.addFilter(filter)
log.addHandler(handler)
Now I have messages like:
> import npt
> df_search = npt.search.ode(dataset=dataset, product_id=pid)
INFO: [npt - npt/search/_ode.py] parse: 1 products found
Which solves the issue perfectly for my/this case.