본문 바로가기
Programmer/C++

[C++] Boost Logging에 대해서

by 손정빈 2019. 5. 13.
728x90
반응형

C++ Log Library에 대해서 알아보고자 합니다.

glog, log4Cxx 등의 라이브러리도 존재하지만 Boost::log에 대해서 한번 알아보고자 합니다.

 

Boost::log Design

Logger (log source) : 다양한 형식의 log stream을 제공하는 객체들, 이미 라이브러리에는 설정되어 있는 여러 logger들이 존재하며, 필요하다면 직접 만들어 쓸 수도 있습니다. 로깅에 필수적인 Log record를 생성하는 역할과 로깅을 시작하는 엔티티를 지정합니다.

 

// Logger 생성하기 ( Default Global Logger )
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt)
// Logger 받아오기
src::logger_mt& lg = my_logger::get();
// Logger로 log record작성하기
logging::record rec = lg.open_record();
if (rec)
{
   logging::record_ostream strm(rec);
   strm << "Hello, World!";
   strm.flush();
   lg.push_record(boost::move(rec));
}

 

Attribute : 로깅을 시작하기 위해서는 Logger(log sources)와 연관된 모든 데이터를 Logging Core에 전달해야 합니다.

위와 같은 데이터 또는 논리 수집의 데이터들을 Attribute(속성)이라고 가르킵니다.

또한 각 각의 Attribute는 함수이며, 결과 값들을 Attribute value라고 부릅니다.

쉽게 말하자면 Attribute는 각 Attribute value를 반환하는 함수라고 생각하시면 될 것 같습니다.

attribute는 어디에서 정보를 받아오느냐에 따라 

- global

- thread-specific 

- source-specific 

3가지 형태로 나누어집니다.

글로벌과 쓰레드의 경우 로거를 거치지 않고 직접 쓰레드나 프로세스로 부터 정보를 얻는 함수들입니다.

void add_common_attributes()
{
    logging::core::get()->add_global_attribute(
        "LineID", attrs::counter< unsigned int >(1));
    logging::core::get()->add_global_attribute(
        "TimeStamp", attrs::local_clock());
    ...
}

 

Logging Core : 로깅 코어로 온 속성 값들은 sink로 전달되기 전에 필터를 한번 걸치게 됩니다. 전체적으로 진행하는 글로벌 필터와 sink별로 따로 있는 sink specific filter들을 거쳐 sink로 전달됩니다. 

즉, 전체 atrribute value(속성 값)를 모아서 필요한 sink에게 속성 값을 전달하는 역할을 수행합니다.

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

 

Sink : 일단 속성 값이 Sink까지 왔다면 이 속성 값(Attribute value)는 반드시 출력이 되어야합니다. sink는 고유한 format에 따라 들어온 속성 값들을 형식화하여 formatted message를 만들어 줍니다.

front-end sink는 앞에서 말한 formatting, filterring, 쓰레드 동기화 작업을 진행하며, 필터와 포멧만 넣어주면 나머지 진행되는 작업은 동길하기에 라이브러리에서 만들어준 front-end sink를 사용하면 됩니다. back-end sink는 처리된 메세지를 원하는 output에 전달하는 역할로, output의 종류가 매우 다양하기 때문에 ,back-end sink의 구현은 가지각색입니다. 그래도 대부분의 경우 라이브러리에서 제공을 해주고 있습니다.

 

Boost::Log에 설계에 대해서 알아보았는데 그렇다면 지금부터 사용법에 대해서 알아보도록 합시다.

우선 https://www.boost.org/doc/libs/1_64_0/libs/log/doc/html/log/tutorial.html 를 확인하시면 설명이 잘 나와있습니다.

하지만 영어로 작성되어 보기 불편하기에 저는 따로 이렇게 글을 작성하고자 하는 것입니다. ㅎㅎ

 

간단한 로깅

#include <boost/log/trivial.hpp>

int main(int, char*[])
{
    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";

    return 0;
}

간단하게 로그 파일을 찍는 코드입니다.

 

필터링과 간단한 로깅

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

int main(int, char*[])
{
    init();

    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";

    return 0;
}

심각도 info이상의 경우에만 출력을 하라는 필터 코드입니다.

info, warning, error, fatal 4개만 출력됩니다.

 

 

Setting up Sinks

간단한 로깅의 경우 유연성을 제공하지 못하는데요. 예를 들어 콘솔에 단순히 출력하는 것보다 좀 더 정교하고 자세한 로그처리를 원할 수 있습니다. 이때 사용자가 직접적으로 정의를 하기 위해서는 로깅 싱크를 로깅 코어에 등록하여야 합니다.

void init()
{
    logging::add_file_log("sample.log");

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

위 코드는 sample.log 파일에 로그를 출력하기 위한 설정 코드입니다. 그 외에도 파일의 간격, 크기 제한 등을 설정 할 수 있습니다.

void init()
{
    logging::add_file_log
    (
        keywords::file_name = "sample_%N.log",
        // 파일의 이름 패턴
        keywords::rotation_size = 10 * 1024 * 1024,
        // 파일의 최대 크기, 즉 "파일의 로테이션를 10MB마다 해라" 라는 의미
        keywords::time_based_rotation = sinks::file::rotation_at_time_point(0, 0, 0),
        // 파일의 로테이션 시간, "자정마다"
        keywords::format = "[%TimeStamp%] [%ThreadID%] [%Severity%] [%ProcessID%] [%LineID%] %Message%"
        // 로그 레코드의 출력 형식
    );

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

위와 같이 좀 더 복잡한 sink를 설정을 할 수 있습니다.

반응형

댓글