2010-04-14 5 views
24

Tôi làm việc trên Unix trên chương trình C++ gửi thư tới syslog.Chuyển hướng C++ std :: clog thành syslog trên Unix

Mã hiện tại sử dụng cuộc gọi hệ thống syslog hoạt động như printf.

Bây giờ, tôi muốn sử dụng luồng cho mục đích đó thay vào đó, thường được xây dựng trong std :: clog. Nhưng clog chỉ đơn thuần là chuyển hướng đầu ra để stderr, không để syslog và đó là vô dụng đối với tôi như tôi cũng sử dụng stderr và stdout cho các mục đích khác.

Tôi đã nhìn thấy trong another answer rằng nó khá dễ dàng để chuyển hướng nó vào một tập tin sử dụng rdbuf() nhưng tôi thấy không có cách nào để áp dụng phương pháp gọi syslog như openlog không trả lại một handler tập tin tôi có thể sử dụng để buộc một dòng trên đó.

Có phương pháp nào khác để làm điều đó không? (trông khá cơ bản cho lập trình Unix)?

Chỉnh sửa: Tôi đang tìm một giải pháp không sử dụng thư viện bên ngoài. Những gì @Chris đang đề xuất có thể là một khởi đầu tốt nhưng vẫn còn một chút mơ hồ để trở thành câu trả lời được chấp nhận.

Chỉnh sửa: sử dụng Boost.IOStreams OK khi dự án của tôi đã sử dụng Tăng tốc.

Liên kết với thư viện bên ngoài là có thể nhưng cũng là mối quan tâm vì đó là mã GPL. Các phụ thuộc cũng là một gánh nặng khi chúng có thể xung đột với các thành phần khác, không có sẵn trên bản phân phối Linux của tôi, giới thiệu các lỗi của bên thứ ba, v.v. Nếu đây là giải pháp duy nhất tôi có thể xem xét hoàn toàn tránh luồng ... (đáng tiếc).

+0

syslog đòi hỏi nhiều hơn chỉ là một chuỗi thông điệp; nó cũng đòi hỏi một 'mức độ lỗi' và vân vân. Tôi không chắc chắn nếu điều này là có thể sử dụng suối. Có lẽ với thao tác (giống như 'std :: hex')? – ereOn

+2

Hãy xem các thư viện ghi nhật ký có sẵn. Nhiều người sẽ cho phép bạn viết các chương trình phụ trợ của riêng bạn để viết thư của bạn tới bất cứ nơi nào bạn muốn viết chúng. nhiều người cũng đi kèm với bộ lọc tích hợp và các tính năng đẹp khác. Chỉ có một số ít trọng lượng nhẹ chỉ có hành lý nhỏ, nhưng bạn có thể tìm thấy chúng nếu bạn muốn. Tôi đang sử dụng cái này: http://www.templog.org/ Nó chỉ là một vài tệp nguồn, gần như tất cả trong các tiêu đề, và lọc tốt tại thời gian biên dịch (đối với mã thời gian quan trọng) cũng như tại run- thời gian. Nhưng bạn có thể thích một số khác. Chỉ cần không tái phát minh ra bánh xe. – sbi

+0

Nếu nó không thể được thực hiện bằng cách sử dụng xây dựng trong tắc nghẽn, một người dùng xác định dòng chuyên ngành sẽ gần như là tốt, tôi không quan tâm nhiều nếu mức được thiết lập bằng cách sử dụng thao tác hoặc chức năng thành viên. – kriss

Trả lời

27

tôi cần một cái gì đó đơn giản như thế này quá, vì vậy tôi chỉ cần đặt này với nhau:

log.h:

enum LogPriority { 
    kLogEmerg = LOG_EMERG, // system is unusable 
    kLogAlert = LOG_ALERT, // action must be taken immediately 
    kLogCrit = LOG_CRIT, // critical conditions 
    kLogErr  = LOG_ERR,  // error conditions 
    kLogWarning = LOG_WARNING, // warning conditions 
    kLogNotice = LOG_NOTICE, // normal, but significant, condition 
    kLogInfo = LOG_INFO, // informational message 
    kLogDebug = LOG_DEBUG // debug-level message 
}; 

std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority); 

class Log : public std::basic_streambuf<char, std::char_traits<char> > { 
public: 
    explicit Log(std::string ident, int facility); 

protected: 
    int sync(); 
    int overflow(int c); 

private: 
    friend std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority); 
    std::string buffer_; 
    int facility_; 
    int priority_; 
    char ident_[50]; 
}; 

log.cc:

Log::Log(std::string ident, int facility) { 
    facility_ = facility; 
    priority_ = LOG_DEBUG; 
    strncpy(ident_, ident.c_str(), sizeof(ident_)); 
    ident_[sizeof(ident_)-1] = '\0'; 

    openlog(ident_, LOG_PID, facility_); 
} 

int Log::sync() { 
    if (buffer_.length()) { 
     syslog(priority_, buffer_.c_str()); 
     buffer_.erase(); 
     priority_ = LOG_DEBUG; // default to debug for each message 
    } 
    return 0; 
} 

int Log::overflow(int c) { 
    if (c != EOF) { 
     buffer_ += static_cast<char>(c); 
    } else { 
     sync(); 
    } 
    return c; 
} 

std::ostream& operator<< (std::ostream& os, const LogPriority& log_priority) { 
    static_cast<Log *>(os.rdbuf())->priority_ = (int)log_priority; 
    return os; 
} 

Trong main() tôi khởi làm tắc nghẽn :

std::clog.rdbuf(new Log("foo", LOG_LOCAL0)); 

Sau đó, bất cứ khi nào tôi muốn đăng nhập, thật dễ dàng:

std::clog << kLogNotice << "test log message" << std::endl; 

std::clog << "the default is debug level" << std::endl; 
+0

Điều này là tuyệt vời!Cảm ơn bạn. – wollud1969

13

Bạn có thể xác định streambuf gọi syslog. Ví dụ:

// Pseudo-code 
class syslog_streambuf : public streambuf { 
private: 
    void internal_log(string& log) { 
     syslog(..., log, ...); 
    } 
public: 
    int sputc (char c) { 
     internal_log(...); 
    } 
    streamsize sputn (const char * s, streamsize n) { 
     internal_log(...); 
    } 
} 

sau đó bạn chỉ cần viết sau đây để chuyển hướng làm tắc nghẽn:

clog.rdbuf(new syslog_streambuf); 

Có một vài chức năng hơn bạn có lẽ sẽ phải ghi đè lên, đây là một tốt reference to the streambuf api.

+3

Ví dụ làm việc ở đây: http://pastebin.org/184922 – Basilevs

+1

Tôi nghĩ rằng đó là một thực hành tốt để tạo ra một đối tượng ostream riêng biệt cho nhiệm vụ cụ thể này. Hãy coi chừng http://www.cplusplus.com/reference/iostream/ios_base/sync_with_stdio/. – Basilevs

+0

@Basilevs: cảm ơn mẫu. Nó vẫn còn ngắn và nó chắc chắn là loại điều tôi đang tìm kiếm. Dù sao tôi sẽ phải phân loại các công cụ ổ cắm cụ thể để thích ứng với mã cho syslog ... có vẻ như nó sẽ đưa tôi ít nhất một vài ngày :-( – kriss

2

tôi thiết kế một lớp OStreamedLog rất giống với những gì đã trình bày ở trên, ngoại trừ đối tượng OStreamedLog của tôi được thiết lập để sử dụng một đối tượng ostringstream tùy ý, như @Basilevs gợi ý.

Đầu tiên, có định nghĩa lớp học của Nhật ký, rất giống với @eater và @Chris Kaminski đã đề cập ở trên.Sau đó, định nghĩa lớp OStreamedLog của tôi, trong đó có một đối tượng Log:

class OStreamedLog : public ostringstream 
{ 
    public: 
    OStreamedLog (const char* ident, int facility) 
    { 
      log = new Log (ident, facility); 
      (static_cast<ostream*>(this))->rdbuf (log); 
    } 
    private: 
    Log* log; 
}; 

Bây giờ, khi bạn cần đăng nhập, chỉ cần gọi:

OStreamedLog someLog ("MyOwnProgramThatNeedsLogging", LOG_LOCAL1); 
someLog << "Log testing" << endl; 
someLog << LOG_ERR << "some other error log" << endl; 

Tất nhiên, bạn có thể sụp đổ những định nghĩa hoàn toàn Đăng nhập vào của bạn Lớp OStreamedLog, nhưng bạn có thể muốn làm những việc khác trong đối tượng Log cơ bản của bạn và sử dụng các trình bao bọc như trên để phân biệt các loại bản ghi khác nhau. Ví dụ: bạn có thể có nhật ký chẩn đoán có thể đọc được con người (được gửi dưới dạng văn bản ASCII), nhật ký nhị phân (để xử lý sau) hoặc nhật ký truyền TLS (đến máy chủ hướng bắc).

3

Một phiên bản khác được lấy cảm hứng từ người ăn. Nó không chuyển hướng std :: clog mỗi se, nhưng sử dụng cú pháp luồng quen thuộc.

#ifndef SYSLOG_HPP 
#define SYSLOG_HPP 

#include <ostream> 
#include <streambuf> 
#include <string> 

#include <syslog.h> 

namespace log 
{ 

enum level 
{ 
    emergency = LOG_EMERG, 
    alert  = LOG_ALERT, 
    critical = LOG_CRIT, 
    error  = LOG_ERR, 
    warning = LOG_WARNING, 
    notice = LOG_NOTICE, 
    info  = LOG_INFO, 
    debug  = LOG_DEBUG, 
}; 

enum type 
{ 
    auth = LOG_AUTH, 
    cron = LOG_CRON, 
    daemon = LOG_DAEMON, 
    local0 = LOG_LOCAL0, 
    local1 = LOG_LOCAL1, 
    local2 = LOG_LOCAL2, 
    local3 = LOG_LOCAL3, 
    local4 = LOG_LOCAL4, 
    local5 = LOG_LOCAL5, 
    local6 = LOG_LOCAL6, 
    local7 = LOG_LOCAL7, 
    print = LOG_LPR, 
    mail = LOG_MAIL, 
    news = LOG_NEWS, 
    user = LOG_USER, 
    uucp = LOG_UUCP, 
}; 

} 

class syslog_stream; 

class syslog_streambuf: public std::basic_streambuf<char> 
{ 
public: 
    explicit syslog_streambuf(const std::string& name, log::type type): 
     std::basic_streambuf<char>() 
    { 
     openlog(name.size() ? name.data() : nullptr, LOG_PID, type); 
    } 
    ~syslog_streambuf() override { closelog(); } 

protected: 
    int_type overflow(int_type c = traits_type::eof()) override 
    { 
     if(traits_type::eq_int_type(c, traits_type::eof())) 
      sync(); 
     else buffer += traits_type::to_char_type(c); 

     return c; 
    } 

    int sync() override 
    { 
     if(buffer.size()) 
     { 
      syslog(level, "%s", buffer.data()); 

      buffer.clear(); 
      level = ini_level; 
     } 
     return 0; 
    } 

    friend class syslog_stream; 
    void set_level(log::level new_level) noexcept { level = new_level; } 

private: 
    static constexpr log::level ini_level = log::info; 
    log::level level = ini_level; 

    std::string buffer; 
}; 

class syslog_stream: public std::basic_ostream<char> 
{ 
public: 
    explicit syslog_stream(const std::string& name = std::string(), log::type type = log::user): 
     std::basic_ostream<char>(&streambuf), 
     streambuf(name, type) 
    { } 

    syslog_stream& operator<<(log::level level) noexcept 
    { 
     streambuf.set_level(level); 
     return (*this); 
    } 

private: 
    syslog_streambuf streambuf; 
}; 

#endif // SYSLOG_HPP 

Để sử dụng nó, bạn có thể làm điều gì đó như:

syslog_stream clog; 

clog << "Hello, world!" << std::endl; 
clog << log::emergency << "foo" << "bar" << "baz" << 42 << std::endl;