मैं कस्टम फ़ील्ड को Python लॉग फॉर्मेट स्ट्रिंग में कैसे जोड़ूँ?


96

मेरा वर्तमान प्रारूप स्ट्रिंग है:

formatter = logging.Formatter('%(asctime)s : %(message)s')

और मैं एक नया फ़ील्ड जोड़ना चाहता हूं, app_nameजिसका प्रत्येक स्क्रिप्ट में एक अलग मान होगा जिसमें यह फ़ॉर्मेटर होता है।

import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)

लेकिन मुझे यकीन नहीं है कि app_nameप्रारूप स्ट्रिंग में प्रक्षेप करने के लिए लॉगर के लिए उस मान को कैसे पारित किया जाए । मैं स्पष्ट रूप से इसे हर बार पास करके लॉग संदेश में प्रदर्शित होने के लिए प्राप्त कर सकता हूं लेकिन यह गड़बड़ है।

मैंने कोशिश की:

logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')

लेकिन कोई काम नहीं।


क्या आप वास्तव में इसे हर logकॉल में पास करना चाहते हैं ? यदि ऐसा है, तो डॉक्स को देखें जहां यह कहती है कि "इस कार्यक्षमता का उपयोग आपके मानों को LogRecord में इंजेक्ट करने के लिए किया जा सकता है ..." लेकिन यह कॉल logger = logging.getLogger('myapp')में बेक किए जाने का उपयोग करने और होने के लिए एक प्रमुख मामले जैसा लगता है logger.info
21

अजगर लॉगिंग पहले से ही उस afaik कर सकते हैं। अगर आप किसी अन्य का उपयोग loggerप्रत्येक अनुप्रयोग में वस्तु आपके पास प्रत्येक उपयोग अपने instantiating द्वारा एक अलग नाम कर सकते हैं loggerतो तरह रों: logger = logging.getLogger(myAppName)। ध्यान दें कि __name__अजगर मॉड्यूल नाम है, इसलिए यदि प्रत्येक ऐप का अपना अजगर मॉड्यूल है, तो यह भी काम करेगा।
फ्लोरियन कैस्टेलन

जवाबों:


135

आप एक लॉगऑगर एडेप्टर का उपयोग कर सकते हैं ताकि आपको हर लॉगिंग कॉल के साथ अतिरिक्त जानकारी न देनी पड़े:

import logging
extra = {'app_name':'Super App'}

logger = logging.getLogger(__name__)
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(syslog)

logger = logging.LoggerAdapter(logger, extra)
logger.info('The sky is so blue')

लॉग (कुछ इस तरह)

2013-07-09 17:39:33,596 Super App : The sky is so blue

फिल्टर का उपयोग प्रासंगिक जानकारी जोड़ने के लिए भी किया जा सकता है।

import logging

class AppFilter(logging.Filter):
    def filter(self, record):
        record.app_name = 'Super App'
        return True

logger = logging.getLogger(__name__)
logger.addFilter(AppFilter())
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(syslog)

logger.info('The sky is so blue')

एक समान लॉग रिकॉर्ड बनाता है।


3
हम इसे एक config.iniफ़ाइल में कैसे निर्दिष्ट कर सकते हैं ? मैं वर्तमान होस्ट नाम जोड़ना चाहता हूं socket.gethostname()
लॉरेंट LAPORTE

मेरे पास यह नमूना मेरे लिए काम नहीं कर रहा है। import uuid uniqueId = str(uuid.uuid4()) extra = {"u_id" : uniqueId} RotatingHandler = RotatingFileHandler(LOG_FILENAME,encoding='utf-8',maxBytes=maxSize, backupCount=batchSize) logger.basicConfig(handlers=[RotatingHandler],level=logLevel.upper(),format='%(levelname)s %(u_id)s %(funcName)s %(asctime)s %(message)s ',datefmt='%m/%d/%Y %I:%M:%S %p') logger = logger.LoggerAdapter(logger=logger, extra=extra)
हयात

क्या एक फ़ील्ड "स्तर" जोड़ना संभव है जो "स्तरनाम" के बराबर है? देखें: मैं पायथन लॉग संदेशों में "स्तर" का नाम "स्तर" कैसे बदल सकता हूं?
मार्टिन थोमा

2
क्या मैं अतिरिक्त जानकारी का एक तार पास कर सकता हूँ। कुछ इस तरह से: "कर्मचारी आईडी 1029382 के लिए त्रुटि" बिना कोई शब्दकोश बनाए।
श्रेयांश कट्टी

51

आपको अतिरिक्त को एक पैरामीटर के रूप में पारित करने की आवश्यकता है ताकि इसे इस तरह से किया जा सके।

logging.info('Log message', extra={'app_name': 'myapp'})

प्रमाण:

>>> import logging
>>> logging.basicConfig(format="%(foo)s - %(message)s")
>>> logging.warning('test', extra={'foo': 'bar'})
bar - test 

इसके अलावा, एक नोट के रूप में, यदि आप किसी संदेश को बिना पास किए पास करने की कोशिश करते हैं, तो यह विफल हो जाएगा।

>>> logging.warning('test')
Traceback (most recent call last):
  File "/usr/lib/python2.7/logging/__init__.py", line 846, in emit
    msg = self.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 723, in format
    return fmt.format(record)
  File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
    s = self._fmt % record.__dict__
KeyError: 'foo'
Logged from file <stdin>, line 1

क्या यह भी काम करेगा logging.info()? जब मैंने आखिरी कोशिश की तो यह विफल हो गया। : /
प्रखर मोहन श्रीवास्तव

2
मुझे @ mr2ert उत्तर पसंद है। आप logging.Formatterक्लास को बढ़ाकर अतिरिक्त फ़ील्ड को एक डिफ़ॉल्ट मान दे सकते हैं : क्लास CustomFormatter (logging.Formatter): डीफ़ फॉर्मेट (सेल्फ, रिकॉर्ड): यदि हैसट्रा (रिकॉर्ड, 'फू') नहीं है: record.foo = default_foo 'वापसी सुपर (CustomFormatter, self.format (रिकॉर्ड) h = loggin.StreamHandler () h.setFormatter (CustomFormatter ('% (foo) s% (संदेश) s) लकड़हारा = लॉगिंग .getLogger (' bar ') logger.addHandler () h) logger.error ('hey!', extra = {'foo': 'FOO'}) logger.error ('hey!')
loutre

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

@ प्रखर मोहन श्रीवास्तव हां, यह लॉगिंग.info () के लिए भी ठीक रहेगा। आपको क्या त्रुटि संदेश मिल रहा है?
श्रेयांश कट्टी १४'१

क्या मैं अतिरिक्त जानकारी का एक तार पास कर सकता हूँ। कुछ इस तरह से: "कर्मचारी आईडी 1029382 के लिए त्रुटि हुई" बिना कोई शब्दकोश
बनाए

25

python3

Python3.2 के रूप में आप अब LogRecordFactory का उपयोग कर सकते हैं

>>> import logging
>>> logging.basicConfig(format="%(custom_attribute)s - %(message)s")
>>> old_factory = logging.getLogRecordFactory()
>>> def record_factory(*args, **kwargs):
        record = old_factory(*args, **kwargs)
        record.custom_attribute = "my-attr"
        return record

>>> logging.setLogRecordFactory(record_factory)
>>> logging.info("hello")
my-attr - hello

निश्चित रूप record_factoryसे किसी भी कॉल करने योग्य होने के लिए अनुकूलित किया जा सकता है और custom_attributeयदि आप फ़ैक्टरी कॉल करने योग्य का संदर्भ रखते हैं तो मूल्य को अपडेट किया जा सकता है।

एडेप्टर / फिल्टर का उपयोग करने से बेहतर क्यों है?

  • आपको एप्लिकेशन के आसपास अपना लकड़हारा पास करने की आवश्यकता नहीं है
  • यह वास्तव में तीसरे पक्ष के पुस्तकालयों के साथ काम करता है जो अपने स्वयं के लकड़हारे (केवल कॉल करके logger = logging.getLogger(..)) का उपयोग करता है अब एक ही लॉग प्रारूप होगा। (यह फ़िल्टर्स / एडेप्टर के मामले में नहीं है जहाँ आपको एक ही लकड़हारे ऑब्जेक्ट का उपयोग करने की आवश्यकता है)
  • आप कई कारखानों को स्टैक / चेन कर सकते हैं

अजगर 2.7 के लिए कोई विकल्प है?
करोलच

एक ही लाभ के साथ नहीं, 2.7 के साथ आपको एडेप्टर या फिल्टर के साथ जाना होगा।
अहमद

5
यह नोवाडे का अजगर 3 सर्वश्रेष्ठ उत्तर है
स्टीफन

Docs.python.org/3/howto/logging-cookbook.html के अनुसार : यह पैटर्न विभिन्न पुस्तकालयों को एक साथ चेन कारखानों को अनुमति देता है, और जब तक वे एक-दूसरे की विशेषताओं को नहीं लिखते हैं या अनजाने में मानक पर उपलब्ध विशेषताओं को अधिलेखित नहीं करते हैं, तब तक कोई आश्चर्य नहीं होना चाहिए। हालांकि, यह ध्यान में रखा जाना चाहिए कि श्रृंखला में प्रत्येक लिंक सभी लॉगिंग ऑपरेशंस में रन-टाइम ओवरहेड जोड़ता है, और तकनीक का उपयोग केवल तब किया जाना चाहिए जब फ़िल्टर का उपयोग वांछित परिणाम प्रदान नहीं करता है।
चरणवीह

1
@ steve0hh प्रमुख वांछित परिणामों में से एक विभिन्न पुस्तकालयों / मॉड्यूलों में प्रासंगिक जानकारी लॉग करने की क्षमता है, जिसे केवल इस तरह से उपयोग करके हासिल किया जा सकता है। ज्यादातर परिस्थितियों में, पुस्तकालयों को लकड़हारा विन्यास को नहीं छूना चाहिए, यह माता-पिता के आवेदन की जिम्मेदारी है।
अहमद

10

एक अन्य तरीका एक कस्टम लॉगर एडेप्टर बनाना है। यह विशेष रूप से तब उपयोगी है जब आप प्रारूप को बदल नहीं सकते हैं या यदि आपका प्रारूप उस कोड के साथ साझा किया गया है जो अद्वितीय कुंजी (आपके मामले में app.name ) को नहीं भेजता है :

class LoggerAdapter(logging.LoggerAdapter):
    def __init__(self, logger, prefix):
        super(LoggerAdapter, self).__init__(logger, {})
        self.prefix = prefix

    def process(self, msg, kwargs):
        return '[%s] %s' % (self.prefix, msg), kwargs

और आपके कोड में, आप हमेशा की तरह अपने लकड़हारे को पैदा और शुरू करेंगे:

    logger = logging.getLogger(__name__)
    # Add any custom handlers, formatters for this logger
    myHandler = logging.StreamHandler()
    myFormatter = logging.Formatter('%(asctime)s %(message)s')
    myHandler.setFormatter(myFormatter)
    logger.addHandler(myHandler)
    logger.setLevel(logging.INFO)

अंत में, आप आवश्यकतानुसार एक उपसर्ग जोड़ने के लिए रैपर एडेप्टर बनाएंगे:

    logger = LoggerAdapter(logger, 'myapp')
    logger.info('The world bores you when you are cool.')

आउटपुट कुछ इस तरह दिखाई देगा:

2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.

1

मुझे इसे लागू करने के बाद यह SO प्रश्न मिला। आशा है कि यह किसी की मदद करता है। नीचे दिए गए कोड में, मैं claim_idलकड़हारा प्रारूप में एक अतिरिक्त कुंजी प्रेरित कर रहा हूं । जब भी claim_idवातावरण में कोई कुंजी मौजूद होगी, वह दावा_ लॉग करेगा । मेरे उपयोग के मामले में, मुझे AWS लैम्ब्डा फ़ंक्शन के लिए इस जानकारी को लॉग करने की आवश्यकता थी।

import logging
import os

LOG_FORMAT = '%(asctime)s %(name)s %(levelname)s %(funcName)s %(lineno)s ClaimID: %(claim_id)s: %(message)s'


class AppLogger(logging.Logger):

    # Override all levels similarly - only info overriden here

    def info(self, msg, *args, **kwargs):
        return super(AppLogger, self).info(msg, extra={"claim_id": os.getenv("claim_id", "")})


def get_logger(name):
    """ This function sets log level and log format and then returns the instance of logger"""
    logging.setLoggerClass(AppLogger)
    logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
    logger = logging.getLogger(name)
    logger.setLevel(logging.INFO)
    return logger


LOGGER = get_logger(__name__)

LOGGER.info("Hey")
os.environ["claim_id"] = "12334"
LOGGER.info("Hey")

Gist: https://gist.github.com/ramanujam/306f2e4e1506f302504fb67abef50652


1

स्वीकृत उत्तर लॉगफ़ाइल में प्रारूप को लॉग नहीं करता था, जबकि प्रारूप sys आउटपुट में परिलक्षित होता था। वैकल्पिक रूप से मैंने एक सरल दृष्टिकोण का इस्तेमाल किया और इस तरह काम किया;

logging.basicConfig(filename="mylogfile.test",
                    filemode="w+",
                    format='%(asctime)s: ' +app_name+': %(message)s ',
                    level=logging.DEBUG)


0

Mr2ert के उत्तर का उपयोग करते हुए, मैं इस आरामदायक समाधान के साथ आया (हालांकि मुझे लगता है कि यह अनुशंसित नहीं है) - कस्टम तर्क को स्वीकार करने और extraविधियों के अंदर शब्दकोश बनाने के लिए अंतर्निहित लॉगिंग विधियों को ओवरराइड करें :

import logging

class CustomLogger(logging.Logger):

   def debug(self, msg, foo, *args, **kwargs):
       extra = {'foo': foo}

       if self.isEnabledFor(logging.DEBUG):
            self._log(logging.DEBUG, msg, args, extra=extra, **kwargs)

   *repeat for info, warning, etc*

logger = CustomLogger('CustomLogger', logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(foo)s] %(message)s') 
handler = logging.StreamHandler()
handler.setFormatter(formatter) 
logger.addHandler(handler)

logger.debug('test', 'bar')

आउटपुट:

2019-03-02 20:06:51,998 [bar] test

यह संदर्भ के लिए फ़ंक्शन में बनाया गया है:

def debug(self, msg, *args, **kwargs):
    """
    Log 'msg % args' with severity 'DEBUG'.

    To pass exception information, use the keyword argument exc_info with
    a true value, e.g.

    logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
    """
    if self.isEnabledFor(DEBUG):
        self._log(DEBUG, msg, args, **kwargs)

0

आयात लॉगिंग;

वर्ग लॉगफ़िल्टर (लॉगिंग। फ़िल्टर):

def __init__(self, code):
    self.code = code

def filter(self, record):
    record.app_code = self.code
    return True

logging.basicConfig (प्रारूप = '[% (asctime) s:% (स्तरनाम) s] :: [% (मॉड्यूल) s ->% (नाम) s] - APP_CODE:% (appcode कोड) - MSG:% (संदेश) ) एस ');

कक्षा लकड़हारा:

def __init__(self, className):
    self.logger = logging.getLogger(className)
    self.logger.setLevel(logging.ERROR)

@staticmethod
def getLogger(className):
    return Logger(className)

def logMessage(self, level, code, msg):
    self.logger.addFilter(LogFilter(code))

    if level == 'WARN':        
        self.logger.warning(msg)
    elif level == 'ERROR':
        self.logger.error(msg)
    else:
        self.logger.info(msg)

वर्ग परीक्षण: लकड़हारा = लकड़हारा।लोगर ('टेस्ट')

if __name__=='__main__':
    logger.logMessage('ERROR','123','This is an error')

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