2008-11-29 8 views
8

मेरे पास एक बहु फ़ाइल सी प्रोग्राम है। मैं चाहता हूं कि उपयोगकर्ता रन टाइम पर अलग-अलग डिबगिंग स्तर निर्दिष्ट कर सकें।बहु फ़ाइल सी प्रोग्राम, वैकल्पिक लॉगिंग को कैसे कार्यान्वित करने के लिए सबसे अच्छा है?

इसे लागू करने का सबसे अच्छा तरीका क्या है?

मैं डीबग (स्तर, "संदेश") प्रकार का फ़ंक्शन निर्यात करने और हर जगह उपयोग करने के बारे में सोच रहा था। कोई बेहतर/अन्य विचार?

+0

इस विषय पर अधिक विचारों के लिए [सी # # डीबग प्रिंटिंग के लिए मैक्रो 'परिभाषित करें] (https://stackoverflow.com/questions/1644868) देखें। चर्चा किसी भी बनाम डीबगिंग/लॉगिंग को संभालने के बारे में अधिक है, लेकिन यह यहां चर्चा की गई बहु-स्तरीय डीबगिंग/लॉगिंग विचारों के लिए कोर बनाती है। –

उत्तर

3

log4j, log4c का एक बहुत अच्छा सी पोर्ट है।

+0

क्या आप अपना खुद का कस्टम मैक्रो + प्रिंफ संयोजन होने की तुलना में log4c का उपयोग करने के लाभ/नुकसान को इंगित कर सकते हैं। –

3

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

आपके प्रश्न से संबोधित अन्य मुद्दा यह नहीं है कि रन टाइम पर डिबगिंग को कैसे सक्षम किया जाए। मैंने हमेशा कमांड लाइन विकल्पों का उपयोग किया है - आमतौर पर '-d' 'स्तर 3 पर मूल डीबगिंग' के लिए, और '-D nn' स्तर एनएन पर डिबगिंग के लिए। या, जटिल प्रणाली के साथ: '-D input=3,macros=5,rules=1'। एक ही अर्थशास्त्र के साथ एक पर्यावरण परिवर्तनीय होना मुश्किल नहीं होगा।

हैडर जो इन को लागू करता है से:

/* 
** Usage: TRACE((level, fmt, ...)) 
** "level" is the debugging level which must be operational for the output 
** to appear. "fmt" is a printf format string. "..." is whatever extra 
** arguments fmt requires (possibly nothing). 
** The non-debug macro means that the code is validated but never called. 
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike. 
*/ 
#ifdef DEBUG 
#define TRACE(x) db_print x 
#else 
#define TRACE(x) do { if (0) db_print x; } while (0) 
#endif /* DEBUG */ 

#ifndef lint 
#ifdef DEBUG 
/* This string can't be made extern - multiple definition in general */ 
static const char jlss_id_debug_enabled[] = "@(#)*** DEBUG ***"; 
#endif /* DEBUG */ 
#ifdef MAIN_PROGRAM 
const char jlss_id_debug_h[] = "@(#)$Id: debug.h,v 3.6 2008/02/11 06:46:37 jleffler Exp $"; 
#endif /* MAIN_PROGRAM */ 
#endif /* lint */ 

#include <stdio.h> 

extern int  db_getdebug(void); 
extern int  db_newindent(void); 
extern int  db_oldindent(void); 
extern int  db_setdebug(int level); 
extern int  db_setindent(int i); 
extern void  db_print(int level, const char *fmt,...); 
extern void  db_setfilename(const char *fn); 
extern void  db_setfileptr(FILE *fp); 
extern FILE *db_getfileptr(void); 

/* Semi-private function */ 
extern const char *db_indent(void); 

/**************************************\ 
** MULTIPLE DEBUGGING SUBSYSTEMS CODE ** 
\**************************************/ 

/* 
** Usage: MDTRACE((subsys, level, fmt, ...)) 
** "subsys" is the debugging system to which this statement belongs. 
** The significance of the subsystems is determined by the programmer, 
** except that the functions such as db_print refer to subsystem 0. 
** "level" is the debugging level which must be operational for the 
** output to appear. "fmt" is a printf format string. "..." is 
** whatever extra arguments fmt requires (possibly nothing). 
** The non-debug macro means that the code is validated but never called. 
*/ 
#ifdef DEBUG 
#define MDTRACE(x) db_mdprint x 
#else 
#define MDTRACE(x) do { if (0) db_mdprint x; } while (0) 
#endif /* DEBUG */ 

extern int  db_mdgetdebug(int subsys); 
extern int  db_mdparsearg(char *arg); 
extern int  db_mdsetdebug(int subsys, int level); 
extern void  db_mdprint(int subsys, int level, const char *fmt,...); 
extern void  db_mdsubsysnames(char const * const *names); 
+0

ध्यान दें कि [डी # # डीबग प्रिंटिंग के लिए परिभाषित करें] (https: // stackoverflow।कॉम/प्रश्न/1644868) मैक्रोज़ में सी 99 और '__VA_ARGS__' पर चर्चा करता है जिसका अर्थ है कि आपको यहां उपयोग किए गए डबल-ब्रांडेसिस नोटेशन का उपयोग करने की आवश्यकता नहीं है - यदि आपके सभी कंपाइलर्स इसका समर्थन करते हैं। –

3

Windows में (और मोटे तौर पर माइक्रोसॉफ्ट के पार), हम Event Tracing for Windows (ETW) बड़े पैमाने पर इस्तेमाल करते हैं। ईटीडब्ल्यू एक कुशल स्थैतिक लॉगिंग तंत्र है। एनटी कर्नेल और कई घटक बहुत अच्छी तरह से वाद्य यंत्र हैं। ईटीडब्ल्यू के बहुत सारे फायदे हैं:

  • किसी भी ईटीडब्ल्यू ईवेंट प्रदाता को रन टाइम पर गतिशील रूप से सक्षम/अक्षम किया जा सकता है - कोई रीबूटिंग या प्रक्रिया पुनरारंभ करने की आवश्यकता नहीं है। अधिकांश ईटीडब्ल्यू प्रदाताओं व्यक्तिगत घटनाओं, या घटनाओं के समूहों पर दानेदार नियंत्रण प्रदान करते हैं।
  • ईवेंट डेटा का स्वरूपण एक रन टाइम नहीं किया जाता है (जो बहुत महंगा हो सकता है)। यह तब किया जाता है जब ईवेंट ट्रेस पोस्ट संसाधित होता है।
  • ईटीडब्ल्यू Windows Event Log के लिए अंतर्निहित तंत्र है। यदि आप अपना उपकरण सही तरीके से बनाते हैं तो आपको मुफ्त में एप्लिकेशन स्तर लॉगिंग मिलती है।
  • आपका घटक घटनाओं, या तो व्यक्तिगत रूप से, स्तरों, या समूहों (या किसी भी संयोजन में) की घटनाओं को बहुत बारीक बनाने में सहायता कर सकता है। आप हमारे कोड में एकाधिक प्रदाताओं को भी डाल सकते हैं।
  • किसी भी प्रदाता (सबसे महत्वपूर्ण रूप से कर्नेल) से होने वाली घटनाओं को एक ही ट्रेस में विलय किया जा सकता है ताकि सभी घटनाओं को सहसंबंधित किया जा सके।
  • एक मर्ज किए गए ट्रेस को बॉक्स से कॉपी किया जा सकता है और पूरी तरह संसाधित किया जा सकता है - प्रतीकों के साथ।
  • NT का कर्नेल नमूना प्रोफ़ाइल बाधा एक ETW घटना उत्पन्न कर सकते हैं - यह एक बहुत ही हल्के वजन नमूना प्रोफ़ाइल इस्तेमाल किया जा सकता है कि किसी भी समय
  • विस्टा और विंडोज सर्वर 2008 पर, एक घटना प्रवेश करने ताला मुक्त और पूरी तरह से बहु है पैदावार -कोर जागरूक - प्रत्येक प्रोसेसर पर एक धागा स्वतंत्र रूप से घटनाओं को लॉग इन कर सकता है जिसमें उनके बीच कोई सिंक्रनाइज़ेशन आवश्यक नहीं है।
  • ईटीडब्ल्यू प्रभावी है जब लॉगिंग बंद है - यह सिर्फ एक साधारण बूलियन चेक (ईटीडब्ल्यू डॉस यह है, या आप इसे स्पष्ट रूप से कर सकते हैं)।नोट, यह को कर्नेल मोड संक्रमण की आवश्यकता है। यह सब प्रक्रिया में किया गया।

यह हमारे लिए बेहद मूल्यवान है, और यह आपके विंडोज कोड के लिए भी हो सकता है - ईटीडब्ल्यू उपयोगकर्ता मोड, ड्राइवर और अन्य कर्नेल घटकों सहित किसी भी घटक द्वारा प्रयोग योग्य है।

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

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

वे यहां कुंजी 'अच्छी तरह कार्यान्वित' शब्द है। इंस्ट्रुमेंटेशन सही जगह पर होना चाहिए। किसी और चीज की तरह, इसके लिए कुछ विचार और योजना की आवश्यकता होती है। यदि यह सहायक/रोचक स्थानों में नहीं है, तो यह आपको विकास, परीक्षण या तैनात परिदृश्य में समस्याओं को खोजने में मदद नहीं करेगा। जब आप चालू होते हैं - या यहां तक ​​कि बंद होने पर भी आपको बहुत अधिक उपकरण मिल सकता है!

ईटीडब्लू आधारित लॉगर्स और घटनाओं के साथ आपकी मदद करने के लिए विंडोज़ में कई टूल हैं। इनमें logman.exe और tracerpt.exe शामिल हैं। xperf tools भी हैं जो प्रदर्शन पर केंद्रित हैं, लेकिन किसी भी ईटीडब्ल्यू प्रदाता और डंप लॉग फ़ाइलों को भी नियंत्रित कर सकते हैं।

+0

हर कोई विंडोज पर काम नहीं कर रहा है ... लेकिन +1 क्योंकि मैं आज सिर्फ एक विंडोज़ समाधान की तलाश में था! –

10

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

प्रवेश हेडर के एक हल्के संस्करण का उपयोग मैं वहाँ जाता है:

#define LOG_FATAL (1) 
#define LOG_ERR  (2) 
#define LOG_WARN  (3) 
#define LOG_INFO  (4) 
#define LOG_DBG  (5) 

#define LOG(level, ...) do { \ 
          if (level <= debug_level) { \ 
           fprintf(dbgstream,"%s:%d:", __FILE__, __LINE__); \ 
           fprintf(dbgstream, __VA_ARGS__); \ 
           fprintf(dbgstream, "\n"); \ 
           fflush(dbgstream); \ 
          } \ 
         } while (0) 
extern FILE *dbgstream; 
extern int debug_level; 

इसलिए आप जहां भी मैं कुछ लॉग इन करने की जरूरत है, मैं तो बस लाइन की तरह

LOG(LOG_ERR, "I/O error %s occured while opening file %s", strerror(errno), filename);

जोड़ने कार्यक्रम initialisation के दौरान आप की जरूरत है dbgstream के लिए मान निर्दिष्ट करें (आमतौर पर stderr पर डिफ़ॉल्ट) और debug_level

के बजाय वास्तविक परियोजनाओं बुला fprintf कई बार मैं सिर्फ LOG मैक्रो से मेरी फ़ंक्शन को कॉल करें और तर्क के रूप में __FILE__, __LINE__ और __VA_ARGS_ पारित के लिए - कि समारोह भी प्रिंट तिथि, समय और पीआईडी ​​लॉग पंक्ति की, और fflush() नहीं करते हर बार - केवल जब बफरिंग काउंटर प्रीसेट मान से अधिक हो जाता है - यह महत्वपूर्ण रूप से लॉगिंग प्रदर्शन को बढ़ाता है।

लेकिन कृपया ध्यान रखें कि कुछ कंपाइलर वैरैडिक मैक्रोज़ का समर्थन नहीं कर सकते क्योंकि इसे केवल सी 99 में पेश किया गया था।

संबंधित मुद्दे