1. 程式人生 > >Boost Log : Log record formatting

Boost Log : Log record formatting

否則 share 使用 etime 教程 cti 對象 core hat

Log record formatting

如果您嘗試運行前幾節中的示例,您可能已經註意到,只有日誌記錄消息被寫到文件中。當沒有設置格式器(formatter)時,這是Log庫的默認行為。除非指定格式器,否則即使向logging core或logger添加了屬性,屬性值將無法輸出。回到前面的教程章節中的一個例子:

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        keywords::rotation_size = 10 * 1024 * 1024,
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
        keywords::format = "[%TimeStamp%]: %Message%"
    );

    logging::core::get()->set_filter
    (
        logging::trivial::severity >= logging::trivial::info
    );
}

對於add_file_log函數,format參數允許指定日誌記錄的格式。如果喜歡手動設置sink,那麽sink前端會為此提供set_formatter成員函數。

如前所述,可以通過多種方式指定格式。

Lambda-style formatters

可以使用如下所示的lambda樣式的表達式創建格式器:

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        // This makes the sink to write log records that look like this:
        // 1: <normal> A normal severity message
        // 2: <error> An error severity message
        keywords::format =
        (
            expr::stream
                << expr::attr< unsigned int >("LineID")
                << ": <" << logging::trivial::severity
                << "> " << expr::smessage
        )
    );
}

完整代碼
在這裏,stream是格式化輸出日誌記錄的stream的占位符。其他插入參數,例如attr和message,是定義應該在stream中存儲什麽內容的操作器。我們已經在過濾表達式中看到了severity占位符,現在,它又在格式器中被使用。這是一個很好的統一:可以在過濾器和格式器中使用相同的占位符。attr占位符與severity占位符相似,因為它也表示屬性值。不同之處是,severity占位符表示名稱為“Severity”這個特定屬性,其類型為trivial::severity_level,但attr可以用來表示任何屬性。在其他方面這兩個占位符是等價的。例如,可以用以下方法替代severity :

expr::attr< logging::trivial::severity_level >("Severity")

Tip
如前一節所示,可以為用戶的屬性定義類似severity這樣的占位符。作為使用更簡單的模板表達式的好處,此類占位符允許在占位符定義中包含所有關於屬性(名稱和值類型)的信息。這使得編碼更不容易出錯(不會拼寫錯誤的屬性名稱或指定不正確的值類型),因此推薦使用這種方式定義新屬性並在模板表達式中使用它們。

還有其他的格式化程序操縱器可以提供對日期的高級支持,時間和其他類型。一些操縱器接受附加參數,用來自定義其行為。這些參數中大多數都是命名的,可以通過 Boost.Parameter風格傳遞。
做一些改變,讓我們看看手動初始化sink是如何完成的:

void init()
{
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    sink->set_formatter
    (
        expr::stream
               // line id will be written in hex, 8-digits, zero-filled
            << std::hex << std::setw(8) << std::setfill('0') << expr::attr< unsigned int >("LineID")
            << ": <" << logging::trivial::severity
            << "> " << expr::smessage
    );

    logging::core::get()->add_sink(sink);
}

完整代碼
可以看到,可以在表達式中綁定格式更改操作器;就像streams,當後續的日誌記錄被格式化時,這些操作器將影響其屬性值格式。更多的操作器在Detailed features description部分被描述。

Boost.Format-style formatters

作為另一種選擇,您可以使用類似Boost.Format的語法來定義格式器。如上所述的格式器可以如下所示:

void init()
{
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    // This makes the sink to write log records that look like this:
    // 1: <normal> A normal severity message
    // 2: <error> An error severity message
    sink->set_formatter
    (
        expr::format("%1%: <%2%> %3%")
            % expr::attr< unsigned int >("LineID")
            % logging::trivial::severity
            % expr::smessage
    );

    logging::core::get()->add_sink(sink);
}

完整代碼
format占位符接受帶有(參數的)位置說明的格式化字符串。註意,目前只支持位置格式。add_file_log和類似函數可以使用相同的格式規範。

Specialized formatters

Log庫為許多類型(如日期、時間和作用域)提供專門的格式器。這些格式器對格式化後的值提供擴展控制。例如,可以使用與Boost.DateTime兼容的格式字符串來描述日期和時間格式:

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        // This makes the sink to write log records that look like this:
        // YYYY-MM-DD HH:MI:SS: <normal> A normal severity message
        // YYYY-MM-DD HH:MI:SS: <error> An error severity message
        keywords::format =
        (
            expr::stream
                << expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
                << ": <" << logging::trivial::severity
                << "> " << expr::smessage
        )
    );
}

完整代碼
同樣的formatter也可以在Boost.Format-style formatter上下文中使用。

String templates as formatters

在某些情況下,文本模板也可以作為格式器。在這種情況下,將調用Log庫初的始化支持代碼來解析模板並重構適當的格式器。當使用這種方法時,需要註意一些事項,但是這裏我們只簡單地描述一下模板格式。

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        keywords::format = "[%TimeStamp%]: %Message%"
    );
}

完整代碼
模板可能包含許多被百分號(%)包含的占位符。每個占位符必須包含要插入的屬性值名稱,而不是占位符。日誌記錄消息將替換%Message%占位符。

Note
sink後端的set_formatter方法中不接受文本格式模板。

Custom formatting functions

您可以向支持格式化的接收器後端添加自定義格式器。格式器實際上是一個函數對象,支持以下簽名:

void (logging::record_view const& rec, logging::basic_formatting_ostream< CharT >& strm);

這裏的CharT是目標字符類型。當日誌記錄視圖rec通過過濾並存儲在日誌中時,將調用格式器。

Tip
記錄視圖與記錄非常相似。值得註意的區別是,視圖是不可變的,並且實現了淺拷貝。格式器和sinks只能對記錄視圖進行操作,這阻止它們修改記錄,從而其他線程中的其他sinks仍然可以使用記錄視圖。

格式化的記錄應該通過插入到與stl兼容的輸出流strm中來構建。下面是一個自定義格式化程序函數使用的示例:

void my_formatter(logging::record_view const& rec, logging::formatting_ostream& strm)
{
    // Get the LineID attribute value and put it into the stream
    strm << logging::extract< unsigned int >("LineID", rec) << ": ";

    // The same for the severity level.
    // The simplified syntax is possible if attribute keywords are used.
    strm << "<" << rec[logging::trivial::severity] << "> ";

    // Finally, put the record message to the stream
    strm << rec[expr::smessage];
}

void init()
{
    typedef sinks::synchronous_sink< sinks::text_ostream_backend > text_sink;
    boost::shared_ptr< text_sink > sink = boost::make_shared< text_sink >();

    sink->locked_backend()->add_stream(
        boost::make_shared< std::ofstream >("sample.log"));

    sink->set_formatter(&my_formatter);

    logging::core::get()->add_sink(sink);
}

完整代碼

Boost Log : Log record formatting