পাইথন লগিংয়ের সাথে লগ বার্তা দু'বার প্রদর্শিত হবে ing


101

আমি পাইথন লগিং ব্যবহার করছি এবং কোনও কারণে আমার সমস্ত বার্তা দু'বার প্রদর্শিত হচ্ছে।

লগিং কনফিগার করার জন্য আমার কাছে একটি মডিউল রয়েছে:

# BUG: It's outputting logging messages twice - not sure why - it's not the propagate setting.
def configure_logging(self, logging_file):
    self.logger = logging.getLogger("my_logger")
    self.logger.setLevel(logging.DEBUG)
    self.logger.propagate = 0
    # Format for our loglines
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    # Setup console logging
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    self.logger.addHandler(ch)
    # Setup file logging as well
    fh = logging.FileHandler(LOG_FILENAME)
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    self.logger.addHandler(fh)

পরে, আমি লগিং কনফিগার করতে এই পদ্ধতিটি কল করি:

if __name__ == '__main__':
    tom = Boy()
    tom.configure_logging(LOG_FILENAME)
    tom.buy_ham()

এবং তারপরে বলুন, বাই_হ্যাম মডিউল, আমি ফোন করব:

self.logger.info('Successfully able to write to %s' % path)

এবং কোনও কারণে, সমস্ত বার্তা দুটিবার প্রদর্শিত হচ্ছে। আমি স্ট্রিম হ্যান্ডলারগুলির মধ্যে একটিতে মন্তব্য করেছি, এখনও একই জিনিস। অদ্ভুত এক বিট, কেন ঘটছে তা নিশ্চিত নয় ... হ্যাঁ। ধরে নিচ্ছি আমি স্পষ্ট কিছু মিস করেছি।

চিয়ার্স, ভিক্টর


4
আপনি কি নিশ্চিত configure_logging()যে দু'বার কল করা হয়নি (যেমন কনস্ট্রাক্টরের কাছ থেকেও)? বয় () এর কেবলমাত্র একটি উদাহরণ তৈরি করা হয়েছে?
জ্যাসেক কোননিজনি

4
self.logger.handlers = [ch]পরিবর্তে ব্যবহার করা এই সমস্যার সমাধান করবে, যদিও এটি নিশ্চিত হওয়া ভাল যে আপনি এই কোডটি দুবার না চালাচ্ছেন, উদাহরণস্বরূপ, if not self.loggerশুরুতে ব্যবহার করে।
নিনজাকাননন

উত্তর:


135

আপনি configure_loggingদু'বার কল করছেন (সম্ভবত এর __init__পদ্ধতিতে Boy): getLoggerএকই বস্তুটি ফিরে আসবে, তবে addHandlerইতিমধ্যে লোগারে কোনও অনুরূপ হ্যান্ডলার যুক্ত হয়েছে কিনা তা পরীক্ষা করে না।

সেই পদ্ধতিতে কলগুলি সনাক্ত করার চেষ্টা করুন এবং এর মধ্যে একটি মুছে ফেলার চেষ্টা করুন। অথবা একটি পতাকা স্থাপন logging_initializedসক্রিয়া Falseমধ্যে __init__পদ্ধতি Boyএবং পরিবর্তন configure_loggingকিছুই করতে হলে logging_initializedহয় True, এবং সেট করতে Trueপরে আপনি এটির সক্রিয়া করেছি।

যদি আপনার প্রোগ্রামটি বেশ কয়েকটি Boyদৃষ্টান্ত তৈরি করে তবে আপনাকে কোনও বিশ্বব্যাপী configure_loggingফাংশন হ্যান্ডলারের সাথে যুক্ত করার Boy.configure_loggingপদ্ধতিটি পরিবর্তন করতে হবে এবং পদ্ধতিটি কেবল self.loggerবৈশিষ্ট্যটিকে আরম্ভ করতে হবে ।

এটির সমাধানের আর একটি উপায় হ'ল আপনার লগারের হ্যান্ডলারের বৈশিষ্ট্যটি যাচাই করা:

logger = logging.getLogger('my_logger')
if not logger.handlers:
    # create the handlers and call logger.addHandler(logging_handler)

4
হ্যাঁ, আপনি ঠিক বলেছেন - আমাকে বোকা। আমি এটিকে ইনকে ডাকলাম , পাশাপাশি স্পষ্টভাবে অন্য কোথাও। হাঃ হাঃ হাঃ. ধন্যবাদ =)।
ভিক্টোহোই

ধন্যবাদ আপনার সমাধান আজ আমাকে বাঁচিয়েছে।
ForeverLearner

4
আমার ক্ষেত্রে, তারা 6 বার উপস্থিত হয়েছিল। আমার সন্দেহ হয়েছিল কারণ আমি
o টি

4
আমি এখানে আমার অভিজ্ঞতাটি ভাগ করে নিতে চাই: ফ্ল্যাস্ক অ্যাপ্লিকেশনটির জন্য যা আমি বিকাশ করেছি, লগ বার্তাগুলি দুই'র চেয়ে বেশি প্রদর্শিত হচ্ছে। আমি বলব যে তারা লগ ফাইলটিতে বর্ধন করছিল, কারণ যখন অ্যাপ্লিকেশন এবং মডিউলগুলি লোড করা হয়েছিল, তখন loggerভেরিয়েবলটি আমার ক্লাসের একটি থেকে ইনস্ট্যান্টেশন করা হয়নি, তবে loggerপাইথন 3 ক্যাশে ভেরিয়েবল উপস্থিত ছিল , এবং হ্যান্ডলারটি প্রতি 60 সেকেন্ডে একটি অ্যাপসচিডুলার দ্বারা যুক্ত করা হয়েছিল যা আমি কনফিগার করেছি। সুতরাং, এই if not logger.handlersধরণের ঘটনাটি এড়াতে এটি একটি দুর্দান্ত স্মার্ট উপায়। সমাধানের জন্য ধন্যবাদ, কমরেড :)!
ivanleoncz

4
আমি আমার ফ্লাস্ক অ্যাপে এই সমস্যাটি দেখছি seeing এই সমাধানটি মূল ফ্লাস্ক অ্যাপ্লিকেশনটিতে উত্পন্ন লগ বার্তাগুলির জন্য সমস্যাটি স্থির করেছে, তবে আমার অ্যাপ্লিকেশনটি একটি লাইব্রেরি মডিউলে কল করে এবং library গ্রন্থাগারের সেই বার্তাগুলি এখনও একাধিকবার লগইন হচ্ছে। আমি এটি ঠিক করতে জানি না।
ক্যাস

27

আপনি যদি এই সমস্যাটি দেখছেন এবং আপনি দুবার হ্যান্ডলারটি যোগ করছেন না তবে এখানে অবার্ন্টের উত্তরটি দেখুন

ডক্স থেকে :

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

সুতরাং, আপনি যদি "পরীক্ষার" উপর একটি কাস্টম হ্যান্ডলার চান এবং এটির বার্তাগুলিও রুট হ্যান্ডলারের কাছে যেতে চান না তবে উত্তরটি সহজ: এর propagateপতাকাটি বন্ধ করুন :

logger.propagate = False

4
এটিই সেরা উত্তর। এটি পোস্টারের উদ্দেশ্য (কোডিংয়ের ক্ষেত্রে যৌক্তিক ত্রুটি) মাপসই করে না তবে বেশিরভাগ ক্ষেত্রে, এটি হওয়া উচিত।
আর্টেম

ব্রাভো। এটি হ'ল অনুলিপিগুলির আসল কারণ (সর্বাধিক সাধারণ ক্ষেত্রে)।
মিঃ ডুহার্ট

এটি আমার কোড নিয়েও সমস্যা ছিল। অনেক ধন্যবাদ.
কঠোরতা

সর্বকালের সেরা উত্তর। ধন্যবাদ!
Foivos Ts

কেন এটি ডিফল্ট হতে পারে। আমি আমার সমস্ত লগারগুলি "মূলের" নীচে তৈরি করি। + ডিরেক্টরি কাঠামো, যাতে আমি সহজেই 'রুট' লগার
মর্টেনবি

8

আপনি বাইরে থেকে প্রতিবার কল করলে হ্যান্ডলার যুক্ত হয়। আপনি নিজের কাজ শেষ করার পরে হ্যান্ডলারটি সরানোর চেষ্টা করুন:

self.logger.removeHandler(ch)

4
আমি logger.handlers.pop() অজগর ২.7 এ ব্যবহার করেছি , কৌতুকটি করেছে
Radtek

7

আমি একটি অজগর নবাগত, তবে এটি আমার পক্ষে কাজ করে বলে মনে হয়েছিল (পাইথন ২.7)

while logger.handlers:
     logger.handlers.pop()

4

আমার ক্ষেত্রে আমি logger.propagate = Falseডাবল মুদ্রণ প্রতিরোধের জন্য সেট করতে চাই ।

নীচের কোডে আপনি সরিয়ে logger.propagate = Falseফেললে আপনি ডাবল প্রিন্টিং দেখতে পাবেন।

import logging
from typing import Optional

_logger: Optional[logging.Logger] = None

def get_logger() -> logging.Logger:
    global _logger
    if _logger is None:
        raise RuntimeError('get_logger call made before logger was setup!')
    return _logger

def set_logger(name:str, level=logging.DEBUG) -> None:
    global _logger
    if _logger is not None:
        raise RuntimeError('_logger is already setup!')
    _logger = logging.getLogger(name)
    _logger.handlers.clear()
    _logger.setLevel(level)
    ch = logging.StreamHandler()
    ch.setLevel(level)
    # warnings.filterwarnings("ignore", "(Possibly )?corrupt EXIF data", UserWarning)
    ch.setFormatter(_get_formatter())
    _logger.addHandler(ch)
    _logger.propagate = False # otherwise root logger prints things again


def _get_formatter() -> logging.Formatter:
    return logging.Formatter(
        '[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')

এটি আমার কাছে সমস্যা। আপনাকে ধন্যবাদ
q0987

দুর্দান্ত উত্তর; যোগ logger.propagate = Falseওয়েট্রেস দ্বারা হোস্ট করা একটি বোতল অ্যাপ্লিকেশন, যখন বোতল এর লগিং দ্বিগুণ লগিং রোধ করার জন্য সমাধান ছিল app.loggerউদাহরণস্বরূপ।
ব্লুবাইনারি

1

একটি কল logging.debug()কল logging.basicConfig()যদি কোন রুট হ্যান্ডেলার ইনস্টল আছে। এটি আমার জন্য একটি পরীক্ষার কাঠামোয় ঘটেছিল যেখানে আমি পরীক্ষার মামলাগুলি বরখাস্ত করার আদেশটি নিয়ন্ত্রণ করতে পারি না। আমার আরম্ভের কোডটি দ্বিতীয়টি ইনস্টল করছিল। ডিফল্ট লগিং ব্যবহার করে B BASIC_FORMAT যা আমি চাইনি।


আমার মনে হয় এটি আমার জন্য কী ঘটছে। আপনি কীভাবে কনসোল লগারের স্বয়ংক্রিয় সৃষ্টি প্রতিরোধ করবেন?
রবার্ট

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

1

দেখে মনে হচ্ছে আপনি যদি লগারকে কোনও কিছু আউটপুট করেন (দুর্ঘটনাক্রমে) তবে এটি কনফিগার করুন, এটি অনেক দেরিতে। উদাহরণস্বরূপ, আমার কোডে আমার ছিল

logging.warning("look out)"

...
ch = logging.StreamHandler(sys.stdout)
root = logging.getLogger()
root.addHandler(ch)

root.info("hello")

আমি কিছু পেতে চাই (বিন্যাস বিকল্পগুলি উপেক্ষা করে)

look out
hello
hello

এবং সবকিছুই দু'বার স্ট্রাউডে লেখা হয়েছিল। আমি বিশ্বাস করি কারণ এটি প্রথম কলটি logging.warningস্বয়ংক্রিয়ভাবে একটি নতুন হ্যান্ডলার তৈরি করে এবং তারপরে আমি স্পষ্টভাবে অন্য হ্যান্ডলার যুক্ত করেছি ler আমি যখন দুর্ঘটনাক্রমে প্রথম logging.warningকলটি সরিয়েছিলাম তখন সমস্যাটি চলে গেল ।


0

আমি একটি অদ্ভুত পরিস্থিতি পেয়েছিলাম যেখানে কনসোল লগগুলি দ্বিগুণ করা হয়েছিল কিন্তু আমার ফাইল লগগুলি ছিল না। এক টন খননের পরে আমি এটি বের করে ফেললাম।

তৃতীয় পক্ষের প্যাকেজগুলি লগার নিবন্ধন করতে পারে দয়া করে সচেতন হন। এটি লক্ষ্য করার মতো জিনিস (এবং কিছু ক্ষেত্রে এটি প্রতিরোধ করা যায় না)। অনেক ক্ষেত্রে তৃতীয় পক্ষের কোডটি বিদ্যমান কোনও মূল রয়েছে কিনা তা পরীক্ষা করে দেখে কোডগুলি লগার হ্যান্ডলারগুলি ; এবং যদি তা না থাকে - তারা একটি নতুন কনসোল হ্যান্ডলার নিবন্ধভুক্ত করে।

এর আমার সমাধানটি হ'ল মূল স্তরে আমার কনসোল লগারটি নিবন্ধিত করা:

rootLogger = logging.getLogger()  # note no text passed in--that's how we grab the root logger
if not rootLogger.handlers:
        ch = logging.StreamHandler()
        ch.setLevel(logging.INFO)
        ch.setFormatter(logging.Formatter('%(process)s|%(levelname)s] %(message)s'))
        rootLogger.addHandler(ch)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.