पायथन लॉगिंग के साथ दो बार दिखने वाले लॉग संदेश


100

मैं पायथन लॉगिंग का उपयोग कर रहा हूं, और किसी कारण से, मेरे सभी संदेश दो बार दिखाई दे रहे हैं।

मेरे पास लॉगिंग कॉन्फ़िगर करने के लिए एक मॉड्यूल है:

# 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()

और फिर कहते हैं, buy_ham मॉड्यूल, मैं कॉल करूंगा:

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

और किसी कारण से, सभी संदेश दो बार दिखाई दे रहे हैं। मैंने एक स्ट्रीम हैंडलर से टिप्पणी की, फिर भी वही बात। एक अजीब सा, यकीन नहीं कि ऐसा क्यों हो रहा है ... योग्य। मैं कुछ स्पष्ट याद किया है मान लिया।

चीयर्स, विक्टर


1
क्या आपको यकीन configure_logging()है कि दो बार नहीं कहा जाता है (जैसे निर्माणकर्ता से भी)? क्या बॉय () केवल एक ही उदाहरण है?
जसेक कोनजेनी

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

1
हाँ, आप सही थे - मुझे मूर्ख। मैंने इसे init कहा , साथ ही स्पष्ट रूप से कहीं और। जबरदस्त हंसी। धन्यवाद =)।
विजोरूई

धन्यवाद। आपके समाधान ने आज मुझे बचा लिया।
फॉरएवर लर्नर

1
मेरे मामले में, वे 6 बार दिखाई दे रहे थे। मुझे संदेह था कि क्योंकि मैंने 6 ऊप कक्षाओं में एक ही प्रकार के
लकड़हारे की घोषणा की है

5
मैं यहां अपना अनुभव साझा करना चाहता हूं: एक फ्लास्क एप्लिकेशन के लिए जो मैंने विकसित किया था, लॉग संदेश अधिक से अधिक दो बार दिखाई दे रहे थे। मैं कहूंगा कि वे लॉग फ़ाइल में वृद्धि कर रहे थे, इस तथ्य के कारण कि, जब एप्लिकेशन और मॉड्यूल लोड किए गए थे, तो loggerउपयोग किया गया चर, मेरी कक्षाओं में से एक से तत्काल नहीं था, लेकिन loggerPython3 कैश पर मौजूद चर , और हैंडलर को AppScheduler द्वारा हर 60 सेकंड में जोड़ा गया था जिसे मैंने कॉन्फ़िगर किया था। तो, इस if not logger.handlersप्रकार की घटना से बचने के लिए यह एक बहुत ही स्मार्ट तरीका है। समाधान के लिए धन्यवाद, कॉमरेड :)!
ivanleoncz

2
मैं अपने फ्लास्क ऐप में इस समस्या को देख रहा हूं। इस समाधान ने मुख्य फ्लास्क ऐप में उत्पन्न लॉग संदेशों के लिए समस्या को ठीक कर दिया, लेकिन मेरा ऐप एक लाइब्रेरी मॉड्यूल में काम करता है, और उस लाइब्रेरी के वे संदेश अभी भी कई बार लॉग इन हो रहे हैं। मुझे नहीं पता कि इसे कैसे ठीक किया जाए।
कैस

26

आप इस समस्या को देख रहे हैं और आप हैंडलर दो बार नहीं जोड़ने रहे हैं तो abarnert का जवाब देखने के लिए यहाँ

से डॉक्स :

नोट: यदि आप एक हैंडलर को एक लकड़हारे और उसके एक या अधिक पूर्वजों से जोड़ते हैं, तो यह एक ही रिकॉर्ड को कई बार फेंक सकता है। सामान्य तौर पर, आपको एक हैंडलर को एक से अधिक लकड़हारे के साथ संलग्न करने की आवश्यकता नहीं होनी चाहिए - यदि आप इसे केवल उस उपयुक्त लकड़हारे से जोड़ते हैं जो लकड़हारे के पदानुक्रम में सबसे अधिक है, तो यह सभी वंशज लकड़हारा द्वारा लॉग की गई सभी घटनाओं को देखेगा, बशर्ते कि उनका प्रचार हो सेटिंग को True पर सेट किया गया है। एक सामान्य परिदृश्य केवल हैंडलर्स को रूट लकड़हारे से जोड़ना है, और प्रचार को बाकी का ध्यान रखना है।

इसलिए, यदि आप "परीक्षण" पर एक कस्टम हैंडलर चाहते हैं, और आप नहीं चाहते हैं कि इसके संदेश रूट हैंडलर पर भी जा रहे हैं, तो इसका उत्तर सरल है: इसका propagateझंडा बंद करें :

logger.propagate = False

1
यही सबसे अच्छा जवाब है। यह पोस्टर के उद्देश्य (कोडिंग में तार्किक त्रुटि) के लिए फिट नहीं था, लेकिन ज्यादातर बार, यह मामला होना चाहिए।
आर्टेम

वाहवाही। यह डुप्लिकेट (सबसे सामान्य मामलों के लिए) का वास्तविक कारण है।
श्री दुहरत

मेरे कोड के साथ भी यही समस्या थी। बहुत बहुत धन्यवाद।
कठोर

सबसे अच्छा जवाब कभी। धन्यवाद!
फिवोस टीएस

यह क्यों डिफ़ॉल्ट नहीं हो सकता। मैं अपने सभी लॉगर "रूट" के तहत बनाता हूं। + निर्देशिका संरचना, इसलिए मैं
मोर्टेनबी

8

हर बार जब आप बाहर से कॉल करते हैं तो हैंडलर जोड़ा जाता है। अपना काम पूरा करने के बाद हैंडलर हटाने की कोशिश करें:

self.logger.removeHandler(ch)

1
मैंने logger.handlers.pop() 2.7 python में उपयोग किया ,
radtek

7

मैं एक अजगर नौसिखिया हूँ, लेकिन यह मेरे लिए काम कर रहा था (पायथन 2.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()। मेरे लिए टेस्ट फ्रेमवर्क में ऐसा हो रहा था, जहां मैं उस आदेश को नियंत्रित नहीं कर सकता था जो परीक्षण के मामलों में निकाल दिया गया था। मेरा प्रारंभ कोड दूसरा स्थापित कर रहा था। डिफ़ॉल्ट लॉगिंग का उपयोग करता है। BASIC_FORMAT जो मैं नहीं चाहता था।


मुझे लगता है कि यह मेरे लिए क्या हो रहा है। आप कंसोल लॉगर्स के स्वचालित निर्माण को कैसे रोक सकते हैं?
रॉबर्ट

@Robert यह सुनिश्चित करने के बारे में है कि आप पहले लॉगिंग कॉल से पहले अपने इच्छित लकड़हारे के साथ आरंभीकृत हैं। परीक्षण ढांचे इसे अस्पष्ट कर सकते हैं, लेकिन इसे करने का एक तरीका होना चाहिए। इसके अलावा, यदि आप मल्टीप्रोसेस कर रहे हैं तो आपको प्रत्येक प्रक्रिया के साथ ऐसा ही करना होगा।
13

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स्वचालित रूप से एक नया हैंडलर बनाता है, और फिर मैंने स्पष्ट रूप से एक और हैंडलर जोड़ा। समस्या तब दूर हुई जब मैंने आकस्मिक पहली 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)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.