Làm cách nào để thêm ghi nhật ký vào thư viện để có thể tích hợp dễ dàng với hệ thống ghi nhật ký của chương trình bằng thư viện?


9

Tôi đang viết một thư viện có nhiều thông tin có thể được sử dụng trong nhật ký của chương trình sử dụng nó, nhưng tôi không biết cách tốt nhất để phơi bày nó theo cách mà chương trình sử dụng thư viện của tôi có thể tích hợp nhật ký thư viện của tôi với nhật ký riêng của nó dường như (nếu muốn).

Chọn một thư viện ghi nhật ký cụ thể cho thư viện của tôi sẽ thêm vào danh sách các phụ thuộc để sử dụng thư viện của tôi cũng như liên kết chương trình chính với thư viện đó - và nếu nhiều thư viện được sử dụng bởi chương trình chính, thì mỗi thư viện có thể chọn một thư viện khác .

Mặc dù tôi đã có chương trình đăng ký một đối tượng luồng C ++ với thư viện để sử dụng. Điều đó có vẻ như là mục đích tương đối chung, nhưng tôi cũng nghĩ về việc chỉ cần chương trình chính đăng ký một hàm gọi lại sẽ được gọi với nội dung và siêu dữ liệu khi dữ liệu được ghi lại. Một tùy chọn khác chỉ là lưu trữ dữ liệu nhật ký trong thư viện trong một danh sách nào đó để chương trình chính lấy bất cứ khi nào nó muốn xử lý dữ liệu đó, để chương trình chính quyết định khi nào có thời gian xử lý dữ liệu.

Tôi đang tìm kiếm các đề xuất và ưu / nhược điểm của các phương pháp khác nhau để tôi có thể quyết định điều gì là tốt nhất trong tình huống của mình.


1
Không thực sự là một câu trả lời nhưng tôi khuyên bạn nên xem bộ công cụ Qt thực hiện nó như thế nào. Google QtMessageHandler. Và QMessageLogger. Nó trông rất giống những gì các câu trả lời khác gợi ý, btw.
Teimpz

Câu trả lời:


8

Bạn có thể hiển thị nhiều phương thức để nhận nhật ký từ thư viện của mình và bao gồm tất cả ngoại trừ một trong các bộ điều hợp với phương thức "thực" được sử dụng trong thư viện.

Ví dụ, bạn quyết định nội bộ có một std::function<void(std::string)>bộ sưu tập là mỗi cuộc gọi lại logger. Bạn cung cấp:

void registerLogCallback(std::function<void(std::string)> callback); 
// Main logging implemention

và cũng

registerLogStream(std::ostream stream) { 
    registerLogCallback([stream](std::string message){ stream << message; }); 
}

và cũng

template<typename OutputIterator>
registerLogOutputIterator(OutputIterator iter) { 
    registerLogCallback([iter](std::string message){ *iter++ = message; }); 
}

và bất kỳ biến thể nào khác của các loại "nhận chuỗi từ đâu đó" mà bạn quan tâm để triển khai bộ điều hợp cho.


Tôi nghĩ rằng cuối cùng nó cũng sẽ cần một số loại "mức ghi nhật ký", như gỡ lỗi, cảnh báo, gây tử vong, ... để người dùng có thể lọc ra những gì anh ta cần. Tốt bắt đầu dù sao.
Teimpz

4

Cách đơn giản nhất để cho phép ứng dụng có thể truy cập vào chức năng ghi nhật ký là cho phép ứng dụng đăng ký một lớp / chức năng để nhận thông điệp tường trình. Những gì họ làm với thông điệp đó hoàn toàn phụ thuộc vào ứng dụng.

Sử dụng C / C ++, bạn có thể sử dụng các mục sau trong thư viện của mình:

typedef void (*LogMessageReceiver)(char const* message,
                                   void* user_data);

void registerLogMessageReceiver(LogMessageReceiver receiver,
                                void* user_data);

Một ứng dụng có thể đăng ký một chức năng bằng cách gọi registerLogMessageReceiver. với sự phù hợp user_data. Trong phần ghi nhật ký của cơ sở mã của bạn, bạn phải đảm bảo gọi hàm đó với thông báo phù hợp và thông báo đã đăng ký user_data.

Nếu bạn không phải lo lắng về C, bạn có thể sử dụng một lớp làm người nhận tin nhắn.

struct LogMessageReceiver
{
   virtual ~LogMessageReceiver() {}
   virtual void receive(std::string const& message) = 0;
};

và thêm một chức năng trong thư viện để cho phép một ứng dụng đăng ký một người nhận thông điệp tường trình.

void registerLogMessageReceiver(LogMessageReceiver* receiver);

Một ứng dụng có thể đăng ký a LogMessageReceiverbằng cách gọi hàm trên. Bạn sẽ phải đưa ra một số quyết định chính sách liên quan đến quyền sở hữu của người đăng ký LogMessageReceiver. Nếu thư viện có quyền sở hữu của người nhận, nó phải deletelà con trỏ. Nếu thư viện không có quyền sở hữu của người nhận, ứng dụng phải chăm sóc deletengười nhận.

Sử dụng một lớp làm trình nhận thông điệp tường trình cho phép user_databỏ qua bit registerLogMessageReceivervì loại phụ LogMessageReceiverlà miễn phí để giữ bất kỳ dữ liệu nào hữu ích cho chức năng của nó. Nó không cần phải được thông qua bất kỳ dữ liệu người dùng bổ sung trong receivechức năng.

Từ đó, nó có thể trở nên phức tạp hơn tùy thuộc vào mức độ tinh vi của cơ chế ghi nhật ký của bạn.

Ví dụ: bạn có thể có các mức ghi nhật ký khác nhau: súc tích, Bình thường, Verbose hoặc LoggingLevel1, LoggingLevel2, ..., LoggingLevelN.

Trong trường hợp đó, bạn sẽ phải cho phép ứng dụng kiểm soát mức độ đăng nhập mà họ muốn sử dụng.

Có một loạt các lựa chọn vô tận một khi bạn quyết định vượt ra ngoài cơ chế ghi nhật ký đơn giản. Nó không có ý nghĩa để đi sâu vào chúng ở đây.


Đây là một câu trả lời tốt nếu trình biên dịch trước C ++ 11 cần được hỗ trợ. Nếu không, tôi sẽ ủng hộ câu trả lời của Caleths. chức năng std :: cho phép chụp ảnh thay thế an toàn hơn nhiều vào khoảng trống *
Teimpz

1

Tôi sẽ khẳng định rằng bạn nên suy nghĩ lại về sự cần thiết phải đăng nhập cùng với thư viện của bạn; Đặc biệt đối với C ++, nơi không có giao diện logger tiêu chuẩn.

Các ứng dụng khác nhau có chính sách khác nhau liên quan đến đăng nhập. Một thư viện nên là chính sách bất khả tri.

Mục đích của thư viện là cung cấp một dịch vụ, và tốt nhất là cho biết liệu một yêu cầu cho dịch vụ đó thành công hay thất bại; lý tưởng với một chỉ dẫn về lý do tại sao [nó thất bại] thông qua errno, mã trả về, ngoại lệ ... Nếu mong muốn đăng nhập của bạn là do một chức năng được cung cấp có thể thất bại ở nhiều nơi, bạn thể đang cố gắng làm quá nhiều trong một chức năng. Có thể không , nhưng xem xét khả năng.


@xaxxon Tôi không hiểu tại sao đây không phải là câu trả lời. Theo Làm thế nào để tôi viết một câu trả lời tốt? một [...] answer can be “don’t do that" [...].
doubleYou

1

Viết thư viện giao diện dễ dàng với hệ thống nhật ký của ứng dụng chủ rất dễ dàng, miễn là bạn biết hệ thống đó là gì. Khi có nhiều khả năng cho điều đó, nó sẽ khó hơn và bạn bị ràng buộc bởi sự chuyên chế của mẫu số chung thấp nhất. Bạn có thể có một lớp tương thích thích ứng thư viện của bạn với các hệ thống nhật ký khác nhau, nhưng bây giờ bạn không thể dựa vào một số tính năng độc đáo, hữu ích mà chỉ có một hệ thống có. Nếu người dùng cuối có hệ thống khác, tính năng độc đáo hữu ích đó không có ở đó.

Trong thế giới .Net, có (ít nhất) hai hệ thống nhật ký thường được sử dụng là log4net và NLog. Cả hai đều giống nhau, nhưng không giống nhau. Tôi đã sử dụng log4net và sau đó bắt đầu sử dụng NHibernate (thư viện ORM). May mắn thay, nó sử dụng log4net trong nội bộ, vì vậy thật dễ dàng để thêm nó vào một dự án. . Tôi chuyển đổi, hoặc có một dự án sử dụng cả hai.

Ít nhất là với việc ghi nhật ký, thường có tùy chọn tạo một ứng dụng thông báo tùy chỉnh mà bạn có thể định cấu hình để chuyển tiếp một bản ghi đã bị rối trong một hệ thống sang hệ thống ghi nhật ký khác. Vì vậy, nếu đã sử dụng NLog và thực sự muốn tiếp tục với nó, nhưng cũng sử dụng NHibernate, tôi có thể nghiến răng viết một app4 log4net để chuyển từng tin nhắn tới NLog. Sẽ rất đơn giản để làm nếu các API tương tự nhau.

Các lựa chọn thay thế thực sự là chọn hệ thống tốt nhất cho nhu cầu của bạn có sẵn và sử dụng nó một cách không hạn chế, hoặc có một loại lớp bộ điều hợp nào đó, có vấn đề về mẫu số chung thấp nhất. Đó không phải là một câu trả lời rất thỏa mãn.


Điều này, cộng với thực tế là C ++ làm cho những điều dễ dàng trở nên khó khăn và những điều khó khăn thậm chí còn khó khăn hơn.
rwong
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.