2009-07-16 60 views
7

Tôi đang viết thư viện chia sẻ C++ được dự định sẽ được sử dụng bởi thư viện khác hoặc tệp thực thi. Cách tốt nhất để thêm đăng nhập chung trong thư viện của tôi là gì? Lý tưởng nhất là tôi muốn điều chỉnh thư viện của mình thành chức năng ghi nhật ký do người dùng của thư viện chọn. Giả sử tôi có một lớp học trong thư viện của tôiThêm hỗ trợ ghi nhật ký chung trong thư viện được chia sẻ C++

class A { 
    public: 
    void method(string param1, int param2); 
} 

void A::method(string param1, int param2){ 
    /* i want to log values of param1 and param2, 
    but actual logging method must be defined outside of my library. 
    Maybe some kind of macro should help here. */ 
    /*e.g.*/ GENERICLOG_DEBUG("param1=" + param1+ " param2="+param1); 
    /*if so, what that macro body should look like ? */ 
} 

Tôi không muốn làm cho thư viện của tôi gắn liền với bất kỳ API cụ thể log4XXX.

Trả lời

8

Bạn có thể cung cấp một cơ chế gọi lại để cho phép người sử dụng của thư viện để cung cấp thư viện của bạn với một adapter vào khai thác gỗ của họ.

Ie, trong thư viện của bạn cung cấp một lớp trừu tượng khai thác gỗ giao diện, ví dụ:

class Logger 
{ 
public: 
    virtual ~Logger() {} 
    virtual void log (const std::string& message) = 0; 
}; 

và một lớp học để regster một Logger với:

class Log 
{ 
private: 
    static Logger* logger_; 
public: 
    static void registerLogger (Logger& logger) 
    { logger_ = &logger; } 

    static void log (const std::string& message) 
    { if (logger_ != 0) logger_->log (message); } 
}; 

thư viện của bạn sau đó đăng nhập với một cái gì đó như:

Log::log ("My log message"); 

Ứng dụng sử dụng thư viện của bạn nên cung cấp triển khai Trình ghi nhật ký (I E. một lớp con cụ thể) và đăng ký nó với lớp Log của bạn. Logger impl của họ sẽ thực hiện khai thác gỗ khi họ thấy phù hợp.

Điều này cho phép thư viện của bạn được sử dụng bởi các ứng dụng sử dụng các thư viện ghi nhật ký khác nhau.

Lưu ý mã ở trên là cơ bản và chưa được kiểm tra. Trong thực tế, bạn có thể muốn thay đổi phương thức ghi nhật ký để bao gồm thông số mức ghi nhật ký, v.v.

+0

Cảm ơn, giải pháp tốt đẹp! Tôi cũng có thể cung cấp nhật ký mặc định (ví dụ: syslog) nếu không có người dùng đăng nhập nào được đăng ký trong lib của tôi. –

+0

yep. –

0

nhật ký hệ thống - sau đó bạn có thể sử dụng bất kỳ tiện ích syslog nào sử dụng.

hoặc cho phép người dùng chỉ định gọi lại.

0

Nếu bạn không muốn sử dụng lib đăng nhập hiện có, bạn có thể thử sử dụng macro. Tôi sẽ khuyên bạn nên cung cấp lib của riêng bạn và để làm cho nó có sẵn với các macro với printf như cơ chế định dạng.

Tôi đã làm điều tương tự như vậy trong quá khứ. Các macro được gọi là một đối tượng đăng nhập mà đóng gói đăng nhập thực sự có thể mở rộng thông qua plugin.

Nhưng tôi nghĩ rằng một cái gì đó tương tự đã được thực hiện bởi Log4xxx vì vậy có lẽ nó là tốt để nhìn vào nó.

Dưới đây là một đề nghị (xin lỗi không có thời gian để thử nghiệm, tôi hy vọng nó hoạt động)

tiêu đề:

 
#ifdef _MYAPI_IMPL 
#define MY_API __declspec(dllexport) 
#else 
#define MY_API __declspec(dllimport) 
#endif 

class MY_API Log 
{ 
public: 
    enum Level {Error, Warning, Info, Debug}; 
    Log(Level level, const char* file, int line); 
    void operator()(const char* Format, ...); 
private: 
     const char* m_file; 
     Level m_level; 
     int m_line; 
}; 

#define __LOG(lvl) (Log(lvl, __FILE__, __LINE__)) 

#define LOG_ERR __LOG(Log::Error) 
#define LOG_WRN __LOG(Log::Warning) 
#define LOG_INF __LOG(Log::Info) 
#define LOG_DBG __LOG(Log::Debug) 

class My_API Logger 
{ 
public: 
    virtual void log(const char* message)=0; 
}; 

class MY_API LoggerManager 
{ 
private: 
    static LoggerManager* s_inst; 
    LoggerManager() {} 
     virtual ~LoggerManager() {} 
public: 
    static LoggerManager* Instance(); 
    static void Clean(); 
    addLogger(Logger* newLogger, Log::Level minlevel = Log::Info); 
     log(const char* file, int line, Log::Level level, const char* message); 
}; 

Cpp:

 

Log::Log(Level level, const char* file, int line) 
: m_file(file), m_level(level), m_line(line) 
{ 
} 

void Log::operator()(const char* format, ...) 
{ 
    va_list va; 
    va_start(va, format); 
    char message[LENGTH+1]={0}; 

    _vsnprintf(message, LENGTH, format, va); 

    va_end(va); 

    LoggerManager::Instance()->log(m_file, m_line, m_level, message); 


}; 

libs và exe khác sẽ có thể gọi như thế này Họ chỉ cần bao gồm .h và liên kết với lib.

 
LOG_INF("Hello %s!", "world"); 

Cập nhật: Tôi đã thêm giải thích cần thiết cho cơ chế ghi nhật ký. Một cách là sử dụng một singleton và để cung cấp một giao diện được phân lớp cho việc ghi nhật ký thực tế.

Lợi ích của việc sử dụng macro là nó cung cấp cho bạn khả năng lấy vị trí của nhật ký có thể là thông tin rất thú vị trong một số trường hợp. Cũng có thể biến macro thành printf thông thường khi bạn không muốn triển khai tất cả cơ chế ghi nhật ký.

+0

void Log :: operator() (const char * format, ...); Trong chức năng này, làm thế nào bạn có thể đảm bảo độ dài của tin nhắn không thể vắt hơn "LENGTH"? – bbg

+0

Tôi nghĩ rằng bạn là đúng và bạn nên chăm sóc điểm này trong bạn sử dụng một giải pháp như vậy. – luc

1

Bạn có thể sử dụng google-glog. Tôi thấy nó đẹp để sử dụng.

https://github.com/google/glog

Nó hỗ trợ cho mỗi giá trị debug tập tin, có thể đăng nhập để syslog, gửi email cho bạn, và vô số các tính năng tuyệt vời khác.

1

Declare nguyên mẫu của chức năng đăng nhập vào thư viện của bạn:

extern void __cdecl UserLog(char* stText);

Đừng thực hiện nó trong thư viện của bạn. Người dùng thư viện của bạn nên triển khai chức năng này và thư viện của bạn sẽ sử dụng triển khai của người dùng.thực hiện

của người sử dụng có thể trông giống như sau (đó chỉ là một mẫu):

void __cdecl UserLog(char* stText) 
{ 
    std::cout << stText << std::endl; 
}

Đó là hợp lệ nếu thư viện của bạn là thư viện tĩnh.

Trong mã của bạn, bạn sẽ có thể sử dụng theo cách sau:

class A { 
    public: 
    void method(string param1, int param2); 
} 

void A::method(string param1, int param2){ 
    string formatted = str(boost::format("param1=%s param2=%d") % param1 % param2); 
    UserLog(formatted.c_str()); 
}
Các vấn đề liên quan