পাইথন লগ বিন্যাসের স্ট্রিংয়ে আমি কীভাবে কাস্টম ফিল্ড যুক্ত করব?


91

আমার বর্তমান ফর্ম্যাট স্ট্রিংটি হ'ল:

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কলটিতে এটি পাস করতে চান ? যদি তা হয় তবে দস্তাবেজগুলির দিকে তাকান যেখানে এটিতে বলা হয়েছে "এই কার্যকারিতাটি আপনার নিজস্ব মানগুলিকে লগরেকর্ডে ইনজেকশনের জন্য ব্যবহার করা যেতে পারে ..." তবে logger = logging.getLogger('myapp')এটি logger.infoকলটি ব্যবহার এবং এটি বেক করার জন্য এটি একটি প্রধান ক্ষেত্রে বলে মনে হচ্ছে ।
অবতারিত

পাইথন লগিং ইতিমধ্যে আফিক করতে পারে। যদি আপনি একটি ভিন্ন ব্যবহার loggerপ্রতিটি অ্যাপের বস্তুর, আপনি প্রতিটি এক ব্যবহার আপনার শুরু করতে গিয়ে দ্বারা একটি আলাদা নাম করতে পারেন loggerতাই মত S: logger = logging.getLogger(myAppName)। দ্রষ্টব্য যে __name__পাইথন মডিউলের নাম, সুতরাং প্রতিটি অ্যাপ্লিকেশন যদি তার নিজস্ব পাইথন মডিউল হয় তবে এটিও কার্যকর হবে।
ফ্লোরিয়ান ক্যাসেলালেন

উত্তর:


131

আপনি একটি লগারএডাপ্টার ব্যবহার করতে পারেন যাতে আপনাকে প্রতিটি লগিং কল সহ অতিরিক্ত তথ্য পাস করতে না হয়:

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')

অনুরূপ লগ রেকর্ড উত্পাদন করে।


4
কীভাবে আমরা একটি 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)
হায়াত


4
আমি কি অতিরিক্ত তথ্যের একটি স্ট্রিং পাস করতে পারি? এর মতো কিছু: "কোনও অভিধান তৈরি না করেই কর্মচারী আইডি 1029382 এর জন্য ত্রুটি ঘটেছে"।
shreesh katti

50

আপনাকে সেভাবে অতিরিক্ত করার জন্য প্যারামিটার হিসাবে ডিকটি পাস করতে হবে।

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()? সর্বশেষ চেষ্টা করার পরে এটি ব্যর্থ হয়েছিল। : /
প্রখর মোহন শ্রীবাস্তব

4
আমি @ mr2ert উত্তর পছন্দ করি। আপনি logging.Formatterশ্রেণি প্রসারিত করে অতিরিক্ত ক্ষেত্রকে একটি ডিফল্ট মান দিতে পারেন : শ্রেণি কাস্টমফরম্যাটর (লগিং Forফর্ম্যাটটার): ডিফ ফর্ম্যাট (স্বতঃ, রেকর্ড): হ্যাশট্রা না হলে (রেকর্ড, 'ফু'): রেকর্ড.ফু = 'ডিফল্ট_ফু' রিটার্ন সুপার (কাস্টমফর্মেটর, স্ব.ফর্ম্যাট (রেকর্ড) এইচ = লগিন.স্ট্রিমহ্যান্ডলার () এইচ.সেটফর্মেটর (কাস্টমফর্মেটর ('% (ফু))% (বার্তা) গুলি') লগার = লগিং.জেটলগার ('বার') লগার জ) logger.error ( '! আরে', অতিরিক্ত = { 'foo বিন্যাস': 'foo' ফাইলটি}) logger.error () 'হেই!'
loutre

এই পদ্ধতিটি দ্রুত তবে আপনার প্রতিটি লগ বার্তায় অতিরিক্ত লাইন যুক্ত করতে হবে যা ভুলে যাওয়া সহজ এবং ত্রুটির প্রবণ। সুপার () কলগুলি প্রতিস্থাপন করা উটবু থেকে প্রাপ্ত উত্তরের চেয়ে মেসেঞ্জার।
পেভোগাম

@ প্রচার মোহন শ্রীবাস্তব হ্যাঁ লগিং.আইনফো () এর জন্যও এটি দুর্দান্ত কাজ করবে। আপনি কী ত্রুটি বার্তা পাচ্ছেন?
শ্রীশ কাট্টি

আমি কি অতিরিক্ত তথ্যের একটি স্ট্রিং পাস করতে পারি? এর মতো কিছু: "কর্মচারী আইডির জন্য ত্রুটি ঘটেছে 1029382" কোনও অভিধান তৈরি না করে এবং কীগুলি পাস না করে
shriesh katti

23

পাইথন 3

পাইথন ৩.২ হিসাবে এখন আপনি লগেরেকর্ডফ্যাক্টরি ব্যবহার করতে পারবেন

>>> 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(..)) এখন একই লগ ফর্ম্যাটটি থাকবে। (ফিল্টার / অ্যাডাপ্টারের ক্ষেত্রে এটি হ'ল না যেখানে আপনাকে একই লগার অবজেক্টটি ব্যবহার করা উচিত)
  • আপনি একাধিক কারখানা স্ট্যাক / চেইন করতে পারেন

পাইথন ২.7 এর কোন বিকল্প আছে?
করোলচ

একই সুবিধাগুলির সাথে নয়, ২.7 এর সাথে আপনাকে অ্যাডাপ্টার বা ফিল্টারগুলির সাথে যেতে হবে।
আহমদ

4
এটি এখনকার অজগর 3 সেরা উত্তর
স্টাফেন

ডকস.পিথন.আর. / ৩ / হাউটো / ব্লগিং- কুকবুক.এইচটিএমএল অনুসারে : এই প্যাটার্নটি বিভিন্ন গ্রন্থাগারগুলিকে একসাথে চেইন ফ্যাক্টরি করতে দেয় এবং যতক্ষণ না তারা একে অপরের বৈশিষ্ট্যগুলি ওভাররাইট করে না বা অজানাভাবে স্ট্যান্ডার্ড হিসাবে সরবরাহিত বৈশিষ্ট্যগুলিকে ওভাররাইট করে না কোন আশ্চর্য হওয়া উচিত। যাইহোক, এটি মনে রাখা উচিত যে চেইনের প্রতিটি লিঙ্কটি সমস্ত লগিং অপারেশনে রান-টাইম ওভারহেড যুক্ত করে এবং যখন কেবল কোনও ফিল্টার ব্যবহার পছন্দসই ফলাফল সরবরাহ না করে তখন কৌশলটি ব্যবহার করা উচিত।
স্টিভ0 এএইচ

4
@ স্টিভ0 এইচ কী-এর অন্যতম পছন্দসই ফলাফল হ'ল বিভিন্ন গ্রন্থাগার / মডিউল জুড়ে প্রাসঙ্গিক তথ্য লগ করার ক্ষমতা, যা কেবল এই উপায়ে ব্যবহার করেই অর্জন করা যেতে পারে। বেশিরভাগ পরিস্থিতিতে লাইব্রেরিগুলিতে লগার কনফিগারেশনটি স্পর্শ করা উচিত নয়, এটি প্যারেন্ট অ্যাপ্লিকেশনটির দায়িত্ব।
আহমদ

9

আর একটি উপায় কাস্টম লগারএডাপ্টার তৈরি করা। এটি বিশেষত কার্যকর যখন আপনি ফর্ম্যাটটি পরিবর্তন করতে পারবেন না বা যদি আপনার ফর্ম্যাটটি এমন কোডের সাথে ভাগ করা হয় যা অনন্য কীটি না পাঠায় (আপনার ক্ষেত্রে অ্যাপ_নামে ):

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

আমি নিজেই এটি প্রয়োগ করার পরে এই এসও প্রশ্নটি পেয়েছি। আশা করি এটি কাউকে সাহায্য করবে। নীচের কোডে, আমি 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")

বক্তব্য: https://gist.github.com/ramanujam/306f2e4e1506f302504fb67abef50652


0

এমআর্টের্টের উত্তরটি ব্যবহার করে, আমি এই আরামদায়ক সমাধানটি নিয়ে এসেছি (যদিও আমি অনুমান করি এটি প্রস্তাবিত নয়) - কাস্টম যুক্তি গ্রহণ করার জন্য বিল্ট-ইন লগিং পদ্ধতিগুলিকে ওভাররাইড করুন এবং পদ্ধতিগুলির মধ্যে 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

লগিং.বাসিক কনফিগ (ফর্ম্যাট = '[% (অ্যাসটাইম)) s:% (স্তর নাম) গুলি] :: [% (মডিউল) গুলি ->% (নাম) গুলি] - অ্যাপ্লিকেশন_কোড:% (অ্যাপ_কোড) গুলি - এমএসজি:% (বার্তা ) এস ');

শ্রেণি লগার:

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')

এই বাস্তবায়ন খুব অদক্ষ হবে।
ব্লেকভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.