नए प्रारूप स्ट्रिंग के साथ चर डेटा लॉग करना


85

मैं अजगर 2.7.3 के लिए लॉगिंग सुविधा का उपयोग करता हूं। इस पायथन संस्करण के लिए दस्तावेज़ कहते हैं :

लॉगिंग पैकेज str -format () और string.Template जैसे नए फॉर्मेटिंग विकल्पों को प्री-डेट करता है। ये नए स्वरूपण विकल्प समर्थित हैं ...

मुझे घुंघराले ब्रेसिज़ के साथ 'नया' प्रारूप पसंद है। तो मैं कुछ करने की कोशिश कर रहा हूँ:

 log = logging.getLogger("some.logger")
 log.debug("format this message {0}", 1)

और त्रुटि प्राप्त करें:

TypeError: स्ट्रिंग स्वरूपण के दौरान परिवर्तित सभी तर्क नहीं

मुझे यहाँ क्या याद आता है?

पुनश्च मैं उपयोग नहीं करना चाहता

log.debug("format this message {0}".format(1))

क्योंकि इस मामले में संदेश हमेशा लकड़हारा स्तर की परवाह किए बिना स्वरूपित किया जा रहा है।


1
आप ऐसा कर सकते हैं: log.debug("format this message%d" % 1)
रोनक

1
आपको Formatterशैली के रूप में '{' का उपयोग करने के लिए कॉन्फ़िगर करने की आवश्यकता है
माता

2
@ क्रोनक सलाह के लिए धन्यवाद, लेकिन नहीं। कृपया, "पीएस" अनुभाग देखें क्यों। BTW log.debug ("यह संदेश% d स्वरूपित करें", 1) - ठीक काम करता है।
राजसी

@ माता इसे कैसे कॉन्फ़िगर करें? क्या इसे करने का प्रत्यक्ष प्रलेखन है?
राजसी

@ ममा मुझे मिल गया है। कृपया इसे एक उत्तर दें ताकि मैं इसे "सही उत्तर" के रूप में सेट कर सकूं। एक बार फिर धन्यवाद।
राजसी

जवाबों:


38

संपादित करें: इस उत्तर के विपरीत StyleAdapter@ ड्यून्स के उत्तर में दृष्टिकोण पर एक नज़र डालें ; यह लॉगर के तरीकों (डिबग (), सूचना (), त्रुटि (), आदि को कॉल करते समय बॉयलरप्लेट के बिना वैकल्पिक स्वरूपण शैलियों का उपयोग करने की अनुमति देता है।


डॉक्स से - वैकल्पिक स्वरूपण शैलियों का उपयोग :

लॉगिंग कॉल (logger.debug (), logger.info () आदि) केवल वास्तविक लॉगिंग संदेश के लिए स्थितीय मानदंड लेते हैं, केवल वास्तविक लॉगिंग कॉल को संभालने के लिए विकल्पों का निर्धारण करने के लिए उपयोग किए जाने वाले कीवर्ड मापदंडों के साथ (उदाहरण के लिए exc_ffo कीवर्ड पैरामीटर) यह इंगित करने के लिए कि ट्रेसबैक जानकारी लॉग की जानी चाहिए, या अतिरिक्त प्रासंगिक जानकारी को लॉग में जोड़ने के लिए अतिरिक्त कीवर्ड पैरामीटर)। तो आप सीधे str.format () या string.Template सिंटैक्स का उपयोग करके लॉगिंग कॉल नहीं कर सकते हैं, क्योंकि आंतरिक रूप से लॉगिंग पैकेज, प्रारूप स्ट्रिंग और चर तर्कों को मर्ज करने के लिए% -formatting का उपयोग करता है। पिछड़ी अनुकूलता को संरक्षित करते हुए इसमें कोई बदलाव नहीं किया जाएगा, क्योंकि मौजूदा कोड में सभी लॉगिंग कॉल% -फॉर्म स्ट्रिंग्स का उपयोग कर रहे हैं।

तथा:

हालाँकि, एक ऐसा तरीका है जिसका उपयोग आप अपने व्यक्तिगत लॉग संदेशों के निर्माण के लिए {} - और $ - स्वरूपण के लिए कर सकते हैं। याद रखें कि एक संदेश के लिए आप एक संदेश प्रारूप स्ट्रिंग के रूप में एक मनमाना वस्तु का उपयोग कर सकते हैं, और लॉगिंग पैकेज वास्तविक ऑब्जेक्ट स्ट्रिंग प्राप्त करने के लिए उस वस्तु पर str () कॉल करेगा।

whereverमॉड्यूल को कॉपी-पेस्ट करें :

class BraceMessage(object):
    def __init__(self, fmt, *args, **kwargs):
        self.fmt = fmt
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return self.fmt.format(*self.args, **self.kwargs)

फिर:

from wherever import BraceMessage as __

log.debug(__('Message with {0} {name}', 2, name='placeholders'))

नोट: वास्तविक स्वरूपण में देरी हो जाती है जब तक कि यह आवश्यक न हो, उदाहरण के लिए, यदि DEBUG संदेश लॉग नहीं किए जाते हैं, तो स्वरूपण बिल्कुल भी निष्पादित नहीं होता है।


4
पायथन 3.6 के रूप में, आप इस तरह से एफ-स्ट्रिंग्स का उपयोग कर सकते हैं:num = 2; name = 'placeholders'; log.debug(f'Message with {num} {name}')
जैकटोज

11
@ P1h3r1e3d13 उत्तर में लॉगिंग कोड के विपरीत, f '' - तार तुरंत स्वरूपण का प्रदर्शन करते हैं।
JFS

1
सही। वे यहां काम करते हैं क्योंकि वे लॉग विधि को कॉल करने से पहले एक नियमित स्ट्रिंग को प्रारूपित और वापस करते हैं। यह किसी के लिए प्रासंगिक हो भी सकता है और नहीं भी, इसलिए मुझे लगता है कि यह एक विकल्प के रूप में ध्यान देने योग्य है।

3
@Jacktose मुझे लगता है कि उपयोगकर्ताओं को एफ-स्ट्रिंग्स का उपयोग करके लॉग नहीं करना चाहिए, यह लॉग एकत्रीकरण सेवाओं (जैसे संतरी) को हरा देता है। वहाँ एक अच्छा कारण है कि stdlib लॉगिंग स्ट्रिंग को नष्ट कर देता है।
विम

31

यहां एक और विकल्प दिया गया है जिसमें ड्यून्स के उत्तर में वर्णित कीवर्ड समस्याएं नहीं हैं। यह केवल स्थितिगत ( {0}) तर्कों को संभाल सकता है और खोजशब्द ( {foo}) तर्कों को नहीं। इसे प्रारूप (अंडरस्कोर का उपयोग करके) दो कॉल की आवश्यकता नहीं है। इसमें उपवर्ग का ick-factor है str:

class BraceString(str):
    def __mod__(self, other):
        return self.format(*other)
    def __str__(self):
        return self


class StyleAdapter(logging.LoggerAdapter):

    def __init__(self, logger, extra=None):
        super(StyleAdapter, self).__init__(logger, extra)

    def process(self, msg, kwargs):
        if kwargs.pop('style', "%") == "{":  # optional
            msg = BraceString(msg)
        return msg, kwargs

आप इसे इस तरह उपयोग करते हैं:

logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")

बेशक, आप # optionalनए शैली के स्वरूपण का उपयोग करने के लिए एडेप्टर के माध्यम से सभी संदेशों को बाध्य करने के लिए नोट किए गए चेक को हटा सकते हैं ।


इस उत्तर को वर्षों बाद पढ़ने वाले किसी भी व्यक्ति के लिए ध्यान दें : पायथन 3.2 के साथ , आप ऑब्जेक्ट के साथ शैली पैरामीटर का उपयोग कर सकते हैंFormatter :

लॉगिंग (3.2 के रूप में) इन दो अतिरिक्त स्वरूपण शैलियों के लिए बेहतर समर्थन प्रदान करता है। नाम के अतिरिक्त, वैकल्पिक कीवर्ड पैरामीटर लेने के लिए फॉर्मेटर क्लास को बढ़ाया गया है style। यह करने के लिए चूक '%', लेकिन अन्य संभावित मान हैं '{'और '$', जो अन्य दो स्वरूपण शैलियों के अनुरूप हैं। पीछे की संगतता को डिफ़ॉल्ट रूप से बनाए रखा जाता है (जैसा कि आप उम्मीद करेंगे), लेकिन एक शैली पैरामीटर को स्पष्ट रूप से निर्दिष्ट करके, आप प्रारूप स्ट्रिंग को निर्दिष्ट करने की क्षमता प्राप्त करते हैं जो साथ काम करते हैं str.format()या string.Template

डॉक्स उदाहरण प्रदान करते हैं logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')

ध्यान दें कि इस मामले में आप अभी भी loggerनए प्रारूप के साथ कॉल नहीं कर सकते हैं । यानी, निम्नलिखित अभी भी काम नहीं करेगा:

logger.info("knights:{say}", say="ni")  # Doesn't work!
logger.info("knights:{0}", "ni")  # Doesn't work either

5
अजगर 3 के बारे में आपका कथन गलत है। शैली पैरामीटर केवल फॉर्मेट प्रारूप स्ट्रिंग पर लागू होता है, न कि व्यक्तिगत लॉग संदेशों पर। आप जिस पेज से जुड़े हैं, वह स्पष्ट रूप से कहता है: "पिछड़ी अनुकूलता को संरक्षित करते हुए इसमें कोई बदलाव नहीं होगा"।
मुहूर्त

1
मुझे ईमानदार रखने के लिए धन्यवाद। पहला भाग अब कम उपयोगी है, लेकिन मैंने इसे अभी के संदर्भ में बदल दिया है Formatter, जो अब सही है (मुझे लगता है)। StyleAdapter अभी भी काम करता है,
फेलिप

@falstro - यह इंगित करने के लिए धन्यवाद। अद्यतन संस्करण को अब काम करना चाहिए। चूंकि BraceStringयह एक स्ट्रिंग उपवर्ग है, इसलिए यह अपने आप से सुरक्षित है__str__
फेलिप

1
केवल उत्तर दें कि शैली का उल्लेख है = "{", +1
टॉम एस।

24

यह समस्या का मेरा समाधान था जब मैंने पाया कि लॉगिंग केवल प्रिंटफ शैली प्रारूपण का उपयोग करता है। यह लॉगिंग कॉल को एक समान रहने की अनुमति देता है - जैसे कोई विशेष वाक्यविन्यास नहीं log.info(__("val is {}", "x"))। कोड में आवश्यक परिवर्तन लकड़हारा को एक में लपेटना है StyleAdapter

from inspect import getargspec

class BraceMessage(object):
    def __init__(self, fmt, args, kwargs):
        self.fmt = fmt
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        return str(self.fmt).format(*self.args, **self.kwargs)

class StyleAdapter(logging.LoggerAdapter):
    def __init__(self, logger):
        self.logger = logger

    def log(self, level, msg, *args, **kwargs):
        if self.isEnabledFor(level):
            msg, log_kwargs = self.process(msg, kwargs)
            self.logger._log(level, BraceMessage(msg, args, kwargs), (), 
                    **log_kwargs)

    def process(self, msg, kwargs):
        return msg, {key: kwargs[key] 
                for key in getargspec(self.logger._log).args[1:] if key in kwargs}

उपयोग है:

log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substitution", type="brace")

यह ध्यान देने योग्य बात की कीमत इस कार्यान्वयन समस्या है कि अगर कुंजी ब्रेस प्रतिस्थापन के लिए इस्तेमाल किया शब्दों में शामिल level, msg, args, exc_info, extraया stack_info। ये तर्क नाम हैं जिनका उपयोग logविधि द्वारा किया जाता है Logger। यदि आपको इन नामों में से एक की आवश्यकता है, तो इन नामों processको बाहर करने के लिए संशोधित करें या केवल कॉल log_kwargsसे हटा दें _log। एक और नोट पर, यह कार्यान्वयन चुपचाप लकड़हारे के लिए बनाए गए गलत वर्तनी वाले कीवर्ड को भी अनदेखा कर देता है (जैसे। ectra)।


4
इस तरह से python doc, docs.python.org/3/howto/…
eshizhan

23

उत्कृष्ट मॉड्यूल का उपयोग करने के लिए आसान समाधान होगाlogbook

import logbook
import sys

logbook.StreamHandler(sys.stdout).push_application()
logbook.debug('Format this message {k}', k=1)

या अधिक पूर्ण:

>>> import logbook
>>> import sys
>>> logbook.StreamHandler(sys.stdout).push_application()
>>> log = logbook.Logger('MyLog')
>>> log.debug('Format this message {k}', k=1)
[2017-05-06 21:46:52.578329] DEBUG: MyLog: Format this message 1

यह बहुत अच्छा लग रहा है, लेकिन क्या केवल सेकंड के बजाय मिलीसेकंड होने का एक तरीका है?
जेफ

@ निश्चित रूप से, लॉगबुक से आप कस्टम स्ट्रिंगर्स को परिभाषित कर सकते हैं और कस्टम स्ट्रिंग प्रारूपों का उपयोग कर सकते हैं।
थॉमस ओरोज्को

5
@ जेफ वर्ष के जोड़े - डिफ़ॉल्ट समय परिशुद्धता मिलीसेकंड है।
Jan Vlcinsky

12

जैसा कि अन्य उत्तर बताते हैं, पायथन 3.2 में शुरू की गई ब्रेस-स्टाइल फ़ॉर्मेटिंग केवल प्रारूप स्ट्रिंग पर उपयोग की जाती है, वास्तविक लॉग संदेशों पर नहीं।

वास्तविक लॉग संदेश पर ब्रेस-स्टाइल फ़ॉर्मेटिंग को सक्षम करने के लिए, हम लकड़हारा कोड का थोड़ा सा हिस्सा बंद कर सकते हैं।

निम्न loggingएक get_loggerफ़ंक्शन बनाने के लिए मॉड्यूल को पैच करता है जो एक लकड़हारा लौटाएगा जो प्रत्येक लॉग रिकॉर्ड के लिए नई शैली स्वरूपण का उपयोग करता है जिसे वह संभालता है।

import functools
import logging
import types

def _get_message(record):
    """Replacement for logging.LogRecord.getMessage
    that uses the new-style string formatting for
    its messages"""
    msg = str(record.msg)
    args = record.args
    if args:
        if not isinstance(args, tuple):
            args = (args,)
        msg = msg.format(*args)
    return msg

def _handle_wrap(fcn):
    """Wrap the handle function to replace the passed in
    record's getMessage function before calling handle"""
    @functools.wraps(fcn)
    def handle(record):
        record.getMessage = types.MethodType(_get_message, record)
        return fcn(record)
    return handle

def get_logger(name=None):
    """Get a logger instance that uses new-style string formatting"""
    log = logging.getLogger(name)
    if not hasattr(log, "_newstyle"):
        log.handle = _handle_wrap(log.handle)
    log._newstyle = True
    return log

उपयोग:

>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>

टिप्पणियाँ:

  • पूरी तरह से सामान्य लॉगिंग विधियों के साथ संगत (बस के logging.getLoggerसाथ बदलें get_logger)
  • केवल get_loggerफ़ंक्शन द्वारा बनाए गए विशिष्ट लॉगर्स को प्रभावित करेगा (3 पार्टी पैकेज नहीं तोड़ता)।
  • यदि लकड़हारा एक सामान्य logging.getLogger()कॉल से फिर से एक्सेस किया जाता है , तो नई शैली का स्वरूपण अभी भी लागू होगा।
  • kwargs समर्थित नहीं हैं (बनाता है यह विरोध करने के लिए असंभव निर्मित exc_info, stack_info, stacklevelऔर extra)।
  • प्रदर्शन हिट न्यूनतम होना चाहिए (प्रत्येक लॉग संदेश के लिए एकल फ़ंक्शन पॉइंटर फिर से लिखना)।
  • संदेश के प्रारूपण में देरी होती है जब तक कि यह आउटपुट नहीं होता है (या यदि लॉग संदेश फ़िल्टर किया गया है तो बिल्कुल नहीं)।
  • आर्ग्स logging.LogRecordको हमेशा की तरह वस्तुओं पर संग्रहीत किया जाता है (कस्टम लॉग हैंडलर्स के साथ कुछ मामलों में उपयोगी)।
  • पर देख से loggingमॉड्यूल स्रोत कोड यह है कि यह जब सभी अजगर 2.6 के लिए रास्ता वापस काम करना चाहिए लगता है str.formatशुरू की गई थी (लेकिन मैं केवल 3.5 में यह परीक्षण किया है और ऊपर है)

2
एकमात्र उत्तर जो विचार करता है कि डिबग स्ट्रिंग की गणना केवल तभी की जानी चाहिए जब डिबगर संदेश मुद्रित किया जाना है। धन्यवाद!
फाफमान

2

logging.setLogRecordFactoryपायथन 3.2+ में कोशिश करें :

import collections
import logging


class _LogRecord(logging.LogRecord):

    def getMessage(self):
        msg = str(self.msg)
        if self.args:
            if isinstance(self.args, collections.Mapping):
                msg = msg.format(**self.args)
            else:
                msg = msg.format(*self.args)
        return msg


logging.setLogRecordFactory(_LogRecord)

यह काम करता है लेकिन समस्या यह है कि आप तीसरे पक्ष के मॉड्यूल को तोड़ते हैं जो %स्वरूपण का उपयोग कर रहे हैं क्योंकि रिकॉर्ड फैक्टरी लॉगिंग मॉड्यूल के लिए वैश्विक है।
जटायलर

1

मैंने एक कस्टम फॉर्मैटर बनाया, जिसका नाम ColorFormatter है जो इस तरह की समस्या को हैंडल करता है:

class ColorFormatter(logging.Formatter):

    def format(self, record):
        # previous stuff, copy from logging.py…

        try:  # Allow {} style
            message = record.getMessage()  # printf
        except TypeError:
            message = record.msg.format(*record.args)

        # later stuff…

यह इसे विभिन्न पुस्तकालयों के साथ संगत रखता है। दोष यह है कि यह संभवतः दो बार स्ट्रिंग के संभावित प्रयास प्रारूप के कारण प्रदर्शन करने वाला नहीं है।


0

यहाँ कुछ वास्तविक सरल है जो काम करता है:

debug_logger: logging.Logger = logging.getLogger("app.debug")

def mydebuglog(msg: str, *args, **kwargs):
    if debug_logger.isEnabledFor(logging.DEBUG):
        debug_logger.debug(msg.format(*args, **kwargs))

फिर:

mydebuglog("hello {} {val}", "Python", val="World")

0

PR0Ps 'के समान समाधान, उदाहरणों getMessageमें LogRecordलपेटकर makeRecord( handleउनके जवाब के बजाय ) में लपेटकर Loggerनए स्वरूपण-सक्षम होना चाहिए:

def getLogger(name):
    log = logging.getLogger(name)
    def Logger_makeRecordWrapper(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None):
        self = log
        record = logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, sinfo)
        def LogRecord_getMessageNewStyleFormatting():
            self = record
            msg = str(self.msg)
            if self.args:
                msg = msg.format(*self.args)
            return msg
        record.getMessage = LogRecord_getMessageNewStyleFormatting
        return record
    log.makeRecord = Logger_makeRecordWrapper
    return log

मैंने पायथन 3.5.3 के साथ इसका परीक्षण किया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.