hobbes3
hobbes3

Reputation: 30288

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".

Upvotes: 0

Views: 258

Answers (2)

Dan D.
Dan D.

Reputation: 74655

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__.

Upvotes: 1

squareRoot17
squareRoot17

Reputation: 890

You can use the inspect module in this case.

import inspect as insp

def record_factory(*args, **kwargs):
    record = old_factory(*args, **kwargs)
    record.session_id = session_id

    (filename, line_number,func_name, lines, index) = insp.getframeinfo(insp.currentframe().f_back)

    #Do whatever with this values.
    return record

The way currentframe() works is it returns the frame object for the caller’s stack frame. This can be passed into the getframeinfo() method.

Find more details here : inspect Module

Upvotes: 1

Related Questions