1. 程式人生 > >boost log庫學習一

boost log庫學習一

對日誌的思考

為什麼需要日誌?

如今的應用程式都非常大,程式碼變得難以測試及除錯。而且更多的時候,這些程式執行在遠離開發人員的遠處,使得開發人員幾乎沒有機會監視程式的執行情況,並且一旦發生錯誤,很難找出錯誤的原因。此外,如果應用程式行為嚴重依非同步事件,例如裝置反饋或其他程序活動,即使是在本地也難以除錯。

這就是日誌可以幫助我們的地方。程式執行時將所有必要的程式執行資訊儲存在日誌上,當程式出錯時可以分析這些日誌中記錄的資訊,找出程式出錯的原因,直到程式正確。日誌還有其他非常有用的應用,例如收集統計資訊和一些重點事件(如,表明某些情況已經發生或應用程式正在遇到一些問題)。這些任務已被證明是非常重要的。

日誌能方便地診斷程式原因、統計程式執行資料,是大型軟體系統必不可少的元件之一。

日誌庫的3點要求

  • 簡單易用
    這是日誌庫存在的最基本的意義
  • 可擴充套件性
    使用者能夠根據自身需要加以擴充套件
  • 高效能
    日誌程式碼對程式本身效能影響越小越好

為什麼用boost log?

boost log :功能豐富,可擴充套件性強,文件全面,例程也很豐富,Google 上的資料也很多。現代化的 C++ 設計,泛型程式設計思維,值得研究。缺點是上手慢,體積大。一旦掌握之後,用起來將會得心應手,遊刃有餘。如果需要豐富的功能(如輸出到網路 /XML 、統計功能、更多的個性化設定),建議選擇 boost log 。

boost log學習要點

boost log 配置和編譯

boost log支援以下配置巨集,只列出一些常用的,如下表所示:

Macro name Effect
BOOST_LOG_DYN_LINK If defined in user code, the library will assume the binary is built as a dynamically loaded library (“dll” or “so”). Otherwise it is assumed that the library is built in static mode. This macro must be either defined or not defined for all translation units of user application that uses logging. This macro can help with auto-linking on platforms that support it.
BOOST_ALL_DYN_LINK Same as BOOST_LOG_DYN_LINK but also affects other Boost libraries the same way.
BOOST_USE_WINAPI_VERSION Affects compilation of both the library and user’s code. This macro is Windows-specific. Selects the target Windows version for various Boost libraries, including Boost.Log. Code compiled for a particular Windows version will likely fail to run on the older Windows versions, but may improve performance because of using newer OS features. The macro is expected to have an integer value equivalent to _WIN32_WINNT.
BOOST_LOG_NO_THREADS If defined, disables multithreading support. Affects the compilation of both the library and users’ code. The macro is automatically defined if no threading support is detected.

有一點要注意:如果你的程式工程中由多個模組構成(例如,由一個.exe和多個.dll構成),當你使用boost log
庫時必須built as a shared object。如果只是單個模組(例如:單個.exe或單個.dll)則可以build the library as a static library.

Boost官方文件原文如下:
One thing should be noted, though. If your application consists of more than one module (e.g. an exe and one or several dll’s) that use Boost.Log, the library must be built as a shared object. If you have a single executable or a single module that works with Boost.Log, you may build the library as a static library.

常用名稱空間定義

為了簡化程式碼,以下程式碼非常常用:

namespace logging = boost::log;
namespace sinks = boost::log::sinks;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;

boost log涉及的重點概念或術語定義解釋

日誌記錄:一個獨立的訊息包,這個訊息包還不是實際寫到日誌裡的訊息,它只是一個候選的訊息。
屬性 : 日誌記錄中的一個訊息片。
屬性值:那就是上面所說的屬性的值了,可以是各種資料型別。
日誌槽(LOG SINK):日誌寫向的目標,它要定義日誌被寫向什麼地方,以及如何寫。
日誌源:應用程式寫日誌時的入口,其實質是一個logger物件的例項。
日誌過濾器:決定日誌記錄是否要被記錄的一組判斷。
日誌格式化:決定日誌記錄輸出的實際格式。
日誌核心:維護者日誌源、日誌槽、日誌過濾器等之間的關係的一個全域性中的實體。主要在初始化logging library時用到。

為了不理解錯誤,這裡附加官方英文解釋如下所示:

概念或術語 定義或解釋
Log record A single bundle of information, collected from the user’s application, that is a candidate to be put into the log. In a simple case the log record will be represented as a line of text in the log file after being processed by the logging library.
Attribute An “attribute” is a piece of meta-information that can be used to specialize a log record. In Boost.Log attributes are represented by function objects with a specific interface, which return the actual attribute value when invoked.
Attribute value Attribute values are the actual data acquired from attributes. This data is attached to the specific log record and processed by the library. Values can have different types (integers, strings and more complex, including user defined types). Some examples of attribute values: current time stamp value, file name, line number, current scope name, etc.. Attribute values are enveloped in a type erasing wrapper, so the actual type of the attribute is not visible in the interface. The actual (erased) type of the value is sometimes called the stored type.
(Attribute) value visitation A way of processing the attribute value. This approach involves a function object (a visitor) which is applied to the attribute value. The visitor should know the stored type of the attribute value in order to process it.
(Attribute) value extraction A way of processing the attribute value when the caller attempts to obtain a reference to the stored value. The caller should know the stored type of the attribute value in order to be able to extract it.
Log sink A target, to which all log records are fed after being collected from the user’s application. The sink defines where and how the log records are going to be stored or processed.
Log source An entry point for the user’s application to put log records to. In a simple case it is an object (logger) which maintains a set of attributes that will be used to form a log record upon the user’s request. However, one can surely create a source that would emit log records on some side events (for example, by intercepting and parsing console output of another application).
Log filter A predicate that takes a log record and tells whether this record should be passed through or discarded. The predicate typically forms its decision based on the attribute values attached to the record.
Log formatter A function object that generates the final textual output from a log record. Some sinks, e.g. a binary logging sink, may not need it, although almost any text-based sink would use a formatter to compose its output.
Logging core The global entity that maintains connections between sources and sinks and applies filters to records. It is mainly used when the logging library is initialized.
RTTI Run-time type information. This is the C++ language support data structures required for dynamic_cast and typeid operators to function properly.
TLS Thread-local storage. The concept of having a variable that has independent values for each thread that attempts to access it.
i18n Internationalization. The ability to manipulate wide characters.

boost log 設計概述

設計概述

boost log日誌庫由3個層次構成:收集日誌資料,處理日誌資料,以及在收集日誌資料和處理日誌資料2個層次之間的一個層次。

boost log 的設計主要由日誌器( Logger )、日誌核心( Logging core )、 Sink 前後端( frontend,backend )組成。日誌文字以及日誌環境由日誌器( Logger )負責蒐集,日誌核心負責處理日誌資料(例如全域性過濾、將日誌記錄傳遞給 Sink ), Sink 前端分為同步、非同步以及不考慮執行緒同步問題的版本,它們負責將日誌記錄傳遞給 Sink 後端處理。 Sink 後端負責把日誌記錄格式化並輸出到不同的介質中(例如日誌檔案、報警以及統計源中)。

如下圖所示:

這裡寫圖片描述

箭頭顯示了日誌資訊的流向。日誌資訊不一定會儲存在日誌記錄中,因為日誌處理的結果可能包括一些操作,而實際上不將資訊儲存在任何地方。舉個例子,如果應用程式處於一個危險的狀態,它可以發出一個特殊的日誌記錄進行特定的處理,以便使用者可以看到在系統托盤上的應用程式圖示上顯示的一個錯誤訊息作為提示通知,並聽到一個報警聲音。這是一個非常重要的特性。boost log不僅僅用於經典日誌記錄,還可以嚮應用程式使用者發出一些重要事件並積累統計資料等(圖中最後三個箭頭所指向的三個用途:經典日誌記錄、報警通知、資料統計)。

日誌源(Logging sources)

boost log 支援自定義日誌源,但它不是用來過濾級別的(因為過濾功能用sink的filtering就夠了),它的日誌源可以包含特定環境的資訊(例如在網路連線 network_connection 中的日誌源可以攜帶遠端 IP 地址這個屬性,這樣從那個日誌源發出的每一條日誌資訊都包含此屬性)。

更多請參考以下官方英文說明:
Getting back to the figure, in the left side your application emits log records with help of loggers - special objects that provide streams to format messages that will eventually be put to log. The library provides a number of different logger types and you can craft many more yourself, extending the existing ones. Loggers are designed as a mixture of distinct features that can be combined with each other in any combination. You can simply develop your own feature and add it to the soup. You will be able to use the constructed logger just like the others - embed it into your application classes or create and use a global instance of the logger. Either approach provides its benefits. Embedding a logger into some class provides a way to differentiate logs from different instances of the class. On the other hand, in functional-style programming it is usually more convenient to have a single global logger somewhere and have a simple access to it.

Generally speaking, the library does not require the use of loggers to write logs. The more generic term “log source” designates the entity that initiates logging by constructing a log record. Other log sources might include captured console output of a child application or data received from network. However, loggers are the most common kind of log sources.

屬性和屬性值(Attributes and attribute values)

有時程式需要記錄的資訊往往不僅僅包含一條訊息,還需要包含執行環境的一些屬性(例如網路對端的 IP 地址)。 boost::log 提供了 屬性集 功能,屬性集不僅僅包括常用的資料(例如計數器、時間、計時器、執行緒 ID 等資訊)還支援自定義屬性。使用者可以將程式的任意上下文放入日誌記錄物件中,然後在 sink 中進行處理。

更多請參考以下官方英文說明:
In order to initiate logging a log source must pass all data, associated with the log record, to the logging core. This data or, more precisely, the logic of the data acquisition is represented with a set of named attributes. Each attribute is, basically, a function, whose result is called “attribute value” and is actually processed on further stages. An example of an attribute is a function that returns the current time. Its result - the particular time point - is the attribute value.

There are three kinds of attribute sets:

  • global
  • thread-specific
  • source-specific

You can see in the figure that the former two sets are maintained by the logging core and thus need not be passed by the log source in order to initiate logging. Attributes that participate in the global attribute set are attached to any log record ever made. Obviously, thread-specific attributes are attached only to the records made from the thread in which they were registered in the set. The source-specific attribute set is maintained by the source that initiates logging, these attributes are attached only to the records being made through that particular source.

When a source initiates logging, attribute values are acquired from attributes of all three attribute sets. These attribute values then form a single set of named attribute values, which is processed further. You can add more attribute values to the set; these values will only be attached to the particular log record and will not be associated with the logging source or logging core. As you may notice, it is possible for a same-named attribute to appear in several attribute sets. Such conflicts are solved on priority basis: global attributes have the least priority, source-specific attributes have the highest; the lower priority attributes are discarded from consideration in case of conflicts.

日誌核心和過濾器(Logging core and filtering)

boost log支援兩層過濾,可以通過core::set_filter 設定全域性過濾器,也能通過 sink::set_filter 設定 sink 過濾器。配合 lambda 表示式 或者函式物件,boost log 可以生成無論多麼複雜的過濾器。(例如只把某個級別的日誌寫入檔案,或者把統計日誌、trace日誌分別重定向到不同的sink中,然後輸出到不同的介質)。

更多請參考以下官方英文說明:
When the set of attribute values is composed, the logging core decides if this log record is going to be processed in sinks. This is called filtering. There are two layers of filtering available: the global filtering is applied first within the logging core itself and allows quickly wiping away unneeded log records; the sink-specific filtering is applied second, for each sink separately. The sink-specific filtering allows directing log records to particular sinks. Note that at this point it is not significant which logging source emitted the record, the filtering relies solely on the set of attribute values attached to the record.

It must be mentioned that for a given log record filtering is performed only once. Obviously, only those attribute values attached to the record before filtering starts can participate in filtering. Some attribute values, like log record message, are typically attached to the record after the filtering is done; such values cannot be used in filters, they can only be used by formatters and sinks themselves.

日誌槽和格式化(Sinks and formatting)

boost log可以針對不同sink設定不同引數。如果想要設定不同的格式,只需要傳遞一個 formatter物件給sink即可。 boost log支援不同的sink按照檔案大小、時間等引數來旋轉 。boost log支援寬字元日誌,sink可以通過設定locale進行必要的編碼轉換。

boost log支援同步和非同步sink,同步sink在將日誌傳遞給backend時會加互斥鎖 。 boost log的同步非同步 sink 切換起來非常方便,只需要修改型別名就可以了。

boost log支援自定義 sink ,可以實現一條日誌資訊複製分流到多個 sink 進行處理,也可以在 sink 中以不同的格式輸出日誌。boost 將日誌抽象成 record 物件,不僅僅包含了日誌的文字,還可以包含更豐富的自定義屬性。

更多請參考以下官方英文說明:
If a log record passes filtering for at least one sink the record is considered to be consumable. If the sink supports formatted output, this is the point when log message formatting takes place. The formatted message along with the composed set of attribute values is passed to the sink that accepted the record. Note that formatting is performed on the per-sink basis so that each sink can output log records in its own specific format.

As you may have noticed on the figure above, sinks consist of two parts: the frontend and the backend. This division is made in order to extract the common functionality of sinks, such as filtering, formatting and thread synchronization, into separate entities (frontends). Sink frontends are provided by the library, most likely users won’t have to re-implement them. Backends, on the other hand, are one of the most likely places for extending the library. It is sink backends that do the actual processing of log records. There can be a sink that stores log records into a file; there can be a sink that sends log records over the network to the remote log processing node; there can be the aforementioned sink that puts record messages into tool-tip notifications - you name it. The most commonly used sink backends are already provided by the library.

Along with the primary facilities described above, the library provides a wide variety of auxiliary tools, such as attributes, support for formatters and filters, represented as lambda expressions, and even basic helpers for the library initialization. You will find their description in the Detailed features description section. However, for new users it is recommended to start discovering the library from the Tutorial section.

其他

  • boost log提供很多使用小工具,例如支援日誌排序、輸出操作、binary dump 操作( logging::dump(packet.data(), packet.size() 對於輸出記憶體塊很方便)。
  • boost log支援從配置檔案中讀取日誌配置資訊,可以通過修改配置檔案來修改日誌過濾器、格式等配置。
  • 對於不同的輸出方式,boost log提供了一些現成的sink backend ,例如輸出到 Windows事件日誌、偵錯程式、Linux syslog介面、文字檔案等。

簡單例子

這是最簡單的一個應用示例,輸出不同等級的日誌資訊到控制檯。這裡[…] 裡面就是不同屬性的屬性值,這裡有時戳、等級等屬性,輸出的是預設format,這裡的日誌等級也是boost log預設定義好的,這裡不再詳述。後面的文章中將會有更加豐富的示例及詳細說明。

#include "stdafx.h"
#include<stdio.h>
#include<boost/log/trivial.hpp>

int _tmain(int argc, _TCHAR* argv[])
{
    BOOST_LOG_TRIVIAL(trace)<<"A trace severity message";
    BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
    BOOST_LOG_TRIVIAL(info) << "An informational severity message";
    BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
    BOOST_LOG_TRIVIAL(error) << "An error severity message";
    BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";

    system("pause");
    return 0;
}

執行結果:

這裡寫圖片描述