2011-08-09 15 views
14

के साथ functools.wraps का उपयोग करके मैं एक साधारण सजावट लिखने की कोशिश कर रहा हूं जो सजाए गए फ़ंक्शन को कॉल करने से पहले दिए गए कथन को लॉग करता है। लॉग बयान दोनों एक ही समारोह से आते हैं, जो मैंने सोचा था functools.wraps() का उद्देश्य था।लॉगिंग सजावट

क्यों निम्नलिखित कोड है: की तरह लॉग बयान में

import logging 
logging.basicConfig(
    level=logging.DEBUG, 
    format='%(funcName)20s - %(message)s') 

from functools import wraps 

def log_and_call(statement): 
    def decorator(func): 
     @wraps(func) 
     def wrapper(*args, **kwargs): 
      logging.info(statement)    
      return func(*args, **kwargs) 
     return wrapper 
    return decorator 


@log_and_call("This should be logged by 'decorated_function'") 
def decorated_function(): 
    logging.info('I ran') 

decorated_function() 

परिणाम:

   wrapper - This should be logged by 'decorated_function' 
    decorated_function - I ran 

मैंने सोचा था कि wraps के लिए कॉल decorated_function के नाम के साथ आवरण का नाम बदलने होगा।

मैं अजगर 2.7.1 का उपयोग कर रहा हूं।

उत्तर

10

दुर्भाग्यवश logging नाम का अनुमान लगाने के लिए फ़ंक्शन कोड ऑब्जेक्ट का उपयोग करता है। रिकॉर्ड के लिए कुछ अतिरिक्त विशेषताओं को निर्दिष्ट करने के लिए आप extra कीवर्ड तर्क का उपयोग करके इसके आसपास काम कर सकते हैं, जिसे आप स्वरूपण के दौरान उपयोग कर सकते हैं।

logging.basicConfig(
    level=logging.DEBUG, 
    format='%(real_func_name)20s - %(message)s', 
) 

... 

logging.info(statement, extra={'real_func_name': func.__name__}) 

इस दृष्टिकोण के लिए केवल नकारात्मक पक्ष यह है आप extra शब्दकोश में हर बार पारित करने के लिए है कि है: आप की तरह कुछ कर सकता है। से बचने के लिए है कि आप एक कस्टम फ़ॉर्मेटर इस्तेमाल कर सकते हैं और यह funcName ओवरराइड है

import logging 
from functools import wraps 

class CustomFormatter(logging.Formatter): 
    """Custom formatter, overrides funcName with value of name_override if it exists""" 
    def format(self, record): 
     if hasattr(record, 'name_override'): 
      record.funcName = record.name_override 
     return super(CustomFormatter, self).format(record) 

# setup logger and handler 
logger = logging.getLogger(__file__) 
handler = logging.StreamHandler() 
logger.setLevel(logging.DEBUG) 
handler.setLevel(logging.DEBUG) 
handler.setFormatter(CustomFormatter('%(funcName)20s - %(message)s')) 
logger.addHandler(handler) 

def log_and_call(statement): 
    def decorator(func): 
     @wraps(func) 
     def wrapper(*args, **kwargs): 
      # set name_override to func.__name__ 
      logger.info(statement, extra={'name_override': func.__name__}) 
      return func(*args, **kwargs) 
     return wrapper 
    return decorator 

@log_and_call("This should be logged by 'decorated_function'") 
def decorated_function(): 
    logger.info('I ran') 

decorated_function() 

आप क्या चाहते हैं करता है कौन सा:

% python logging_test.py 
    decorated_function - This should be logged by 'decorated_function' 
    decorated_function - I ran 
+0

क्या हर समय वक्तव्य पास किए बिना ऐसा करने का कोई तरीका है। का मतलब है कि मैं सिर्फ अपने कोड में logger.info ('test {}', 123) का उपयोग करना चाहता हूं लेकिन यह स्वचालित रूप से एक अतिरिक्त निर्देश जोड़ता है। अब मैं आपके विचार का उपयोग करता हूं। def log_and_call (कथन, स्तर) की तरह: .... और डीफ़ टेस्ट (सेंट, स्तर): @ log_and_call() def api(): api() पास करें। और logger.debug ('test {}' प्रारूप (123), 'डीबग' जैसे उपयोग करें)। इसका काम कोई बेहतर तरीका है। – Jisson

0

मुझे संदेह है कि लॉगिंग मॉड्यूल फ़ंक्शन ऑब्जेक्ट पर __name__ विशेषता का उपयोग करता है। यदि आप किसी अन्य नाम के फ़ंक्शन असाइन कि आम तौर पर भी नहीं बदलता है ... आप की तरह कुछ कर रही एक ही परिणाम देखना चाहते हैं:

def foo() 
    logging.info("in foo") 
bar = foo 
bar() 

आप foo - in foo, नहीं bar - in foo मिल जाएगा जब आप बार कहते हैं।

सजावटी हुड के नीचे कुछ ऐसा कर रहे हैं।

+2

क्या आपने इसे पोस्ट करने से पहले 'wraps' के बारे में कुछ भी पढ़ा था? इसका पूरा उद्देश्य यह है कि आप जो कह रहे हैं वह ठीक से करना है। – agf

0

विपरीत यदि आपको संदेह हो सकता है, लॉगिंग। फ़ंक्शंस __name__ विशेषता का उपयोग न करें। इसका मतलब है @wraps (या मैन्युअल रूप से रैपर के __name__ को सेट करना) का उपयोग नहीं करता है!

इसके बजाए, उस शो का नाम, कॉल-फ्रेम की जांच की जाती है। इसमें कोड -items (मूल रूप से स्टैक) की एक सूची शामिल है। वहां फ़ंक्शन नाम पढ़ा जाता है, साथ ही साथ फ़ाइल नाम और लाइन-नंबर भी पढ़ा जाता है। लॉगिंग-सजावट का उपयोग करते समय, रैपर-नाम हमेशा मुद्रित होता है, क्योंकि यह लॉग कॉल करने वाला व्यक्ति होता है।

बीटीडब्ल्यू। लॉगिंग स्तर() फ़ंक्शंस सभी कॉल logging._log(*level*, ...) पर कॉल करता है, जो अन्य (लॉग) फ़ंक्शंस को भी कॉल करता है। जो ढेर पर सभी अंत-अप। उन लॉग-फ़ंक्शंस को रोकने के लिए, फ्रेम की सूची पहले (निम्नतम) फ़ंक्शन की खोज की जाती है, जो फ़ंक्शननाम 'लॉगिंग' का हिस्सा नहीं है। लॉग इन करने के लिए यह वास्तविक कार्य होना चाहिए: एक कॉलर लॉगर। func()।

अफसोस की बात है, यह wrapper है।

हालांकि, लॉग-सजावट का उपयोग करना संभव होगा: जब यह लॉगिंग स्रोत फ़ाइल का हिस्सा होता है। लेकिन कोई भी नहीं है (अभी तक)

+0

तो मुझे लगता है कि इसका मतलब यह है कि यह असंभव है कि पायथन 2.7.एक्स श्रृंखला कभी भी इसे ठीक करेगी, है ना? – kakyo

+0

यह ओपन-सोर्स है, इसलिए पैच बनाएं! कोड वहां है, पठनीय और जटिल नहीं है ... और जहां तक ​​मुझे याद है 2.7 और 3. * लगभग समान है। तो 3 के लिए इंतजार मत करो – Albert

संबंधित मुद्दे