Python Logging (function name, file name, line number) using a single file

Python Logging (function name, file name, line number) using a single file

The correct answer for this is to use the already provided funcName variable

import logging
logger = logging.getLogger(__name__)
FORMAT = [%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s
logging.basicConfig(format=FORMAT)
logger.setLevel(logging.DEBUG)

Then anywhere you want, just add:

logger.debug(your message) 

Example output from a script Im working on right now:

[invRegex.py:150 -          handleRange() ] [[A-Z]]
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03050>, {, 1, }]]
[invRegex.py:197 -          handleMacro() ] [\d]
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03950>, {, 1, }]]
[invRegex.py:210 -       handleSequence() ] [[<__main__.GroupEmitter object at 0x10b9fedd0>, <__main__.GroupEmitter object at 0x10ba03ad0>]]

You have a few marginally related questions here.

Ill start with the easiest: (3). Using logging you can aggregate all calls to a single log file or other output target: they will be in the order they occurred in the process.

Next up: (2). locals() provides a dict of the current scope. Thus, in a method that has no other arguments, you have self in scope, which contains a reference to the current instance. The trick being used that is stumping you is the string formatting using a dict as the RHS of the % operator. %(foo)s % bar will be replaced by whatever the value of bar[foo] is.

Finally, you can use some introspection tricks, similar to those used by pdb that can log more info:

def autolog(message):
    Automatically log the current function details.
    import inspect, logging
    # Get the previous frame in the stack, otherwise it would
    # be this function!!!
    func = inspect.currentframe().f_back.f_code
    # Dump the message + the name of this function to the log.
    logging.debug(%s: %s in %s:%i % (
        message, 
        func.co_name, 
        func.co_filename, 
        func.co_firstlineno
    ))

This will log the message passed in, plus the (original) function name, the filename in which the definition appears, and the line in that file. Have a look at inspect – Inspect live objects for more details.

As I mentioned in my comment earlier, you can also drop into a pdb interactive debugging prompt at any time by inserting the line import pdb; pdb.set_trace() in, and re-running your program. This enables you to step through the code, inspecting data as you choose.

Python Logging (function name, file name, line number) using a single file

funcname, linename and lineno provide information about the last function that did the logging.

If you have wrapper of logger (e.g singleton logger), then @synthesizerpatels answer might not work for you.

To find out the other callers in the call stack you can do:

import logging
import inspect

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class MyLogger(metaclass=Singleton):
    logger = None

    def __init__(self):
        logging.basicConfig(
            level=logging.INFO,
            format=%(asctime)s - %(threadName)s - %(message)s,
            handlers=[
                logging.StreamHandler()
            ])

        self.logger = logging.getLogger(__name__ + .logger)

    @staticmethod
    def __get_call_info():
        stack = inspect.stack()

        # stack[1] gives previous function (info in our case)
        # stack[2] gives before previous function and so on

        fn = stack[2][1]
        ln = stack[2][2]
        func = stack[2][3]

        return fn, func, ln

    def info(self, message, *args):
        message = {} - {} at line {}: {}.format(*self.__get_call_info(), message)
        self.logger.info(message, *args)

Leave a Reply

Your email address will not be published. Required fields are marked *