2011-10-25 33 views
5

Tôi muốn thêm nhật ký vào ứng dụng của mình. Tôi đã chọn một thư viện đăng nhập nhưng tôi muốn có thể chuyển sang một thư viện khác mà không phải thay đổi bất kỳ mã nào sử dụng tính năng ghi nhật ký.C++ thiết kế trình bao bọc ghi nhật ký

Vì vậy, tôi cần một số loại trình bao bọc ghi nhật ký đủ linh hoạt để sử dụng khá nhiều chức năng của thư viện ghi nhật ký thiếu.

Bất kỳ đề xuất nào cho thiết kế của trình bao bọc như vậy?

EDIT: một tính năng tôi phải có trong trình bao bọc này là gắn thẻ thành phần. Tôi muốn lớp thuật toán của tôi có "X:" xuất hiện phía trước dòng đăng nhập của nó và lớp người quản lý của tôi có "Y:" xuất hiện. Làm thế nào để tuyên truyền này các thẻ vào nhật ký underling và làm thế nào để xây dựng cơ chế đặt tên thẻ thành phần là một câu hỏi thiết kế lớn ở đây.

+2

Bạn có * thực sự * cần một trình bao bọc không? Hãy tự hỏi. Có thực sự khả năng rằng bạn sẽ chuyển đổi logger của bạn (nhiều lần, có thể) hoặc bạn sẽ sử dụng nhiều khung công tác ghi nhật ký? Viết một wrapper mất tài nguyên (thời gian), và chúng có thể được đưa vào những thứ khác, nếu bạn không * thực sự * cần wrapper. – Xeo

+0

cảm ơn, bạn có thể tin tưởng rằng tôi thực sự làm. Vì mục đích đơn giản, tôi sẵn sàng giải quyết một phiên bản rất nhỏ gọn của nhật ký với tính năng gắn thẻ mà tôi đã đề cập. – Leo

Trả lời

2

Đặt cược tốt nhất của bạn là làm cho giao diện đơn giản nhất có thể. Hoàn toàn tách biệt giao diện của người dùng đăng nhập từ cách thực hiện ghi nhật ký.

Các mối quan tâm cắt ngang luôn tốn kém để duy trì, vì vậy việc làm mọi thứ phức tạp hơn sẽ khiến bạn ghét cuộc sống.

Một số thư viện chỉ muốn một cái gì đó đơn giản như thế này:

void logDebug(const std::string &msg); 
void logWarning(const std::string &msg); 
void logError(const std::string &msg); 

Họ không nên thêm hoặc chỉ định bất kỳ bối cảnh nhiều hơn. Không ai có thể sử dụng các thông tin anyway, do đó, không qua thiết kế nó.

Nếu bạn bắt đầu thêm nhiều thông tin hơn vào cuộc gọi ghi nhật ký, việc này sẽ khiến việc sử dụng lại mã khách hàng sử dụng nó trở nên khó khăn hơn. Thông thường bạn sẽ thấy bề mặt này khi các thành phần được sử dụng ở các mức trừu tượng khác nhau. Đặc biệt khi một số mã cấp thấp cung cấp thông tin gỡ lỗi chỉ liên quan đến các cấp cao hơn.

Điều này không buộc triển khai ghi nhật ký của bạn (hoặc thậm chí giao diện triển khai ghi nhật ký phù hợp!) Vào bất kỳ thứ gì, vì vậy bạn có thể thay đổi nó bất cứ khi nào.

CẬP NHẬT:

Tức là gắn thẻ, đó là mối quan tâm cấp cao. Tôi sẽ suy đoán rằng nó không thuộc về nhật ký, nhưng đó không phải là ở đây cũng không có ở đó.

Giữ nguyên thông số kỹ thuật ghi nhật ký. Mã cấp thấp không nên cung cấp cho một chiếc xe tải bay mà bạn hoặc người quản lý của bạn đang sử dụng.

Tôi không biết cách bạn chỉ định X hoặc Y trong ví dụ của bạn. Làm thế nào bạn làm điều đó là không thực sự rõ ràng từ mô tả chúng tôi được đưa ra. Tôi sẽ chỉ sử dụng một chuỗi để trình diễn, nhưng bạn nên thay thế nó bằng một cái gì đó kiểu an toàn nếu có thể.

Nếu điều này luôn bật, thì chỉ cần có ngữ cảnh cá thể (có thể là biến toàn cầu) có thể phù hợp. Khi bạn đăng nhập, hãy đặt bối cảnh và quên nó đi. Nếu nó chưa bao giờ được thiết lập, hãy ném với định kiến ​​cực đoan. Nếu bạn không thể ném khi nó không được thiết lập, sau đó nó không phải là luôn luôn trên.

void setLoggingContext("X:"); 

Nếu điều này thay đổi ở mức trừu tượng khác nhau, tôi sẽ xem xét việc thực hiện RAII dựa trên ngăn xếp.

LoggingTag tag("X:"); 

Tôi không chắc chắn yêu cầu của bạn là gì khi các khung ngăn xếp khác nhau vượt qua các giá trị khác nhau. Tôi có thể thấy nơi đầu hoặc cuối của ngăn xếp sẽ là hợp lý cho các trường hợp sử dụng khác nhau.

void foo() { 
    LoggingTag tag("X:"); 
    logWarning("foo"); 
    bar(); 
    baz(); 
} 

void bar() { 
    LoggingTag tag("Y:"); 
    logWarning("bar"); 
    baz(); 
} 

void baz() { 
    logWarning("baz"); 
} 

Cách này không ảnh hưởng đến cách bạn thêm thư vào nhật ký. Hàm baz không có ngữ cảnh để chỉ định LoggingTag. Điều quan trọng là sử dụng logWarning không biết về thẻ vì lý do này.

Nếu bạn muốn gắn thẻ dựa trên một số loại, bạn có thể làm điều gì đó đơn giản như thế này.

struct LoggingTag { 
    LoggingTag(const std::string &tag_) : tag(tag_) {} 
    template<typename T> 
    static LoggingTag ByType() { 
     return LoggingTag(typeid(T).name()); 
    } 
    std::string tag; 
}; 

void foo() { 
    LoggingTag tag = LogginTag::ByType<int>(); 
} 

này sẽ không lực một người nào đó để sử dụng typeid(T).name() nếu họ không muốn, nhưng đã cho bạn sự tiện lợi.

+0

Một mối quan tâm tôi phải tính đến là gắn thẻ thành phần khác nhau. – Leo

+0

Tôi đã có cú pháp này trong tâm trí của tôi: Log log = GetLogger (MyClassName); log.Warn (...). Tôi không chắc chắn về những gì MyClassName nên được, nếu đó là một chuỗi, nơi nào tôi nhận được nó từ đâu? Nếu đó là một số mẹo để chuyển đổi một tên lớp thành một chuỗi, làm thế nào nó có thể được thực hiện? – Leo

+0

@ user991339 Tôi không hiểu bạn đang hỏi gì cả. Bạn đang cố gắng đăng nhập một loại? –

1

Tôi thích cách tiếp cận này:

class Log { 
public: 
    virtual logString(const std::string&)=0; 
}; 

template <typename T> 
Log& operator<<(Log& logger, const T& object) { 
     std::stringstream converter; 
     converter << object; 
     logger.logString(converter.str()); 
     return logger; 
} 

đơn giản và nhanh chóng! Tất cả những gì bạn cần làm là thực hiện lại phương thức logString ...

+0

Đây là một ý tưởng hay. Bạn có thể đề xuất một cách để triển khai tính năng gắn thẻ không? – Leo

+0

@CatPlusPlus Bạn có thể dễ dàng sử dụng các mẫu ... –

+0

@ user991339 Tôi sẽ thêm thuộc tính Thẻ vào trình ghi nhật ký và một bool để chỉ ra dòng bắt đầu, do đó, lần đầu tiên nhật ký nhận được thẻ dấu. Bạn có thể chuyên toán tử << cho trường hợp endl để bạn biết rằng đầu vào tiếp theo là một sự bắt đầu của dòng. –

0

Hãy xem thư viện zf_log. Nó rất nhỏ (~ 2000k dòng, ~ 10KB khi biên dịch) và nhanh (xem bảng so sánh trong README.md). Nó rất gần với những gì bạn mô tả như là wrapper. Nó cung cấp cho bạn một API trừu tượng mà bạn có thể sử dụng trong dự án của mình và cho phép chỉ định thực thi khai thác gỗ thực tế nào để sử dụng. Xem ví dụ custom_output.c nơi syslog được sử dụng làm cơ sở đầu ra. Nó cũng có thể được sử dụng riêng trong các thư viện mà không có nguy cơ bị xung đột với mã khác có thể sử dụng thư viện này (xem ZF_LOG_LIBRARY_PREFIX xác định để biết thêm thông tin). Thậm chí nếu nó không chính xác những gì bạn đang tìm kiếm, tôi đoán nó có thể là một ví dụ tốt cho điều wrapper của bạn.

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