নতুন ফর্ম্যাট স্ট্রিং সহ ভেরিয়েবল ডেটা লগিং


85

আমি অজগর ২.7.৩ এর জন্য লগিং সুবিধা ব্যবহার করি। এই পাইথন সংস্করণটির জন্য ডকুমেন্টেশন বলতে :

লগিং প্যাকেজ স্ট্রিং ফরম্যাট () এবং স্ট্রিং। টেম্পলেট হিসাবে নতুন ফর্ম্যাটিং বিকল্পগুলির প্রাক-তারিখগুলি। এই নতুন ফর্ম্যাটিং বিকল্পগুলি সমর্থিত ...

আমি কোঁকড়া ধনুর্বন্ধনী সহ 'নতুন' ফর্ম্যাটটি পছন্দ করি। তাই আমি এরকম কিছু করার চেষ্টা করছি:

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

এবং ত্রুটি পান:

TypeError: স্ট্রিং ফর্ম্যাটিংয়ের সময় সমস্ত আর্গুমেন্ট রূপান্তরিত হয় না

আমি এখানে কি মিস করছি?

পিএস আমি ব্যবহার করতে চাই না

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

কারণ এক্ষেত্রে লগার স্তর নির্বিশেষে বার্তাটি সর্বদা ফরম্যাট করা হয়।


4
আপনি এটি করতে পারেন: log.debug("format this message%d" % 1)
রোনাক করুন

4
আপনি কনফিগার করতে Formatterব্যবহার করতে '{' হিসেবে শৈলী
মাতা

4
@ রোনাক পরামর্শের জন্য ধন্যবাদ তবে না। দয়া করে, কেন "পিএস" বিভাগটি দেখুন। BTW log.debug ("এই বার্তাকে% d ফর্ম্যাট করুন", 1) - ঠিক আছে works
ম্যাজেস্টিকআর

@ মাতা কীভাবে এটি কনফিগার করবেন? এটি করার সরাসরি ডকুমেন্টেশন আছে কি?
ম্যাজেস্টিকআর

@ মাতা আমি এটি খুঁজে পেয়েছি। দয়া করে এটিকে একটি উত্তর দিন যাতে আমি এটিকে "সঠিক উত্তর হিসাবে সেট করতে পারি। আরও একবার আপনাকে ধন্যবাদ।
ম্যাজেস্টিকআর

উত্তর:


38

সম্পাদনা করুন: কটাক্ষপাত করা StyleAdapter@Dunes 'উত্তরে পদ্ধতির এই উত্তর অসদৃশ; লগারের পদ্ধতিগুলি (ডিবাগ (), তথ্য (), ত্রুটি () ইত্যাদি কল করার সময় এটি বয়লারপ্লেট ছাড়াই বিকল্প বিন্যাসকরণ শৈলী ব্যবহার করতে দেয়।


দস্তাবেজগুলি থেকে - বিকল্প বিন্যাস শৈলীর ব্যবহার :

লগিং কলগুলি (logger.debug (), logger.info () ইত্যাদি) কেবল আসল লগিং বার্তার জন্য অবস্থানগত পরামিতি গ্রহণ করে, কীওয়ার্ড প্যারামিটারগুলি কেবল আসল লগিং কলকে কীভাবে পরিচালনা করতে পারে তার বিকল্পগুলি নির্ধারণের জন্য ব্যবহৃত হয় (উদাহরণস্বরূপ exc_info কীওয়ার্ড প্যারামিটার চিহ্নিত করতে যে ট্রেসব্যাক তথ্য লগ করা উচিত, বা অতিরিক্ত কীওয়ার্ড প্যারামিটার লগতে অতিরিক্ত প্রাসঙ্গিক তথ্য যুক্ত করতে)) সুতরাং আপনি সরাসরি স্ট্রিংফরম্যাট () বা স্ট্রিং ব্যবহার করে লগিং কলগুলি করতে পারবেন না e টেম্পলেট সিনট্যাক্স, কারণ অভ্যন্তরীণভাবে লগিং প্যাকেজটি ফর্ম্যাট স্ট্রিং এবং ভেরিয়েবল আর্গুমেন্টগুলিকে মার্জ করার জন্য%-ফর্ম্যাটিং ব্যবহার করে। পশ্চাদপদ সামঞ্জস্যতা সংরক্ষণ করার সময় এটির কোনও পরিবর্তন হবে না, যেহেতু বিদ্যমান কোডটিতে রয়েছে এমন সমস্ত লগিং কল%-ফর্ম্যাট স্ট্রিং ব্যবহার করবে।

এবং:

তবে, আপনার ব্যক্তিগত লগ বার্তাগুলি তৈরির জন্য আপনি {} - এবং। - ফর্ম্যাট ব্যবহার করতে পারেন এমন একটি উপায় রয়েছে। মনে রাখবেন যে কোনও বার্তার জন্য আপনি একটি স্বেচ্ছাসেবী বস্তুকে বার্তা বিন্যাসের স্ট্রিং হিসাবে ব্যবহার করতে পারেন এবং লগিং প্যাকেজটি সত্যিকারের বিন্যাসের স্ট্রিং পেতে সেই বস্তুটিতে স্ট্রিং () কল করবে।

এটি 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'))

দ্রষ্টব্য: আসল ফর্ম্যাটিংটি প্রয়োজনীয় না হওয়া পর্যন্ত দেরী হয় যেমন, ডিইবিইউজি বার্তা লগ না করা থাকলে ফরম্যাটিংটি মোটেই সম্পাদিত হয় না।


4
পাইথন ৩.6 অনুসারে, আপনি এফ-স্ট্রিংগুলি এর মতো ব্যবহার করতে পারেন:num = 2; name = 'placeholders'; log.debug(f'Message with {num} {name}')
জ্যাকটোজ

12
@ P1h3r1e3d13 উত্তরে লগিং কোডের বিপরীতে, f '' - স্ট্রিংগুলি তত্ক্ষণাত্ বিন্যাস সম্পাদন করে।
jfs

4
ঠিক। তারা এখানে কাজ করে কারণ তারা লগ পদ্ধতিতে কল করার আগে নিয়মিত স্ট্রিংটি ফর্ম্যাট করে এবং ফিরে আসে। এটি কারওর সাথে প্রাসঙ্গিক হতে পারে বা নাও থাকতে পারে, তাই আমি মনে করি এটি বিকল্প হিসাবে উল্লেখ করার মতো।
জ্যাকটোজ

6
@ জ্যাকটোজ আমি মনে করি ব্যবহারকারীদের এফ-স্ট্রিং ব্যবহার করে লগ করা উচিত নয়, এটি লগ একত্রিতকরণ পরিষেবাদিগুলিকে (যেমন সেন্ড্রি) পরাস্ত করে। স্টাডলিব লগিং স্ট্রিং টেম্প্লেটিংয়ের পিছনে পিছনে একটি ভাল কারণ আছে।
উইম

30

এখানে আরেকটি বিকল্প রয়েছে যা ডুনসের উত্তরে উল্লিখিত মূল শব্দটির সমস্যা নেই। এটি কেবলমাত্র অবস্থানগত ( {0}) আর্গুমেন্টগুলি পরিচালনা করতে পারে এবং কীওয়ার্ড ( {foo}) যুক্তিগুলি নয়। এটি বিন্যাস করতে (আন্ডারস্কোর ব্যবহার করে) দুটি কলেরও প্রয়োজন হয় না। এটিতে সাবক্লাসিংয়ের আইক-ফ্যাক্টর রয়েছে 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অ্যাডাপ্টারের মাধ্যমে সমস্ত বার্তাকে নতুন-স্টাইলের ফর্ম্যাটিং ব্যবহার করতে বাধ্য করার জন্য উল্লিখিত চেকটি সরাতে পারেন ।


এই উত্তরটি কয়েক বছর পরে পড়ার জন্য নোট করুন : পাইথন ৩.২ দিয়ে শুরু করে আপনি স্টাইলের প্যারামিটারগুলিFormatter অবজেক্ট সহ ব্যবহার করতে পারেন :

লগিং (৩.২ হিসাবে) এই দুটি অতিরিক্ত বিন্যাস শৈলীর উন্নত সমর্থন সরবরাহ করে। নামের অতিরিক্ত, alচ্ছিক কীওয়ার্ড প্যারামিটার নিতে ফর্ম্যাটর ক্লাসটি বাড়ানো হয়েছে 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

6
পাইথন 3 সম্পর্কে আপনার বক্তব্য ভুল। স্টাইলের প্যারামিটারটি কেবলমাত্র ফর্ম্যাটর বিন্যাসের স্ট্রিংয়ের ক্ষেত্রে প্রযোজ্য, পৃথক লগ বার্তা নয়। আপনি যে পৃষ্ঠায় লিঙ্ক করেছেন সেটিতে স্পষ্টভাবে বলা হয়েছে: "পশ্চাদপদ সামঞ্জস্যতা সংরক্ষণের সময় কোনও পরিবর্তন হবে না"।
mhmmith

4
আমাকে সৎ রাখার জন্য ধন্যবাদ প্রথম অংশটি এখন কম দরকারী, তবে আমি এটির পরিবর্তে পুনরায় প্রতিক্রিয়া করেছি Formatter, যা এখন সঠিক (আমার মনে হয়)। StyleAdapter এখনও কাজ,
ফিলিপ

@ ফালস্ট্রো - এটি নির্দেশ করার জন্য ধন্যবাদ। আপডেট করা সংস্করণ এখন কাজ করা উচিত। যেহেতু BraceStringএকটি স্ট্রিং সাবক্লাস, তাই নিজের থেকে ফিরে আসা নিরাপদ__str__
ফিলিপ

4
কেবল উত্তর দিন যে শৈলীর উল্লেখ = "{", +1
টম এস

24

সহজ সমাধানটি হ'ল দুর্দান্ত মডিউলটি ব্যবহার করা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

এটি দুর্দান্ত দেখাচ্ছে, তবে কি মাত্র কয়েক সেকেন্ডের চেয়ে মিলিসেকেন্ড থাকার কোনও উপায় আছে?
জেফ

@ জেফ নিশ্চিত, লগবুক আপনাকে কাস্টম স্ট্রিং ফর্ম্যাটগুলির সাথে কাস্টম হ্যান্ডলারগুলি সংজ্ঞায়িত করতে এবং ব্যবহার করতে দেয়।
টমাস অরোজকো

4
@ জেফ কয়েক বছর পরে - ডিফল্ট সময়ের যথার্থতা মিলিসেকেন্ড।
জান ভি্লকিনস্কি

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) এর জন্য বোঝানো ভুল বানানযুক্ত কীওয়ার্ডগুলিকেও নিঃশব্দে উপেক্ষা করে ।



12

অন্যান্য উত্তরের হিসাবে উল্লেখ করা হয়েছে, পাইথন ৩.২- তে প্রবর্তিত ব্রেস-স্টাইল বিন্যাসটি কেবল ফর্ম্যাট স্ট্রিংয়ে ব্যবহৃত হয়, আসল লগ বার্তা নয়।

প্রকৃত লগ বার্তায় ব্রেস-স্টাইল বিন্যাস সক্ষম করতে আমরা লগার কোডের কিছুটা বানর-প্যাচ করতে পারি।

নিম্নলিখিত ক্রিয়াকলাপটি 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ফাংশন দ্বারা নির্মিত নির্দিষ্ট লগারকে প্রভাবিত করবে (তৃতীয় পক্ষের প্যাকেজগুলি ভাঙবে না)।
  • যদি লগারটি কোনও সাধারণ logging.getLogger()কল থেকে আবার অ্যাক্সেস করা হয় তবে নতুন স্টাইলের ফর্ম্যাটিংটি এখনও প্রযোজ্য হবে।
  • kwargs সমর্থিত নয় (তোলে সাথে বিবাদ করছে বলে অসম্ভব বিল্ট-ইন exc_info, stack_info, stacklevelএবং extra)।
  • পারফরম্যান্স হিটটি সর্বনিম্ন হওয়া উচিত (প্রতিটি লগ বার্তার জন্য একটি একক ফাংশন পয়েন্টার পুনরায় লেখা)।
  • বার্তাটির বিন্যাসটি আউটপুট না হওয়া পর্যন্ত বিলম্বিত হয় (বা লগ বার্তাটি ফিল্টার করা হয় না তবে)।
  • আরোগুলি logging.LogRecordযথারীতি বস্তুগুলিতে সংরক্ষণ করা হয় (কিছু ক্ষেত্রে কাস্টম লগ হ্যান্ডলারের সাথে দরকারী)।
  • দিকে তাকিয়ে থেকে loggingমডিউল সোর্স কোড তা পছন্দ হল যখন সমস্ত পাইথন 2.6 পথে ফিরে কাজ করা উচিত বলে মনে হয় str.formatচালু করা হয় (কিন্তু আমি শুধুমাত্র 3.5 এটা পরীক্ষিত এবং আপ করে থাকেন)

4
একমাত্র উত্তর যা বিবেচনা করে যে ডিবাগ স্ট্রিংটি কেবল তখনই গণনা করা উচিত যদি ডিবাগার বার্তা মুদ্রণ করা হয়। ধন্যবাদ!
ফাফমান

2

logging.setLogRecordFactoryপাইথন ৩.২++ এ চেষ্টা করুন :

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)

এটি কাজ করে তবে সমস্যাটি হ'ল আপনি তৃতীয় পক্ষের মডিউলগুলি ভেঙে ফেলছেন যা %লগিং মডিউলে রেকর্ড কারখানাটি বৈশ্বিক হিসাবে ফরম্যাটিং ব্যবহার করছে।
jtaylor

1

আমি একটি পছন্দসই ফর্ম্যাটর তৈরি করেছি, যার নাম রঙিন ফরমেটার যা সমস্যাটি এইভাবে পরিচালনা করে:

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

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 সঙ্গে এটি পরীক্ষা করেছি।


এটি নির্ধারণ করে যে স্ট্রিংকে প্রকৃতপক্ষে ইন্টারপোলটিংয়ের লোডটি কোথায় যায়। আপনি কি রেকর্ড তৈরির সময় এটিকে সামনের অংশে লোড করছেন, স্থির স্ট্রিংটি নিশ্চিত করে যা পিছনের দিক থেকে অব্যাহত থাকে বা বার্তাটি শেষ পর্যন্ত প্রদর্শিত হয় তবে আপনি কেবল বিন্যাস সম্পাদন করেন। সাধারণ ক্ষেত্রে: বার্তাটি প্রদর্শনের জন্য গ্রহণযোগ্য স্তরের নীচে। অতিরিক্তভাবে: জিনিসগুলি "প্যাচ" করার ভাল উপায় নয়। আসলে একটি লগার সাবক্লাস নির্মাণ করুন এবং এটি ব্যবহার করুন, ম্যান।
amcgregor

-1

এখানে বাস্তব কিছু সহজ যা কাজ করে:

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")
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.