중국어(중국어)
BqLog는 "Honor of Kings"와 같은 프로젝트에 사용되는 경량 고성능 로깅 시스템으로, 성공적으로 배포되어 원활하게 실행되고 있습니다.
윈도우 64비트
맥OS
리눅스
iOS
안드로이드(X86_64, arm64-v8a, armeabi-v7a)
Unix(FreeBSD에서 테스트 통과)
C++
자바
코틀린
기음#
기존 오픈 소스 로깅 라이브러리와 비교하여 BqLog는 상당한 성능 이점을 제공합니다(벤치마크 참조). 서버와 클라이언트에 적합할 뿐만 아니라 모바일 장치와의 호환성도 뛰어납니다.
낮은 메모리 소비로 인해 스레드 10개와 로그 항목 20,000,000개의 벤치마크 사례에서 BqLog 자체는 1MB 미만의 메모리를 소비합니다.
고성능, 고압축 실시간 로그 형식 제공
Unreal에 제공되는 공통 유형을 지원하여 게임 엔진( Unity
, Unreal
)에서 정상적으로 사용할 수 있습니다.
UTF-8
, UTF-16
, UTF-32
문자 및 문자열은 물론 bool, float, double, 다양한 길이 및 유형의 정수와 같은 일반 매개변수 유형을 지원합니다.
C++20
format specifications
지원
비동기 로깅은 데이터 손실을 방지하기 위해 충돌 검토를 지원합니다(XLog에서 영감을 얻음)
Android 컴파일 후 동적 라이브러리가 약 200k에 불과한 매우 작은 크기
Java 및 C#에서 추가 힙 할당을 생성하지 않으므로 런타임 중에 지속적인 새 객체 생성을 방지합니다.
표준 C 언어 라이브러리 및 플랫폼 API에만 의존하며 Android의 ANDROID_STL = none
모드에서 컴파일할 수 있습니다.
C++11
이상의 컴파일 표준을 지원하며 -Wall -Wextra -pedantic -Werror의 엄격한 요구 사항에 따라 컴파일할 수 있습니다.
컴파일 모듈은 CMake
기반으로 하며 다양한 플랫폼에 대한 컴파일 스크립트를 제공하므로 사용하기 쉽습니다.
맞춤 매개변수 유형 지원
코드 제안에 매우 친절함
BqLog가 그렇게 빠른 이유 - 고성능 실시간 압축 로그 형식
BqLog가 그렇게 빠른 이유 - 높은 동시성 링 버퍼
프로젝트에 BqLog 통합
간단한 데모
아키텍처 개요
주요 프로세스 API 사용 지침
1-로그 객체 생성
2-로그 객체 검색
3-로깅 메시지
4-기타 API
동기 및 비동기 로깅
1. 비동기 로깅의 스레드 안전성
어펜더 소개
1. ConsoleAppender
2. TextFileAppender
3. CompressedFileAppender(강력 추천)
4. RawFileAppender
구성 지침
1. 완전한 예
2. 자세한 설명
바이너리 형식 어펜더의 오프라인 디코딩
빌드 지침
1. 도서관 구축
2. 데모 빌드 및 실행
3. 자동 테스트 실행 지침
4. 벤치마크 실행 지침
고급 사용법 주제
1. 힙 할당 없음
2. 카테고리 지원을 통한 로그 객체
3. 프로그램 비정상 종료 시 데이터 보호
4. NDK 및 ANDROID_STL 정보 = 없음
5. 사용자 정의 매개변수 유형
6. 언리얼 엔진에서 BqLog 사용
기준
1. 벤치마크 설명
2. BqLog C++ 벤치마크 코드
3. BqLog Java 벤치마크 코드
4. Log4j 벤치마크 코드
5. 벤치마크 결과
BqLog는 다양한 형태로 프로젝트에 통합될 수 있습니다. C++의 경우 동적 라이브러리, 정적 라이브러리 및 소스 파일을 지원합니다. Java 및 C#의 경우 래퍼 소스 코드가 포함된 동적 라이브러리를 지원합니다. 다음은 BqLog를 포함하는 방법입니다.
코드 저장소에는 /dist/dynamic_lib/에 있는 미리 컴파일된 동적 라이브러리 파일이 포함되어 있습니다. 라이브러리 파일을 사용하여 BqLog를 프로젝트에 통합하려면 다음을 수행해야 합니다.
플랫폼에 해당하는 동적 라이브러리 파일을 선택하고 프로젝트의 빌드 시스템에 추가합니다.
/dist/dynamic_lib/include 디렉터리를 프로젝트에 복사하고 포함 디렉터리 목록에 추가합니다. (XCode의 .framework 라이브러리를 사용하는 경우 .framework 파일에 이미 헤더 파일이 포함되어 있으므로 이 단계를 건너뛸 수 있습니다.)
코드 저장소에는 /dist/static_lib/에 있는 미리 컴파일된 정적 라이브러리 파일이 포함되어 있습니다. 라이브러리 파일을 사용하여 BqLog를 프로젝트에 통합하려면 다음을 수행해야 합니다.
플랫폼에 해당하는 정적 라이브러리 파일을 선택하고 프로젝트의 빌드 시스템에 추가합니다.
/dist/static_lib/include 디렉터리를 프로젝트에 복사하고 포함 디렉터리 목록에 추가합니다. (XCode의 .framework 라이브러리를 사용하는 경우 .framework 파일에 이미 헤더 파일이 포함되어 있으므로 이 단계를 건너뛸 수 있습니다.)
BqLog는 또한 컴파일을 위해 소스 코드를 프로젝트에 직접 포함하는 것을 지원합니다. 소스 코드를 사용하여 BqLog를 통합하려면 다음 단계를 따르세요.
/src 디렉터리를 소스 코드 참조로 프로젝트에 복사합니다.
/include 디렉터리를 프로젝트에 복사하고 포함 디렉터리 목록에 추가합니다.
Visual Studio에서 Windows 버전을 컴파일하는 경우 컴파일 옵션에 /Zc:__cplusplus를 추가하여 현재 C++ 컴파일러 표준 지원이 올바르게 결정되었는지 확인하세요.
Android NDK에서 소스 코드를 사용하는 경우 중요한 고려 사항은 4. NDK 정보 및 ANDROID_STL = none을 참조하세요.
C#에서는 BqLog를 네이티브 동적 라이브러리와 C# 래퍼를 통해 사용할 수 있으며 Mono, Microsoft CLR 및 Unity 엔진을 지원합니다. Unity는 Mono 및 IL2CPP 모드와 모두 호환됩니다. C#에서 BqLog를 사용하려면 다음 단계를 따르세요.
/dist/dynamic_lib/에서 플랫폼에 해당하는 동적 라이브러리 파일을 선택하고 프로젝트에 추가합니다(Unity의 경우 Unity 가져오기 및 플러그인 구성 참조).
/wrapper/csharp/src의 소스 코드 파일을 프로젝트에 복사합니다.
Java에서는 BqLog를 기본 동적 라이브러리와 Java Wrapper를 통해 사용할 수 있으며 일반적인 JVM 환경과 Android를 지원합니다. BqLog를 JVM에 통합하려면 다음 단계를 따르세요.
/dist/dynamic_lib/에서 플랫폼에 해당하는 동적 라이브러리 파일을 선택하고 프로젝트에 추가합니다.
/wrapper/java/src의 소스 코드 파일을 프로젝트에 복사합니다.
(선택 사항) NDK에서 BqLog를 호출하려는 경우 /dist/dynamic_lib/include 디렉터리를 프로젝트에 복사하고 포함 디렉터리 목록에 추가합니다.
다음 코드는 1000개가 넘는 로그를 콘솔(또는 Android의 경우 ADB Logcat)에 출력합니다.
#정의된 경우(WIN32) #include#endif#include #include int main() { #if Defined(WIN32) // BqLog는 표시 문제를 방지하기 위해 모든 최종 텍스트를 UTF-8 인코딩으로 출력하므로 Windows 명령줄을 UTF-8로 전환합니다. SetConsoleOutputCP(CP_UTF8); SetConsoleCP(CP_UTF8); #endif // 이 문자열은 로그 구성입니다. 여기서는 콘솔에 출력하는appender_0이라는 하나의 어펜더(출력 대상)로 로거를 구성합니다. std::string config = R"( # 이 어펜더의 출력 대상은 콘솔입니다.appenders_config.appender_0.type=console # 이 어펜더는 타임스탬프에 현지 시간을 사용합니다.appenders_config.appender_0.time_zone=default local time # 이 어펜더는 이 6개 수준의 로그를 출력합니다. (사이에 공백 없음)appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] )"; bq::log log = bq::log::create_log("my_first_log", config); // 구성을 사용하여 로그 객체를 생성합니다. for(int i = 0; i < 1024; ++i) { log.info("이것은 정보 테스트 로그입니다. 형식 문자열은 UTF-8, param int:{}, param bool :{}, param string8:{}, param string16:{}, param string32:{} , param float:{}", i, true, "utf8-string", u"utf16-string", U"utf32-string", 4.3464f); } log.error(U"오류 테스트 로그입니다. 형식 문자열은 UTF-32입니다."); bq::log::force_flush_all_logs(); // BqLog의 기본값은 비동기 출력입니다. 프로그램이 종료되기 전에 로그가 표시되도록 하려면 강제로 플러시하여 출력을 한 번 동기화하세요. 0을 반환합니다. }
System.Text 사용; System 사용; 공개 클래스 데모_main { 공개 정적 void Main(string[] args) { Console.OutputEncoding = Encoding.UTF8; Console.InputEncoding = Encoding.UTF8; string config = @" # 이 어펜더의 출력 대상은 콘솔입니다.appenders_config.appender_0.type=console # 이 어펜더는 타임스탬프에 현지 시간을 사용합니다. ppenders_config.appender_0.time_zone=default local time # 이 어펜더는 이 6개 수준의 로그를 출력합니다(공백 없음). 사이)appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] "; bq.log log = bq.log.create_log("my_first_log", config); // 구성을 사용하여 로그 객체를 생성합니다. for (int i = 0; i < 1024; ++i) { log.info("이것은 정보 테스트 로그입니다. 형식 문자열은 UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, " 문자열 텍스트", 4.3464f); } bq.log.force_flush_all_logs(); Console.ReadKey(); }}
public class deco_main { public static void main(String[] args) { // TODO 자동 생성 메서드 스텁 String config = """ # 이 어펜더의 출력 대상은 콘솔입니다.appenders_config.appender_0.type=console # 이 어펜더는 현지 시간을 사용합니다. for timestampsappenders_config.appender_0.time_zone=default local time # 이 어펜더는 이러한 6개 수준의 로그를 출력합니다(사이에 공백 없음).appenders_config.appender_0.levels=[verbose,debug,info,warning,error,fatal] """; bq.log log = bq.log.create_log("my_first_log", config); // config for (int i = 0; i < 1024; ++i)를 사용하여 로그 객체를 생성합니다. { log.info("이것은 정보 테스트 로그입니다. 형식 문자열은 UTF-16, param int:{}, param bool :{}, param string:{}, param float:{}", i, true, "문자열 텍스트", 4.3464f); } bq.log.force_flush_all_logs(); } }
위의 다이어그램은 BqLog의 기본 구조를 명확하게 보여줍니다. 다이어그램의 오른쪽에는 BqLog 라이브러리의 내부 구현이 있고 왼쪽에는 프로그램과 코드가 있습니다. 프로그램은 제공된 래퍼(다양한 언어에 대한 객체 지향 API)를 사용하여 BqLog를 호출할 수 있습니다. 다이어그램에는 두 개의 로그가 생성됩니다. 하나는 "Log A"이고 다른 하나는 "Log B"입니다. 각 로그는 하나 이상의 Appender에 첨부됩니다. Appender는 로그 콘텐츠의 출력 대상으로 이해될 수 있습니다. 이는 콘솔(Android용 ADB Logcat 로그), 텍스트 파일 또는 압축 로그 파일이나 일반 바이너리 로그 형식 파일과 같은 특수 형식일 수도 있습니다.
동일한 프로세스 내에서 다른 언어의 래퍼는 동일한 Log 개체에 액세스할 수 있습니다. 예를 들어 Log A라는 Log 객체가 Java로 생성된 경우 C++ 측에서도 Log A라는 이름으로 액세스하고 사용할 수 있습니다.
Android 시스템에서 실행되는 Unity 개발 게임과 같은 극단적인 경우에는 동일한 앱 내에 Java, Kotlin, C# 및 C++ 언어가 포함될 수 있습니다. 그들은 모두 동일한 Log 객체를 공유할 수 있습니다. create_log를 사용하여 Java 측에서 로그를 생성한 다음 get_log_by_name을 사용하여 다른 언어로 로그에 액세스할 수 있습니다.
참고: 다음 API는 bq::log(또는 bq.log) 클래스에 선언됩니다. 공간을 절약하기 위해 C++ API만 나열됩니다. Java와 C#의 API는 동일하므로 여기서는 반복하지 않습니다.
C++에서 bq::string
BqLog 라이브러리의 UTF-8 문자열 유형입니다. char, std::string
또는 std::string_view
와 같은 C 스타일 문자열을 전달할 수도 있습니다. 이는 자동으로 암시적으로 변환됩니다.
create_log 정적 함수를 사용하여 로그 객체를 생성할 수 있습니다. 그 선언은 다음과 같습니다:
//C++ API ////// 로그 객체 생성 /// /// 로그 이름이 빈 문자열이면 bqLog는 자동으로 고유한 로그 이름. 로그 이름이 이미 존재하는 경우 기존 로그 개체를 반환하고 이전 구성을 새 구성으로 덮어씁니다. /// 로그 구성 문자열 ///로그 객체, 생성이 실패하면 해당 객체의 is_valid() 메서드는 false를 반환합니다. static log create_log(const bq::string& log_name, const bq::string& config_content);
코드는 로그 개체의 이름과 구성 문자열을 전달하여 로그 개체를 생성합니다. 로그 구성은 구성 지침에서 참조할 수 있습니다. 다음은 주목해야 할 몇 가지 핵심 사항입니다.
C#인지 Java인지에 관계없이 반환된 로그 개체는 null이 아닙니다. 그러나 구성 오류 또는 기타 이유로 인해 잘못된 로그 개체가 생성될 수 있습니다. 따라서 반환된 객체를 확인하려면 is_valid() 함수를 사용해야 합니다. 잘못된 개체에 작업을 수행하면 프로그램이 중단될 수 있습니다.
빈 문자열이 로그 이름으로 전달되면 bqLog는 "AutoBqLog_1"과 같은 고유한 로그 이름을 자동으로 생성합니다.
동일한 이름을 가진 기존 로그 객체에 대해 create_log를 호출하면 새 로그 객체가 생성되지 않지만 이전 구성을 새 구성으로 덮어씁니다. 그러나 이 프로세스에서는 일부 매개변수를 수정할 수 없습니다. 자세한 내용은 구성 지침을 참조하세요.
NDK에서 사용하는 경우를 제외하고(4. NDK 정보 및 ANDROID_STL = 없음 참조) 다른 상황에서는 이 API를 사용하여 전역 또는 정적 변수에서 직접 로그 개체를 초기화할 수 있습니다.
이미 다른 곳에서 로그 객체가 생성된 경우, get_log_by_name 함수를 사용하여 생성된 로그 객체를 직접 얻을 수 있습니다.
//C++ API ////// 이름으로 로그 개체 가져오기 /// /// 찾으려는 로그 개체의 이름 ///로그 객체, 특정 이름의 로그 객체가 발견되지 않으면 is_valid() 메서드는 false를 반환합니다. static log get_log_by_name(const bq::string& log_name);
이 함수를 사용하여 전역 변수나 정적 함수에서 로그 개체를 초기화할 수도 있습니다. 그러나 지정된 이름을 가진 로그 개체가 이미 존재하는지 확인해야 합니다. 그렇지 않으면 반환된 로그 객체를 사용할 수 없으며 해당 is_valid() 메서드는 false를 반환합니다.
///핵심 로그 기능에는 6가지 로그 수준이 있습니다: ///verbose, debug, info, warning, error, fatal templatebq::enable_if_t ::value, bool> verbose(const STR& log_content) const; 템플릿<유형 이름 STR, 유형 이름...Args> bq::enable_if_t ::value, bool> verbose(const STR& log_format_content, const Args&... args) const; 템플릿<유형 이름 STR> bq::enable_if_t ::value, bool> debug(const STR& log_content) const; 템플릿<유형 이름 STR, 유형 이름...Args> bq::enable_if_t ::value, bool> debug(const STR& log_format_content, const Args&... args) const; 템플릿<유형 이름 STR> bq::enable_if_t ::value, bool> info(const STR& log_content) const; 템플릿<유형 이름 STR, 유형 이름...Args> bq::enable_if_t ::value, bool> info(const STR& log_format_content, const Args&... args) const; 템플릿<유형 이름 STR> bq::enable_if_t ::value, bool> 경고(const STR& log_content) const; 템플릿<유형 이름 STR, 유형 이름...Args> bq::enable_if_t ::value, bool> warning(const STR& log_format_content, const Args&... args) const; 템플릿<유형 이름 STR> bq::enable_if_t ::value, bool> error(const STR& log_content) const; 템플릿<유형 이름 STR, 유형 이름...Args> bq::enable_if_t ::value, bool> error(const STR& log_format_content, const Args&... args) const; 템플릿<유형 이름 STR> bq::enable_if_t ::value, bool> fatal(const STR& log_content) const; 템플릿<유형 이름 STR, 유형 이름...Args> bq::enable_if_t ::value, bool> fatal(const STR& log_format_content, const Args&... args) const;
메시지를 기록할 때 다음 세 가지 핵심 사항에 주의하세요.
보시다시피, 로그는 Android에 따라 상세, 디버그, 정보, 경고, 오류, 치명적이라는 6가지 수준으로 나뉩니다. 그 중요성은 순차적으로 증가합니다. 콘솔로 출력하면 다른 색상으로 나타납니다.
STR 매개변수는 printf의 첫 번째 매개변수와 유사하며 다음을 포함하여 다양한 일반 문자열 유형이 될 수 있습니다.
자바의 java.lang.String
C#의 문자열
C++의 C 스타일 문자열 및 std::string
( char*
, char16_t*
, char32_t*
, wchar_t*
, std::string
, std::u8string
, std::u16string
, std::u32string
, std::wstring
의 다양한 인코딩 , std::string_view
, std::u16string_view
, std::u32string_view
, std::wstring_view
및 심지어 사용자 정의 문자열 유형(사용자 정의 매개변수 유형에서 참조할 수 있음)
STR 매개변수 뒤에 다양한 매개변수를 추가할 수 있습니다. 이러한 매개변수는 C++20의 std::format과 유사한 규칙에 따라 STR의 지정된 위치로 형식화됩니다(위치 인수 및 날짜 시간 형식에 대한 지원 부족 제외). 예를 들어, 단일 {} 사용은 매개변수의 기본 형식을 나타내고, {:.2f}는 부동 소수점 숫자 형식의 정밀도를 지정합니다. 문자열을 수동으로 연결하는 대신 형식이 지정된 매개변수를 사용하여 로그를 출력해 보세요. 이 접근 방식은 성능 및 압축 형식 저장소에 최적입니다.
현재 지원되는 매개변수 유형은 다음과 같습니다.
널 포인터(널로 출력)
포인터(0x로 시작하는 16진수 주소로 출력)
부울
단일 바이트 문자(char)
더블바이트 문자(char16_t, wchar_t, C#의 char, Java의 char)
4바이트 문자(char32_t 또는 wchar_t)
8비트 정수
8비트 부호 없는 정수
16비트 정수
16비트 부호 없는 정수
32비트 정수
32비트 부호 없는 정수
64비트 정수
64비트 부호 없는 정수
32비트 부동 소수점 숫자
64비트 부동 소수점 숫자
C++의 기타 알려지지 않은 POD 유형(크기가 1, 2, 4 또는 8바이트로 제한되며 각각 int8, int16, int32 및 int64로 처리됨)
STR 매개변수에 언급된 모든 문자열 유형을 포함한 문자열
C# 및 Java의 모든 클래스 또는 개체(ToString() 문자열 출력)
사용자 정의 매개변수 유형에 설명된 대로 사용자 정의 매개변수 유형
특정 작업을 수행할 수 있는 일반적으로 사용되는 추가 API가 있습니다. 자세한 API 설명은 bq_log/bq_log.h와 Java 및 C#의 bq.log 클래스를 참조하세요. 강조해야 할 몇 가지 주요 API는 다음과 같습니다.
////// BqLog 초기화를 취소합니다. 프로그램이 존재하기 전에 이 함수를 호출하십시오. /// static void uninit();
프로그램을 종료하거나 BqLog를 사용하는 자체 구현 동적 라이브러리를 제거하기 전에 uninit()
를 실행하는 것이 좋습니다. 그렇지 않으면 특정 상황에서 종료할 때 프로그램이 중단될 수 있습니다.
////// bqLog가 비동기식인 경우 프로그램 충돌로 인해 버퍼의 로그가 디스크에 유지되지 않을 수 있습니다. /// 이 기능이 활성화되면 bqLog는 충돌 발생 시 버퍼에 있는 로그의 강제 플러시를 수행하려고 시도합니다. 그러나 /// 이 기능은 성공을 보장하지 않으며 POSIX 시스템만 지원합니다. /// static void 활성화_auto_crash_handle();
자세한 소개는 프로그램 비정상 종료 시 데이터 보호를 참조하세요.
////// 모든 로그 개체의 버퍼를 동기적으로 플러시합니다. /// 호출 후 버퍼의 모든 데이터가 처리되도록 합니다. /// static void force_flush_all_logs(); ////// 이 로그 개체의 버퍼를 /// 동기적으로 플러시하여 호출 후 버퍼의 모든 데이터가 처리되도록 합니다. /// void force_flush();
bqLog는 기본적으로 비동기 로깅을 사용하므로 모든 로그를 즉시 동기화하고 출력하고 싶을 때가 있습니다. 이러한 경우 force_flush()를 강제로 호출해야 합니다.
////// 콘솔 로그 메시지가 출력될 때마다 호출될 콜백을 등록합니다. /// 외부 시스템에서 콘솔 로그 출력을 모니터링하는 데 사용할 수 있습니다. /// /// static void Register_console_callback(bq::type_func_ptr_console_callback callback); ////// 콘솔 콜백 등록을 취소합니다. /// /// static void unregister_console_callback(bq::type_func_ptr_console_callback callback);
ConsoleAppender의 출력은 Android의 콘솔 또는 ADB Logcat 로그로 이동하지만 모든 상황을 다룰 수는 없습니다. 예를 들어 사용자 지정 게임 엔진이나 사용자 지정 IDE에서는 각 콘솔 로그 출력에 대해 콜백 함수를 호출하는 메커니즘이 제공됩니다. 이를 통해 프로그램의 어느 곳에서나 콘솔 로그를 재처리하고 출력할 수 있습니다.
추가 주의: 교착 상태가 쉽게 발생할 수 있으므로 콘솔 콜백 내에서 동기화된 BQ 로그를 출력하지 마십시오.
////// 콘솔 어펜더 버퍼를 활성화하거나 비활성화합니다. /// 래퍼는 C#과 Java 가상 머신 모두에서 실행될 수 있고 네이티브 스레드에서 콜백을 직접 호출하고 싶지 않으므로 /// 이 옵션을 활성화할 수 있습니다. 이렇게 하면 모든 콘솔 출력이 가져올 때까지 버퍼에 저장됩니다. /// /// ///static void set_console_buffer_enable(bool 활성화); /// /// 스레드로부터 안전한 방식으로 콘솔 어펜더 버퍼에서 로그 항목을 가져오고 제거합니다. /// 콘솔 어펜더 버퍼가 비어 있지 않으면 이 로그 항목에 대해 on_console_callback 함수가 호출됩니다. /// 콜백 함수 내에서 동기화된 BQ 로그를 출력하지 않도록 주의하세요. /// /// 콘솔 어펜더 버퍼가 비어 있지 않은 경우 가져온 로그 항목에 대해 호출할 콜백 함수 ///다음인 경우 True입니다. 콘솔 어펜더 버퍼가 비어 있지 않고 로그 항목을 가져옵니다. 그렇지 않으면 False가 반환됩니다. static bool fetch_and_remove_console_buffer(bq::type_func_ptr_console_callback on_console_callback);
콘솔 콜백을 통해 콘솔 출력을 가로채는 것 외에도 콘솔 로그 출력을 적극적으로 가져올 수 있습니다. 콜백이 어느 스레드에서 나올지 모르기 때문에 콘솔 로그 출력이 콜백을 통해 전달되는 것을 원하지 않는 경우도 있습니다. 예를 들어 일부 C# 가상 머신 또는 JVM에서는 콘솔이 실행될 때 VM이 가비지 수집을 수행할 수 있습니다. 콜백이 호출되며 이로 인해 잠재적으로 중단이나 충돌이 발생할 수 있습니다.
여기서 사용되는 방법은 set_console_buffer_enable
통해 콘솔 버퍼를 활성화하는 것입니다. 이로 인해 fetch_and_remove_console_buffer
적극적으로 호출하여 검색할 때까지 모든 콘솔 로그 출력이 메모리에 저장됩니다. 따라서 이 방법을 사용하기로 선택한 경우 해제되지 않은 메모리를 방지하려면 즉시 로그를 가져오고 삭제해야 합니다.
추가 주의: 교착 상태가 쉽게 발생할 수 있으므로 콘솔 콜백 내에서 동기화된 BQ 로그를 출력하지 마십시오.
추가 주의 사항: IL2CPP 환경에서 이 코드를 사용하는 경우 on_console_callback이 static unsafe로 표시되고 [MonoPInvokeCallback(typeof(type_console_callback))] 속성으로 장식되어 있는지 확인하십시오.
////// 로그 구성을 수정했지만 buffer_size 등 일부 필드는 수정할 수 없습니다. /// /// ///bool Reset_config(const bq::string& config_content);
때로는 프로그램 내에서 로그 구성을 수정하고 싶을 수도 있습니다. 구성을 덮어쓰기 위해 로그 개체를 다시 생성하는 것 외에도(로그 개체 생성 참조),재설정 인터페이스를 사용할 수도 있습니다. 그러나 모든 구성 항목을 이 방법으로 수정할 수 있는 것은 아닙니다. 자세한 내용은 구성 지침을 참조하세요.
////// 특정 Appender를 일시적으로 비활성화하거나 활성화합니다. /// /// /// void set_appenders_enable(const bq::string&appender_name, bool 활성화) ;
기본적으로 구성의 Appender는 활성화되어 있지만 일시적으로 비활성화하고 다시 활성화하는 메커니즘이 여기에 제공됩니다.
////// 스냅샷이 구성된 경우에만 작동합니다. /// 스냅샷 버퍼를 텍스트로 디코딩합니다. /// /// 각 로그의 타임스탬프가 GMT 시간인지 현지 시간인지 여부 ///디코딩된 스냅샷 버퍼 bq::string take_snapshot(bool use_gmt_time) const;
특정 특수 기능을 사용하려면 로그의 마지막 부분을 출력해야 하는 경우가 있는데, 이는 스냅샷 기능을 사용하여 수행할 수 있습니다. 이 기능을 활성화하려면 먼저 로그 구성에서 스냅샷을 활성화하고 최대 버퍼 크기(바이트)를 설정해야 합니다. 또한 스냅샷에 대해 필터링할 로그 수준과 범주를 지정해야 합니다(선택 사항). 자세한 구성은 스냅샷 구성을 참조하세요. 스냅샷이 필요할 때 take_snapshot()을 호출하면 스냅샷 버퍼에 저장된 최신 로그 항목이 포함된 형식화된 문자열이 반환됩니다. C++에서 유형은 bq::string
이며 암시적으로 std::string
으로 변환될 수 있습니다.
네임스페이스 bq{ 네임스페이스 도구 { //바이너리 로그 형식을 디코딩하기 위한 유틸리티 클래스입니다. //이를 사용하려면 먼저 log_decoder 개체를 만든 다음 //해당 디코드 함수를 호출하여 디코딩합니다. //호출이 성공할 때마다 //get_last_decoded_log_entry()를 사용하여 디코딩된 결과를 검색할 수 있습니다. //각 호출은 하나의 로그 항목을 디코딩합니다. 구조체 log_decoder { 사적인: bq::string decode_text_; bq::appender_decode_result 결과_ = bq::appender_decode_result::성공; uint32_t 핸들_ = 0; public: ////// 각 log_decoder 개체가 바이너리 로그 파일에 해당하는 log_decoder 개체를 만듭니다. /// /// 바이너리 로그 파일의 경로는 상대 경로 또는 절대 경로일 수 있습니다. log_decoder(const bq::string& log_file_path); ~log_decoder(); ////// 로그 항목을 디코딩합니다. 이 함수를 호출할 때마다 1개의 로그 항목만 디코딩됩니다. /// ///디코드 결과, appender_decode_result::eof는 전체 로그 파일이 디코딩되었음을 의미합니다. bq::appender_decode_result 디코드(); ////// 마지막 디코드 결과 가져오기 /// ///bq::appender_decode_result get_last_decode_result() const; /// /// 마지막 디코드 로그 항목 내용 가져오기 /// ///const bq::string& get_last_decoded_log_entry() const; }; } }
이는 CompressedFileAppender 및 RawFileAppender와 같은 바이너리 유형 Appender가 런타임에 출력하는 로그 파일을 디코딩할 수 있는 유틸리티 클래스입니다.
이를 사용하려면 먼저 log_decoder 객체를 생성하세요. 그런 다음 decode() 함수를 호출할 때마다 하나의 로그 항목을 순차적으로 디코딩합니다. 반환된 결과가 bq::appender_decode_result::success인 경우 get_last_decoded_log_entry()를 호출하여 마지막으로 디코딩된 로그 항목의 형식이 지정된 텍스트 콘텐츠를 가져올 수 있습니다. 결과가 bq::appender_decode_result::eof이면 모든 로그를 완전히 읽었다는 의미입니다.
BqLog를 사용하면 thread_mode 설정을 통해 로그 객체가 동기식인지 비동기식인지 구성할 수 있습니다. 이 두 모드의 주요 차이점은 다음과 같습니다.
동기 로깅 | 비동기 로깅 | |
---|---|---|
행동 | 로깅 함수를 호출한 후 로그는 즉시 해당 어펜더에 기록됩니다. | 로깅 기능을 호출한 후 로그가 즉시 기록되지 않습니다. 대신 정기적인 처리를 위해 작업자 스레드로 전달됩니다. |
성능 | 낮음. 로그를 쓰는 스레드는 로깅 함수에서 반환되기 전에 로그가 해당 어펜더에 기록될 때까지 차단하고 기다려야 하기 때문입니다. | 높음. 로그를 작성하는 스레드는 실제 출력을 기다릴 필요가 없으며 로깅 후 즉시 반환될 수 있습니다. |
스레드 안전성 | 높지만 로깅 기능을 실행하는 동안 로그 매개변수가 수정되지 않아야 합니다. | 높지만 로깅 기능을 실행하는 동안 로그 매개변수가 수정되지 않아야 합니다. |
비동기 로깅에 대한 일반적인 오해는 작업자 스레드가 로그를 처리할 때 매개변수가 회수될 수 있다는 점을 사용자가 염려하기 때문에 스레드로부터 덜 안전하다는 것입니다. 예를 들어:
{ const char str_array[5] = {'T', 'E', 'S', 'T', '�'}; const char* str_ptr = str_array; log_obj.info("테스트 매개변수입니다: {}, {}", str_array, str_ptr); }
위의 예에서 str_array
스택에 저장되며 범위가 종료되면 해당 메모리는 더 이상 유효하지 않습니다. 사용자는 비동기 로깅을 사용하는 경우 작업자 스레드가 로그를 처리할 때 str_array
및 str_ptr
이 잘못된 변수가 될 것이라고 걱정할 수 있습니다.
그러나 BqLog는 info
함수 실행 중에 모든 매개변수 내용을 내부 ring_buffer
에 복사하므로 이러한 상황은 발생하지 않습니다. info
함수가 반환되면 str_array
또는 str_ptr
과 같은 외부 변수는 더 이상 필요하지 않습니다. 게다가, ring_buffer
const char*
포인터 주소를 저장하지 않고 항상 전체 문자열을 저장합니다.
실제 잠재적인 문제는 다음 시나리오에서 발생합니다.
static std::string global_str = "안녕하세요 세계"; // 여러 스레드에 의해 수정된 전역 변수입니다.void thread_a() { log_obj.info("테스트 매개변수입니다: {}", global_str); }
info
함수 실행 중에 global_str
의 내용이 변경되면 정의되지 않은 동작이 발생할 수 있습니다. BqLog는 충돌을 방지하기 위해 최선을 다하지만 최종 출력의 정확성은 보장할 수 없습니다.
Appender는 로그 출력 대상을 나타냅니다. bqLog의 Appender 개념은 기본적으로 Log4j와 동일합니다. 현재 bqLog는 다음 유형의 Appender를 제공합니다.
이 Appender의 출력 대상은 Android의 ADB와 iOS의 해당 콘솔을 포함한 콘솔입니다. 텍스트 인코딩은 UTF-8입니다.
이 Appender는 로그 파일을 UTF-8 텍스트 형식으로 직접 출력합니다.
이 Appender는 highly recommended format by bqLog
인 압축 형식으로 로그 파일을 출력합니다. 모든 Appender 중에서 가장 높은 성능을 가지며 가장 작은 출력 파일을 생성합니다. 그러나 최종 파일을 디코딩해야 합니다. 디코딩은 런타임 디코딩 또는 오프라인 디코딩에서 수행될 수 있습니다.
이 Appender는 바이너리 로그 내용을 메모리에서 파일로 직접 출력합니다. TextFileAppender보다 성능은 높지만 저장 공간을 더 많이 소비합니다. 최종 파일을 디코딩해야 합니다. 디코딩은 런타임 디코딩 또는 오프라인 디코딩 시 수행될 수 있습니다. 이 Appender는 사용을 권장하지 않습니다.
다음은 다양한 Appender를 포괄적으로 비교한 것입니다.
이름 | 출력 대상 | 직접 읽을 수 있음 | 출력 성능 | 출력 크기 |
---|---|---|---|---|
ConsoleAppender | 콘솔 | ✔ | 낮은 | - |
TextFileAppender | 파일 | ✔ | 낮은 | 크기가 큰 |
압축파일어펜더 | 파일 | ✘ | 높은 | 작은 |
RawFileAppender | 파일 | ✘ | 중간 | 크기가 큰 |
구성은 create_log 및 Reset_config 함수의 구성 문자열을 참조합니다. 이 문자열은 속성 파일 형식을 사용하고 # 주석을 지원합니다(그러나 주석의 경우 #으로 새 줄을 시작해야 합니다).
다음은 완전한 예입니다.
# 이 구성은 두 개의 서로 다른 파일로 출력하는 두 개의 TextFileAppender를 포함하여 총 5개의 Appender로 로그 개체를 설정합니다.# 첫 번째 Appender의 이름은appender_0이고 유형은 ConsoleAppenderappenders_config.appender_0.type=console#appender_0의 시간대는 다음과 같습니다. 시스템의 로컬 시간appenders_config.appender_0.time_zone=기본 로컬 시간# appender_0은 6개 수준의 로그를 모두 출력합니다(참고: 로그 수준 사이에 공백이 없어야 합니다. 그렇지 않으면 구문 분석에 실패합니다)appenders_config.appender_0.levels=[verbose,debug ,info,warning,error,fatal]# 두 번째 Appender의 이름은appender_1이고 해당 유형은 TextFileAppenderappenders_config.appender_1.type=text_file#입니다.appender_1의 시간대는 GMT(UTC+0appenders_config.appender_1.time_zone=gmt#appender_1 전용)입니다. info 이상의 레벨 로그를 출력하고 나머지는 무시됩니다appenders_config.appender_1.levels=[info,warning,error,fatal]#appender_1의 경로는 프로그램의 상대 bqLog 디렉토리에 있으며 파일 이름은 Normal로 시작하고 그 뒤에 날짜 및 .log 확장자# iOS의 경우 /var/mobile/Containers/Data/Application/[APP]/Library/Caches/bqLog# Android의 경우 [android.content.Context에 저장됩니다. .getExternalFilesDir()]/bqLogappenders_config.appender_1.file_name=bqLog/normal# 최대 파일 크기는 10,000,000바이트입니다. 초과하면 새 파일이 생성됩니다appenders_config.appender_1.max_file_size=10000000# 10일이 지난 파일은 정리됩니다appenders_config.appender_1.expire_time_days=10# 총 출력 크기가 100,000,000바이트를 초과하면 파일은 다음부터 정리됩니다. 가장 오래된appenders_config.appender_1.capacity_limit=100000000# 세 번째 Appender의 이름은appender_2이고 유형은 TextFileAppenderappenders_config.appender_2.type=text_file#appender_2는 모든 수준의 로그appenders_config.appender_2.levels=[all]#을 출력합니다.appender_2의 경로는 다음과 같습니다. 파일 이름이 new_normal로 시작하고 그 뒤에 날짜와 .log 확장명이 붙는 프로그램의 상대 bqLog 디렉터리입니다.appenders_config.appender_2.file_name=bqLog/new_normal# 이 옵션은 Android에서만 유효하며 내부 저장소 디렉터리인 [android .content.Context.getFilesDir()]/bqLogappenders_config.appender_2.is_in_sandbox=true# 네 번째 Appender의 이름은appender_3이고 유형은 CompressedFileAppenderappenders_config.appender_3.type=compressed_file#appender_3은 모든 수준의 로그appenders_config.appender_3.levels=[all을 출력합니다. ]# Appender_3의 경로는 프로그램의 절대 경로 ~/bqLog 디렉터리에 있으며 파일 이름은 압축_log로 시작하고 그 뒤에 날짜와 .logcompr이 옵니다. Extensionappenders_config.appender_3.file_name=~/bqLog/compress_log# 다섯 번째 Appender의 이름은 appender_4 및 해당 유형은 RawFileAppenderappenders_config.appender_4.type=raw_file#appender_4는 기본적으로 비활성화되어 있으며 나중에 set_appenders_enableappenders_config.appender_4.enable=false#을 사용하여 활성화할 수 있습니다.appender_4는 모든 수준의 로그를 출력합니다appenders_config.appender_4.levels=[all]# 경로 appender_4의 경우 프로그램의 상대 bqLog 디렉토리에 있으며, 파일 이름은 raw_log로 시작하고 그 뒤에 날짜와 .lograw가 붙습니다. SystemC, 그렇지 않으면 모두 무시됩니다(범주 개념은 나중에 고급 사용 항목에서 자세히 설명합니다)appenders_config.appender_4.categories_mask=[ModuleA,ModuleB.SystemC]# 총 비동기 버퍼 크기는 65535바이트입니다. 구체적인 의미는 나중에 설명합니다.log.buffer_size=65535# 로그의 신뢰성 수준은 정상입니다. 구체적인 의미는 나중에 설명합니다.log.reliable_level=normal# 로그는 해당 범주가 다음 세 개의 와일드카드와 일치하는 경우에만 처리됩니다. 그렇지 않으면 모두 무시됩니다(범주 개념은 나중에 고급 사용 항목에서 자세히 설명합니다)log.categories_mask= [*default,ModuleA,ModuleB.SystemC]# 이것은 비동기 로그입니다. 비동기 로그는 성능이 가장 뛰어나고 권장되는 로그 유형입니다.log.thread_mode=async# 로그 수준이 오류 또는 치명적이면 각 로그 항목에 호출 스택 정보를 포함합니다.log.print_stack_levels=[error,fatal]# 스냅샷 기능을 활성화합니다. 스냅샷 캐시 크기는 64Ksnapshot입니다. .buffer_size=65536# 정보 및 오류 수준이 있는 로그만 스냅샷에 기록됩니다.snapshot.levels=[info,error]# 범주가 ModuleA, ModuleB.SystemC로 시작하는 로그만 스냅샷에 기록됩니다. 그렇지 않으면 스냅샷이 무시됩니다. .categories_mask=[ModuleA.SystemA.ClassA,ModuleB]
appenders_config
는 Appender에 대한 구성 집합입니다. appenders_config
다음의 첫 번째 매개변수는 Appender의 이름이며, 동일한 이름을 가진 모든 Appender는 동일한 구성을 공유합니다.
이름 | 필수의 | 구성 가능한 값 | 기본 | ConsoleAppender에 적용 가능 | TextFileAppender에 적용 가능 | CompressedFileAppender에 적용 가능 | RawFileAppender에 적용 가능 |
---|---|---|---|---|---|---|---|
유형 | ✔ | 콘솔, 텍스트_파일, 압축_파일, 원시_파일 | ✔ | ✔ | ✔ | ✔ | |
~할 수 있게 하다 | ✘ | Appender가 기본적으로 활성화되어 있는지 여부 | 진실 | ✔ | ✔ | ✔ | ✔ |
레벨 | ✘ | 로그 수준 배열 | [모두] | ✔ | ✔ | ✔ | ✔ |
시간대 | ✘ | GMT 또는 다른 문자열 | 현지 시간 | ✔ | ✔ | ✔ | ✔ |
파일_이름 | ✔ | 상대 또는 절대 경로 | ✘ | ✔ | ✔ | ✔ | |
is_in_sandbox | ✘ | 사실, 거짓 | 거짓 | ✘ | ✔ | ✔ | ✔ |
최대_파일_크기 | ✘ | 양의 정수 또는 0 | 0 | ✘ | ✔ | ✔ | ✔ |
만료_시간_일 | ✘ | 양의 정수 또는 0 | 0 | ✘ | ✔ | ✔ | ✔ |
용량_한도 | ✘ | 양의 정수 또는 0 | 0 | ✘ | ✔ | ✔ | ✔ |
카테고리_마스크 | ✘ | []로 묶인 문자열 배열 | 비어 있는 | ✔ | ✔ | ✔ | ✔ |
Appender의 유형을 지정합니다.
console
: ConsoleAppender를 나타냅니다.
text_file
: TextFileAppender를 나타냅니다.
compressed_file
: CompressedFileAppender를 나타냅니다.
raw_file
: RawFileAppender를 나타냅니다.
기본값은 true
입니다. false
로 설정하면 Appender는 기본적으로 비활성화되며 나중에 set_appenders_enable
사용하여 활성화할 수 있습니다.
모든 수준을 허용하는 verbose
, debug
, info
, warning
, error
, fatal
또는 [all]
의 조합을 포함하는 []
로 묶인 배열입니다. 참고: 레벨 사이에 공백을 포함하지 마십시오. 그렇지 않으면 구문 분석에 실패합니다.
로그의 시간대를 지정합니다. gmt
그리니치 표준시(UTC+0)를 나타내며, 다른 문자열이나 비워두면 현지 시간대가 사용됩니다. 시간대는 다음 두 가지에 영향을 미칩니다.
서식이 지정된 텍스트 로그의 타임스탬프(ConsoleAppender 및 TextFileAppender에 적용 가능)
지정된 시간대에서 자정이 지나면 새 로그 파일이 생성됩니다(TextFileAppender, CompressedFileAppender 및 RawFileAppender에 적용 가능).
파일을 저장하기 위한 경로 및 파일 이름 접두사입니다. 경로는 절대(Android 및 iOS에는 권장되지 않음) 또는 상대 경로일 수 있습니다. 최종 출력 파일 이름은 이 경로와 이름, 날짜, 파일 번호, Appender 확장자가 이어집니다.
Android에서만 의미가 있는 항목:
true
: 파일이 내부 저장소 디렉터리(android.content.Context.getFilesDir())에 저장됩니다. 사용할 수 없는 경우 외부 저장소 디렉터리(android.content.Context.getExternalFilesDir())에 저장됩니다. 그것도 사용할 수 없는 경우 캐시 디렉터리(android.content.Context.getCacheDir())에 저장됩니다.
false
: 파일은 기본적으로 외부 저장소 디렉터리에 저장됩니다. 사용할 수 없는 경우 내부 저장소 디렉터리에 저장됩니다. 해당 항목도 사용할 수 없는 경우 캐시 디렉터리에 저장됩니다.
최대 파일 크기(바이트)입니다. 저장된 파일이 이 크기를 초과하면 파일 번호가 순차적으로 증가하면서 새 로그 파일이 생성됩니다. 0
이 기능을 비활성화합니다.
파일을 보관할 최대 일수입니다. 이보다 오래된 파일은 자동으로 삭제됩니다. 0
이 기능을 비활성화합니다.
출력 디렉터리에서 이 Appender가 출력하는 파일의 최대 총 크기입니다. 이 제한을 초과하면 전체 크기가 제한 내에 있을 때까지 가장 오래된 파일부터 파일이 삭제됩니다. 0
이 기능을 비활성화합니다.
로그 개체가 카테고리를 지원하는 로그 개체인 경우 트리 형식의 카테고리 목록을 필터링하는 데 사용할 수 있습니다. 배열이 비어 있지 않으면 이 기능이 활성화됩니다. 예를 들어, [*default,ModuleA,ModuleB.SystemC]
기본 범주의 로그를 의미합니다.