glog의 기본적인 사용법에 대해 설명한다. (원문: http://rpg.ifi.uzh.ch/docs/glog.html)
본 문서는 Ubuntu 18.04 환경 기준으로 작성되었다.
glog library와 관련된 자세한 내용은 🏠 https://github.com/google/glog 에서 확인할 수 있다.
Install
google-glog 사용을 위해 다음과 같이 설치를 진행한다.
$ sudo apt update
$ sudo apt install libgoogle-glog-dev
+) MacOS의 경우 다음과 같이 설치할 수 있다.
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
$ brew install glog
Introduction
Google glog 라이브러리는 C++ 스타일의 stream과 다양한 helper macro를 기반으로 하는 logging API를 제공한다.
아래 sample code에서 작성된 예와 같이 LOG(serverity level) API에 원하는 message를 간단히 streaming하는 것만으로 logging을 할 수 있다.
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
// gflags library를 이용할 경우 아래와 같이 command line flag option을 변경해 주어야 한다.
// (빌드시에도 gflag library가 linking되어야 함)
google::ParseCommandLineFlags(&argc, &argv, true);
// default 설정 시, INFO, WARNING 레벨은 로그 파일에만 출력됨
LOG(INFO) << "INFO 레벨의 로그";
LOG(WARNING) << "WARNING 레벨의 로그";
// default 설정 시, ERROR 레벨 이상부터 stderr 로 출력된다.
LOG(ERROR) << "ERROR 레벨의 로그";
// FATAL의 경우, Stack trace를 출력하고 프로그램을 종료시킨다.
LOG(FATAL) << "FATAL 레벨의 로그";
return 0;
}
이를 실행하면 다음과 같이 로그가 출력된다.
$ ./ a.out
E0205 11:20:40.303856 13248 main.cpp:16] ERROR 레벨의 로그
F0205 11:20.40.303978 13248 main.cpp:19] FATAL 레벨의 로그
*** Check failure stack trace: ***
@ 0x7f931d7155cd google::LogMessage::Fail()
@ 0x7f931d717433 google::LogMessage::SendToLog()
@ 0x7f931d71515b google::LogMessage::Flush()
@ 0x7f931d717e1e google::LogMessageFatal::~LogMessageFatal()
@ 0x400bca main
@ 0x7f931cdc8830 __libc_start_main
@ 0x4009d9 _start
@ (nil) (unknown)
Abort (core dumped)
$ ls -la /tmp/a.out.
a.out.host-name.user-name.log.ERROR.20180205-112040.13248 a.out.ERROR
a.out.host-name.user-name.log.FATAL.20180205-112040.13248 a.out.FATAL
a.out.host-name.user-name.log.INFO.20180205-112040.13248 a.out.INFO
a.out.host-name.user-name.log.WARNING.20180205-112040.13248 a.out.WARNING
Log line format은 다음과 같다.
[IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] message...
Google glog는 다양하고 일반적인 logging task들을 단순화한 일련의 매크로를 정의한다.
glog를 사용할 경우, 우리가 얻을 수 있는 이점은 다음과 같다.
- 여러가지 severity level에 따라 message를 logging하는 것이 가능하다. (Secerity Level)
- command line상에서 logging 동작을 조작할 수 있다. (Setting Flags)
- 특정 조건에 따른 logging이 가능하다. (Conditional / Occasional Logging)
- 기대했던 조건데 맞지 않을 경우, 프로그램을 종료시킬 수 있다. (FATAL Level Log)
- customize된 상세 logging level을 적용할 수 있다. (Verbose Logging)
Severity Level
Google glog는 다음과 같은 severity level을 제공한다. (severity 값 증가 순서)
Severity Level | Value | Description |
INFO | 0 | 로그 파일에만 출력 됨 |
WARNING | 1 | 로그 파일에만 출력 됨 |
ERROR | 2 | 로그 파일 및 stderr로 출력 됨 |
FATAL | 3 | 로그 파일 및 stderr로 출력 됨 message를 출력 후, 프로그램을 종료시킨다. |
각 severity의 message들은 해당 severity 로그 파일에 기록될 뿐만 아니라, 자신보다 낮은 level의 로그 파일에도 기록된다.
예를 들면, FATAL level의 message는 FATAL, ERROR, WARNING, INFO level 의 로그 파일에 모두 기록된다.
DFATAL 이라는 특별 severity가 존재하는데, 이는 debug mode (NDEBUG 매크로가 정의되지 않은 경우) 에서는 FATAL level로 동작하지만, normal/release mode 에서는 ERROR level로 동작한다. (severity level을 낮춤)
glog는 로그 파일을 각 severity level에 따라 생성하며, default 설정의 경우 /tmp 경로 하위에 <program name>.<host name>.<user name>.log.<severity level>.<date>.<time>.<pid> 의 이름으로 생성한다.
Setting Flags
glog의 플래그 값을 설정하여, 로그 출력 방법을 변경하거나, 출력 가능한 최소 severity level을 변경, 또는 로그 파일의 생성 위치를 변경하는 등 logging 동작을 제어할 수 있다.
플래그는 1) Command Line을 이용하는 경우, 2) 환경 변수를 이용하는 경우 그리고 3) 전역 변수를 이용하는 경우 총 세가지 방법으로 설정할 수 있다.
공통적으로 사용되는 플래그들은 다음과 같다.
Flag | Type | Default | Description |
logtostderr | bool | false | 로그 파일 대신 stderr로 message를 남긴다. 플래그 값으로 true를 의미하는 1/true/yes, false를 의미하는 0/false/no를 사용할 수 있다. (대소문자 구분 없음) |
stderrthreshold | int | 2 (ERROR) |
설정한 level 이상의 message들을 로그 파일 외에 stderr로도 출력되도록 한다. (severity level INFO, WARNING, ERROR, FATAL 은 각각 0, 1, 2, 3 에 대응된다.) |
minloglevel | int | 0 (INFO) |
설정한 level 이상의 message들만 logging 되도록 한다. (severity level INFO, WARNING, ERROR, FATAL 은 각각 0, 1, 2, 3 에 대응된다.) |
log_dir | string | "" | 로그 파일들이 기본 경로 (/tmp) 대신 설정된 path에 저장되도록 한다. |
v | int | 0 | 설정된 값 m에 대하여, 이 값과 같거나 작은 경우의 message들만 VLOV(m) message로 보여준다. (--vmodule 에 의해 오버라이드 될 수도 있다. 자세한 내용은 Verbose Logging 참고) |
vmodule | string | "" | 모듈별 상세 정보 수준 이 인자는 쉼표로 구분되는 <module name> = <log level> 의 리스트를 포함해야 한다. <module name> 은 파일 기반으로 매칭되는 glob 패턴이다. <log level> 은 --v 플래그에 의해 주어진 값은 오버라이드한다. (자세한 내용은 Verbose Logging 참고) |
이 밖에 다른 플래그들은 logging.h 에 정의 되어 있다. 모든 플래그 리스트를 확인하고 싶다면, 해당 파일에서 DECLARE 로 검색하여 확인할 수 있다.
1) Command Line을 이용하는 경우
Google gflgs 라이브러리를 이용하여, command line 상에서 glog 플래그 값을 설정할 수 있다.
gflags 라이브러리는, glog 라이브러리 설치 시 함께 설치되나, 별도 설치가 필요한 경우 다음과 같이 설치할 수 있다.
$ sudo apt isntall libgflags2.2 libgflags-dev
+) MacOS의 경우 다음과 같이 설치할 수 있다.
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null
$ brew install gflags
앞서 소개된 플래그들 중 logtostderr 플래그를 true로 설정하고 싶다면, 다음과 같이 커맨드 라인에서 어플리케이션을 실행하면 된다.
(단, gflags 라이브러리를 사용하므로, Introduction에 설명된 것과 같이 google::ParseCommandLineFlags 를 호출하여 관련 내용이 설정되어 있어야 한다.)
$ ./a.out --logtostderr=true
I0205 13:02:59.448195 15296 main.cpp:12] INFO 레벨의 로그
W0205 13:02:59.448351 15296 main.cpp:13] WARNING 레벨의 로그
E0205 13:02:59.448366 15296 main.cpp:16] ERROR 레벨의 로그
F0205 13:02.59.448377 15296 main.cpp:19] FATAL 레벨의 로그
...
2) 환경 변수를 이용하는 경우
Google gflags 라이브러리를 사용하지 않고, 환경 변수를 이용해 플래그 값을 설정할 수도 있다.
다음과 같이 "GLOG_" 라는 prefix를 붙인 환경 변수를 설정하면 된다.
$ GLOG_logtostderr=true ./a.out
I0205 13:02:59.448195 15296 main.cpp:12] INFO 레벨의 로그
W0205 13:02:59.448351 15296 main.cpp:13] WARNING 레벨의 로그
E0205 13:02:59.448366 15296 main.cpp:16] ERROR 레벨의 로그
F0205 13:02.59.448377 15296 main.cpp:19] FATAL 레벨의 로그
...
3) 전역 변수를 이용하는 경우
이 밖에도 코드 상에서 전역 변수(FLAGS_*)를 직접 수정하여 플래그 값을 설정할 수도 있다.
대부분의 설정이 업데이트 한 수 즉시 적용되나, 파일과 관련된 플래그들은 예외이다. 예를 들어, 로그 디렉토리 설정을 위한 log_dir 플래그는 google::InitGoogleLogging 을 호출하기 전에만 설정 가능하다.
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
LOG(INFO) << "file로 출력";
// 대부분의 flag들은 값을 업데이트하면 즉시 적용됨
FLAGS_logtostderr = 1;
LOG(INFO) << "stderr로 출력";
FLAGS_logtostderr = 0;
// 이 경우, 로그 파일의 생성 경로가 변경되지 않는다.
// 만약 이 값을 변경하고 싶다면 google::InitGoogleLogging 호출 전에 설정해야 한다.
FLAGS_log_dir = "/some/log/directory";
// 설정한 "/some/log/directory"가 아닌 기본 경로인 "/tmp"에 로깅된다.
LOG(INFO) << "같은 file로 출력";
return 0;
}
이를 실행하면 다음과 같이 로그가 출력된다.
$ a.out
I0205 13:35:52.235412 18714 main.cpp:11] stderr로 출력
$ cat /tmp/a.out.INFO
Log file create at: 2018/02/05 13:35:52
Running on machine: host-name
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
I0205 13:35:52.235045 18714 main.cpp:7] file로 출력
I0205 13:35:52.235443 18714 main.cpp:19] 같은 file로 출력
Conditional / Occasional Logging
때때로, 특정 조건에서만 메세지를 logging 하고 싶을 경우, glog에서 제공하는 매크로드를 사용할 수 있다.
1) Conditional Logging
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
int num_cookies = 0;
LOG(INFO) << "1. Conditional Logging";
for(num_cookies = 0; num_cookies < 30; num_cookies++)
{
LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
}
return 0;
}
"Got lots of cookies" 라는 메세지가 num_cookies 변수 값이 10을 초과하는 경우에만 logging된다.
코드가 여러번 수행되는 경우, 이 매크로는 특정 조건이 충족될 때, 메세지를 남기는 데 유용하다. 이런 종류의 로깅은 정보 메세지에 매우 유용하다.
이를 실행하면 다음과 같이 로그가 출력된다.
$ a.out
I0205 17:34:12.574044 23149 main.cpp:9] 1. Conditional Logging
I0205 17:34:12.574200 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574214 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574224 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574234 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574250 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574262 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574275 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574287 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574299 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574311 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574324 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574337 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574345 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574357 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574368 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574378 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574390 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574403 23149 main.cpp:12] Got lots of cookies
I0205 17:34:12.574414 23149 main.cpp:12] Got lots of cookies
2) Occasional Logging
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
int num_cookies = 0;
LOG(INFO) << "2. Occasional Logging";
for(num_cookies = 0; num_cookies < 30; num_cookies++)
{
LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie";
}
return 0;
}
1번째, 11번째, 21번째 실행 될 때만 메세지를 출력한다. google::COUNTER 값은 몇 번 반복이 일어났는지 식별하는 데 사용한다.
이를 실행하면 다음과 같이 로그가 출력된다.
$ a.out
I0205 17:34:12.574425 23149 main.cpp:9] 2. Occasional Logging
I0205 17:34:12.574440 23149 main.cpp:12] Got the 1st cookie
I0205 17:34:12.574456 23149 main.cpp:12] Got the 11st cookie
I0205 17:34:12.574468 23149 main.cpp:12] Got the 21st cookie
3) Conditional + Occasional Logging
Conditinal과 Occasional을 조합하여 사용할 수도 있다.
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
int num_cookies = 0;
LOG(INFO) << "3. Conditional + Occasional Logging 1";
for(num_cookies = 0; num_cookies < 30; num_cookies++)
{
int size = 64 * num_cookies;
LOG_IF_EVERY_N(INFO, (size >= 1024), 10) << "Got the " << gooogle::COUNTER << "th big cookie";
}
LOG(INFO) << "4. Conditional + Occasional Logging 2";
for(num_cookies = 0; num_cookies < 30; num_cookies++)
{
LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie";
}
return 0;
}
앞 예제의 경우, size가 1024보다 커지는 첫번째 경우 (17번째)를 시작으로 10을 주기 (27번째, ...) 로 메세지를 출력한다.
뒤 예제의 경우, 매번 메세지를 남기는 대신에, 처음 n (20번째) 번째 까지의 메세지만 출력되도록 제한하고 있다.
이를 실행하면 다음과 같이 로그가 출력된다.
$ a.out
I0205 17:34:12.574481 23149 main.cpp:9] 3. Conditional + Occasional Logging 1
I0205 17:34:12.574493 23149 main.cpp:13] Got the 17th big cookie
I0205 17:34:12.574506 23149 main.cpp:13] Got the 27th big cookie
I0205 17:34:12.574518 23149 main.cpp:16] 4. Conditional + Occasional Logging 2
I0205 17:34:12.574532 23149 main.cpp:19] Got the 1th cookie
I0205 17:34:12.574544 23149 main.cpp:19] Got the 2th cookie
I0205 17:34:12.574556 23149 main.cpp:19] Got the 3th cookie
I0205 17:34:12.574568 23149 main.cpp:19] Got the 4th cookie
I0205 17:34:12.574580 23149 main.cpp:19] Got the 5th cookie
I0205 17:34:12.574592 23149 main.cpp:19] Got the 6th cookie
I0205 17:34:12.574604 23149 main.cpp:19] Got the 7th cookie
I0205 17:34:12.574616 23149 main.cpp:19] Got the 8th cookie
I0205 17:34:12.574630 23149 main.cpp:19] Got the 9th cookie
I0205 17:34:12.574641 23149 main.cpp:19] Got the 10th cookie
I0205 17:34:12.574653 23149 main.cpp:19] Got the 11th cookie
I0205 17:34:12.574666 23149 main.cpp:19] Got the 12th cookie
I0205 17:34:12.574677 23149 main.cpp:19] Got the 13th cookie
I0205 17:34:12.574690 23149 main.cpp:19] Got the 14th cookie
I0205 17:34:12.574702 23149 main.cpp:19] Got the 15th cookie
I0205 17:34:12.574714 23149 main.cpp:19] Got the 16th cookie
I0205 17:34:12.574725 23149 main.cpp:19] Got the 17th cookie
I0205 17:34:12.574738 23149 main.cpp:19] Got the 18th cookie
I0205 17:34:12.574750 23149 main.cpp:19] Got the 19th cookie
I0205 17:34:12.574764 23149 main.cpp:19] Got the 20th cookie
Debug Mode Support
debug mode 로깅 매크로는 디버그 모드에서만 효과가 있고, 디버그 모드가 아닐 경우에는 컴파일 시 제외된다.
과도한 로깅으로 인해 출시되는 프로그램의 속도가 느려지지 않도록 하려면, 이 매크로를 사용하면 된다.
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
int num_cookies = 20;
// 1. Normal 매크로
LOG(INFO) << "Found normal cookies";
LOG_IF(INFO, num_cookies > 10) << "Got lots of normal cookies";
// 2. Debug 매크로
DLOG(INFO) << "Found debug cookies";
DLOG_IF(INFO, num_cookies > 10) << "Got lots of debug cookies";
return 0;
}
debug mode로 컴파일하여 실행하면 다음과 같이 로그가 출력된다.
I0205 18:24:47.970146 25718 main.cpp:10] Found normal cookies
I0205 18:24:47.970731 25718 main.cpp:11] Got lots of normal cookies
I0205 18:24:47.970738 25718 main.cpp:14] Found debug cookies
I0205 18:24:47.970742 25718 main.cpp:15] Got lots of debug cookies
NDEBUG 옵션을 추가하여 normal mode로 컴파일하여 실행하면 다음과 같이 로그가 출력된다.
I0205 18:24:47.970146 25718 main.cpp:10] Found normal cookies
I0205 18:24:47.970731 25718 main.cpp:11] Got lots of normal cookies
CHECK Macros
가능한 빨리 오류를 감지하기 위해 어플리케이션에서 기대했던 조건을 자주 확인하는 것이 좋다.
CHECK 매크로는 표준 C 라이브러리에 정의된 assert 매크로와 유사하게 조건이 충족되지 않을 경우, 어플리케이션을 중단하는 기능을 제공한다. (조건이 true가 아닌 경우 중단)
assert 와는 달리 NDEBUG 에 의해 제어되지 않으므로, 컴파일 모드에 관계 없이 항상 실행된다.
CHECK 매크로에는 equality/inequality 확인을 위한 다양한 helper 매크로들이 있다. (CHECK_EQ, CHECK_NE, CHECK_LE 등)
이 매크로들은 두 값을 비교하고, 결과가 예상과 다를 때 두 값을 포함한 메세지를 FATAL level로 남긴다.
// 1. NDEBUG와 관계 없이 항상 실행 됨
CHECK(fp->Write(x) == 4) << "Write failed!";
// 2. CHECK_NE : inequality를 확인
CHECK_NE(1, 2) << ": The world must be ending!";
// 3. 임시 표현이 인자로 사용되는 경우
CHECK_EQ(string("abc")[1], 'b');
// 4-1. 포인터가 NULL인지 확인하는 경우 1
CHECK_EQ(some_ptr, static_cast<SomeType*>(NULL));
// 4-2. 포인터가 NULL인지 확인하는 경우 2
CHECK_NOTNULL(some_ptr);
some_ptr->DoSomething();
// 5. CHECK_NOTNULL을 생성자 초기화에 사용
struct S {
S(Something* ptr) : ptr_(CHECK_NOT_NULL(ptr)) {}
Something* ptr_;
};
우리는 각각의 인수자가 정확히 한번만 계산되고, 함수 인자로서 전달이 될 수 있는 것들은 어떤 것이든 전달이 될 수 있는 것에 주의해야 한다. 특히 3. 임시 표현이 인자로 사용되는 경우 와 같이 인자들이 구문의 끝에서 파괴되어 끝나는 임시 표현일 수도 있다.
4-1. 포인터가 NULL인지 확인하는 경우 1 에서는 포인터가 NULL 인지 확인하기 위해, 간단히 확인을 원하는 포인터 타입으로 static casting하여 사용하고 있다. 그러나 더 나은 방법은 4-2. 포인터가 NULL인지 확인하는 경우 2 와 같이 CHECK_NOTNULL 매크로를 사용하는 것이다. CHECK_NOTNULL 매크로는 주어진 포인터를 리턴하기 때문에, 5. CHECK_NOTNULL을 생성자 초기화에 사용 과 같이 생성자 초기화 리스트에 매우 유용하게 사용할 수 있다. (단, 이러한 기능 때문이 이 매크로를 C++ 스트림에 사용할 수 없다. 어플리케이션이 종료되기 전에 custom 메세지를 남기고 싶다면, 4-1. 포인터가 NULL인지 확인하는 경우 1 에서와 같이 CHECK_EQ 매크로를 사용하는 것이 좋다.)
C 문자열(char*)을 비교하는 경우, 대소문자를 구분하는 매크로도 있고, 구분하지 않는 매크로도 있다. (CHECK_STREQ, CHECK_STRNE, CHECK_STRCASEEQ, CHECK_STRCASENE) 이 매크로를 이용하면, 안전하게 NULL 포인터를 전달할 수 있다. NULL과 NULL이 아닌 문자열을 같지 않다고 판단하며, 둘 다 NULL인 경우에는 동일한 것으로 간주된다.
그 밖에도 약간의 오차를 허용하면서, 두 개의 실수형 값들이 같은 지 검사할 수 있는 CHECK_DOUBLE_EQ 매크로, 허용되는 오차를 명시적으로 전달할 수 있는 CHECK_NEAR 매크로 등이 있다.
Verbose Logging
어려운 버그를 추적하고 있을 떄, 철저한 로그 메세지는 매우 유용하다. 그러나 일반적인 개발에서 지나치게 많은 로그 메세지들은 무시하고 싶을 것이다. 이러한 상세한 메세지들을 위해 glog는 VLOG 매크로를 제공하여 사용자가 직접 logging level을 정의할 수 있도록 한다.
커맨드 라인에서 --v 옵션을 이용하여 어떤 Verbose message들을 남길 것인지 제어할 수 있다.
VLOG(1) << "I'm printed when you run the program with --v=1 or higher";
VLOG(2) << "I'm printed when you run the program with --v=2 or higher";
VLOG 를 사용하면, 설정한 레벨의 숫자가 더 클 수록 더 많은 메세지가 출력된다. 예를 들어, --v=1 이면, VLOG(1) 은 로깅되지만, VLOG(2) 는 로깅되지 않는다. 이는 INFO가 0이고, ERROR가 2인 severity level과는 반대이다. --minloglevel 의 경우 1로 설정되면, WARNING level 이상의 메세지를 로깅하지만, --v 의 경우 1로 설정되면, 1 level 이하의 메세지들이 로깅되는 것이다.
VLOG 매크로와 --v 플래그에 대해서는 모든 정수를 설정할 수 있지만, 이들의 일반적인 값은 양의 정수이다. 예를 들어, VLOG(0) 가 사용된 경우, 로그를 남기지 않으려면, 반드시 --v=-1 이하로 명시해야 하기 떄문이다. (--v 의 default 값이 0이므로)
VLOG 매크로를 이용한 로그들은 항상 INFO level 로 로깅된다.
1) Verbose Logging 제어
Verbose logging은 커맨드 라인을 이용하여 모듈 단위로 제어할 수 있다.
$ ./a.out --vmodule=mapreduce=2,file=1,gfs*=3 --v=0
위와 같이 플래그 옵션을 주면, 다음과 같이 동작한다.
- mapreduce.{h/cc} 에서 발생되는 VLOG(2) 이하의 메세지들을 출력한다.
- file.{h/cc} 에서 발생되는 VLOG(1) 이하의 메세지들을 출력한다.
- prefix가 gfs 인 파일들로부터 발생되는 VLOG(3) 이하의 메세지들을 출력한다. ('*', '?' 와 같은 와일드 카드를 지원한다.)
- 다른 곳에서 발생되는 VLOG(0) 이하의 메세지들을 출력한다.
VLOG_IS_ON(n) "verbose level" 조건 매크로도 지원한다. 이 매크로는 --v 가 n 보다 크거나 같을 때 true를 반환하며, 다음과 같이 사용할 수 있다.
if (VLOG_IS_ON(2)) {
// do some logging preparation and logging
// that can't be accoplished with just VLOG(2) << ...;
}
2) Conditional / Occasional Verbose Logging
Verbose Logging에서도 VLOG_IF, VLOG_EVERY_N 및 VLOG_IF_EVERY_N 과 같은 매크로를 사용할 수 있다. (마찬가지로 level 값은 severity level과 반대로 적용된다.)
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
google::ParseCommandLineFlags(&argc, &argv, true);
FLAGS_logtostderr = 1;
int size = 2048;
VLOG(1) << "1. Conditional Logging";
VLOG_IF(1, (size > 1024))
<< "I'm printed when size is more than 1024 and when you run the program with --v=1 or more";
VLOG(2) << "2. Occasional Logging";
for(int i = 0; i < 30; i++)
{
VLOG_EVERY_N(1, 10)
<< "I'm printed every 10th occurrence, "
"and when you run the program with --v=1 or more. "
"Present occurence is " << google::COUNTER;
}
VLOG(3) << "Conditional + Occasional Logging";
for(int i = 0; i < 30; i++)
{
size = 64 * i;
VLOG_IF_EVERY_N(1, (size >= 1024), 10)
<< "I'm printed on every 10th occurrence of case when size is more than 1024, "
"when you run the program with --v=1 or more. "
"Present occurence is " << google::COUNTER;
}
return 0;
}
--v=1 플래그를 설정하여 실행하면 다음과 같이 로그가 출력된다.
I0205 18:31:33.215885 24513 main.cpp:10] 1. Conditional Logging
I0205 18:31:33.216437 24513 main.cpp:11] I'm printed when size is more than 1024 and when you run the program with --v=1 or more
I0205 18:31:33.216445 24513 main.cpp:17] I'm printed every 10th occurrence, and when you run the program with --v=1 or more. Present occurence is 1
I0205 18:31:33.216461 24513 main.cpp:17] I'm printed every 10th occurrence, and when you run the program with --v=1 or more. Present occurence is 11
I0205 18:31:33.216466 24513 main.cpp:17] I'm printed every 10th occurrence, and when you run the program with --v=1 or more. Present occurence is 21
I0205 18:31:33.216471 24513 main.cpp:27] I'm printed on every 10th occurrence of case when size is more than 1024, when you run the program with --v=1 or more. Present occurence is 17
I0205 18:31:33.216477 24513 main.cpp:27] I'm printed on every 10th occurrence of case when size is more than 1024, when you run the program with --v=1 or more. Present occurence is 27
Failure Signal Handler
glog 라이브러리는 어플리케이션이 SIGSEGV 와 같은 특정 시그널에서 crash되었을 때, 유용한 정보를 dump하는 편리한 시그널 핸들러를 제공한다. 시그널 핸들러는 google::InstallFailureSignalHandler() 를 이용하여 설치한다.
#include <glog/logging.h>
int main(int argc, char* argv[])
{
google::InitGoogleLogging(argv[0]);
google::InstallFailureSignalHandler();
int a = 2;
int b = 0;
int c = a / b;
return 0;
}
실행하면 다음과 같이 로그가 출력된다.
$ ./a.out
*** Aborted at 1517878674 (unix time) try "date -d @1517878674" if you are using GNU date ***
PC: @ 0x40074b main
*** SIGFPE (@0x40074b) received by PID 6703 (TID 0x7f08cb4a7740) from PID 4196171l stack trace: ***
@ 0x7f08cace34b0 (unknown)
@ 0x40074b main
@ 0x7f08cacce830 __libc_start_main
@ 0x400649 _start
@ 0x0 (unknown)
Floating point exception (core dump)
기본적으로 시그널 핸들러는 stderr로 failure dump를 기록하지만, google::InstallFailureWriter() 를 이용하여 dump할 경로를 customize할 수도 있다.
'일하는' 카테고리의 다른 글
How To Use Google Mock (gmock) (0) | 2020.08.28 |
---|