अपवाद क्यों है ।printStackTrace () बुरा अभ्यास माना जाता है?


126

वहाँ एक है बहुत की सामग्री बाहर वहाँ जो पता चलता है कि मुद्रण एक अपवाद के स्टैक ट्रेस बुरा व्यवहार है।

चेकस्टाइल में RegexpSingleline चेक से जैसे:

इस जाँच का उपयोग किया जा सकता है [...] सामान्य बुरे अभ्यास को खोजने के लिए जैसे कि ex.printStacktrace ()

हालांकि, मैं कहीं भी खोजने के लिए संघर्ष कर रहा हूं जो एक वैध कारण देता है कि निश्चित रूप से स्टैक ट्रेस को ट्रैक करने में बहुत उपयोगी है जो अपवाद का कारण बना। जिन चीजों से मैं अवगत हूं:

  1. उपयोगकर्ताओं को समाप्त करने के लिए स्टैक ट्रेस कभी भी दिखाई नहीं देना चाहिए (उपयोगकर्ता अनुभव और सुरक्षा उद्देश्यों के लिए)

  2. स्टैक ट्रेस बनाना एक अपेक्षाकृत महंगी प्रक्रिया है (हालांकि अधिकांश 'असाधारण' परिस्थितियों में एक मुद्दा होने की संभावना नहीं है)

  3. कई लॉगिंग फ्रेमवर्क आपके लिए स्टैक ट्रेस को प्रिंट करेंगे (हमारा नहीं और नहीं, हम इसे आसानी से नहीं बदल सकते)

  4. स्टैक ट्रेस को प्रिंट करना त्रुटि हैंडलिंग का गठन नहीं करता है। इसे अन्य सूचना लॉगिंग और अपवाद हैंडलिंग के साथ जोड़ा जाना चाहिए।

आपके कोड में स्टैक ट्रेस छापने से बचने के और क्या कारण हैं?


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

4
क्या आपको वास्तव में अन्य कारणों की आवश्यकता है? मुझे लगता है कि आपने अपने प्रश्न का उचित उत्तर दिया है। हालांकि, स्टैकट्रेस को असाधारण अपवादों में मुद्रित किया जाना चाहिए। :)
नील

जवाबों:


125

Throwable.printStackTrace()System.errPrintStream में स्टैक ट्रेस लिखता है । System.errJVM प्रक्रिया की धारा और अंतर्निहित मानक "त्रुटि" आउटपुट स्ट्रीम द्वारा पुनर्निर्देशित किया जा सकता है

  • आह्वान System.setErr()जो गंतव्य की ओर इशारा करता है बदल जाता है System.err
  • या प्रक्रिया की त्रुटि आउटपुट स्ट्रीम को पुनर्निर्देशित करके। त्रुटि आउटपुट स्ट्रीम को फ़ाइल / डिवाइस पर पुनर्निर्देशित किया जा सकता है
    • जिनकी सामग्री कर्मियों द्वारा नजरअंदाज की जा सकती है,
    • फ़ाइल / डिवाइस लॉग रोटेशन के लिए सक्षम नहीं हो सकता है, यह उल्लेख करते हुए कि फ़ाइल / डिवाइस की मौजूदा सामग्री को संग्रहीत करने से पहले, खुली फ़ाइल / डिवाइस हैंडल को बंद करने के लिए एक प्रक्रिया पुनरारंभ की आवश्यकता होती है।
    • या फ़ाइल / डिवाइस वास्तव में लिखे गए सभी डेटा को छोड़ देता है, जैसा कि है /dev/null

ऊपर से संदर्भित, इनवोकिंग Throwable.printStackTrace()वैध (अच्छा / महान नहीं) अपवाद हैंडलिंग व्यवहार है, केवल

  • यदि आपको System.errआवेदन के पूरे जीवनकाल के दौरान पुन: असाइन नहीं किया जाता है ,
  • और अगर आपको एप्लिकेशन के चलने के दौरान लॉग रोटेशन की आवश्यकता नहीं है,
  • और यदि आवेदन के स्वीकार किए गए / डिज़ाइन किए गए लॉगिंग अभ्यास को System.err(और JVM के मानक त्रुटि आउटपुट स्ट्रीम) को लिखना है ।

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

अंत में, किसी को यह याद रखना चाहिए कि Throwable.printStackTrace()निश्चित रूप से अन्य सामग्री के साथ लिखे गए System.err(और संभवत: तब भी System.outजब दोनों को एक ही फ़ाइल / डिवाइस पर पुनर्निर्देशित किया गया हो) से आउटपुट प्राप्त होगा । यह एक झुंझलाहट है (एकल-थ्रेडेड ऐप्स के लिए) जिसे किसी को भी निपटना चाहिए, अपवाद के आसपास के डेटा के लिए ऐसी घटना में आसानी से पार्स नहीं किया जा सकता है। इससे भी बदतर, यह बहुत संभावना है कि एक बहु-थ्रेडेड अनुप्रयोग बहुत भ्रमित लॉग का उत्पादन करेगा Throwable.printStackTrace() जैसा कि थ्रेड-सुरक्षित नहीं है

System.errजब एक Throwable.printStackTrace()ही समय में कई थ्रेड प्रारंभ होते हैं , तो स्टैक ट्रेस के लेखन को सिंक्रनाइज़ करने के लिए कोई सिंक्रनाइज़ेशन तंत्र नहीं है । इसे हल करने के लिए वास्तव में आपके कोड के साथ जुड़े मॉनिटर पर सिंक्रनाइज़ करने की आवश्यकता होती है System.err(और System.outयदि गंतव्य फ़ाइल / डिवाइस समान है), और लॉग फ़ाइल की शुद्धता के लिए भुगतान करने के लिए यह भारी कीमत है। एक उदाहरण लेने के लिए, ConsoleHandlerऔर StreamHandlerकक्षाएं लॉग इन रिकॉर्ड्स को सांत्वना देने के लिए जिम्मेदार हैं, जो प्रदान की गई लॉगिंग सुविधा में है java.util.logging; लॉग रिकॉर्ड्स को प्रकाशित करने का वास्तविक संचालन सिंक्रनाइज़ किया जाता है - लॉग रिकॉर्ड को प्रकाशित करने का प्रयास करने वाले प्रत्येक थ्रेड के साथ जुड़े मॉनिटर पर लॉक का अधिग्रहण भी करना होगाStreamHandlerउदाहरण। यदि आप System.out/ का उपयोग करते हुए गैर-अंतर्क्रिया लॉग रिकॉर्ड होने की समान गारंटी चाहते हैं System.err, तो आपको यह सुनिश्चित करना होगा - संदेश इन धाराओं को क्रमबद्ध तरीके से प्रकाशित किए जाते हैं।

उपरोक्त सभी को ध्यान में रखते हुए, और बहुत ही सीमित परिदृश्य जिसमें Throwable.printStackTrace()वास्तव में उपयोगी है, अक्सर यह पता चलता है कि इसे लागू करना एक बुरा अभ्यास है।


पिछले पैराग्राफ में से एक में तर्क का विस्तार करते हुए, यह Throwable.printStackTraceकंसोल के लिए लिखने वाले लकड़हारे के साथ संयोजन में उपयोग करने के लिए एक खराब विकल्प भी है । यह उस भाग में है, इस कारण से कि लकड़हारा एक अलग मॉनीटर पर सिंक्रोनाइज़ करेगा, जबकि आपका आवेदन (संभवतः, अगर आप इंटरलेयर्ड लॉग रिकॉर्ड नहीं चाहते हैं) एक अलग मॉनीटर पर सिंक्रोनाइज़ करेंगे। जब आप दो अलग-अलग लॉगर का उपयोग करते हैं तो तर्क अच्छा होता है जो आपके आवेदन में एक ही गंतव्य पर लिखते हैं।


शानदार जवाब, धन्यवाद। हालाँकि, जब मैं मानता हूं कि अधिकांश समय इसका शोर या आवश्यक नहीं है, ऐसे समय होते हैं जब इसकी बिल्कुल आलोचना होती है।
क्रिस नाइट

1
@Chris हाँ, कई बार जब आप लेकिन उपयोग नहीं कर सकते हैं System.out.printlnऔर Throwable.printStackTraceऔर निश्चित रूप से, डेवलपर निर्णय की आवश्यकता है। मैं थोड़ा चिंतित था कि धागा-सुरक्षा के बारे में हिस्सा छूट गया था। यदि आप अधिकांश लकड़हारे कार्यान्वयन को देखते हैं, तो आप देखेंगे कि वे उस हिस्से को सिंक्रनाइज़ करते हैं जहां लॉग रिकॉर्ड लिखे जाते हैं (यहां तक ​​कि कंसोल), हालांकि वे मॉनिटर पर System.errया अधिग्रहण नहीं करते हैं System.out
विनीत रेनॉल्ड्स

2
क्या मैं यह नोट करना गलत होगा कि इन कारणों में से कोई भी प्रिंटस्टैक्सट्रेस पर लागू नहीं होता है जो एक पैरामीटर के रूप में प्रिंटस्ट्रीम या प्रिंटवेयर ऑब्जेक्ट को ओवरराइड करता है?
ESRogs

1
@ गीक, बाद वाला मूल्य के साथ प्रक्रिया फ़ाइल विवरणक है। पूर्व केवल एक अमूर्त है।
विनीत रेनॉल्ड्स

1
PrintStackTrace के JDK स्रोत कोड में, यह System.err PrintStream को लॉक करने के लिए सिंक्रनाइज़ का उपयोग करता है, इसलिए यह एक थ्रेड-सुरक्षित तरीका होना चाहिए।
आईओएसगो

33

आप यहां कई मुद्दों को छू रहे हैं:

1) एक स्टैक ट्रेस कभी नहीं होना चाहिए अंत उपयोगकर्ताओं के लिए (उपयोगकर्ता अनुभव और सुरक्षा उद्देश्यों के लिए)

हां, एंड-यूजर्स की समस्याओं के निदान के लिए यह सुलभ होना चाहिए, लेकिन एंड-यूजर को उन्हें दो कारणों से नहीं देखना चाहिए:

  • वे बहुत अस्पष्ट और अपठनीय हैं, एप्लिकेशन बहुत उपयोगकर्ता-अप्राकृतिक लगेगा।
  • एंड-यूज़र के लिए एक स्टैक ट्रेस दिखाना संभावित सुरक्षा जोखिम पेश कर सकता है। अगर मैं गलत हूं तो मुझे सुधारें, PHP वास्तव में स्टैक ट्रेस - शानदार, लेकिन बहुत खतरनाक में फ़ंक्शन मापदंडों को प्रिंट करता है - यदि आप डेटाबेस से कनेक्ट करते समय अपवाद प्राप्त करेंगे, तो स्टैकट्रेस में क्या होने की संभावना है?

2) स्टैक ट्रेस बनाना एक अपेक्षाकृत महंगी प्रक्रिया है (हालांकि अधिकांश 'अपवाद परिस्थितियों में एक मुद्दा होने की संभावना नहीं है)

स्टैक ट्रेस बनाना तब होता है जब अपवाद बनाया जा रहा है / फेंका जा रहा है (यही कारण है कि अपवाद फेंकना एक मूल्य के साथ आता है), मुद्रण इतना महंगा नहीं है। वास्तव में आप Throwable#fillInStackTrace()अपने कस्टम अपवाद को ओवरराइड कर सकते हैं प्रभावी रूप से एक साधारण GOTO स्टेटमेंट के रूप में लगभग एक अपवाद को फेंकना।

3) कई लॉगिंग फ्रेमवर्क आपके लिए स्टैक ट्रेस प्रिंट करेंगे (हमारा नहीं और नहीं, हम इसे आसानी से नहीं बदल सकते)

बहुत अच्छी बात है। मुख्य मुद्दा यहाँ है: अगर ढांचे आप के लिए अपवाद लॉग करता है, कुछ भी नहीं है (लेकिन यकीन है कि यह होता है बनाने!) आप अपवाद प्रवेश करना चाहते हैं, तो अपने आप को, की तरह उपयोग लॉगिंग ढांचे Logback या Log4J , उन्हें कच्चे कंसोल पर नहीं डाल करने के लिए क्योंकि इसे नियंत्रित करना बहुत कठिन है।

लॉगिंग ढांचे के साथ आप आसानी से स्टैक के निशान को फ़ाइल, कंसोल या यहां तक ​​कि एक निर्दिष्ट ई-मेल पते पर भेज सकते हैं। हार्डकोड के साथ printStackTrace()आपको रहना होगा sysout

4) स्टैक ट्रेस को प्रिंट करना त्रुटि से निपटने का गठन नहीं करता है। इसे अन्य सूचना लॉगिंग और अपवाद हैंडलिंग के साथ जोड़ा जाना चाहिए।

फिर से: SQLExceptionसही तरीके से लॉग करें (पूर्ण स्टैक ट्रेस के साथ, लॉगिंग फ्रेमवर्क का उपयोग करके) और अच्छा दिखाएं: " क्षमा करें, हम वर्तमान में आपके अनुरोध को संसाधित करने में सक्षम नहीं हैं " संदेश। क्या आपको वास्तव में लगता है कि उपयोगकर्ता कारणों में दिलचस्पी रखते हैं? क्या आपने StackOverflow error screen देखी है? यह बहुत हास्यप्रद है, लेकिन कोई विवरण नहीं बताता है । हालांकि यह उपयोगकर्ता को सुनिश्चित करता है कि समस्या की जांच की जाएगी।

लेकिन वह आपको तुरंत कॉल करेगा और आपको समस्या का निदान करने में सक्षम होने की आवश्यकता है। इसलिए आपको दोनों की आवश्यकता है: उचित अपवाद लॉगिंग और उपयोगकर्ता के अनुकूल संदेश।


चीजों को लपेटने के लिए: हमेशा अपवाद लॉग करें (अधिमानतः लॉगिंग फ्रेमवर्क का उपयोग करके ), लेकिन उन्हें अंतिम-उपयोगकर्ता के लिए उजागर न करें। ध्यान से सोचें और अपने GUI में त्रुटि-संदेशों के बारे में, स्टैक के निशान केवल विकास मोड में दिखाएं।


आपका जवाब वही है जो मुझे मिला है।
Blasanka

"सबसे 'अपवाद' परिस्थितियाँ"। अच्छा।
awsmm

19

पहली बात प्रिंटस्टैकट्रेस () आपके राज्य के अनुसार महंगी नहीं है, क्योंकि स्टैक ट्रेस भर जाता है जब अपवाद खुद बनाया जाता है।

विचार कुछ भी पारित करने के लिए है जो एक लकड़हारा ढांचे के माध्यम से लॉग में जाता है, ताकि लॉगिंग को नियंत्रित किया जा सके। इसलिए प्रिंटस्टैकट्रेस का उपयोग करने के बजाय, जैसे कुछ का उपयोग करेंLogger.log(msg, exception);


11

अपने आप में अपवाद के स्टैक ट्रेस को प्रिंट करना खराब अभ्यास नहीं है, लेकिन केवल प्रिंट करना अपवाद होने पर स्टेस ट्रेस को प्रिंट करना संभवतः यहाँ समस्या है - अक्सर बार, स्टैक ट्रेस को प्रिंट करना ही पर्याप्त नहीं होता है।

इसके अलावा, यह संदेह करने की प्रवृत्ति है कि यदि एक catchब्लॉक में प्रदर्शन किया जा रहा है तो उचित अपवाद हैंडलिंग नहीं किया जा रहा है e.printStackTrace। अनुचित हैंडलिंग का सबसे अच्छा मतलब हो सकता है कि किसी समस्या को नजरअंदाज किया जा रहा है, और सबसे खराब कार्यक्रम जिसे अपरिभाषित या अप्रत्याशित स्थिति में निष्पादित करना जारी है।

उदाहरण

आइए निम्नलिखित उदाहरण पर विचार करें:

try {
  initializeState();

} catch (TheSkyIsFallingEndOfTheWorldException e) {
  e.printStackTrace();
}

continueProcessingAssumingThatTheStateIsCorrect();

यहां, हम कुछ प्रोसेसिंग शुरू करने से पहले कुछ प्रोसेसिंग करना चाहते हैं, जिसके लिए जरूरी है कि इनिशियलाइजेशन हुआ हो।

उपरोक्त कोड में, प्रोग्राम को आगे बढ़ने से रोकने के लिए अपवाद को पकड़ा जाना चाहिए और ठीक से हैंडल किया जाना चाहिए continueProcessingAssumingThatTheStateIsCorrect जिससे हम मान सकते हैं कि समस्याएँ पैदा होंगी।

कई उदाहरणों में, e.printStackTrace()एक संकेत है कि कुछ अपवाद को निगल लिया जा रहा है और प्रसंस्करण को आगे बढ़ने की अनुमति दी जाती है जैसे कि कोई समस्या नहीं हुई।

यह एक समस्या क्यों बन गई है?

संभवतः सबसे बड़ा कारण यह है कि खराब अपवाद हैंडलिंग अधिक प्रचलित हो गई है, इस कारण से कि ग्रहण जैसे आईडीई स्वत:-उत्पन्न कोड कैसे होंगे e.printStackTraceजो अपवाद हैंडलिंग के लिए प्रदर्शन करेंगे :

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

(ऊपर एक वास्तविक try-catchऑटो-एक्लिप्स द्वारा जनरेट किया गया है ताकि एक InterruptedExceptionफेंका गया हैंडल हो सके Thread.sleep।)

अधिकांश अनुप्रयोगों के लिए, स्टैक ट्रेस को मानक त्रुटि पर प्रिंट करना संभवतः पर्याप्त नहीं होगा। कई मामलों में अनुचित अपवाद से निपटने से एक ऐसी स्थिति पैदा हो सकती है जो अप्रत्याशित है और अप्रत्याशित और अपरिभाषित व्यवहार के लिए अग्रणी हो सकती है।


1
यह। काफी अक्सर अपवाद को बिल्कुल नहीं पकड़ रहा है, कम से कम विधि स्तर पर, पकड़ने से बेहतर है, स्टैक ट्रेस का प्रिंट करना और जारी रखना जैसे कि कोई समस्या नहीं हुई।
eis

10

मुझे लगता है कि आपके कारणों की सूची काफी व्यापक है।

एक विशेष रूप से बुरा उदाहरण जो मैंने एक से अधिक बार सामना किया है वह इस प्रकार है:

    try {
      // do stuff
    } catch (Exception e) {
        e.printStackTrace(); // and swallow the exception
    }

उपरोक्त कोड के साथ समस्या यह है कि हैंडलिंग में पूरी तरह से printStackTraceकॉल शामिल है: अपवाद वास्तव में ठीक से संभाला नहीं गया है और न ही इसे भागने की अनुमति है।

दूसरी ओर, एक नियम के रूप में मैं हमेशा स्टैक ट्रेस लॉग करता हूं जब भी मेरे कोड में कोई अप्रत्याशित अपवाद नहीं होता है। वर्षों से इस नीति ने मुझे बहुत डिबगिंग समय की बचत की है।

अंत में, एक हल्के नोट पर, भगवान की सही अपवाद


"अपवाद से बचने की अनुमति नहीं है" - क्या आपका मतलब यह है कि अपवाद स्टैक तक प्रचारित है? प्रिंटस्टैकट्रेस () से अलग कैसे लॉग इन होता है?
मास्टरजॉ डे

5

printStackTrace()एक कंसोल को प्रिंट करता है। उत्पादन सेटिंग्स में, कोई भी उस पर कभी नहीं देख रहा है। सूरज सही है, यह जानकारी एक लकड़हारे को देनी चाहिए।


मान्य बिंदु, हालांकि हम ध्यान से हमारे उत्पादन कंसोल आउटपुट को देखते हैं।
क्रिस नाइट

क्या आप समझा सकते हैं कि ध्यान से देखने से आपका क्या मतलब है? कैसे और कौन? किस आवृत्ति पर?
ओह चिन बून

ध्यान से देखने पर मुझे आवश्यकता पड़ने पर कहना चाहिए था। इसकी रोलिंग लॉग फ़ाइल जो कॉल के कई पोर्ट्स में से एक है अगर हमारे ऐप में कुछ गलत होता है।
क्रिस नाइट

3

सर्वर अनुप्रयोगों में स्टैकट्रेस आपकी stdout / Stderr फ़ाइल को उड़ा देता है। यह बड़ा और बड़ा हो सकता है और बेकार डेटा से भरा होता है क्योंकि आमतौर पर आपके पास कोई संदर्भ नहीं होता है और न ही टाइमस्टैम्प और इतने पर।

उदाहरण के लिए, जब कंटेनर के रूप में टॉमकट का उपयोग कर कैटलिना.आउट करें


अच्छी बात। प्रिंटस्टैकट्रेस () का अपमानजनक उपयोग हमारी लॉगिंग फ़ाइलों को उड़ा सकता है, या बहुत कम से कम इसे बेकार सूचनाओं के साथ भर सकता है
क्रिस नाइट

@ChrisKnight - क्या आप कृपया एक लेख का सुझाव दे सकते हैं जो बताता है कि लॉगिंग फ़ाइलों को बेकार जानकारी के साथ कैसे उड़ाया जा सकता है? धन्यवाद।
मास्टरजो

3

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

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


0

जैसा कि कुछ लोगों ने पहले ही उल्लेख किया है कि समस्या आपको ब्लॉक e.printStackTrace()में कॉल करने के मामले में निगलने के अपवाद के साथ है catch। यह थ्रेड निष्पादन बंद नहीं करेगा और सामान्य स्थिति में प्रयास ब्लॉक के बाद जारी रहेगा।

इसके बजाय आपको या तो अपवाद से उबरने की कोशिश करनी चाहिए (यदि यह ठीक हो जाए), या फेंकने के लिए RuntimeException, या कॉल करने वाले को अपवाद देने के लिए चुप दुर्घटनाओं से बचने के लिए (उदाहरण के लिए, अनुचित लॉगर कॉन्फ़िगरेशन के कारण)।


0

@Vineet रेनॉल्ड्स द्वारा संदर्भित पेचीदा आउटपुट स्ट्रीम समस्या से बचने के लिए

आप इसे प्रिंटआउट पर प्रिंट कर सकते हैं: e.printStackTrace(System.out);

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