Boost Log 的 attribute 的簡易使用

| | 0 Comments| 17:13
Categories:

這篇是 Boost Log 這個函式庫的使用紀錄的第三篇(第一篇第二篇),主要是大概紀錄一下,要怎麼使用 Boost Log 在每一筆紀錄裡面附加的 attribute。

說實話,雖然用 Boost Log 來作紀錄是滿方便的~但是,Heresy 不是很喜歡 Boost Log 在格式化、以及篩選時所使用 expression…不知道之後有沒有可能直接用 C 標準的 lambda expression 來做?Heresy 覺得那樣應該會好寫很多…

總之,這邊還是來大概寫一下,在 Boost Log 裡面,要怎麼附加 attribute 的資料吧~沒意外的話,這篇就是 Heresy 寫 Boost Log 這個函式庫的最後一篇了。


Attribute 的使用

首先,在前面的範例裡可以看到, Boost Log 的每一筆紀錄(record),基本上都是透過文字、來做沒有特定格式的紀錄的。

但是,仔細想的話,也會注意到,在「trivial」模式、或是 severity_logger<>, 其實都還是有一些特定格式的資料在紀錄裡,例如時間、severity level 等等;而這些有特定格式的資料,就是 Boost Log 提供的「attribute」。透過這些可以自己定義的 attribute,在某些時候,可以讓我們更好地去結構化我們的 log 系統的資料。

基本上,Boost Log 的 attribute 是每筆紀錄都各自獨立的、並且可以用一個名字來做存取;Heresy 個人覺得最典型的讀取方法,就是使用 set_formatter() 的時候,透過 boost::log::expressions::attr<TYPE>(NAME) 這個函式來取得資料、並指定資料的型別。

像是在《Boost Log 的一些 logger 使用細節》這篇文章裡面最後的範例,就是用這個方式來讀取 severity level:

pSink->set_formatter(
   boost::log::expressions::stream
      << "<" << boost::log::expressions::attr<severity_level>("Severity")
      << ">\t" <<boost::log::expressions::message
);

而如果想要簡化程式碼的話,建議可以搭配 namespace alias 來使用:

namespace expr = boost::log::expressions;
pSink->set_formatter(
   expr::stream
     << "<" << expr::attr<severity_level>("Severity")
     << ">\t" << expr::message
);

這樣的話,程式碼是會看起來乾淨不少的。

而如果想要簡化 attribute 的讀取的話,可以搭配 BOOST_LOG_ATTRIBUTE_KEYWORD() 這個 macro 來使用。


設定 Attribute

而要指定 attribute 的值的話,個人是覺得應該可以分為兩大類:第一種是在建立每一筆紀錄時再加上去各自的 attribute,另一種方法則是在建立 Boost.Log 的環境的時候、就直接設定完成。

前者的話,可以在使用 BOOST_LOG() 建立紀錄的時候,用 boost::log::add_value() 來加入。
(需要 include <boost/log/utility/manipulators/add_value.hpp> 這個檔案)

例如下面的程式碼裡面,就是在這筆紀錄裡面、附加一個名為「att1」的 attribute,其值為「1」。

BOOST_LOG( mLogger ) << boost::log::add_value("att1",1) << "log 2";

而除了這樣在每筆紀錄裡面,都各自去附加 attribute 外,另一種方法,就是針對不同的物件、直接附加固定的 attribute。像是在《程式的記錄輔助工具:Boost Log》一文中所提到的 add_common_attributes(),就是這種類型。

而根據需求的不同,attribute 可以附加在 core 上、當作全域的 attribute(大家都有)(global),或是根據 thread 不同做設定的(thread-specific);另外也可以附加在不同的 logger 物件上,作為 log 來源的區隔(source-specific)。

而以全域的 attribute 來說,它的使用方法,是透過 boost::log::coreadd_global_attribute() 這個函式來做設定;其使用範例如下:

boost::shared_ptr<boost::log::core> core = boost::log::core::get();
core->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());

這個函式的第一個參數是一個字串、代表這個 attribute 的名字,而第二個參數則是型別為 attribute 的物件,用來讓 Boost Log 在建立紀錄的時候,可以取得 attribute 的值。

這邊的 local_clock 是一個繼承 attribute 的類別,定義在 boost/log/attributes/clock.hpp 這個檔案裡;他會在建立每一筆紀錄的時候,去讀取現在的時間、並當作 attribute 的值、附加到紀錄裡。

而 thread-specific 的 attribute,則是呼叫 coreadd_thread_attribute() 函式,他的參數和 add_global_attribute() 是相同的;和 add_global_attribute() 不同的地方在於,使用 add_thread_attribute() 加入的 attribute 只會出現在這個 thread 產生的紀錄裡,其他 thread 產生的紀錄不會有這個 attribute。

至於 source-specific 的 attribute,則是呼叫 loggeradd_attribute() 這個函式,他的介面基本上也是相同的;不同的地方在於,透過這種方法加入的 attribute 只有在使用這個 logger 的時候才會有,如果使用別的 logger 則不會有。


至於用來產生值得 attribute 的部分,Boost Log 也有提供許多不同的功能可以使用,像是上面的 local_clock 就是用來產生時間的資料的;而其他也還有像是 timercurrent_process_idcurrent_scope_info 等等,有興趣的話請參考官方文件(連結)。

而如果是要用常數(固定的值)的話,則可以使用 constant<> 這個 template 型別、或是直接使用 mask_constant() 這個函式。

如果想要使用自己的函式來產生值的話,除了繼承 attribute 寫一個新的的外,也可以使用 make_function() 這個函式,來透過指定 functiuon object 的方法來做。
(不過在 Visual C 2010 下,似乎和 C 11 的 lambda expression 有相容性問題…)


Boost Log 的 lambda expression

Boost Log 的格式設定、以及篩選條件,都是自己定義的 lambda exprerssion、而非標準的 lambda expression 或 function object,這點算是 Heresy 比較討厭的地方。而為了做到更多功能,所以 Boost Log 在這邊,也提供了很多不同的功能。不過這邊就是只是大概列一下一些東西、不打算仔細寫了,細節請參考官方文件

  • 由於紀錄裡面不一定會有對應的 attribute,所以在讀取 attribute 的值的時候,也可以透過 or_none()or_throw()or_default() 這三個韓式,來做額外的處理。

  • 可以透過 has_attr() 來判斷此筆紀錄是否有指定的 attribute;而在這部分,boost log 也有提供其他的資料判斷函式,可以做一些簡單的 filter。

  • Boost Log 的 expression 也有提供語法很詭異的 if 來做條件判斷…

  • 可以搭配 Boost Phoenix 來使用一班的函式


這篇就寫到這了。說實話,自己都覺得這系列寫的超凌亂的… orz
基本上,就只能算是自己的紀錄了;要拿來當作學習的參考,可能會有點難度。除非之後遇到特殊的需求,不然應該也不會再繼續寫、或是重新整理了吧…

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *