-1

I am currently using the following code to log messages based on their log_level in customized fashion.
What i am struggling is how to update or change the function name in the common LOG_MSG function.

Currently it logs all messages with function name as LOG_MSG, I want to replace it with the actual function names FunctionNum1 or FunctionNum2.

import logging
import inspect

INFO = "INFO"
DEBUG = "DEBUG"
WARN = "WARNING"
ERROR = "ERROR"

def LOG_MSG(level, msg_str, msg_val,tee_output):

    if msg_val == None:
        msg_val = ""

    if(level == INFO):
        logging.info ( msg_str + msg_val )
    elif(level == DEBUG):
        logging.debug ( msg_str + msg_val )
    elif(level == WARN):
        logging.warn ( msg_str + msg_val )
    elif(level == ERROR):
        logging.error ( msg_str + msg_val )

    if(tee_output):
        print ( msg_str + msg_val )

date_strftime_format = "%d-%b-%y %H:%M:%S"
message_format='%(asctime)s %(levelname)s %(module)s - %(funcName)s: %(message)s'
logging.basicConfig(filename = 'log_messages.log', format = message_format, datefmt = date_strftime_format,level=logging.DEBUG)

def FunctionNum1():
    this_function_name = inspect.currentframe().f_code.co_name
    print this_function_name

    LOG_MSG(INFO,"This is an Info Msg=","Any Value",1)
    LOG_MSG(WARN,"This is a Warning Msg",None,0)
    LOG_MSG(INFO,"This is an Info Msg",None,1)

def FunctionNum2():
    this_function_name = inspect.currentframe().f_code.co_name
    print this_function_name
    somevalue = "someValue"
    LOG_MSG(DEBUG,"This is a Debug Msg = ",somevalue,1)
    LOG_MSG(INFO,"This is an Info Msg=","12",1)
    LOG_MSG(ERROR,"This is a Error Msg",None,1)

if __name__ == "__main__":
    FunctionNum1()
    FunctionNum2()

OUTPUT:

20-Apr-21 15:55:58 INFO log_utility - LOG_MSG: This is an Info Msg=Any Value
20-Apr-21 15:55:58 WARNING log_utility - LOG_MSG: This is a Warning Msg
20-Apr-21 15:55:58 INFO log_utility - LOG_MSG: This is an Info Msg
20-Apr-21 15:55:58 DEBUG log_utility - LOG_MSG: This is a Debug Msg= someValue
20-Apr-21 15:55:58 INFO log_utility - LOG_MSG: This is an Info Msg=12
20-Apr-21 15:55:58 ERROR log_utility - LOG_MSG: This is a Error Msg
Cheppy
  • 25
  • 5

1 Answers1

1

You can do so by changing your logger's information (after importing the logging module) :

logger = logging.getLogger() # load logging manager
oldMakeRecord = logger.makeRecord

def customMakeRecord(*args, **kwargs):
    # get usual record information
    rv = oldMakeRecord(*args, **kwargs)

    # change function name with what you want
    rv.funcName = "abc"

    return rv

# replace original manager with new one
logger.makeRecord = customMakeRecord

Which outputs (in log_messages.log):

20-Apr-21 14:08:39 INFO main - abc: This is an Info Msg=Any Value
20-Apr-21 14:08:39 WARNING main - abc: This is a Warning Msg
20-Apr-21 14:08:39 INFO main - abc: This is an Info Msg
20-Apr-21 14:08:39 DEBUG main - abc: This is a Debug Msg = someValue
20-Apr-21 14:08:39 INFO main - abc: This is an Info Msg=12
20-Apr-21 14:08:39 ERROR main - abc: This is a Error Msg

With a bit of tweaking here and there, I'm pretty confident you'll be able to find the function name automatically (How to use inspect to get the caller's info from callee in Python? might be a good source of information).

There might be a better way to do this though, I don't use the logging & inspect modules that much.

EDIT : More complete solution

We can create a function that will update the logger with the given function name ; and use it every time we try to log something. When we do so, we can use the inspect module to find the correct function name to give as a parameter.

def update_logger(function_name: str):
    logger = logging.getLogger()
    oldMakeRecord = logger.makeRecord

    def customMakeRecord(*args, **kwargs):
        rv = oldMakeRecord(*args, **kwargs)
        rv.funcName = function_name
        return rv

    logger.makeRecord = customMakeRecord

def LOG_MSG(level, msg_str, msg_val,tee_output):
    # find function name with inspect module
    caller_function_name = inspect.currentframe().f_back.f_code.co_name
    update_logger(function_name=caller_function_name)

    # the rest is the normal behavior of the log_msg function
    if msg_val == None:
        msg_val = ""

    if(level == INFO):
        logging.info ( msg_str + msg_val )
    elif(level == DEBUG):
        logging.debug ( msg_str + msg_val )
    elif(level == WARN):
        logging.warn ( msg_str + msg_val )
    elif(level == ERROR):
        logging.error ( msg_str + msg_val )

    if(tee_output):
        print ( msg_str + msg_val )

The log_messages.log file now contains :

22-Apr-21 13:41:58 INFO logging_format - FunctionNum1: This is an Info Msg=Any Value
22-Apr-21 13:41:58 WARNING logging_format - FunctionNum1: This is a Warning Msg
22-Apr-21 13:41:58 INFO logging_format - FunctionNum1: This is an Info Msg
22-Apr-21 13:41:58 DEBUG logging_format - FunctionNum2: This is a Debug Msg = someValue
22-Apr-21 13:41:58 INFO logging_format - FunctionNum2: This is an Info Msg=12
22-Apr-21 13:41:58 ERROR logging_format - FunctionNum2: This is a Error Msg
SpaceBurger
  • 537
  • 2
  • 12
  • this is hardcoding function name, but i want "abc" to be any function that calls `LOG_MSG` – Cheppy Apr 21 '21 at 10:02
  • Yes, you are right ; I just wrote about the crux of the matter. A more complete solution would replace the logger before calling `LOG_MSG` and each time it is done, the function name would be guessed with the `inspect` module. I will try yo update my solution when I have the time. – SpaceBurger Apr 22 '21 at 11:36
  • I made an edit to the solution above. It should now work as intended. – SpaceBurger Apr 22 '21 at 11:48
  • Thanks that worked, What do you think is the proper approach for this kind of requirement – Cheppy Apr 24 '21 at 05:46
  • I honestly don't know. In terms of code, there might be a better solution, maybe more elegant or efficient ; in terms of elegance, maybe we can avoid modifying the `LOG_MSG` function by using a decorator instead ; and in terms of efficiency, one would need to test how much time is taken by adding these lines of code and see if it is a prohibitive addition. But other than that, I think you would have a valid approach as long as it works and doesn't make your code hard to read, you can always improve on it later (however, don't quote me on this !). – SpaceBurger Apr 26 '21 at 08:19