Nanolog는 C++로 사용할 수 있는 매우 성능이 뛰어난 나노초 규모 로깅 시스템으로, 간단한 printf와 유사한 API를 제공하고 7나노초가 조금 넘는 중간 지연 시간으로 초당 8,000만 개 이상의 로그를 처리합니다.
이 미친 성능을 달성하는 방법은 컴파일 타임에 정적 로그 정보를 추출하고, 런타임 핫패스에 동적 구성 요소만 로깅하고, 오프라인 프로세스에 포맷팅을 연기하는 것입니다. 이는 기본적으로 작업을 런타임에서 벗어나 컴파일 및 사후 실행 단계로 옮깁니다.
이 로깅 시스템에서 사용된 기술에 대한 자세한 내용은 2018년 USENIX 연례 기술 컨퍼런스에서 발표된 NanoLog 논문이나 원 저자의 박사학위 논문을 참조하세요.
이 섹션에서는 spdlog v1.1.0, Log4j2 v2.8, Boost 1.55, glog v0.3.5 및 Windows 10에서 Windows 소프트웨어 추적 전처리기를 사용한 Windows 이벤트 추적(WPP)과 같은 기존 로깅 시스템에서 NanoLog의 성능을 보여줍니다.
지연 없이 100만 개의 메시지를 연달아 기록하고 1-16개의 로깅 스레드로 측정한 최대 처리량(NanoLog는 비슷한 크기의 로그 파일을 생성하기 위해 1억 개의 메시지를 기록). ETW는 "Windows용 이벤트 추적"입니다. 사용된 로그 메시지는 아래의 로그 메시지 맵에서 찾을 수 있습니다.
나노초로 측정되며 각 셀은 50/99.9번째 테일 대기 시간을 나타냅니다. 사용된 로그 메시지는 아래의 로그 메시지 맵에서 찾을 수 있습니다.
메시지 | NanoLog | spdlog | Log4j2 | glog | boost | ETW |
---|---|---|---|---|---|---|
staticString | 7/ 37 | 214/ 2546 | 174 / 3364 | 1198/ 5968 | 1764/ 3772 | 161/ 2967 |
stringConcat | 7/ 36 | 279/ 905 | 256 / 25087 | 1212/ 5881 | 1829/ 5548 | 191/ 3365 |
singleInteger | 7/ 3 | 268/ 855 | 180 / 9305 | 1242/ 5482 | 1914/ 5759 | 167/ 3007 |
twoIntegers | 8/ 62 | 437/ 1416 | 183 / 10896 | 1399/ 6100 | 2333/ 7235 | 177/ 3183 |
singleDouble | 8/ 43 | 585/ 1562 | 175/ 4351 | 1983/ 6957 | 2610/ 7079 | 165/ 3182 |
complexFormat | 8/ 40 | 1776/ 5267 | 202/ 18207 | 2569/ 8877 | 3334/ 11038 | 218/ 3426 |
위 벤치마크에서 사용된 로그 메시지. 기울임꼴은 동적 로그 인수를 나타냅니다.
메시지 ID | 사용된 로그 메시지 |
---|---|
staticString | 백업 복제본 가비지 수집기 스레드 시작 |
singleInteger | 백업 저장 속도(분): 181MB/s 읽기 |
twoIntegers | 버퍼가 1032024바이트의 추가 저장 공간을 소모했습니다. 현재 할당량: 1016544바이트 |
singleDouble | 비율 = 0.4인 묘비 비율 밸런서 사용 |
complexFormat | InfUdDriver 버퍼 초기화: 50000개 수신 버퍼(97MB), 50개 전송 버퍼(0MB), 26.2ms 소요 |
stringConcat | basic+udp:host=192.168.1.140,port=12246에서 코디네이터와 세션을 열었습니다. |
현재 NanoLog는 Linux 기반 시스템에서만 작동하며 다음 사항에 따라 달라집니다.
- C++17 컴파일러: GNU g++ 7.5.0 이상
- GNU Make 4.0 이상
- Python 3.4.2 이상
- POSIX AIO 및 스레드(일반적으로 Linux와 함께 설치됨)
NanoLog 시스템은 정적 로그 메타데이터를 중복 제거하고 동적 로그 데이터를 바이너리 형식으로 출력하여 저지연 로깅을 가능하게 합니다. 즉, NanoLog에서 생성된 로그 파일은 바이너리 형식이며 별도의 압축 해제 프로그램을 거쳐 사람이 읽을 수 있는 전체 ASCII 로그를 생성해야 합니다.
NanoLog에는 두 가지 버전(프리프로세서 버전과 C++17 버전)이 있으며, 상호 운용이 불가능하므로 애플리케이션에서 사용할 하나를 선택해야 합니다. 두 가지의 가장 큰 차이점은 프리프로세서 버전은 빌드 체인에 Python 스크립트를 통합해야 하는 반면, C++17 버전은 일반 라이브러리에 더 가깝다는 것입니다(단순히 빌드하고 링크하기만 하면 됩니다). 프리프로세서 버전을 사용하는 이점은 컴파일 타임에 더 많은 작업을 수행하여 런타임이 약간 더 최적화된다는 것입니다.
어느 것을 사용해야 할지 모르겠다면 C++17 NanoLog를 사용하면 더 편리합니다.
NanoLog의 C++17 버전은 기존 라이브러리처럼 작동합니다. #include "NanoLogCpp17.h"
를 실행하고 NanoLog 라이브러리에 링크하기만 하면 됩니다. 샘플 애플리케이션은 sample directory에서 찾을 수 있습니다.
C++17 NanoLog 런타임 라이브러리를 빌드하려면 런타임 디렉토리로 이동하여 make
를 호출합니다. 이렇게 하면 애플리케이션과 ./libNanoLog.a
를 연결하고 바이너리 로그를 다시 팽창시키는 데 사용할 수 있는 ./decompressor
애플리케이션이 생성됩니다.
애플리케이션을 컴파일할 때 NanoLog 헤더 디렉토리(-I ./runtime
)를 포함하고 NanoLog, pthreads 및 POSIX AIO(-L ./runtime/ -lNanoLog -lrt -pthread
)에 대한 링크를 사용하고 컴파일러에서 형식 검사를 활성화해야 합니다(예: 컴파일 플래그로 -Werror=format
을 전달). 후자의 단계는 형식 오류가 런타임에 로그 파일을 자동으로 손상시킬 수 있으므로 매우 중요합니다. 샘플 g++ 호출은 sample GNUmakefile에서 찾을 수 있습니다.
애플리케이션을 컴파일하고 실행한 후 생성된 로그 파일을 ./decompressor
애플리케이션으로 전달하면 사람이 읽을 수 있는 전체 로그 파일이 생성됩니다(지침은 아래 참조).
NanoLog의 전처리기 버전은 사용자 빌드 체인과의 긴밀한 통합이 필요하며 고급/초고급 사용자만을 대상으로 합니다.
사용자의 GNUmakefile에 NanoLogMakeFrag를 포함시키고, USR_SRCS 및 USR_OBJS 변수를 선언하여 모든 앱의 소스 및 개체 파일을 각각 나열하고, 미리 정의된 ```run-cxx매크로를 사용하여
g++`` 대신 모든 사용자 .cc 파일을 .o 파일로 컴파일해야 합니다. 자세한 내용은 프리프로세서 샘플 GNUmakefile을 참조하세요.
내부적으로 run-cxx
호출은 소스 파일에 Python 스크립트를 실행하고 사용자 애플리케이션의 각 컴파일에 특정한 라이브러리 코드를 생성합니다. 즉, 컴파일은 동일한 애플리케이션의 컴파일 간에도 이식 불가능한 NanoLog 라이브러리 버전을 빌드하고 각 make
호출은 이 라이브러리를 다시 빌드합니다.
또한 컴파일은 앱 디렉토리에 ./decompressor
실행 파일을 생성해야 하며, 이를 사용하여 사람이 읽을 수 있는 전체 로그 파일을 재구성할 수 있습니다(지침은 아래 참조).
샘플 애플리케이션은 사용자가 NanoLog 라이브러리와 인터페이스하는 방법에 대한 가이드로 의도되었습니다. 사용자는 이러한 애플리케이션을 수정하여 NanoLog의 다양한 API와 기능을 테스트할 수 있습니다. 이러한 애플리케이션의 C++17 및 프리프로세서 버전은 각각 ./sample 및 ./sample_preprocessor에 있습니다. 각 디렉토리에서 main.cc
를 수정하고, 애플리케이션을 빌드/실행하고, 압축 해제기를 실행하여 결과를 검토할 수 있습니다.
아래는 C++17 NanoLog의 샘플 애플리케이션에 대한 예입니다.
cd sample
# Modify the application
nano main.cc
make clean-all
make
./sampleApplication
./decompressor decompress /tmp/logFile
참고: 샘플 애플리케이션은 로그 파일을 /tmp/logFile
로 설정합니다.
코드에서 NanoLog 시스템을 사용하려면 NanoLog 헤더(C++17 NanoLog의 경우 NanoLogCpp17.h 또는 전처리기 NanoLog의 경우 NanoLog.h)를 포함하고 printf와 비슷한 방식으로 NANO_LOG()
함수를 호출하면 됩니다. 단, 앞에 로그 수준이 있어야 합니다. 아래 예:
#include "NanoLogCpp17.h"
using namespace NanoLog::LogLevels;
int main()
{
NANO_LOG(NOTICE, "Hello World! This is an integer %d and a double %lf\r\n", 1, 2.0);
return 0;
}
유효한 로그 수준은 DEBUG, NOTICE, WARNING 및 ERROR이며 로깅 수준은 NanoLog::setLogLevel(...)
를 통해 설정할 수 있습니다.
나머지 NanoLog API는 NanoLog.h 헤더 파일에 기록되어 있습니다.
사용자 애플리케이션을 실행하면 압축된 바이너리 로그 파일(기본 위치: ./compressedLog 또는 /tmp/logFile)이 생성됩니다. 로그 파일을 사람이 읽을 수 있게 만들려면 로그 파일을 사용하여 decompressor
애플리케이션을 호출하기만 하면 됩니다.
./decompressor decompress ./compressedLog
NanoLog 라이브러리를 빌드한 후 압축 해제 실행 파일은 ./runtime 디렉토리(C++17 NanoLog의 경우) 또는 사용자 앱 디렉토리(Preprocessor NanoLog의 경우)에서 찾을 수 있습니다.
NanoLog 프로젝트에는 정확성을 보장하기 위한 수많은 테스트가 포함되어 있습니다. 아래는 각각에 대한 설명과 액세스/빌드/실행 방법입니다.
통합 테스트는 Nanolog 시스템을 엔드투엔드로 빌드하고 테스트합니다. C++17 NanoLog와 Preprocessor NanoLog 모두 NanoLog 라이브러리로 클라이언트 애플리케이션을 컴파일하고, 애플리케이션을 실행하고, 압축 해제기를 통해 결과 로그 파일을 실행합니다. 또한 압축 해제기의 출력을 비교하여 로그 내용이 예상 결과와 일치하는지 확인합니다.
다음 명령을 사용하여 이러한 테스트를 실행할 수 있습니다.
cd integrationTest
./run.sh
NanoLog 라이브러리와 전처리기 엔진에는 자체 단위 테스트도 포함되어 있습니다. 이는 개별 함수를 호출하고 반환 값이 예상 결과와 일치하는지 확인하여 각 구성 요소의 내부 작동을 테스트합니다.
NanoLog 전처리기 단위 테스트를 실행하려면 다음 명령을 실행하세요.
cd preprocessor
python UnitTests.py
NanoLog 라이브러리 단위 테스트를 빌드하고 실행하려면 다음 명령을 실행하세요.
git submodule update --init
cd runtime
make clean
make test
./test --gtest_filter=-*assert*
참고: gtest 필터는 assert 종료 명령문이 포함된 테스트를 제거하는 데 사용됩니다.
'C C++' 카테고리의 다른 글
CopperSpice (구리 향신료) : C++ 크로스 플랫폼 소프트웨어 (0) | 2021.06.30 |
---|---|
[리눅스] C++ 에서 칼라 글자 출력 (0) | 2021.05.16 |
Bitsery : header 전용 C++ 이진 직렬화 라이브러리 (0) | 2020.01.25 |
C++ std::any (0) | 2019.08.22 |
libcurl 빌드 하기 (Visual Sutdio) (0) | 2019.08.15 |