包装器导致自定义python记录器中funcName属性的更改

时间:2017-05-24 17:02:26

标签: python logging wrapper

我在自定义python logger中创建了一个包装器,以在日志格式化程序中添加其他属性。

logFormat = logging.Formatter('[%(levelname)s],[%(asctime)-15s], %(API_SERVER)s, %(funcName)s,%(lineno)d, %(message)s')

附加属性 - > API_SERVER

日志将包装器方法显示为funcName而不是实际的funcName。

[DEBUG],[2017-05-23 17:52:13,865], jupiter-api-server, loggingMethodsWrapper,91, Response returned from DolphinD is 200, returning status success, to caller.

包装代码 - >

def loggingMethodsWrapper(self, logLevelMethod, *kargs):
    # Calling method logging methods dynamcially
    # Add the parameters in the extra if you want to add
    # more columns in logging format
    getattr(self.log, logLevelMethod)(
          *kargs, extra={'API_SERVER': self.API_SERVER})

3 个答案:

答案 0 :(得分:0)

发生了错误的函数名称,因为loggingMethodsWrapper实际上正在进行日志调用。


你想要的似乎是调用loggingMethodsWrapper的函数。修复方法是更改​​格式化程序,使其不包含%(funcName)s并将其移动为传递给%(message)s的参数的一部分或作为额外参数。


选项1:

这样做,一个肮脏的修复就是让你的包装器走上堆栈并抓住下一个功能。

在python2中,这就是:

caller = inspect.stack()[1][3]

在python3上,这将是:

caller = inspect.stack()[1].filename

所以:

logFormat = logging.Formatter('[%(levelname)s],[%(asctime)-15s], %(API_SERVER)s, %(CALLER)s,%(lineno)d, %(message)s')

...

def loggingMethodsWrapper(self, logLevelMethod, *kargs):
    caller = # python-specific method of getting caller
    getattr(self.log, logLevelMethod)(
          *kargs, extra={'CALLER' : caller, 'API_SERVER': self.API_SERVER})


选项2:

如果您可以更改调用者,则可以进行简单修复 - 将函数名称传递给记录器包装器

loggingMethodsWrapper(self, func_name, logLevelMethod, *kargs):
    ....

然后当你打电话时

def foo():
    loggingMethodsWrapper(foo.__name__, log_level, ...)

答案 1 :(得分:0)

Log.py

class Log(Singleton):
log = None
caller = None
def __init__(self):
    ### Configure log
    # TODO: The config has to come from Dictconfig YAML file
    logFolder = CONSTANTS['LOG']['DIR']
    self.API_SERVER = API_SERVER
    caller = inspect.stack()[1][3]
    print caller
    self.log = logging.getLogger(__name__)

    # If handlers are already set donot do it again
    if not len(self.log.handlers):
        logFormat = logging.Formatter('[%(levelname)s],[%(asctime)-15s], %(API_SERVER)s, %(caller)s,%(lineno)d, %(message)s')
def loggingMethodsWrapper(self, logLevelMethod, *kargs):
    # Calling method logging methods dynamically
    # Add the parameters in the extra if you want to add
    # more columns in logging format

    getattr(self.log, logLevelMethod)(
          *kargs, extra={'API_SERVER': self.API_SERVER, 'caller': self.caller})

示例API文件 - >

from flask import Flask, Response, request
from base import base
from jupiter_api.lib.jupFlaskClassy import *

from jupiter_api.business.accounts import accounts as bzAccounts
from jupiter_api.business.role_decorators import permGate, login_required
from flask.ext.cors import CORS
from jupiter_api.utils.log import Log
Log = Log()

class accounts(base):

    supported_methods = "OPTIONS, GET, POST, DELETE"
    ##  GET request to pull all user accounts
    @route('/<userId>/<companyId>/accounts/<accountId>', methods=['GET'])
    @permGate('list_accounts')
    @login_required
    def get_details(self, userId, companyId, accountId):
        #import pdb; pdb.set_trace()
        Log.debug("In get accounts for user")

        # Validate arguments
        if not userId or not companyId:
            return self.invalidData()

        # Print arguments
        Log.debug("Getting account details for user %s ", userId)

注意 - 我有100个类似于调用Log()的Accounts API的API。

答案 2 :(得分:0)

您可以使用inspect包来获取先前函数的名称。这是一个记录功能的单例包装器示例,显示了“ true”调用者:

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)