2008-11-29 29 views
8

Tôi có một chương trình đa tệp C. Tôi muốn người dùng có thể chỉ định các mức gỡ lỗi khác nhau trong thời gian chạy.chương trình đa tệp C, cách tốt nhất để triển khai ghi nhật ký tùy chọn?

Cách tốt nhất để thực hiện điều này là gì?

Tôi đã nghĩ đến việc có chức năng loại gỡ lỗi (cấp, "tin nhắn") được xuất và sử dụng ở mọi nơi. Bất kỳ ý tưởng tốt hơn/khác?

+0

Xem thêm [C '# define' macro để gỡ lỗi in] (https://stackoverflow.com/questions/1644868) để biết thêm ý tưởng về chủ đề này. Các cuộc thảo luận có nhiều hơn về xử lý bất kỳ so với không gỡ lỗi/đăng nhập, nhưng điều đó tạo thành một lõi cho các ý tưởng gỡ lỗi/đăng nhập đa cấp được thảo luận ở đây. –

Trả lời

3

Có một cổng C rất tốt của log4j, log4c.

+0

Bạn có thể chỉ ra lợi thế/bất lợi của việc sử dụng log4c so với việc kết hợp macro + prinf tùy chỉnh của riêng bạn. –

3

Tôi có hai hệ thống gỡ lỗi liên quan chặt chẽ mà tôi sử dụng (được khai báo trong một tiêu đề cho nho khô kích động). Trình đơn giản hơn có một mức gỡ rối đơn và các hàm giống như printf có mức gỡ lỗi và chỉ phát ra đầu ra nếu mức gỡ lỗi được đặt đủ cao. Trình phức tạp hơn cung cấp cho các hệ thống con gỡ lỗi khác nhau, mỗi hệ điều hành giống như một hệ thống đơn giản hơn (ví dụ, tôi có thể gỡ lỗi macro ở mức khác với gỡ lỗi đầu vào hoặc gỡ lỗi quy tắc hoặc ...).

Sự cố khác không được giải quyết bởi câu hỏi của bạn là cách bật gỡ lỗi khi chạy. Tôi đã luôn sử dụng các tùy chọn dòng lệnh - thường là '-d' cho 'gỡ lỗi cơ bản ở cấp 3' và '-D nn' để gỡ lỗi ở cấp nn. Hoặc, với hệ thống phức tạp: '-D input=3,macros=5,rules=1'. Sẽ không khó để có một biến môi trường với cùng ngữ nghĩa.

Từ tiêu đề mà thực hiện sau đây:

/* 
** 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

Lưu ý rằng [C '# define' để gỡ lỗi] (https: // stackoverflow).com/questions/1644868) thảo luận về C99 và '__VA_ARGS__' trong các macro có nghĩa là bạn không phải sử dụng ký hiệu ngoặc kép được sử dụng ở đây - nếu tất cả các trình biên dịch của bạn hỗ trợ nó. –

3

Trong Windows (và rộng khắp của Microsoft), chúng tôi sử dụng Event Tracing for Windows (ETW) rộng rãi. ETW là một cơ chế ghi nhật ký tĩnh hiệu quả. Hạt nhân NT và nhiều thành phần được thiết kế rất tốt. ETW có nhiều lợi thế:

  • Bất kỳ nhà cung cấp sự kiện ETW nào có thể được bật/tắt động trong thời gian chạy - không khởi động lại hoặc xử lý yêu cầu khởi động lại. Hầu hết các nhà cung cấp ETW cung cấp quyền kiểm soát chi tiết cho các sự kiện riêng lẻ hoặc các nhóm sự kiện.
  • Định dạng dữ liệu sự kiện không được thực hiện một thời gian chạy (có thể là rất đắt tiền). Nó được thực hiện khi dấu vết sự kiện được xử lý.
  • ETW là cơ chế cơ bản cho Windows Event Log. Nếu bạn xây dựng thiết bị của mình một cách chính xác, bạn sẽ nhận được đăng nhập cấp ứng dụng miễn phí.
  • Thành phần của bạn có thể hỗ trợ việc kích hoạt các sự kiện rất chi tiết, riêng lẻ, theo cấp độ hoặc theo nhóm (hoặc trong bất kỳ kết hợp nào). Bạn cũng có thể đặt nhiều nhà cung cấp trong mã của chúng tôi.
  • Sự kiện từ bất kỳ nhà cung cấp nào (quan trọng nhất là hạt nhân) có thể được hợp nhất thành một dấu vết để tất cả các sự kiện có thể được tương quan.
  • Một dấu vết hợp nhất có thể được sao chép ra khỏi hộp và xử lý hoàn toàn - với các biểu tượng.
  • Các NT hồ sơ mẫu hạt nhân ngắt có thể tạo ra một sự kiện ETW - điều này mang lại một khối lượng hồ sơ mẫu rất nhẹ, có thể được sử dụng bất cứ lúc nào
  • Trên Vista và Windows Server 2008, đăng nhập một sự kiện là khóa miễn phí và đầy đủ đa -core nhận thức - một thread trên mỗi bộ xử lý độc lập có thể đăng nhập các sự kiện mà không cần đồng bộ hóa giữa chúng.
  • ETW hiệu quả khi đăng xuất cũng bị tắt - chỉ cần kiểm tra Boolean đơn giản (ETW dos this hoặc bạn có thể làm rõ ràng).Lưu ý, điều này không không yêu cầu chuyển đổi chế độ hạt nhân. Tất cả được thực hiện trong quá trình.

Điều này cực kỳ có giá trị đối với chúng tôi và cũng có thể dùng cho mã Windows của bạn - ETW có thể sử dụng được bởi bất kỳ thành phần nào - bao gồm chế độ người dùng, trình điều khiển và các thành phần hạt nhân khác.

Nhiều người thích xem kết quả đăng nhập của họ khi chương trình hoặc thành phần của họ chạy. Điều này là dễ dàng để làm với ETW. Chúng tôi thường làm là viết một người tiêu dùng ETW trực tuyến. Thay vì đặt printfs trong mã, tôi chỉ đặt các sự kiện ETW ở những nơi thú vị. Khi thành phần của tôi đang chạy, tôi chỉ có thể chạy trình xem ETW của mình bất kỳ lúc nào - người xem nhận các sự kiện và hiển thị chúng, đếm chúng hoặc thực hiện những điều thú vị khác với chúng.

Hầu hết mã đều có thể hưởng lợi từ việc ghi nhật ký được triển khai tốt. Việc triển khai ghi nhật ký thời gian chạy tĩnh được thực hiện tốt có thể giúp tìm ra nhiều vấn đề ngay lập tức - không có nó, bạn không có khả năng hiển thị vào những gì đang xảy ra trong thành phần của bạn. Bạn có thể nhìn vào đầu vào, đầu ra và đoán đó là về nó.

Chìa khóa ở đây là thuật ngữ 'được triển khai tốt'. Thiết bị đo phải ở đúng nơi. Giống như bất kỳ điều gì khác, điều này đòi hỏi một số suy nghĩ và lập kế hoạch. Nếu nó không ở những nơi hữu ích/thú vị, thì nó sẽ không giúp bạn tìm ra các vấn đề trong một kịch bản phát triển, thử nghiệm hoặc triển khai. Bạn cũng có thể có quá nhiều thiết bị đo đạc gây ra vấn đề về độ hoàn hảo khi bật - hoặc thậm chí là tắt!

Có một số công cụ trong cửa sổ để giúp bạn với nhật ký và sự kiện dựa trên ETW. Chúng bao gồm logman.exe và tracerpt.exe. Ngoài ra còn có xperf tools được tập trung vào hiệu suất, nhưng cũng có thể kiểm soát bất kỳ nhà cung cấp ETW và tệp nhật ký kết xuất.

+0

Không phải ai cũng đang làm việc trên Windows ... nhưng 1 vì tôi đang tìm kiếm một giải pháp Windowsy ngay hôm nay! –

10

Đề xuất của Jonathan là tốt nhưng vì C99 chúng tôi có macro Vdd nên không cần sử dụng dấu ngoặc kép cho macro gỡ lỗi.

Có một phiên bản nhẹ của tiêu đề khai thác gỗ tôi sử dụng:

#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; 

Vì vậy, bất cứ nơi nào tôi cần phải đăng nhập gì đó, tôi chỉ cần thêm dòng như

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

Trong chương trình khởi động bạn cần chỉ định các giá trị cho dbgstream (thường mặc định là stderr) và debug_level.

Đối với dự án thực thay vì gọi fprintf nhiêu lần tôi chỉ cần gọi chức năng của tôi từ LOG vĩ mô và vượt qua __FILE__, __LINE____VA_ARGS_ như là đối số - mà chức năng cũng in ngày, giờ và pid trong dòng nhật ký, và không làm fflush() mọi lúc - chỉ khi bộ đếm đệm vượt quá giá trị đặt trước - tăng hiệu suất ghi nhật ký một cách đáng kể.

Nhưng xin lưu ý rằng một số trình biên dịch có thể không hỗ trợ các macro variadic vì nó chỉ được giới thiệu trong C99.

Các vấn đề liên quan