Search code examples
pythonloggingpython-module

How to distinguish between files and submodules in Python logging?


I wrote a submodule where I define logging there since I mainly use my submodule to reuse code in my different rest api projects. How can I set up my custom logging record so that logging knows which code (main.py or sub.py) called the logging? I tried using __file__, but then it would always say "sub.py".

My submodule:

# sub.py
import logging
from secrets import token_urlsafe

# https://stackoverflow.com/a/57820456/1150923
def record_factory(*args, **kwargs):
    record = old_factory(*args, **kwargs)
    record.session_id = session_id

    # What do I do here?
    # This doesn't work:
    record.src = __file__

    return record

session_id = token_urlsafe(8)

logger = logging.getLogger(__name__)
old_factory = logging.getLogRecordFactory()
logging.setLogRecordFactory(record_factory)

# Always prepend session_id and src to logs
format = logging.Formatter("%(asctime)s %(session_id) %(src) %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
handler = logging.StreamHandler()
handler.setFormatter(format)
logger.addHandler(handler)

logger.info("Hello!") # Should also print out "sub.py".

My main script:

# main.py
import logging
import sub

logger = logging.getLogger("sub")
logger.info("Hi!") # Should also print out "main.py".

Solution

  • Logging provides an attribute for the module name, the same value as __file__, under the name pathname.

    Replace %(src)s with %(pathname)s.

    The way the Logger does it is via stack inspection in the Logger.findCaller method. It walks up the stack until it finds a frame that isn't in the logging module. And then extracts that frame's filename, lineno, and function name.

    See Is module __file__ attribute absolute or relative? for the issue with __name__.