├── TODO.md ├── images └── default.png ├── bin └── .gitignore ├── tests ├── test.cpp ├── test.h ├── test_appender_file.cpp ├── test_layout_text.cpp ├── test_appender_file_rolling.cpp ├── test_layout_binary.cpp └── test_record_format.cpp ├── scripts └── hashlog-map │ ├── hashlog-map.bat │ ├── hashlog-map │ ├── setup.cfg │ ├── README.md │ ├── setup.py │ ├── .gitignore │ └── hashlog-map.py ├── modules ├── CMakeLists.txt ├── CppCommon.cmake ├── CppBenchmark.cmake ├── Catch2.cmake ├── cpp-optparse.cmake └── zlib.cmake ├── .gitignore ├── .gitattributes ├── source └── logging │ ├── filters │ ├── logger_filter.cpp │ ├── message_filter.cpp │ ├── switch_filter.cpp │ └── level_filter.cpp │ ├── logger.cpp │ ├── appenders │ ├── memory_appender.cpp │ ├── ostream_appender.cpp │ ├── error_appender.cpp │ ├── minizip │ │ ├── mztools.h │ │ ├── iowin32.h │ │ └── crypt.h │ ├── debug_appender.cpp │ ├── syslog_appender.cpp │ ├── console_appender.cpp │ └── file_appender.cpp │ ├── processors │ ├── sync_processor.cpp │ ├── exclusive_processor.cpp │ ├── buffered_processor.cpp │ ├── async_wait_processor.cpp │ └── async_wait_free_processor.cpp │ ├── layouts │ ├── binary_layout.cpp │ └── hash_layout.cpp │ ├── config.cpp │ └── processor.cpp ├── include └── logging │ ├── filters.h │ ├── layouts.h │ ├── processors.h │ ├── appenders.h │ ├── layouts │ ├── null_layout.h │ ├── empty_layout.h │ ├── binary_layout.h │ ├── hash_layout.h │ └── text_layout.h │ ├── level.h │ ├── layout.h │ ├── appenders │ ├── null_appender.h │ ├── error_appender.h │ ├── rolling_file_appender.inl │ ├── syslog_appender.h │ ├── console_appender.h │ ├── debug_appender.h │ ├── ostream_appender.h │ ├── memory_appender.h │ ├── file_appender.h │ └── rolling_file_appender.h │ ├── filter.h │ ├── level.inl │ ├── version.h │ ├── appender.h │ ├── element.h │ ├── processors │ ├── exclusive_processor.h │ ├── sync_processor.h │ ├── buffered_processor.h │ ├── async_wait_processor.h │ ├── async_wait_free_processor.h │ ├── async_wait_free_queue.h │ └── async_wait_free_queue.inl │ ├── filters │ ├── switch_filter.h │ ├── logger_filter.h │ ├── message_filter.h │ └── level_filter.h │ ├── trigger.h │ ├── config.h │ ├── logger.inl │ ├── record.h │ ├── processor.h │ └── logger.h ├── .gitlinks ├── examples ├── default.cpp ├── syslog.cpp ├── console.cpp ├── file.cpp ├── hashlog.cpp ├── rolling_size.cpp ├── rolling_time.cpp ├── layout.cpp ├── async.cpp ├── sync.cpp └── format.cpp ├── .github └── workflows │ ├── build-windows-vs.yml │ ├── build-macos.yml │ ├── build-windows-mingw.yml │ ├── build-linux-gcc.yml │ ├── build-linux-clang.yml │ ├── build-windows-cygwin.yml │ ├── doxygen.yml │ └── build-windows-msys2.yml ├── performance ├── appender_console.cpp ├── format.cpp ├── layout.cpp ├── async_format.cpp ├── file_sync.cpp ├── appender_null.cpp ├── processor_sync.cpp ├── appender_file.cpp ├── file_async.cpp └── processor_async.cpp ├── LICENSE └── CMakeLists.txt /TODO.md: -------------------------------------------------------------------------------- 1 | # CppLogging todo 2 | -------------------------------------------------------------------------------- /images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chronoxor/CppLogging/HEAD/images/default.png -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /tests/test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 26.05.2016 3 | // 4 | 5 | #include "test.h" 6 | -------------------------------------------------------------------------------- /scripts/hashlog-map/hashlog-map.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SET curdir=%~dp0 3 | python "%curdir:~0,-1%\hashlog-map.py" %* 4 | -------------------------------------------------------------------------------- /tests/test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 26.05.2016 3 | // 4 | 5 | #include 6 | -------------------------------------------------------------------------------- /scripts/hashlog-map/hashlog-map: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | curdir=`dirname $0` 4 | python3 $curdir/hashlog-map.py $* 5 | -------------------------------------------------------------------------------- /scripts/hashlog-map/setup.cfg: -------------------------------------------------------------------------------- 1 | # Inside of setup.cfg 2 | [metadata] 3 | description-file = README.md 4 | license_files=../../LICENSE 5 | -------------------------------------------------------------------------------- /modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include("Catch2.cmake") 2 | include("cpp-optparse.cmake") 3 | include("CppBenchmark.cmake") 4 | include("CppCommon.cmake") 5 | include("zlib.cmake") 6 | -------------------------------------------------------------------------------- /scripts/hashlog-map/README.md: -------------------------------------------------------------------------------- 1 | # Hashlog map generate tool 2 | 3 | Hashlog map generate tool is used to parse C++ source files for logging format messages 4 | and create the corresponding .hashlog binary file 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | build 3 | 4 | # CMake 5 | build 6 | cmake 7 | cmake-build-* 8 | 9 | # Ctags files 10 | tags 11 | tags.idx 12 | 13 | # Modules 14 | modules/* 15 | !modules/CMakeLists.txt 16 | !modules/*.cmake 17 | 18 | # Temporary 19 | .idea 20 | temp 21 | -------------------------------------------------------------------------------- /modules/CppCommon.cmake: -------------------------------------------------------------------------------- 1 | if(NOT TARGET cppcommon) 2 | 3 | # Module flag 4 | set(CPPCOMMON_MODULE Y) 5 | 6 | # Module subdirectory 7 | add_subdirectory("CppCommon") 8 | 9 | # Module folder 10 | set_target_properties(cppcommon PROPERTIES FOLDER "modules/CppCommon") 11 | 12 | endif() 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | bin/* linguist-vendored 2 | build/* linguist-vendored 3 | cmake/* linguist-vendored 4 | documents/* linguist-vendored 5 | images/* linguist-vendored 6 | modules/* linguist-vendored 7 | source/logging/appenders/minizip/* linguist-vendored 8 | temp/* linguist-vendored 9 | tools/* linguist-vendored 10 | -------------------------------------------------------------------------------- /modules/CppBenchmark.cmake: -------------------------------------------------------------------------------- 1 | if(NOT TARGET cppbenchmark) 2 | 3 | # Module flag 4 | set(CPPBENCHMARK_MODULE Y) 5 | 6 | # Module subdirectory 7 | add_subdirectory("CppBenchmark") 8 | 9 | # Module folder 10 | set_target_properties(cppbenchmark PROPERTIES FOLDER "modules/CppBenchmark") 11 | 12 | endif() 13 | -------------------------------------------------------------------------------- /modules/Catch2.cmake: -------------------------------------------------------------------------------- 1 | if(NOT TARGET Catch2) 2 | 3 | # Module library 4 | file(GLOB SOURCE_FILES "Catch2/extras/catch_amalgamated.cpp") 5 | add_library(Catch2 ${SOURCE_FILES}) 6 | target_include_directories(Catch2 PUBLIC "Catch2/extras") 7 | 8 | # Module folder 9 | set_target_properties(Catch2 PROPERTIES FOLDER "modules/Catch2") 10 | 11 | endif() 12 | -------------------------------------------------------------------------------- /source/logging/filters/logger_filter.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file logger_filter.cpp 3 | \brief Logger filter implementation 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/filters/logger_filter.h" 10 | 11 | namespace CppLogging { 12 | 13 | bool LoggerFilter::FilterRecord(Record& record) 14 | { 15 | bool result = (_pattern.compare(record.logger) == 0); 16 | return _positive ? result : !result; 17 | } 18 | 19 | } // namespace CppLogging 20 | -------------------------------------------------------------------------------- /source/logging/filters/message_filter.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file message_filter.cpp 3 | \brief Message filter implementation 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/filters/message_filter.h" 10 | 11 | namespace CppLogging { 12 | 13 | bool MessageFilter::FilterRecord(Record& record) 14 | { 15 | bool result = std::regex_match(record.message, _pattern); 16 | return _positive ? result : !result; 17 | } 18 | 19 | } // namespace CppLogging 20 | -------------------------------------------------------------------------------- /source/logging/filters/switch_filter.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file switch_filter.cpp 3 | \brief Switch filter implementation 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/filters/switch_filter.h" 10 | 11 | namespace CppLogging { 12 | 13 | void SwitchFilter::Update(bool enabled) 14 | { 15 | _enabled = enabled; 16 | } 17 | 18 | bool SwitchFilter::FilterRecord(Record& record) 19 | { 20 | return _enabled; 21 | } 22 | 23 | } // namespace CppLogging 24 | -------------------------------------------------------------------------------- /include/logging/filters.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file filters.h 3 | \brief Logging filters definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_FILTERS_H 10 | #define CPPLOGGING_FILTERS_H 11 | 12 | #include "logging/trigger.h" 13 | #include "logging/filters/logger_filter.h" 14 | #include "logging/filters/level_filter.h" 15 | #include "logging/filters/message_filter.h" 16 | #include "logging/filters/switch_filter.h" 17 | 18 | #endif // CPPLOGGING_FILTERS_H 19 | -------------------------------------------------------------------------------- /include/logging/layouts.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file layouts.h 3 | \brief Logging layouts definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUTS_H 10 | #define CPPLOGGING_LAYOUTS_H 11 | 12 | #include "logging/layouts/null_layout.h" 13 | #include "logging/layouts/empty_layout.h" 14 | #include "logging/layouts/binary_layout.h" 15 | #include "logging/layouts/hash_layout.h" 16 | #include "logging/layouts/text_layout.h" 17 | 18 | #endif // CPPLOGGING_LAYOUTS_H 19 | -------------------------------------------------------------------------------- /.gitlinks: -------------------------------------------------------------------------------- 1 | # Modules 2 | Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git devel 3 | cpp-optparse modules/cpp-optparse https://github.com/chronoxor/cpp-optparse.git main 4 | CppBenchmark modules/CppBenchmark https://github.com/chronoxor/CppBenchmark.git master 5 | CppCommon modules/CppCommon https://github.com/chronoxor/CppCommon.git master 6 | zlib modules/zlib https://github.com/madler/zlib.git master 7 | 8 | # Scripts 9 | build build https://github.com/chronoxor/CppBuildScripts.git master 10 | cmake cmake https://github.com/chronoxor/CppCMakeScripts.git master 11 | -------------------------------------------------------------------------------- /include/logging/processors.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file processors.h 3 | \brief Logging processors definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_H 10 | #define CPPLOGGING_PROCESSORS_H 11 | 12 | #include "logging/processors/exclusive_processor.h" 13 | #include "logging/processors/buffered_processor.h" 14 | #include "logging/processors/sync_processor.h" 15 | #include "logging/processors/async_wait_processor.h" 16 | #include "logging/processors/async_wait_free_processor.h" 17 | 18 | #endif // CPPLOGGING_PROCESSORS_H 19 | -------------------------------------------------------------------------------- /examples/default.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file default.cpp 3 | \brief Default logger example 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/logger.h" 10 | 11 | int main(int argc, char** argv) 12 | { 13 | // Create default logger 14 | CppLogging::Logger logger; 15 | 16 | // Log some messages with different level 17 | logger.Debug("Debug message"); 18 | logger.Info("Info message"); 19 | logger.Warn("Warning message"); 20 | logger.Error("Error message"); 21 | logger.Fatal("Fatal message"); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-vs.yml: -------------------------------------------------------------------------------- 1 | name: Windows (Visual Studio) 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: "Setup Visual Studio" 15 | uses: egor-tensin/vs-shell@v2 16 | 17 | - name: "Setup cmake" 18 | run: cmake --version 19 | 20 | - name: "Setup gil" 21 | run: | 22 | pip3 install gil 23 | gil update 24 | 25 | - name: "Build" 26 | run: | 27 | cd build 28 | ./vs.bat 29 | -------------------------------------------------------------------------------- /source/logging/logger.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file logger.cpp 3 | \brief Logger interface implementation 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/logger.h" 10 | 11 | #include "logging/config.h" 12 | 13 | namespace CppLogging { 14 | 15 | Logger::Logger() : _sink(Config::CreateLogger()._sink) 16 | { 17 | } 18 | 19 | Logger::Logger(const std::string& name) : _name(name), _sink(Config::CreateLogger(name)._sink) 20 | { 21 | } 22 | 23 | void Logger::Update() 24 | { 25 | _sink = Config::CreateLogger(_name)._sink; 26 | } 27 | 28 | } // namespace CppLogging 29 | -------------------------------------------------------------------------------- /.github/workflows/build-macos.yml: -------------------------------------------------------------------------------- 1 | name: MacOS 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: macos-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: "Setup python" 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: '3.x' 18 | 19 | - name: "Setup cmake" 20 | run: cmake --version 21 | 22 | - name: "Setup gil" 23 | run: | 24 | pip3 install gil 25 | gil update 26 | 27 | - name: "Build" 28 | run: | 29 | cd build 30 | ./unix.sh 31 | -------------------------------------------------------------------------------- /tests/test_appender_file.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 08.09.2016 3 | // 4 | 5 | #include "test.h" 6 | 7 | #include "logging/appenders/file_appender.h" 8 | 9 | using namespace CppCommon; 10 | using namespace CppLogging; 11 | 12 | TEST_CASE("File appender", "[CppLogging]") 13 | { 14 | File file("test.log"); 15 | { 16 | FileAppender appender(file, true, true); 17 | 18 | Record record; 19 | record.raw.resize(11); 20 | 21 | appender.AppendRecord(record); 22 | appender.Flush(); 23 | } 24 | REQUIRE(file.IsFileExists()); 25 | REQUIRE(file.size() == 10); 26 | File::Remove(file); 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-mingw.yml: -------------------------------------------------------------------------------- 1 | name: Windows (MinGW) 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: "Setup cmake" 15 | run: cmake --version 16 | 17 | - name: "Setup gil" 18 | run: | 19 | pip3 install gil 20 | gil update 21 | 22 | - name: "Build" 23 | env: 24 | INCLUDE: C:\mingw64\x86_64-w64-mingw32\include 25 | LIB: C:\mingw64\x86_64-w64-mingw32\lib 26 | run: | 27 | cd build 28 | ./mingw.bat 29 | -------------------------------------------------------------------------------- /source/logging/appenders/memory_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file memory_appender.cpp 3 | \brief Memory appender implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/memory_appender.h" 10 | 11 | namespace CppLogging { 12 | 13 | void MemoryAppender::AppendRecord(Record& record) 14 | { 15 | // Skip logging records without layout 16 | if (record.raw.empty()) 17 | return; 18 | 19 | // Append logging record content into the allocated memory buffer 20 | _buffer.insert(_buffer.end(), record.raw.begin(), record.raw.end()); 21 | } 22 | 23 | } // namespace CppLogging 24 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-gcc.yml: -------------------------------------------------------------------------------- 1 | name: Linux (gcc) 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: "Setup required packages" 15 | run: sudo apt-get install -y binutils-dev libssl-dev uuid-dev 16 | 17 | - name: "Setup gcc" 18 | uses: egor-tensin/setup-gcc@v1 19 | 20 | - name: "Setup cmake" 21 | run: cmake --version 22 | 23 | - name: "Setup gil" 24 | run: | 25 | pip3 install gil 26 | gil update 27 | 28 | - name: "Build" 29 | run: | 30 | cd build 31 | ./unix.sh 32 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-clang.yml: -------------------------------------------------------------------------------- 1 | name: Linux (clang) 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: "Setup required packages" 15 | run: sudo apt-get install -y binutils-dev libc++-dev libssl-dev uuid-dev 16 | 17 | - name: "Setup clang" 18 | uses: egor-tensin/setup-clang@v1 19 | 20 | - name: "Setup cmake" 21 | run: cmake --version 22 | 23 | - name: "Setup gil" 24 | run: | 25 | pip3 install gil 26 | gil update 27 | 28 | - name: "Build" 29 | run: | 30 | cd build 31 | ./unix.sh 32 | -------------------------------------------------------------------------------- /modules/cpp-optparse.cmake: -------------------------------------------------------------------------------- 1 | if(NOT TARGET cpp-optparse) 2 | 3 | # Module library 4 | file(GLOB SOURCE_FILES "cpp-optparse/OptionParser.cpp") 5 | add_library(cpp-optparse ${SOURCE_FILES}) 6 | if(MSVC) 7 | # C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data 8 | # C4996:
is removed in C++20 9 | set_target_properties(cpp-optparse PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS} /wd4244 /wd4996") 10 | else() 11 | set_target_properties(cpp-optparse PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS}") 12 | endif() 13 | target_include_directories(cpp-optparse PUBLIC "cpp-optparse") 14 | 15 | # Module folder 16 | set_target_properties(cpp-optparse PROPERTIES FOLDER "modules/cpp-optparse") 17 | 18 | endif() 19 | -------------------------------------------------------------------------------- /source/logging/appenders/ostream_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file ostream_appender.cpp 3 | \brief Output stream (std::ostream) appender implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/ostream_appender.h" 10 | 11 | namespace CppLogging { 12 | 13 | void OstreamAppender::AppendRecord(Record& record) 14 | { 15 | // Skip logging records without layout 16 | if (record.raw.empty()) 17 | return; 18 | 19 | // Append logging record content 20 | _ostream.write((char*)record.raw.data(), record.raw.size() - 1); 21 | } 22 | 23 | void OstreamAppender::Flush() 24 | { 25 | // Flush stream 26 | _ostream.flush(); 27 | } 28 | 29 | } // namespace CppLogging 30 | -------------------------------------------------------------------------------- /include/logging/appenders.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file appenders.h 3 | \brief Logging appenders definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_H 10 | #define CPPLOGGING_APPENDERS_H 11 | 12 | #include "logging/appenders/null_appender.h" 13 | #include "logging/appenders/console_appender.h" 14 | #include "logging/appenders/debug_appender.h" 15 | #include "logging/appenders/error_appender.h" 16 | #include "logging/appenders/file_appender.h" 17 | #include "logging/appenders/memory_appender.h" 18 | #include "logging/appenders/ostream_appender.h" 19 | #include "logging/appenders/rolling_file_appender.h" 20 | #include "logging/appenders/syslog_appender.h" 21 | 22 | #endif // CPPLOGGING_APPENDERS_H 23 | -------------------------------------------------------------------------------- /source/logging/appenders/error_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file error_appender.cpp 3 | \brief Error (stderr) appender implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/error_appender.h" 10 | 11 | #include 12 | 13 | namespace CppLogging { 14 | 15 | void ErrorAppender::AppendRecord(Record& record) 16 | { 17 | // Skip logging records without layout 18 | if (record.raw.empty()) 19 | return; 20 | 21 | // Append logging record content 22 | std::fwrite(record.raw.data(), 1, record.raw.size() - 1, stderr); 23 | } 24 | 25 | void ErrorAppender::Flush() 26 | { 27 | // Flush stream 28 | std::fflush(stderr); 29 | } 30 | 31 | } // namespace CppLogging 32 | -------------------------------------------------------------------------------- /source/logging/processors/sync_processor.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file sync_processor.cpp 3 | \brief Synchronous logging processor implementation 4 | \author Ivan Shynkarenka 5 | \date 28.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/processors/sync_processor.h" 10 | 11 | namespace CppLogging { 12 | 13 | bool SyncProcessor::ProcessRecord(Record& record) 14 | { 15 | CppCommon::Locker locker(_lock); 16 | 17 | // Process the given logging record under the critical section lock 18 | return Processor::ProcessRecord(record); 19 | } 20 | 21 | void SyncProcessor::Flush() 22 | { 23 | CppCommon::Locker locker(_lock); 24 | 25 | // Flush under the critical section lock 26 | return Processor::Flush(); 27 | } 28 | 29 | } // namespace CppLogging 30 | -------------------------------------------------------------------------------- /performance/appender_console.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 29.07.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppLogging; 11 | 12 | class ConsoleConfigFixture : public virtual CppBenchmark::Fixture 13 | { 14 | protected: 15 | void Initialize(CppBenchmark::Context& context) override 16 | { 17 | auto sink = std::make_shared(std::make_shared()); 18 | sink->appenders().push_back(std::make_shared()); 19 | Config::ConfigLogger("test", sink); 20 | Config::Startup(); 21 | } 22 | }; 23 | 24 | BENCHMARK_FIXTURE(ConsoleConfigFixture, "ConsoleAppender") 25 | { 26 | static Logger logger = Config::CreateLogger("test"); 27 | logger.Info("Test message"); 28 | } 29 | 30 | BENCHMARK_MAIN() 31 | -------------------------------------------------------------------------------- /source/logging/appenders/minizip/mztools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Additional tools for Minizip 3 | Code: Xavier Roche '2004 4 | License: Same as ZLIB (www.gzip.org) 5 | */ 6 | 7 | #ifndef _zip_tools_H 8 | #define _zip_tools_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #ifndef _ZLIB_H 15 | #include "zlib.h" 16 | #endif 17 | 18 | #include "unzip.h" 19 | 20 | /* Repair a ZIP file (missing central directory) 21 | file: file to recover 22 | fileOut: output file after recovery 23 | fileOutTmp: temporary file name used for recovery 24 | */ 25 | extern int ZEXPORT unzRepair(const char* file, 26 | const char* fileOut, 27 | const char* fileOutTmp, 28 | uLong* nRecovered, 29 | uLong* bytesRecovered); 30 | 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /source/logging/appenders/minizip/iowin32.h: -------------------------------------------------------------------------------- 1 | /* iowin32.h -- IO base function header for compress/uncompress .zip 2 | Version 1.1, February 14h, 2010 3 | part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) 4 | 5 | Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) 6 | 7 | Modifications for Zip64 support 8 | Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) 9 | 10 | For more info read MiniZip_info.txt 11 | 12 | */ 13 | 14 | #include 15 | 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | void fill_win32_filefunc(zlib_filefunc_def* pzlib_filefunc_def); 22 | void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def); 23 | void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def); 24 | void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /source/logging/filters/level_filter.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file level_filter.cpp 3 | \brief Level filter implementation 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/filters/level_filter.h" 10 | 11 | namespace CppLogging { 12 | 13 | void LevelFilter::Update(Level level, bool positive) 14 | { 15 | _positive = positive; 16 | _from = Level::NONE; 17 | _to = level; 18 | } 19 | 20 | void LevelFilter::Update(Level from, Level to, bool positive) 21 | { 22 | _positive = positive; 23 | if (from <= to) 24 | { 25 | _from = from; 26 | _to = to; 27 | } 28 | else 29 | { 30 | _from = to; 31 | _to = from; 32 | } 33 | } 34 | 35 | bool LevelFilter::FilterRecord(Record& record) 36 | { 37 | if (_positive) 38 | return ((record.level >= _from) && (record.level <= _to)); 39 | else 40 | return ((record.level < _from) || (record.level > _to)); 41 | } 42 | 43 | } // namespace CppLogging 44 | -------------------------------------------------------------------------------- /include/logging/layouts/null_layout.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file null_layout.h 3 | \brief Null layout definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUTS_NULL_LAYOUT_H 10 | #define CPPLOGGING_LAYOUTS_NULL_LAYOUT_H 11 | 12 | #include "logging/layout.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Null layout 17 | /*! 18 | Null layout does nothing with a given logging record. 19 | 20 | Thread-safe. 21 | */ 22 | class NullLayout : public Layout 23 | { 24 | public: 25 | NullLayout() = default; 26 | NullLayout(const NullLayout&) = delete; 27 | NullLayout(NullLayout&&) = delete; 28 | virtual ~NullLayout() = default; 29 | 30 | NullLayout& operator=(const NullLayout&) = delete; 31 | NullLayout& operator=(NullLayout&&) = delete; 32 | 33 | // Implementation of Layout 34 | void LayoutRecord(Record& record) override {} 35 | }; 36 | 37 | } // namespace CppLogging 38 | 39 | #endif // CPPLOGGING_LAYOUTS_NULL_LAYOUT_H 40 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-cygwin.yml: -------------------------------------------------------------------------------- 1 | name: Windows (Cygwin) 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | defaults: 12 | run: 13 | shell: C:\cygwin\bin\bash.exe --login -o igncr {0} 14 | steps: 15 | - name: "Setup git" 16 | shell: cmd 17 | run: git config --global core.autocrlf input 18 | 19 | - uses: actions/checkout@v4 20 | 21 | - name: "Setup Cygwin" 22 | uses: cygwin/cygwin-install-action@v2 23 | with: 24 | check-sig: false 25 | platform: x86_64 26 | packages: automake make cmake gcc-g++ doxygen graphviz libssl-devel libuuid-devel 27 | 28 | - name: "Setup cmake" 29 | run: cmake --version 30 | 31 | - name: "Setup gil" 32 | shell: cmd 33 | run: | 34 | pip3 install gil 35 | gil update 36 | 37 | - name: "Build" 38 | run: | 39 | cd $GITHUB_WORKSPACE/build 40 | ./unix.sh 41 | -------------------------------------------------------------------------------- /include/logging/level.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file level.h 3 | \brief Logging level definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LEVEL_H 10 | #define CPPLOGGING_LEVEL_H 11 | 12 | #include 13 | 14 | namespace CppLogging { 15 | 16 | //! Logging level 17 | enum class Level : uint8_t 18 | { 19 | NONE = 0x00, //!< Log nothing 20 | FATAL = 0x1F, //!< Log fatal errors 21 | ERROR = 0x3F, //!< Log errors 22 | WARN = 0x7F, //!< Log warnings 23 | INFO = 0x9F, //!< Log information 24 | DEBUG = 0xBF, //!< Log debug 25 | ALL = 0xFF //!< Log everything 26 | }; 27 | 28 | //! Stream output: Logging level 29 | /*! 30 | \param stream - Output stream 31 | \param level - Logging level 32 | \return Output stream 33 | */ 34 | template 35 | TOutputStream& operator<<(TOutputStream& stream, Level level); 36 | 37 | } // namespace CppLogging 38 | 39 | #include "level.inl" 40 | 41 | #endif // CPPLOGGING_LEVEL_H 42 | -------------------------------------------------------------------------------- /include/logging/layout.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file layout.h 3 | \brief Logging layout interface definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUT_H 10 | #define CPPLOGGING_LAYOUT_H 11 | 12 | #include "logging/element.h" 13 | #include "logging/record.h" 14 | 15 | namespace CppLogging { 16 | 17 | //! Logging layout interface 18 | /*! 19 | Logging layout takes an instance of a single logging record 20 | and convert it into a raw buffer (raw filed will be updated). 21 | 22 | \see NullLayout 23 | \see EmptyLayout 24 | \see BinaryLayout 25 | \see TextLayout 26 | */ 27 | class Layout : public Element 28 | { 29 | public: 30 | //! Layout the given logging record into a raw buffer 31 | /*! 32 | This method will update the raw filed of the given logging record. 33 | 34 | \param record - Logging record 35 | */ 36 | virtual void LayoutRecord(Record& record) = 0; 37 | }; 38 | 39 | } // namespace CppLogging 40 | 41 | #endif // CPPLOGGING_LAYOUT_H 42 | -------------------------------------------------------------------------------- /.github/workflows/doxygen.yml: -------------------------------------------------------------------------------- 1 | name: Doxygen 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "Setup git" 13 | run: | 14 | git config --global user.name "${{ github.actor }}" 15 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 16 | 17 | - uses: actions/checkout@v4 18 | 19 | - name: "Setup cmake" 20 | run: cmake --version 21 | 22 | - name: "Setup doxygen" 23 | run: | 24 | sudo apt-get -y install doxygen doxygen-latex graphviz binutils-dev 25 | doxygen --version 26 | dot -V 27 | 28 | - name: "Setup gil" 29 | run: | 30 | pip3 install gil 31 | gil update 32 | 33 | - name: "Doxygen" 34 | env: 35 | GITHUB_ACTOR: ${{ github.actor }} 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | run: | 38 | cd build/Unix 39 | ./01-generate.sh 40 | ./05-doxygen.sh 41 | -------------------------------------------------------------------------------- /include/logging/appenders/null_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file null_appender.h 3 | \brief Null appender definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_NULL_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_NULL_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Null appender 17 | /*! 18 | Null appender does nothing with a given logging record. 19 | 20 | Thread-safe. 21 | */ 22 | class NullAppender : public Appender 23 | { 24 | public: 25 | NullAppender() = default; 26 | NullAppender(const NullAppender&) = delete; 27 | NullAppender(NullAppender&&) = delete; 28 | virtual ~NullAppender() = default; 29 | 30 | NullAppender& operator=(const NullAppender&) = delete; 31 | NullAppender& operator=(NullAppender&&) = delete; 32 | 33 | // Implementation of Appender 34 | void AppendRecord(Record& record) override {} 35 | }; 36 | 37 | } // namespace CppLogging 38 | 39 | #endif // CPPLOGGING_APPENDERS_NULL_APPENDER_H 40 | -------------------------------------------------------------------------------- /scripts/hashlog-map/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import distutils.core 4 | 5 | if __name__ == "__main__": 6 | distutils.core.setup( 7 | name="hashlog-map", 8 | version="1.5.0.0", 9 | author="Ivan Shynkarenka", 10 | author_email="chronoxor@gmail.com", 11 | url="https://github.com/chronoxor/CppLogging/scripts/hashlog-map", 12 | download_url = "https://github.com/chronoxor/CppLogging/archive/refs/tags/1.5.0.0.tar.gz", 13 | description="Hashlog map generate tool", 14 | long_description="Hashlog map generate tool is used to parse C++ source files for logging format messages and create the corresponding .hashlog binary file", 15 | license="MIT License", 16 | scripts=["hashlog-map.py", "hashlog-map.bat", "hashlog-map"], 17 | platforms="Linux, MacOS, Windows", 18 | classifiers=[ 19 | "Operating System :: POSIX", 20 | "Operating System :: MacOS :: MacOS X", 21 | "Operating System :: Microsoft :: Windows", 22 | "Programming Language :: Python :: 3" 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /source/logging/appenders/debug_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file debug_appender.cpp 3 | \brief Debug appender implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/debug_appender.h" 10 | 11 | #if defined(_WIN32) || defined(_WIN64) 12 | #include 13 | #else 14 | #include 15 | #endif 16 | 17 | namespace CppLogging { 18 | 19 | void DebugAppender::AppendRecord(Record& record) 20 | { 21 | // Skip logging records without layout 22 | if (record.raw.empty()) 23 | return; 24 | 25 | #if defined(_WIN32) || defined(_WIN64) 26 | // Append logging record content 27 | OutputDebugStringA((LPCSTR)record.raw.data()); 28 | #else 29 | // Append logging record content 30 | std::fwrite(record.raw.data(), 1, record.raw.size() - 1, stdout); 31 | #endif 32 | } 33 | 34 | void DebugAppender::Flush() 35 | { 36 | #if defined(_WIN32) || defined(_WIN64) 37 | // Do nothing here... 38 | #else 39 | // Flush stream 40 | std::fflush(stdout); 41 | #endif 42 | } 43 | 44 | } // namespace CppLogging 45 | -------------------------------------------------------------------------------- /include/logging/layouts/empty_layout.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file empty_layout.h 3 | \brief Empty layout definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUTS_EMPTY_LAYOUT_H 10 | #define CPPLOGGING_LAYOUTS_EMPTY_LAYOUT_H 11 | 12 | #include "logging/layout.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Empty layout 17 | /*! 18 | Empty layout performs zero memory operation to convert 19 | the given logging record into the empty raw buffer. 20 | 21 | Thread-safe. 22 | */ 23 | class EmptyLayout : public Layout 24 | { 25 | public: 26 | EmptyLayout() = default; 27 | EmptyLayout(const EmptyLayout&) = delete; 28 | EmptyLayout(EmptyLayout&&) = delete; 29 | virtual ~EmptyLayout() = default; 30 | 31 | EmptyLayout& operator=(const EmptyLayout&) = delete; 32 | EmptyLayout& operator=(EmptyLayout&&) = delete; 33 | 34 | // Implementation of Layout 35 | void LayoutRecord(Record& record) override { record.raw.clear(); } 36 | }; 37 | 38 | } // namespace CppLogging 39 | 40 | #endif // CPPLOGGING_LAYOUTS_EMPTY_LAYOUT_H 41 | -------------------------------------------------------------------------------- /performance/format.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 28.09.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/record.h" 8 | 9 | using namespace CppLogging; 10 | 11 | BENCHMARK("Format(int)") 12 | { 13 | static Record record; 14 | record.Clear(); 15 | record.Format("test {} test", context.metrics().total_operations()); 16 | } 17 | 18 | BENCHMARK("StoreFormat(int)") 19 | { 20 | static Record record; 21 | record.Clear(); 22 | record.StoreFormat("test {} test", context.metrics().total_operations()); 23 | } 24 | 25 | BENCHMARK("Format(int, double, string)") 26 | { 27 | static Record record; 28 | record.Clear(); 29 | record.Format("test {}-{}-{} test", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 30 | } 31 | 32 | BENCHMARK("StoreFormat(int, double, string)") 33 | { 34 | static Record record; 35 | record.Clear(); 36 | record.StoreFormat("test {}-{}-{} test", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 37 | } 38 | 39 | BENCHMARK_MAIN() 40 | -------------------------------------------------------------------------------- /include/logging/layouts/binary_layout.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file binary_layout.h 3 | \brief Binary layout definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUTS_BINARY_LAYOUT_H 10 | #define CPPLOGGING_LAYOUTS_BINARY_LAYOUT_H 11 | 12 | #include "logging/layout.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Binary layout 17 | /*! 18 | Binary layout performs simple memory copy operation to convert 19 | the given logging record into the plane raw buffer. 20 | 21 | Thread-safe. 22 | */ 23 | class BinaryLayout : public Layout 24 | { 25 | public: 26 | BinaryLayout() = default; 27 | BinaryLayout(const BinaryLayout&) = delete; 28 | BinaryLayout(BinaryLayout&&) = delete; 29 | virtual ~BinaryLayout() = default; 30 | 31 | BinaryLayout& operator=(const BinaryLayout&) = delete; 32 | BinaryLayout& operator=(BinaryLayout&&) = delete; 33 | 34 | // Implementation of Layout 35 | void LayoutRecord(Record& record) override; 36 | }; 37 | 38 | } // namespace CppLogging 39 | 40 | #endif // CPPLOGGING_LAYOUTS_BINARY_LAYOUT_H 41 | -------------------------------------------------------------------------------- /include/logging/filter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file filter.h 3 | \brief Logging filter interface definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_FILTER_H 10 | #define CPPLOGGING_FILTER_H 11 | 12 | #include "logging/element.h" 13 | #include "logging/record.h" 14 | 15 | namespace CppLogging { 16 | 17 | //! Logging filter interface 18 | /*! 19 | Logging filter takes an instance of a single logging record and 20 | performs some checks to detect if the record should be filered 21 | out and not processed anymore. 22 | 23 | \see SwitchFilter 24 | \see LoggerFilter 25 | \see LevelFilter 26 | \see MessageFilter 27 | */ 28 | class Filter : public Element 29 | { 30 | public: 31 | //! Filter the given logging record 32 | /*! 33 | \param record - Logging record 34 | \return 'true' if the logging record should be processed, 'false' if the logging record was filtered out 35 | */ 36 | virtual bool FilterRecord(Record& record) = 0; 37 | }; 38 | 39 | } // namespace CppLogging 40 | 41 | #endif // CPPLOGGING_FILTER_H 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2026 Ivan Shynkarenka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/logging/level.inl: -------------------------------------------------------------------------------- 1 | /*! 2 | \file level.inl 3 | \brief Logging level inline implementation 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | namespace CppLogging { 10 | 11 | template 12 | inline TOutputStream& operator<<(TOutputStream& stream, Level level) 13 | { 14 | switch (level) 15 | { 16 | case Level::NONE: 17 | stream << "None"; 18 | break; 19 | case Level::FATAL: 20 | stream << "Fatal"; 21 | break; 22 | case Level::ERROR: 23 | stream << "Error"; 24 | break; 25 | case Level::WARN: 26 | stream << "Warn"; 27 | break; 28 | case Level::INFO: 29 | stream << "Info"; 30 | break; 31 | case Level::DEBUG: 32 | stream << "Debug"; 33 | break; 34 | case Level::ALL: 35 | stream << "All"; 36 | break; 37 | default: 38 | stream << ""; 39 | break; 40 | } 41 | return stream; 42 | } 43 | 44 | } // namespace CppLogging 45 | -------------------------------------------------------------------------------- /include/logging/version.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file version.h 3 | \brief Version definition 4 | \author Ivan Shynkarenka 5 | \date 26.05.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_VERSION_H 10 | #define CPPLOGGING_VERSION_H 11 | 12 | /*! \mainpage C++ Logging Library 13 | 14 | C++ Logging Library provides functionality to log different events with a high 15 | throughput in multi-thread environment into different sinks (console, files, 16 | rolling files, syslog, etc.). Logging configuration is very flexible and gives 17 | functionality to build flexible logger hierarchy with combination of logging 18 | processors (sync, async), filters, layouts (binary, text) and appenders. 19 | 20 | This document contains CppLogging API references. 21 | 22 | Library description, features, requirements and usage examples can be find on 23 | GitHub: https://github.com/chronoxor/CppLogging 24 | 25 | */ 26 | 27 | /*! 28 | \namespace CppLogging 29 | \brief C++ Logging project definitions 30 | */ 31 | namespace CppLogging { 32 | 33 | //! Project version 34 | const char version[] = "1.0.5.0"; 35 | 36 | } // namespace CppLogging 37 | 38 | #endif // CPPLOGGING_VERSION_H 39 | -------------------------------------------------------------------------------- /include/logging/appenders/error_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file error_appender.h 3 | \brief Error (stderr) appender definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_ERROR_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_ERROR_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Error (stderr) appender 17 | /*! 18 | Error appender prints the given logging record into 19 | the console or system error stream (stderr). 20 | 21 | Thread-safe. 22 | */ 23 | class ErrorAppender : public Appender 24 | { 25 | public: 26 | ErrorAppender() = default; 27 | ErrorAppender(const ErrorAppender&) = delete; 28 | ErrorAppender(ErrorAppender&&) = delete; 29 | virtual ~ErrorAppender() = default; 30 | 31 | ErrorAppender& operator=(const ErrorAppender&) = delete; 32 | ErrorAppender& operator=(ErrorAppender&&) = delete; 33 | 34 | // Implementation of Appender 35 | void AppendRecord(Record& record) override; 36 | void Flush() override; 37 | }; 38 | 39 | } // namespace CppLogging 40 | 41 | #endif // CPPLOGGING_APPENDERS_ERROR_APPENDER_H 42 | -------------------------------------------------------------------------------- /.github/workflows/build-windows-msys2.yml: -------------------------------------------------------------------------------- 1 | name: Windows (MSYS2) 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | defaults: 12 | run: 13 | shell: msys2 {0} 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: "Setup MSYS2" 18 | uses: msys2/setup-msys2@v2 19 | with: 20 | msystem: UCRT64 21 | release: false 22 | install: >- 23 | git 24 | make 25 | mingw-w64-ucrt-x86_64-cmake 26 | mingw-w64-ucrt-x86_64-gcc 27 | mingw-w64-ucrt-x86_64-doxygen 28 | mingw-w64-ucrt-x86_64-graphviz 29 | msys2-w32api-runtime 30 | python-pip 31 | 32 | - name: "Setup cmake" 33 | run: cmake --version 34 | 35 | - name: "Setup gil" 36 | run: | 37 | pip3 install gil --break-system-packages 38 | gil update 39 | 40 | - name: "Build" 41 | env: 42 | INCLUDE: C:\msys64\usr\include\w32api 43 | LIB: C:\msys64\usr\lib\w32api 44 | run: | 45 | cd build 46 | ./unix.sh 47 | -------------------------------------------------------------------------------- /include/logging/appenders/rolling_file_appender.inl: -------------------------------------------------------------------------------- 1 | /*! 2 | \file rolling_file_appender.inl 3 | \brief Rolling file appender inline implementation 4 | \author Ivan Shynkarenka 5 | \date 12.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | namespace CppLogging { 10 | 11 | template 12 | inline TOutputStream& operator<<(TOutputStream& stream, TimeRollingPolicy policy) 13 | { 14 | switch (policy) 15 | { 16 | case TimeRollingPolicy::YEAR: 17 | stream << "Year"; 18 | break; 19 | case TimeRollingPolicy::MONTH: 20 | stream << "Month"; 21 | break; 22 | case TimeRollingPolicy::DAY: 23 | stream << "Day"; 24 | break; 25 | case TimeRollingPolicy::HOUR: 26 | stream << "Hour"; 27 | break; 28 | case TimeRollingPolicy::MINUTE: 29 | stream << "Minute"; 30 | break; 31 | case TimeRollingPolicy::SECOND: 32 | stream << "Second"; 33 | break; 34 | default: 35 | stream << ""; 36 | break; 37 | } 38 | return stream; 39 | } 40 | 41 | } // namespace CppLogging 42 | -------------------------------------------------------------------------------- /examples/syslog.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file syslog.cpp 3 | \brief Syslog logger example 4 | \author Ivan Shynkarenka 5 | \date 30.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create default logging sink processor with a text layout 15 | auto sink = std::make_shared(std::make_shared()); 16 | // Add syslog appender 17 | sink->appenders().push_back(std::make_shared()); 18 | 19 | // Configure default logger 20 | CppLogging::Config::ConfigLogger(sink); 21 | 22 | // Startup the logging infrastructure 23 | CppLogging::Config::Startup(); 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | // Configure logger 29 | ConfigureLogger(); 30 | 31 | // Create default logger 32 | CppLogging::Logger logger; 33 | 34 | // Log some messages with different level 35 | logger.Debug("Debug message"); 36 | logger.Info("Info message"); 37 | logger.Warn("Warning message"); 38 | logger.Error("Error message"); 39 | logger.Fatal("Fatal message"); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /include/logging/appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file appender.h 3 | \brief Logging appender interface definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDER_H 10 | #define CPPLOGGING_APPENDER_H 11 | 12 | #include "logging/element.h" 13 | #include "logging/record.h" 14 | 15 | namespace CppLogging { 16 | 17 | //! Logging appender interface 18 | /*! 19 | Logging appender takes an instance of a single logging record 20 | and store it content in some storage or show it in console. 21 | 22 | \see NullAppender 23 | \see ConsoleAppender 24 | \see DebugAppender 25 | \see ErrorAppender 26 | \see MemoryAppender 27 | \see OstreamAppender 28 | \see FileAppender 29 | \see RollingFileAppender 30 | \see SysLogAppender 31 | */ 32 | class Appender : public Element 33 | { 34 | public: 35 | //! Append the given logging record 36 | /*! 37 | \param record - Logging record 38 | */ 39 | virtual void AppendRecord(Record& record) = 0; 40 | 41 | //! Flush the logging appender 42 | virtual void Flush() {} 43 | }; 44 | 45 | } // namespace CppLogging 46 | 47 | #endif // CPPLOGGING_APPENDER_H 48 | -------------------------------------------------------------------------------- /examples/console.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file console.cpp 3 | \brief Console logger example 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create default logging sink processor with a text layout 15 | auto sink = std::make_shared(std::make_shared()); 16 | // Add console appender 17 | sink->appenders().push_back(std::make_shared()); 18 | 19 | // Configure default logger 20 | CppLogging::Config::ConfigLogger(sink); 21 | 22 | // Startup the logging infrastructure 23 | CppLogging::Config::Startup(); 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | // Configure logger 29 | ConfigureLogger(); 30 | 31 | // Create default logger 32 | CppLogging::Logger logger; 33 | 34 | // Log some messages with different level 35 | logger.Debug("Debug message"); 36 | logger.Info("Info message"); 37 | logger.Warn("Warning message"); 38 | logger.Error("Error message"); 39 | logger.Fatal("Fatal message"); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /include/logging/appenders/syslog_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file syslog_appender.h 3 | \brief Syslog appender definition 4 | \author Ivan Shynkarenka 5 | \date 28.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_SYSLOG_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_SYSLOG_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Syslog appender 17 | /*! 18 | Syslog appender forward logging record to the syslog() system call 19 | for Unix systems. Under Windows systems this appender does nothing! 20 | 21 | Thread-safe. 22 | */ 23 | class SyslogAppender : public Appender 24 | { 25 | public: 26 | SyslogAppender(); 27 | SyslogAppender(const SyslogAppender&) = delete; 28 | SyslogAppender(SyslogAppender&&) = delete; 29 | virtual ~SyslogAppender(); 30 | 31 | SyslogAppender& operator=(const SyslogAppender&) = delete; 32 | SyslogAppender& operator=(SyslogAppender&&) = delete; 33 | 34 | // Implementation of Appender 35 | void AppendRecord(Record& record) override; 36 | }; 37 | 38 | } // namespace CppLogging 39 | 40 | /*! \example syslog.cpp Syslog logger example */ 41 | 42 | #endif // CPPLOGGING_APPENDERS_SYSLOG_APPENDER_H 43 | -------------------------------------------------------------------------------- /examples/file.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file file.cpp 3 | \brief File logger example 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create default logging sink processor with a binary layout 15 | auto sink = std::make_shared(std::make_shared()); 16 | // Add file appender 17 | sink->appenders().push_back(std::make_shared("file.bin.log")); 18 | 19 | // Configure example logger 20 | CppLogging::Config::ConfigLogger("example", sink); 21 | 22 | // Startup the logging infrastructure 23 | CppLogging::Config::Startup(); 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | // Configure logger 29 | ConfigureLogger(); 30 | 31 | // Create example logger 32 | CppLogging::Logger logger("example"); 33 | 34 | // Log some messages with different level 35 | logger.Debug("Debug message {}", 1); 36 | logger.Info("Info message {}", 2); 37 | logger.Warn("Warning message {}", 3); 38 | logger.Error("Error message {}", 4); 39 | logger.Fatal("Fatal message {}", 5); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /examples/hashlog.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file hashlog.cpp 3 | \brief Hash logger example 4 | \author Ivan Shynkarenka 5 | \date 19.12.2021 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create default logging sink processor with a hash layout 15 | auto sink = std::make_shared(std::make_shared()); 16 | // Add file appender 17 | sink->appenders().push_back(std::make_shared("file.hash.log")); 18 | 19 | // Configure example logger 20 | CppLogging::Config::ConfigLogger("example", sink); 21 | 22 | // Startup the logging infrastructure 23 | CppLogging::Config::Startup(); 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | // Configure logger 29 | ConfigureLogger(); 30 | 31 | // Create example logger 32 | CppLogging::Logger logger("example"); 33 | 34 | // Log some messages with different level 35 | logger.Debug("Debug message {}", 1); 36 | logger.Info("Info message {}", 2); 37 | logger.Warn("Warning message {}", 3); 38 | logger.Error("Error message {}", 4); 39 | logger.Fatal("Fatal message {}", 5); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /include/logging/appenders/console_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file console_appender.h 3 | \brief Console (stdout) appender definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_CONSOLE_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_CONSOLE_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Console (stdout) appender 17 | /*! 18 | Console appender prints the given logging record into 19 | the console or system output stream (stdout). 20 | 21 | Thread-safe. 22 | */ 23 | class ConsoleAppender : public Appender 24 | { 25 | public: 26 | ConsoleAppender() = default; 27 | ConsoleAppender(const ConsoleAppender&) = delete; 28 | ConsoleAppender(ConsoleAppender&&) = delete; 29 | virtual ~ConsoleAppender() = default; 30 | 31 | ConsoleAppender& operator=(const ConsoleAppender&) = delete; 32 | ConsoleAppender& operator=(ConsoleAppender&&) = delete; 33 | 34 | // Implementation of Appender 35 | void AppendRecord(Record& record) override; 36 | void Flush() override; 37 | }; 38 | 39 | } // namespace CppLogging 40 | 41 | /*! \example console.cpp Console logger example */ 42 | 43 | #endif // CPPLOGGING_APPENDERS_CONSOLE_APPENDER_H 44 | -------------------------------------------------------------------------------- /source/logging/processors/exclusive_processor.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file exclusive_processor.cpp 3 | \brief Exclusive logging processor implementation 4 | \author Ivan Shynkarenka 5 | \date 11.04.2019 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/processors/exclusive_processor.h" 10 | 11 | namespace CppLogging { 12 | 13 | bool ExclusiveProcessor::ProcessRecord(Record& record) 14 | { 15 | // Check if the logging processor started 16 | if (!IsStarted()) 17 | return true; 18 | 19 | // Filter the given logging record 20 | if (!FilterRecord(record)) 21 | return true; 22 | 23 | // Layout the given logging record 24 | if (_layout && _layout->IsStarted()) 25 | _layout->LayoutRecord(record); 26 | 27 | // Append the given logging record 28 | for (auto& appender : _appenders) 29 | if (appender && appender->IsStarted()) 30 | appender->AppendRecord(record); 31 | 32 | // Process the given logging record with sub processors 33 | for (auto& processor : _processors) 34 | if (processor && processor->IsStarted() && !processor->ProcessRecord(record)) 35 | return false; 36 | 37 | // Logging record was exclusively processed! 38 | return false; 39 | } 40 | 41 | } // namespace CppLogging 42 | -------------------------------------------------------------------------------- /include/logging/appenders/debug_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file debug_appender.h 3 | \brief Debug appender definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_DEBUG_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_DEBUG_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Debug appender 17 | /*! 18 | Debug appender prints the given logging record into 19 | the attached debugger if present for Windows system. 20 | Under Unix systems this appender prints the given 21 | logging record into the system error stream (stderr). 22 | 23 | Thread-safe. 24 | */ 25 | class DebugAppender : public Appender 26 | { 27 | public: 28 | DebugAppender() = default; 29 | DebugAppender(const DebugAppender&) = delete; 30 | DebugAppender(DebugAppender&&) = delete; 31 | virtual ~DebugAppender() = default; 32 | 33 | DebugAppender& operator=(const DebugAppender&) = delete; 34 | DebugAppender& operator=(DebugAppender&&) = delete; 35 | 36 | // Implementation of Appender 37 | void AppendRecord(Record& record) override; 38 | void Flush() override; 39 | }; 40 | 41 | } // namespace CppLogging 42 | 43 | #endif // CPPLOGGING_APPENDERS_DEBUG_APPENDER_H 44 | -------------------------------------------------------------------------------- /include/logging/element.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file element.h 3 | \brief Logging element interface definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_ELEMENT_H 10 | #define CPPLOGGING_ELEMENT_H 11 | 12 | namespace CppLogging { 13 | 14 | //! Logging element interface 15 | /*! 16 | Logging filter takes an instance of a single logging record and 17 | performs some checks to detect if the record should be filered 18 | out and not processed anymore. 19 | 20 | \see Appender 21 | \see Filter 22 | \see Layout 23 | \see Processor 24 | */ 25 | class Element 26 | { 27 | public: 28 | //! Is the logging element started? 29 | virtual bool IsStarted() const noexcept { return true; } 30 | 31 | //! Start the logging element 32 | /*! 33 | \return 'true' if the logging element was successfully started, 'false' if the logging element failed to start 34 | */ 35 | virtual bool Start() { return true; } 36 | //! Stop the logging element 37 | /*! 38 | \return 'true' if the logging element was successfully stopped, 'false' if the logging element failed to stop 39 | */ 40 | virtual bool Stop() { return true; } 41 | }; 42 | 43 | } // namespace CppLogging 44 | 45 | #endif // CPPLOGGING_ELEMENT_H 46 | -------------------------------------------------------------------------------- /examples/rolling_size.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file rolling_size.cpp 3 | \brief Rolling file appender with size-based policy example 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create default logging sink processor with a binary layout 15 | auto sink = std::make_shared(std::make_shared()); 16 | // Add rolling file appender which rolls after append 4kb of logs and will keep only 5 recent archives 17 | sink->appenders().push_back(std::make_shared(".", "file", "bin.log", 4096, 5, true)); 18 | 19 | // Configure example logger 20 | CppLogging::Config::ConfigLogger("example", sink); 21 | 22 | // Startup the logging infrastructure 23 | CppLogging::Config::Startup(); 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | // Configure logger 29 | ConfigureLogger(); 30 | 31 | // Create example logger 32 | CppLogging::Logger logger("example"); 33 | 34 | // Log some messages with different level 35 | logger.Debug("Debug message"); 36 | logger.Info("Info message"); 37 | logger.Warn("Warning message"); 38 | logger.Error("Error message"); 39 | logger.Fatal("Fatal message"); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /tests/test_layout_text.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 22.07.2016 3 | // 4 | 5 | #include "test.h" 6 | 7 | #include "logging/layouts/text_layout.h" 8 | #include "system/environment.h" 9 | 10 | using namespace CppCommon; 11 | using namespace CppLogging; 12 | 13 | TEST_CASE("Text layout", "[CppLogging]") 14 | { 15 | Record record; 16 | record.timestamp = 1468408953123456789ll; 17 | record.thread = 0x98ABCDEF; 18 | record.level = Level::WARN; 19 | record.logger = "Test logger"; 20 | record.message = "Test message"; 21 | 22 | TextLayout layout1; 23 | layout1.LayoutRecord(record); 24 | REQUIRE(record.raw.size() > 0); 25 | 26 | std::string utc_sample = "2016-07-13T11:22:33.123Z - 456.789 - [0x98ABCDEF] - WARN - Test logger - Test message - " + Environment::EndLine(); 27 | 28 | TextLayout layout2("{UtcYear}-{UtcMonth}-{UtcDay}T{UtcHour}:{UtcMinute}:{UtcSecond}.{Millisecond}{UtcTimezone} - {Microsecond}.{Nanosecond} - [{Thread}] - {Level} - {Logger} - {Message} - {EndLine}"); 29 | layout2.LayoutRecord(record); 30 | REQUIRE(std::string(record.raw.begin(), record.raw.end() - 1) == utc_sample); 31 | 32 | TextLayout layout3("{UtcDateTime} - {Microsecond}.{Nanosecond} - [{Thread}] - {Level} - {Logger} - {Message} - {EndLine}"); 33 | layout3.LayoutRecord(record); 34 | REQUIRE(std::string(record.raw.begin(), record.raw.end() - 1) == utc_sample); 35 | } 36 | -------------------------------------------------------------------------------- /examples/rolling_time.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file rolling_time.cpp 3 | \brief Rolling file appender with time-based policy example 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create default logging sink processor with a text layout 15 | auto sink = std::make_shared(std::make_shared()); 16 | // Add rolling file appender which rolls each second and create log file with a pattern "{UtcDateTime}.log" 17 | sink->appenders().push_back(std::make_shared(".", CppLogging::TimeRollingPolicy::SECOND, "{UtcDateTime}.log", true)); 18 | 19 | // Configure example logger 20 | CppLogging::Config::ConfigLogger("example", sink); 21 | 22 | // Startup the logging infrastructure 23 | CppLogging::Config::Startup(); 24 | } 25 | 26 | int main(int argc, char** argv) 27 | { 28 | // Configure logger 29 | ConfigureLogger(); 30 | 31 | // Create example logger 32 | CppLogging::Logger logger("example"); 33 | 34 | // Log some messages with different level 35 | logger.Debug("Debug message"); 36 | logger.Info("Info message"); 37 | logger.Warn("Warning message"); 38 | logger.Error("Error message"); 39 | logger.Fatal("Fatal message"); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /source/logging/processors/buffered_processor.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file buffered_processor.cpp 3 | \brief Buffered logging processor implementation 4 | \author Ivan Shynkarenka 5 | \date 28.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/processors/buffered_processor.h" 10 | 11 | namespace CppLogging { 12 | 13 | bool BufferedProcessor::ProcessRecord(Record& record) 14 | { 15 | // Check if the logging processor started 16 | if (!IsStarted()) 17 | return true; 18 | 19 | // Process all buffered logging records if the buffer limit is reached 20 | if ((_buffer.size() + 1) > _limit) 21 | ProcessBufferedRecords(); 22 | 23 | // Move the given logging record into the buffer 24 | _buffer.emplace_back(std::move(record)); 25 | 26 | // Always return false to stop further logging record processing 27 | return false; 28 | } 29 | 30 | void BufferedProcessor::ProcessBufferedRecords() 31 | { 32 | // Process all buffered logging records 33 | for (auto& record : _buffer) 34 | Processor::ProcessRecord(record); 35 | 36 | // Clear buffer 37 | _buffer.clear(); 38 | } 39 | 40 | void BufferedProcessor::Flush() 41 | { 42 | // Check if the logging processor started 43 | if (!IsStarted()) 44 | return; 45 | 46 | // Process all buffered logging records 47 | ProcessBufferedRecords(); 48 | 49 | // Flush the logging processor 50 | Processor::Flush(); 51 | } 52 | 53 | } // namespace CppLogging 54 | -------------------------------------------------------------------------------- /examples/layout.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file layout.cpp 3 | \brief Custom text layout pattern example 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | void ConfigureLogger() 13 | { 14 | // Create a custom text layout pattern 15 | const std::string pattern = "{UtcYear}-{UtcMonth}-{UtcDay}T{UtcHour}:{UtcMinute}:{UtcSecond}.{Millisecond}{UtcTimezone} - {Microsecond}.{Nanosecond} - [{Thread}] - {Level} - {Logger} - {Message} - {EndLine}"; 16 | 17 | // Create default logging sink processor with a text layout 18 | auto sink = std::make_shared(std::make_shared(pattern)); 19 | // Add console appender 20 | sink->appenders().push_back(std::make_shared()); 21 | 22 | // Configure default logger 23 | CppLogging::Config::ConfigLogger(sink); 24 | 25 | // Startup the logging infrastructure 26 | CppLogging::Config::Startup(); 27 | } 28 | 29 | int main(int argc, char** argv) 30 | { 31 | // Configure logger 32 | ConfigureLogger(); 33 | 34 | // Create default logger 35 | CppLogging::Logger logger; 36 | 37 | // Log some messages with different level 38 | logger.Debug("Debug message"); 39 | logger.Info("Info message"); 40 | logger.Warn("Warning message"); 41 | logger.Error("Error message"); 42 | logger.Fatal("Fatal message"); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /include/logging/appenders/ostream_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file ostream_appender.h 3 | \brief Output stream (std::ostream) appender definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_OSTREAM_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_OSTREAM_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | //! Output stream (std::ostream) appender 19 | /*! 20 | Output stream (std::ostream) appender prints the given logging record 21 | into the given instance of std::ostream. 22 | 23 | Not thread-safe. 24 | */ 25 | class OstreamAppender : public Appender 26 | { 27 | public: 28 | //! Initialize the appender with a given output stream 29 | /*! 30 | \param stream - Output stream 31 | */ 32 | explicit OstreamAppender(std::ostream& stream) : _ostream(stream) {} 33 | OstreamAppender(const OstreamAppender&) = delete; 34 | OstreamAppender(OstreamAppender&&) = delete; 35 | virtual ~OstreamAppender() = default; 36 | 37 | OstreamAppender& operator=(const OstreamAppender&) = delete; 38 | OstreamAppender& operator=(OstreamAppender&&) = delete; 39 | 40 | // Implementation of Appender 41 | void AppendRecord(Record& record) override; 42 | void Flush() override; 43 | 44 | private: 45 | std::ostream& _ostream; 46 | }; 47 | 48 | } // namespace CppLogging 49 | 50 | #endif // CPPLOGGING_APPENDERS_OSTREAM_APPENDER_H 51 | -------------------------------------------------------------------------------- /include/logging/processors/exclusive_processor.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file exclusive_processor.h 3 | \brief Exclusive logging processor definition 4 | \author Ivan Shynkarenka 5 | \date 11.04.2019 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_EXCLUSIVE_PROCESSOR_H 10 | #define CPPLOGGING_PROCESSORS_EXCLUSIVE_PROCESSOR_H 11 | 12 | #include "logging/processor.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Exclusive logging processor 17 | /*! 18 | Exclusive logging processor filters out the given logging record 19 | and process it exclusively without providing the record to other 20 | processors. 21 | 22 | Not thread-safe. 23 | */ 24 | class ExclusiveProcessor : public Processor 25 | { 26 | public: 27 | //! Initialize exclusive logging processor with a given layout interface 28 | /*! 29 | \param layout - Logging layout interface 30 | */ 31 | explicit ExclusiveProcessor(const std::shared_ptr& layout) : Processor(layout) {} 32 | ExclusiveProcessor(const ExclusiveProcessor&) = delete; 33 | ExclusiveProcessor(ExclusiveProcessor&&) = delete; 34 | virtual ~ExclusiveProcessor() = default; 35 | 36 | ExclusiveProcessor& operator=(const ExclusiveProcessor&) = delete; 37 | ExclusiveProcessor& operator=(ExclusiveProcessor&&) = delete; 38 | 39 | // Implementation of Processor 40 | bool ProcessRecord(Record& record) override; 41 | }; 42 | 43 | } // namespace CppLogging 44 | 45 | #endif // CPPLOGGING_PROCESSORS_EXCLUSIVE_PROCESSOR_H 46 | -------------------------------------------------------------------------------- /include/logging/filters/switch_filter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file switch_filter.h 3 | \brief Switch filter definition 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_FILTERS_SWITCH_FILTER_H 10 | #define CPPLOGGING_FILTERS_SWITCH_FILTER_H 11 | 12 | #include "logging/filter.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | //! Switch filter 19 | /*! 20 | Switch filters is used to turn on/off logging. 21 | 22 | Thread-safe. 23 | */ 24 | class SwitchFilter : public Filter 25 | { 26 | public: 27 | //! Initialize switch filter with a given enabled flag 28 | /*! 29 | \param enabled - Enabled flag 30 | */ 31 | explicit SwitchFilter(bool enabled) { Update(enabled); } 32 | SwitchFilter(const SwitchFilter&) = delete; 33 | SwitchFilter(SwitchFilter&&) = delete; 34 | virtual ~SwitchFilter() = default; 35 | 36 | SwitchFilter& operator=(const SwitchFilter&) = delete; 37 | SwitchFilter& operator=(SwitchFilter&&) = delete; 38 | 39 | //! Get the filter enabled flag 40 | bool enabled() const noexcept { return _enabled; } 41 | 42 | //! Update switch filter with a given enabled flag 43 | /*! 44 | \param enabled - Enabled flag 45 | */ 46 | void Update(bool enabled); 47 | 48 | // Implementation of Filter 49 | bool FilterRecord(Record& record) override; 50 | 51 | private: 52 | std::atomic _enabled; 53 | }; 54 | 55 | } // namespace CppLogging 56 | 57 | #endif // CPPLOGGING_FILTERS_SWITCH_FILTER_H 58 | -------------------------------------------------------------------------------- /performance/layout.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 11.07.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/layouts/binary_layout.h" 8 | #include "logging/layouts/hash_layout.h" 9 | #include "logging/layouts/text_layout.h" 10 | 11 | using namespace CppLogging; 12 | 13 | BENCHMARK("BinaryLayout") 14 | { 15 | static BinaryLayout layout; 16 | static Record record; 17 | 18 | record.Clear(); 19 | record.logger = "Test logger"; 20 | record.StoreFormat("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, "bin"); 21 | 22 | layout.LayoutRecord(record); 23 | context.metrics().AddBytes(record.raw.size()); 24 | } 25 | 26 | BENCHMARK("HashLayout") 27 | { 28 | static HashLayout layout; 29 | static Record record; 30 | 31 | record.Clear(); 32 | record.logger = "Test logger"; 33 | record.StoreFormat("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, "bin"); 34 | 35 | layout.LayoutRecord(record); 36 | context.metrics().AddBytes(record.raw.size()); 37 | } 38 | 39 | BENCHMARK("TextLayout") 40 | { 41 | static TextLayout layout; 42 | static Record record; 43 | 44 | record.Clear(); 45 | record.logger = "Test logger"; 46 | record.StoreFormat("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, "txt"); 47 | 48 | layout.LayoutRecord(record); 49 | context.metrics().AddBytes(record.raw.size()); 50 | } 51 | 52 | BENCHMARK_MAIN() 53 | -------------------------------------------------------------------------------- /include/logging/trigger.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file trigger.h 3 | \brief Logging trigger definition 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_TRIGGER_H 10 | #define CPPLOGGING_TRIGGER_H 11 | 12 | #include "logging/filter.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | //! Logging trigger 19 | /*! 20 | Logging trigger is a special filter that allows to enable or disable logging. 21 | 22 | Thread-safe. 23 | */ 24 | class Trigger : public Filter 25 | { 26 | public: 27 | //! Initialize trigger with an initial logging state (enabled or disabled) 28 | /*! 29 | \param state - Initial logging state (default is true) 30 | */ 31 | explicit Trigger(bool state = true) : _state(state) {} 32 | Trigger(const Trigger&) = delete; 33 | Trigger(Trigger&&) = delete; 34 | virtual ~Trigger() = default; 35 | 36 | Trigger& operator=(const Trigger&) = delete; 37 | Trigger& operator=(Trigger&&) = delete; 38 | 39 | //! Is logging enabled? 40 | bool IsEnabled() noexcept { return _state; } 41 | 42 | //! Enable logging 43 | void Enable() noexcept { _state = true; } 44 | //! Disable logging 45 | void Disable() noexcept { _state = false; } 46 | //! Toggle logging 47 | void Toggle() noexcept { _state = !_state; } 48 | 49 | // Implementation of Filter 50 | bool FilterRecord(Record& record) override 51 | { return _state; } 52 | 53 | private: 54 | std::atomic _state; 55 | }; 56 | 57 | } // namespace CppLogging 58 | 59 | #endif // CPPLOGGING_TRIGGER_H 60 | -------------------------------------------------------------------------------- /include/logging/appenders/memory_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file memory_appender.h 3 | \brief Memory appender definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_MEMORY_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_MEMORY_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Memory appender 17 | /*! 18 | Memory appender collects all given logging records into 19 | growing memory buffer with the given initial capacity. 20 | 21 | Not thread-safe. 22 | */ 23 | class MemoryAppender : public Appender 24 | { 25 | public: 26 | //! Initialize the appender with a given capacity 27 | /*! 28 | \param capacity - Memory buffer capacity (default is 0) 29 | */ 30 | explicit MemoryAppender(size_t capacity = 0) : _buffer(capacity) {} 31 | MemoryAppender(const MemoryAppender&) = delete; 32 | MemoryAppender(MemoryAppender&&) = delete; 33 | virtual ~MemoryAppender() = default; 34 | 35 | MemoryAppender& operator=(const MemoryAppender&) = delete; 36 | MemoryAppender& operator=(MemoryAppender&&) = delete; 37 | 38 | //! Get memory buffer 39 | std::vector& buffer() noexcept { return _buffer; } 40 | //! Get constant memory buffer 41 | const std::vector& buffer() const noexcept { return _buffer; } 42 | 43 | // Implementation of Appender 44 | void AppendRecord(Record& record) override; 45 | 46 | private: 47 | std::vector _buffer; 48 | }; 49 | 50 | } // namespace CppLogging 51 | 52 | #endif // CPPLOGGING_APPENDERS_MEMORY_APPENDER_H 53 | -------------------------------------------------------------------------------- /include/logging/processors/sync_processor.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file sync_processor.h 3 | \brief Synchronous logging processor definition 4 | \author Ivan Shynkarenka 5 | \date 28.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_SYNC_PROCESSOR_H 10 | #define CPPLOGGING_PROCESSORS_SYNC_PROCESSOR_H 11 | 12 | #include "logging/processor.h" 13 | 14 | #include "threads/critical_section.h" 15 | 16 | namespace CppLogging { 17 | 18 | //! Synchronous logging processor 19 | /*! 20 | Synchronous logging processor process the given logging record 21 | under the critical section to avoid races in not thread-safe 22 | layouts, filters and appenders. 23 | 24 | Thread-safe. 25 | */ 26 | class SyncProcessor : public Processor 27 | { 28 | public: 29 | //! Initialize synchronous logging processor with a given layout interface 30 | /*! 31 | \param layout - Logging layout interface 32 | */ 33 | explicit SyncProcessor(const std::shared_ptr& layout) : Processor(layout) {} 34 | SyncProcessor(const SyncProcessor&) = delete; 35 | SyncProcessor(SyncProcessor&&) = delete; 36 | virtual ~SyncProcessor() = default; 37 | 38 | SyncProcessor& operator=(const SyncProcessor&) = delete; 39 | SyncProcessor& operator=(SyncProcessor&&) = delete; 40 | 41 | // Implementation of Processor 42 | bool ProcessRecord(Record& record) override; 43 | void Flush() override; 44 | 45 | private: 46 | CppCommon::CriticalSection _lock; 47 | }; 48 | 49 | } // namespace CppLogging 50 | 51 | /*! \example sync.cpp Synchronous logger processor example */ 52 | 53 | #endif // CPPLOGGING_PROCESSORS_SYNC_PROCESSOR_H 54 | -------------------------------------------------------------------------------- /performance/async_format.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 28.09.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppCommon; 11 | using namespace CppLogging; 12 | 13 | const auto settings = CppBenchmark::Settings().ThreadsRange(1, 8, [](int from, int to, int& result) { int r = result; result *= 2; return r; }); 14 | 15 | class LogConfigFixture 16 | { 17 | protected: 18 | LogConfigFixture() 19 | { 20 | auto binary_sink = std::make_shared(std::make_shared()); 21 | binary_sink->appenders().push_back(std::make_shared(_file)); 22 | Config::ConfigLogger("binary", binary_sink); 23 | Config::Startup(); 24 | } 25 | 26 | ~LogConfigFixture() 27 | { 28 | Config::Shutdown(); 29 | if (_file.IsFileExists()) 30 | File::Remove(_file); 31 | } 32 | 33 | private: 34 | File _file{"test.bin.log"}; 35 | }; 36 | 37 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "Format(int, double, string)", settings) 38 | { 39 | thread_local Logger logger = Config::CreateLogger("binary"); 40 | logger.Info(CppCommon::format("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name())); 41 | } 42 | 43 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "Serialize(int, double, string)", settings) 44 | { 45 | thread_local Logger logger = Config::CreateLogger("binary"); 46 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 47 | } 48 | 49 | BENCHMARK_MAIN() 50 | -------------------------------------------------------------------------------- /include/logging/layouts/hash_layout.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file hash_layout.h 3 | \brief Hash layout definition 4 | \author Ivan Shynkarenka 5 | \date 12.12.2021 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUTS_HASH_LAYOUT_H 10 | #define CPPLOGGING_LAYOUTS_HASH_LAYOUT_H 11 | 12 | #include "logging/layout.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Hash layout 17 | /*! 18 | Hash layout performs simple memory copy operation to convert 19 | the given logging record into the plane raw buffer. Logging 20 | message is stored as a 32-bit hash of the message string. 21 | 22 | Hash algorithm is 32-bit FNV-1a string hashing. 23 | 24 | Thread-safe. 25 | */ 26 | class HashLayout : public Layout 27 | { 28 | public: 29 | HashLayout() = default; 30 | HashLayout(const HashLayout&) = delete; 31 | HashLayout(HashLayout&&) = delete; 32 | virtual ~HashLayout() = default; 33 | 34 | HashLayout& operator=(const HashLayout&) = delete; 35 | HashLayout& operator=(HashLayout&&) = delete; 36 | 37 | //! Hash the given string message using FNV-1a hashing algorithm 38 | /*! 39 | FNV-1a string hashing is the fast non-cryptographic hash function created by 40 | Glenn Fowler, Landon Curt Noll, and Kiem-Phong Vo. 41 | 42 | https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function 43 | 44 | \param message - Message string 45 | \return Calculated 32-bit hash value of the message 46 | */ 47 | static uint32_t Hash(std::string_view message); 48 | 49 | // Implementation of Layout 50 | void LayoutRecord(Record& record) override; 51 | }; 52 | 53 | } // namespace CppLogging 54 | 55 | #endif // CPPLOGGING_LAYOUTS_HASH_LAYOUT_H 56 | -------------------------------------------------------------------------------- /include/logging/filters/logger_filter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file logger_filter.h 3 | \brief Logger filter definition 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_FILTERS_LOGGER_FILTER_H 10 | #define CPPLOGGING_FILTERS_LOGGER_FILTER_H 11 | 12 | #include "logging/filter.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace CppLogging { 18 | 19 | //! Logger filter 20 | /*! 21 | Logger filters out logging records which logger filed is not matched 22 | to the given pattern. 23 | 24 | Thread-safe. 25 | */ 26 | class LoggerFilter : public Filter 27 | { 28 | public: 29 | //! Initialize logger filter with a given pattern 30 | /*! 31 | \param pattern - Logger pattern 32 | \param positive - Positive filtration (default is true) 33 | */ 34 | explicit LoggerFilter(const std::string& pattern, bool positive = true) : _positive(positive), _pattern(pattern) {} 35 | LoggerFilter(const LoggerFilter&) = delete; 36 | LoggerFilter(LoggerFilter&&) = delete; 37 | virtual ~LoggerFilter() = default; 38 | 39 | LoggerFilter& operator=(const LoggerFilter&) = delete; 40 | LoggerFilter& operator=(LoggerFilter&&) = delete; 41 | 42 | //! Get the positive filtration flag 43 | bool positive() const noexcept { return _positive; } 44 | 45 | //! Get the logger pattern 46 | const std::string& pattern() const noexcept { return _pattern; } 47 | 48 | // Implementation of Filter 49 | bool FilterRecord(Record& record) override; 50 | 51 | private: 52 | std::atomic _positive; 53 | std::string _pattern; 54 | }; 55 | 56 | } // namespace CppLogging 57 | 58 | #endif // CPPLOGGING_FILTERS_LOGGER_FILTER_H 59 | -------------------------------------------------------------------------------- /scripts/hashlog-map/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /include/logging/filters/message_filter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file message_filter.h 3 | \brief Message filter definition 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_FILTERS_MESSAGE_FILTER_H 10 | #define CPPLOGGING_FILTERS_MESSAGE_FILTER_H 11 | 12 | #include "logging/filter.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace CppLogging { 18 | 19 | //! Message filter 20 | /*! 21 | Message filters out logging records which message field is not matched 22 | to the given regular expression pattern. 23 | 24 | Thread-safe. 25 | */ 26 | class MessageFilter : public Filter 27 | { 28 | public: 29 | //! Initialize message filter with a given regular expression pattern 30 | /*! 31 | \param pattern - Regular expression pattern 32 | \param positive - Positive filtration (default is true) 33 | */ 34 | explicit MessageFilter(const std::regex& pattern, bool positive = true) : _positive(positive), _pattern(pattern) {} 35 | MessageFilter(const MessageFilter&) = delete; 36 | MessageFilter(MessageFilter&&) = delete; 37 | virtual ~MessageFilter() = default; 38 | 39 | MessageFilter& operator=(const MessageFilter&) = delete; 40 | MessageFilter& operator=(MessageFilter&&) = delete; 41 | 42 | //! Get the positive filtration flag 43 | bool positive() const noexcept { return _positive; } 44 | 45 | //! Get the message regular expression pattern 46 | const std::regex& pattern() const noexcept { return _pattern; } 47 | 48 | // Implementation of Filter 49 | bool FilterRecord(Record& record) override; 50 | 51 | private: 52 | std::atomic _positive; 53 | std::regex _pattern; 54 | }; 55 | 56 | } // namespace CppLogging 57 | 58 | #endif // CPPLOGGING_FILTERS_MESSAGE_FILTER_H 59 | -------------------------------------------------------------------------------- /source/logging/appenders/syslog_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file syslog_appender.cpp 3 | \brief Syslog appender implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/syslog_appender.h" 10 | 11 | #if defined(unix) || defined(__unix) || defined(__unix__) 12 | #include 13 | #endif 14 | 15 | namespace CppLogging { 16 | 17 | SyslogAppender::SyslogAppender() 18 | { 19 | #if defined(unix) || defined(__unix) || defined(__unix__) 20 | openlog(nullptr, LOG_NDELAY | LOG_PID, LOG_USER); 21 | #endif 22 | } 23 | 24 | SyslogAppender::~SyslogAppender() 25 | { 26 | #if defined(unix) || defined(__unix) || defined(__unix__) 27 | closelog(); 28 | #endif 29 | } 30 | 31 | void SyslogAppender::AppendRecord(Record& record) 32 | { 33 | // Skip logging records without layout 34 | if (record.raw.empty()) 35 | return; 36 | 37 | #if defined(unix) || defined(__unix) || defined(__unix__) 38 | // Setup syslog priority depends on the logging level 39 | int priority; 40 | switch (record.level) 41 | { 42 | case Level::FATAL: 43 | priority = LOG_CRIT; 44 | break; 45 | case Level::ERROR: 46 | priority = LOG_ERR; 47 | break; 48 | case Level::WARN: 49 | priority = LOG_WARNING; 50 | break; 51 | case Level::INFO: 52 | priority = LOG_INFO; 53 | break; 54 | case Level::DEBUG: 55 | priority = LOG_DEBUG; 56 | break; 57 | default: 58 | priority = LOG_INFO; 59 | break; 60 | } 61 | 62 | // Append logging record content 63 | syslog(priority, "%.*s", (int)record.raw.size() - 1, (char*)record.raw.data()); 64 | #endif 65 | } 66 | 67 | } // namespace CppLogging 68 | -------------------------------------------------------------------------------- /tests/test_appender_file_rolling.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 13.09.2016 3 | // 4 | 5 | #include "test.h" 6 | 7 | #include "logging/appenders/rolling_file_appender.h" 8 | 9 | using namespace CppCommon; 10 | using namespace CppLogging; 11 | 12 | TEST_CASE("Rolling file appender with size-based policy", "[CppLogging]") 13 | { 14 | { 15 | RollingFileAppender appender(".", "test", "log", 10, 3, true); 16 | 17 | Record record; 18 | record.raw.resize(11); 19 | 20 | for (int i = 0; i < 10; ++i) 21 | { 22 | appender.AppendRecord(record); 23 | appender.Flush(); 24 | } 25 | } 26 | 27 | // Sleep for one second 28 | Thread::Sleep(1000); 29 | 30 | REQUIRE(File("test.1.log.zip").IsFileExists()); 31 | REQUIRE(File("test.1.log.zip").size() > 0); 32 | REQUIRE(File("test.2.log.zip").IsFileExists()); 33 | REQUIRE(File("test.2.log.zip").size() > 0); 34 | REQUIRE(File("test.3.log.zip").IsFileExists()); 35 | REQUIRE(File("test.3.log.zip").size() > 0); 36 | REQUIRE(!File("test.4.log.zip").IsFileExists()); 37 | 38 | File::RemoveIf(".", ".*.log.zip"); 39 | } 40 | 41 | TEST_CASE("Rolling file appender with time-based policy", "[CppLogging]") 42 | { 43 | { 44 | RollingFileAppender appender(".", TimeRollingPolicy::SECOND, "{UtcDateTime}.log", true); 45 | 46 | Record record; 47 | record.raw.resize(11); 48 | 49 | for (int i = 0; i < 3; ++i) 50 | { 51 | record.timestamp = Timestamp::utc(); 52 | appender.AppendRecord(record); 53 | appender.Flush(); 54 | 55 | // Sleep for one second 56 | Thread::Sleep(1000); 57 | } 58 | } 59 | 60 | // Sleep for one second 61 | Thread::Sleep(1000); 62 | 63 | REQUIRE(Directory(".").GetFiles(".*log.zip").size() == 3); 64 | 65 | File::RemoveIf(".", ".*.log.zip"); 66 | } 67 | -------------------------------------------------------------------------------- /source/logging/appenders/console_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file console_appender.cpp 3 | \brief Console (stdout) appender implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/console_appender.h" 10 | 11 | #include "system/console.h" 12 | 13 | #include 14 | 15 | namespace CppLogging { 16 | 17 | void ConsoleAppender::AppendRecord(Record& record) 18 | { 19 | // Skip logging records without layout 20 | if (record.raw.empty()) 21 | return; 22 | 23 | // Setup console color depends on the logging level 24 | switch (record.level) 25 | { 26 | case Level::NONE: 27 | CppCommon::Console::SetColor(CppCommon::Color::DARKGREY); 28 | break; 29 | case Level::FATAL: 30 | CppCommon::Console::SetColor(CppCommon::Color::WHITE, CppCommon::Color::LIGHTRED); 31 | break; 32 | case Level::ERROR: 33 | CppCommon::Console::SetColor(CppCommon::Color::LIGHTRED); 34 | break; 35 | case Level::WARN: 36 | CppCommon::Console::SetColor(CppCommon::Color::YELLOW); 37 | break; 38 | case Level::INFO: 39 | CppCommon::Console::SetColor(CppCommon::Color::WHITE); 40 | break; 41 | case Level::DEBUG: 42 | CppCommon::Console::SetColor(CppCommon::Color::LIGHTMAGENTA); 43 | break; 44 | case Level::ALL: 45 | CppCommon::Console::SetColor(CppCommon::Color::GREY); 46 | break; 47 | } 48 | 49 | // Append logging record content 50 | std::fwrite(record.raw.data(), 1, record.raw.size() - 1, stdout); 51 | 52 | // Reset console color 53 | CppCommon::Console::SetColor(CppCommon::Color::WHITE); 54 | } 55 | 56 | void ConsoleAppender::Flush() 57 | { 58 | // Flush stream 59 | std::fflush(stdout); 60 | } 61 | 62 | } // namespace CppLogging 63 | -------------------------------------------------------------------------------- /performance/file_sync.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 09.09.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppCommon; 11 | using namespace CppLogging; 12 | 13 | const auto settings = CppBenchmark::Settings().ThreadsRange(1, 8, [](int from, int to, int& result) { int r = result; result *= 2; return r; }); 14 | 15 | class LogConfigFixture 16 | { 17 | protected: 18 | LogConfigFixture() 19 | { 20 | auto sync_binary_sink = std::make_shared(std::make_shared()); 21 | sync_binary_sink->appenders().push_back(std::make_shared(_binary_file)); 22 | Config::ConfigLogger("sync-binary", sync_binary_sink); 23 | 24 | auto sync_text_sink = std::make_shared(std::make_shared()); 25 | sync_text_sink->appenders().push_back(std::make_shared(_text_file)); 26 | Config::ConfigLogger("sync-text", sync_text_sink); 27 | 28 | Config::Startup(); 29 | } 30 | 31 | ~LogConfigFixture() 32 | { 33 | Config::Shutdown(); 34 | if (_binary_file.IsFileExists()) 35 | File::Remove(_binary_file); 36 | if (_text_file.IsFileExists()) 37 | File::Remove(_text_file); 38 | } 39 | 40 | private: 41 | File _binary_file{"test.bin.log"}; 42 | File _text_file{"test.log"}; 43 | }; 44 | 45 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "FileSync-binary", settings) 46 | { 47 | thread_local Logger logger = Config::CreateLogger("sync-binary"); 48 | logger.Info("Test message {}-{}-{}", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 49 | } 50 | 51 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "FileSync-text", settings) 52 | { 53 | thread_local Logger logger = Config::CreateLogger("sync-text"); 54 | logger.Info("Test message {}-{}-{}", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 55 | } 56 | 57 | BENCHMARK_MAIN() 58 | -------------------------------------------------------------------------------- /include/logging/processors/buffered_processor.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file buffered_processor.h 3 | \brief Buffered logging processor definition 4 | \author Ivan Shynkarenka 5 | \date 28.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_BUFFERED_PROCESSOR_H 10 | #define CPPLOGGING_PROCESSORS_BUFFERED_PROCESSOR_H 11 | 12 | #include "logging/processor.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Buffered logging processor 17 | /*! 18 | Buffered logging processor stores all logging records in the 19 | limited size buffer until Flush() method is invoked or buffer 20 | has not enough space. 21 | 22 | Please note that buffered logging processor moves the given 23 | logging record (ProcessRecord() method always returns false) 24 | into the buffer! 25 | 26 | Not thread-safe. 27 | */ 28 | class BufferedProcessor : public Processor 29 | { 30 | public: 31 | //! Initialize buffered processor with a given layout interface, limit and capacity 32 | /*! 33 | \param layout - Logging layout interface 34 | \param limit - Buffer limit in logging records (default is 65536) 35 | \param capacity - Buffer initial capacity in logging records (default is 8192) 36 | */ 37 | explicit BufferedProcessor(const std::shared_ptr& layout, size_t limit = 65536, size_t capacity = 8192) : Processor(layout), _limit(limit) 38 | { _buffer.reserve(capacity); } 39 | BufferedProcessor(const BufferedProcessor&) = delete; 40 | BufferedProcessor(BufferedProcessor&&) = delete; 41 | virtual ~BufferedProcessor() = default; 42 | 43 | BufferedProcessor& operator=(const BufferedProcessor&) = delete; 44 | BufferedProcessor& operator=(BufferedProcessor&&) = delete; 45 | 46 | // Implementation of Processor 47 | bool ProcessRecord(Record& record) override; 48 | void Flush() override; 49 | 50 | private: 51 | size_t _limit; 52 | std::vector _buffer; 53 | 54 | void ProcessBufferedRecords(); 55 | }; 56 | 57 | } // namespace CppLogging 58 | 59 | #endif // CPPLOGGING_PROCESSORS_BUFFERED_PROCESSOR_H 60 | -------------------------------------------------------------------------------- /performance/appender_null.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 29.07.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppLogging; 11 | 12 | class BinaryConfigFixture : public virtual CppBenchmark::Fixture 13 | { 14 | protected: 15 | void Initialize(CppBenchmark::Context& context) override 16 | { 17 | auto binary_sink = std::make_shared(std::make_shared()); 18 | binary_sink->appenders().push_back(std::make_shared()); 19 | Config::ConfigLogger("binary", binary_sink); 20 | Config::Startup(); 21 | } 22 | }; 23 | 24 | class HashConfigFixture : public virtual CppBenchmark::Fixture 25 | { 26 | protected: 27 | void Initialize(CppBenchmark::Context& context) override 28 | { 29 | auto hash_sink = std::make_shared(std::make_shared()); 30 | hash_sink->appenders().push_back(std::make_shared()); 31 | Config::ConfigLogger("hash", hash_sink); 32 | Config::Startup(); 33 | } 34 | }; 35 | 36 | class TextConfigFixture : public virtual CppBenchmark::Fixture 37 | { 38 | protected: 39 | void Initialize(CppBenchmark::Context& context) override 40 | { 41 | auto text_sink = std::make_shared(std::make_shared()); 42 | text_sink->appenders().push_back(std::make_shared()); 43 | Config::ConfigLogger("text", text_sink); 44 | Config::Startup(); 45 | } 46 | }; 47 | 48 | BENCHMARK_FIXTURE(BinaryConfigFixture, "NullAppender-binary") 49 | { 50 | static Logger logger = Config::CreateLogger("binary"); 51 | logger.Info("Test message"); 52 | } 53 | 54 | BENCHMARK_FIXTURE(HashConfigFixture, "NullAppender-hash") 55 | { 56 | static Logger logger = Config::CreateLogger("hash"); 57 | logger.Info("Test message"); 58 | } 59 | 60 | BENCHMARK_FIXTURE(TextConfigFixture, "NullAppender-text") 61 | { 62 | static Logger logger = Config::CreateLogger("text"); 63 | logger.Info("Test message"); 64 | } 65 | 66 | BENCHMARK_MAIN() 67 | -------------------------------------------------------------------------------- /performance/processor_sync.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 30.07.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppLogging; 11 | 12 | const auto settings = CppBenchmark::Settings().ThreadsRange(1, 8, [](int from, int to, int& result) { int r = result; result *= 2; return r; }); 13 | 14 | class LogConfigFixture 15 | { 16 | protected: 17 | LogConfigFixture() 18 | { 19 | auto sync_null_sink = std::make_shared(std::make_shared()); 20 | sync_null_sink->appenders().push_back(std::make_shared()); 21 | Config::ConfigLogger("sync-null", sync_null_sink); 22 | 23 | auto sync_binary_sink = std::make_shared(std::make_shared()); 24 | sync_binary_sink->appenders().push_back(std::make_shared()); 25 | Config::ConfigLogger("sync-binary", sync_binary_sink); 26 | 27 | auto sync_text_sink = std::make_shared(std::make_shared()); 28 | sync_text_sink->appenders().push_back(std::make_shared()); 29 | Config::ConfigLogger("sync-text", sync_text_sink); 30 | 31 | Config::Startup(); 32 | } 33 | }; 34 | 35 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "SyncProcessor-null", settings) 36 | { 37 | thread_local Logger logger = Config::CreateLogger("sync-null"); 38 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 39 | } 40 | 41 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "SyncProcessor-binary", settings) 42 | { 43 | thread_local Logger logger = Config::CreateLogger("sync-binary"); 44 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 45 | } 46 | 47 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "SyncProcessor-text", settings) 48 | { 49 | thread_local Logger logger = Config::CreateLogger("sync-text"); 50 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 51 | } 52 | 53 | BENCHMARK_MAIN() 54 | -------------------------------------------------------------------------------- /examples/async.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file async.cpp 3 | \brief Asynchronous logger processor example 4 | \author Ivan Shynkarenka 5 | \date 15.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void ConfigureLogger() 18 | { 19 | // Create default logging sink processor with a text layout 20 | auto sink = std::make_shared(std::make_shared()); 21 | // Add file appender with time-based rolling policy and archivation 22 | sink->appenders().push_back(std::make_shared(".", CppLogging::TimeRollingPolicy::SECOND, "{UtcDateTime}.log", true)); 23 | 24 | // Configure example logger 25 | CppLogging::Config::ConfigLogger("example", sink); 26 | 27 | // Startup the logging infrastructure 28 | CppLogging::Config::Startup(); 29 | } 30 | 31 | int main(int argc, char** argv) 32 | { 33 | // Configure logger 34 | ConfigureLogger(); 35 | 36 | std::cout << "Press Enter to stop..." << std::endl; 37 | 38 | int concurrency = 1; 39 | 40 | // Start some threads 41 | std::atomic stop(false); 42 | std::vector threads; 43 | for (int thread = 0; thread < concurrency; ++thread) 44 | { 45 | threads.push_back(std::thread([&stop]() 46 | { 47 | // Create example logger 48 | CppLogging::Logger logger("example"); 49 | 50 | while (!stop) 51 | { 52 | // Log some messages with different level 53 | logger.Debug("Debug message"); 54 | logger.Info("Info message"); 55 | logger.Warn("Warning message"); 56 | logger.Error("Error message"); 57 | logger.Fatal("Fatal message"); 58 | 59 | // Yield for a while... 60 | CppCommon::Thread::Yield(); 61 | } 62 | })); 63 | } 64 | 65 | // Wait for input 66 | std::cin.get(); 67 | 68 | // Stop threads 69 | stop = true; 70 | 71 | // Wait for all threads 72 | for (auto& thread : threads) 73 | thread.join(); 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /source/logging/layouts/binary_layout.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file binary_layout.cpp 3 | \brief Binary layout implementation 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/layouts/binary_layout.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace CppLogging { 15 | 16 | void BinaryLayout::LayoutRecord(Record& record) 17 | { 18 | // Calculate logging record size 19 | uint32_t size = (uint32_t)(sizeof(uint64_t) + sizeof(uint64_t) + sizeof(Level) + sizeof(uint8_t) + record.logger.size() + sizeof(uint16_t) + record.message.size() + sizeof(uint32_t) + record.buffer.size()); 20 | 21 | // Resize the raw buffer to the required size 22 | record.raw.resize(sizeof(uint32_t) + size + 1); 23 | 24 | // Get the raw buffer start position 25 | uint8_t* buffer = record.raw.data(); 26 | 27 | // Serialize the logging record 28 | std::memcpy(buffer, &size, sizeof(uint32_t)); 29 | buffer += sizeof(uint32_t); 30 | std::memcpy(buffer, &record.timestamp, sizeof(uint64_t)); 31 | buffer += sizeof(uint64_t); 32 | std::memcpy(buffer, &record.thread, sizeof(uint64_t)); 33 | buffer += sizeof(uint64_t); 34 | std::memcpy(buffer, &record.level, sizeof(Level)); 35 | buffer += sizeof(Level); 36 | 37 | // Serialize the logger name 38 | uint8_t logger_size = (uint8_t)record.logger.size(); 39 | std::memcpy(buffer, &logger_size, sizeof(uint8_t)); 40 | buffer += sizeof(uint8_t); 41 | std::memcpy(buffer, record.logger.data(), record.logger.size()); 42 | buffer += record.logger.size(); 43 | 44 | // Serialize the logging message 45 | uint16_t message_size = (uint16_t)record.message.size(); 46 | std::memcpy(buffer, &message_size, sizeof(uint16_t)); 47 | buffer += sizeof(uint16_t); 48 | std::memcpy(buffer, record.message.data(), record.message.size()); 49 | buffer += record.message.size(); 50 | 51 | // Serialize the logging buffer 52 | uint32_t buffer_size = (uint32_t)record.buffer.size(); 53 | std::memcpy(buffer, &buffer_size, sizeof(uint32_t)); 54 | buffer += sizeof(uint32_t); 55 | std::memcpy(buffer, record.buffer.data(), record.buffer.size()); 56 | buffer += record.buffer.size(); 57 | 58 | // Write the last zero byte 59 | *buffer = 0; 60 | } 61 | 62 | } // namespace CppLogging 63 | -------------------------------------------------------------------------------- /examples/sync.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file sync.cpp 3 | \brief Synchronous logger processor example 4 | \author Ivan Shynkarenka 5 | \date 15.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | #include "logging/logger.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | void ConfigureLogger() 18 | { 19 | // Create default logging sink processor with a binary layout 20 | auto sink = std::make_shared(std::make_shared()); 21 | // Add file appender with size-based rolling policy and archivation 22 | sink->appenders().push_back(std::make_shared(".", "rolling", "bin.log", 4096, 9, true)); 23 | 24 | // Configure example logger 25 | CppLogging::Config::ConfigLogger("example", sink); 26 | 27 | // Startup the logging infrastructure 28 | CppLogging::Config::Startup(); 29 | } 30 | 31 | int main(int argc, char** argv) 32 | { 33 | // Configure logger 34 | ConfigureLogger(); 35 | 36 | std::cout << "Press Enter to stop..." << std::endl; 37 | 38 | int concurrency = 4; 39 | 40 | // Start some threads 41 | std::atomic stop(false); 42 | std::vector threads; 43 | for (int thread = 0; thread < concurrency; ++thread) 44 | { 45 | threads.push_back(std::thread([&stop]() 46 | { 47 | // Create example logger 48 | CppLogging::Logger logger("example"); 49 | 50 | int index = 0; 51 | 52 | while (!stop) 53 | { 54 | ++index; 55 | 56 | // Log some messages with different level 57 | logger.Debug("Debug message {}", index); 58 | logger.Info("Info message {}", index); 59 | logger.Warn("Warning message {}", index); 60 | logger.Error("Error message {}", index); 61 | logger.Fatal("Fatal message {}", index); 62 | 63 | // Yield for a while... 64 | CppCommon::Thread::Yield(); 65 | } 66 | })); 67 | } 68 | 69 | // Wait for input 70 | std::cin.get(); 71 | 72 | // Stop threads 73 | stop = true; 74 | 75 | // Wait for all threads 76 | for (auto& thread : threads) 77 | thread.join(); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /modules/zlib.cmake: -------------------------------------------------------------------------------- 1 | if(NOT TARGET zlib) 2 | 3 | # Module configuration 4 | include(CheckTypeSize) 5 | include(CheckFunctionExists) 6 | include(CheckIncludeFile) 7 | include(CheckCSourceCompiles) 8 | check_include_file(sys/types.h HAVE_SYS_TYPES_H) 9 | check_include_file(stdint.h HAVE_STDINT_H) 10 | check_include_file(stddef.h HAVE_STDDEF_H) 11 | set(CMAKE_REQUIRED_DEFINITIONS -D_LARGEFILE64_SOURCE=1) 12 | if(HAVE_SYS_TYPES_H) 13 | list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_SYS_TYPES_H) 14 | endif() 15 | if(HAVE_STDINT_H) 16 | list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDINT_H) 17 | endif() 18 | if(HAVE_STDDEF_H) 19 | list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_STDDEF_H) 20 | endif() 21 | check_type_size(off64_t OFF64_T) 22 | if(HAVE_OFF64_T) 23 | add_definitions(-D_LARGEFILE64_SOURCE=1) 24 | endif() 25 | set(CMAKE_REQUIRED_DEFINITIONS) 26 | check_function_exists(fseeko HAVE_FSEEKO) 27 | if(NOT HAVE_FSEEKO) 28 | add_definitions(-DNO_FSEEKO) 29 | endif() 30 | check_include_file(unistd.h Z_HAVE_UNISTD_H) 31 | if(Z_HAVE_UNISTD_H) 32 | add_definitions(-DZ_HAVE_UNISTD_H) 33 | endif() 34 | 35 | # Module definitions 36 | if(MSVC) 37 | add_definitions(-D_CRT_SECURE_NO_DEPRECATE) 38 | add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) 39 | endif() 40 | 41 | # Module library 42 | file(GLOB SOURCE_FILES "zlib/*.c") 43 | if(MSVC) 44 | list(APPEND ${SOURCE_FILES} "zlib/contrib/masmx64/inffas8664.c") 45 | # C4127: conditional expression is constant 46 | # C4131: 'function' : uses old-style declarator 47 | # C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data 48 | # C4245: 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch 49 | # C4267: 'var' : conversion from 'size_t' to 'type', possible loss of data 50 | set_source_files_properties(${SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS} /wd4127 /wd4131 /wd4244 /wd4245 /wd4267") 51 | else() 52 | set_source_files_properties(${SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS} -Wno-implicit-function-declaration -Wno-shift-negative-value") 53 | endif() 54 | add_library(zlib ${SOURCE_FILES}) 55 | target_include_directories(zlib PUBLIC "zlib") 56 | target_link_libraries(zlib) 57 | 58 | # Module folder 59 | set_target_properties(zlib PROPERTIES FOLDER "modules/zlib") 60 | 61 | endif() 62 | -------------------------------------------------------------------------------- /source/logging/layouts/hash_layout.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file hash_layout.cpp 3 | \brief Hash layout implementation 4 | \author Ivan Shynkarenka 5 | \date 13.12.2021 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/layouts/hash_layout.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace CppLogging { 15 | 16 | uint32_t HashLayout::Hash(std::string_view message) 17 | { 18 | const uint32_t FNV_PRIME = 16777619u; 19 | const uint32_t OFFSET_BASIS = 2166136261u; 20 | 21 | uint32_t hash = OFFSET_BASIS; 22 | for (size_t i = 0; i < message.size(); ++i) 23 | { 24 | hash ^= message[i]; 25 | hash *= FNV_PRIME; 26 | } 27 | return hash; 28 | } 29 | 30 | void HashLayout::LayoutRecord(Record& record) 31 | { 32 | // Calculate logging record size 33 | uint32_t size = (uint32_t)(sizeof(uint64_t) + sizeof(uint64_t) + sizeof(Level) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + record.buffer.size()); 34 | 35 | // Resize the raw buffer to the required size 36 | record.raw.resize(sizeof(uint32_t) + size + 1); 37 | 38 | // Get the raw buffer start position 39 | uint8_t* buffer = record.raw.data(); 40 | 41 | // Serialize the logging record 42 | std::memcpy(buffer, &size, sizeof(uint32_t)); 43 | buffer += sizeof(uint32_t); 44 | std::memcpy(buffer, &record.timestamp, sizeof(uint64_t)); 45 | buffer += sizeof(uint64_t); 46 | std::memcpy(buffer, &record.thread, sizeof(uint64_t)); 47 | buffer += sizeof(uint64_t); 48 | std::memcpy(buffer, &record.level, sizeof(Level)); 49 | buffer += sizeof(Level); 50 | 51 | // Serialize the logger name hash 52 | uint32_t logger_hash = Hash(record.logger); 53 | std::memcpy(buffer, &logger_hash, sizeof(uint32_t)); 54 | buffer += sizeof(uint32_t); 55 | 56 | // Serialize the logging message hash 57 | uint32_t message_hash = Hash(record.message); 58 | std::memcpy(buffer, &message_hash, sizeof(uint32_t)); 59 | buffer += sizeof(uint32_t); 60 | 61 | // Serialize the logging buffer 62 | uint32_t buffer_size = (uint32_t)record.buffer.size(); 63 | std::memcpy(buffer, &buffer_size, sizeof(uint32_t)); 64 | buffer += sizeof(uint32_t); 65 | std::memcpy(buffer, record.buffer.data(), record.buffer.size()); 66 | buffer += record.buffer.size(); 67 | 68 | // Write the last zero byte 69 | *buffer = 0; 70 | } 71 | 72 | } // namespace CppLogging 73 | -------------------------------------------------------------------------------- /include/logging/config.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file config.h 3 | \brief Logger configuration definition 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_CONFIG_H 10 | #define CPPLOGGING_CONFIG_H 11 | 12 | #include "logging/logger.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | //! Logger configuration static class 19 | /*! 20 | Logger configuration provides static interface to configure loggers. 21 | 22 | Thread-safe. 23 | */ 24 | class Config 25 | { 26 | public: 27 | Config(const Config&) = delete; 28 | Config(Config&&) = delete; 29 | ~Config(); 30 | 31 | Config& operator=(const Config&) = delete; 32 | Config& operator=(Config&&) = delete; 33 | 34 | //! Configure default logger with a given logging sink processor 35 | /*! 36 | \param sink - Logging sink processor 37 | */ 38 | static void ConfigLogger(const std::shared_ptr& sink); 39 | //! Configure named logger with a given logging sink processor 40 | /*! 41 | \param name - Logger name 42 | \param sink - Logging sink processor 43 | */ 44 | static void ConfigLogger(const std::string& name, const std::shared_ptr& sink); 45 | 46 | //! Create default logger 47 | /*! 48 | If the default logger was not configured before it will be automatically 49 | configured to a one with TextLayout and ConsoleAppender. 50 | 51 | \return Created instance of the default logger 52 | */ 53 | static Logger CreateLogger(); 54 | //! Create named logger 55 | /*! 56 | If the named logger was not configured before an instance of the default 57 | logger will be returned. 58 | 59 | \param name - Logger name 60 | \return Created instance of the named logger 61 | */ 62 | static Logger CreateLogger(const std::string& name); 63 | 64 | //! Startup the logging infrastructure 65 | static void Startup(); 66 | //! Shutdown the logging infrastructure 67 | static void Shutdown(); 68 | 69 | private: 70 | CppCommon::CriticalSection _lock; 71 | std::map> _config; 72 | std::map> _working; 73 | 74 | Config() = default; 75 | 76 | //! Get singleton instance 77 | static Config& GetInstance() 78 | { static Config instance; return instance; } 79 | }; 80 | 81 | } // namespace CppLogging 82 | 83 | #endif // CPPLOGGING_LOGGER_H 84 | -------------------------------------------------------------------------------- /include/logging/filters/level_filter.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file level_filter.h 3 | \brief Level filter definition 4 | \author Ivan Shynkarenka 5 | \date 27.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_FILTERS_LEVEL_FILTER_H 10 | #define CPPLOGGING_FILTERS_LEVEL_FILTER_H 11 | 12 | #include "logging/filter.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | //! Level filter 19 | /*! 20 | Level filters out logging records which level filed is not matched 21 | to the given level value or range. 22 | 23 | Thread-safe. 24 | */ 25 | class LevelFilter : public Filter 26 | { 27 | public: 28 | //! Initialize level filter with a given level value 29 | /*! 30 | \param level - Level value 31 | \param positive - Positive filtration (default is true) 32 | */ 33 | explicit LevelFilter(Level level, bool positive = true) { Update(level, positive); } 34 | //! Initialize level filter with a given level range 35 | /*! 36 | \param from - Level from value 37 | \param to - Level to value 38 | \param positive - Positive filtration (default is true) 39 | */ 40 | explicit LevelFilter(Level from, Level to, bool positive = true) { Update(from, to, positive); } 41 | LevelFilter(const LevelFilter&) = delete; 42 | LevelFilter(LevelFilter&&) = delete; 43 | virtual ~LevelFilter() = default; 44 | 45 | LevelFilter& operator=(const LevelFilter&) = delete; 46 | LevelFilter& operator=(LevelFilter&&) = delete; 47 | 48 | //! Get the positive filtration flag 49 | bool positive() const noexcept { return _positive; } 50 | 51 | //! Get Level from value 52 | Level from() const noexcept { return _from; } 53 | //! Get Level to value 54 | Level to() const noexcept { return _to; } 55 | 56 | //! Update level filter with a given level value 57 | /*! 58 | \param level - Level value 59 | \param positive - Positive filtration (default is true) 60 | */ 61 | void Update(Level level, bool positive = true); 62 | //! Update level filter with a given level range 63 | /*! 64 | \param from - Level from value 65 | \param to - Level to value 66 | \param positive - Positive filtration (default is true) 67 | */ 68 | void Update(Level from, Level to, bool positive = true); 69 | 70 | // Implementation of Filter 71 | bool FilterRecord(Record& record) override; 72 | 73 | private: 74 | std::atomic _positive; 75 | std::atomic _from; 76 | std::atomic _to; 77 | }; 78 | 79 | } // namespace CppLogging 80 | 81 | #endif // CPPLOGGING_FILTERS_LEVEL_FILTER_H 82 | -------------------------------------------------------------------------------- /tests/test_layout_binary.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 09.07.2016 3 | // 4 | 5 | #include "test.h" 6 | 7 | #include "logging/layouts/binary_layout.h" 8 | 9 | #include 10 | 11 | using namespace CppLogging; 12 | 13 | namespace { 14 | 15 | Record ParseBinaryLayout(const std::vector& raw) 16 | { 17 | Record record; 18 | 19 | // Get the buffer start position 20 | const uint8_t* buffer = raw.data(); 21 | 22 | // Deserialize logging record 23 | uint32_t size; 24 | std::memcpy(&size, buffer, sizeof(uint32_t)); 25 | buffer += sizeof(uint32_t); 26 | std::memcpy(&record.timestamp, buffer, sizeof(uint64_t)); 27 | buffer += sizeof(uint64_t); 28 | std::memcpy(&record.thread, buffer, sizeof(uint64_t)); 29 | buffer += sizeof(uint64_t); 30 | std::memcpy(&record.level, buffer, sizeof(Level)); 31 | buffer += sizeof(Level); 32 | 33 | uint8_t logger_size; 34 | std::memcpy(&logger_size, buffer, sizeof(uint8_t)); 35 | buffer += sizeof(uint8_t); 36 | record.logger.insert(record.logger.begin(), buffer, buffer + logger_size); 37 | buffer += logger_size; 38 | 39 | uint16_t message_size; 40 | std::memcpy(&message_size, buffer, sizeof(uint16_t)); 41 | buffer += sizeof(uint16_t); 42 | record.message.insert(record.message.begin(), buffer, buffer + message_size); 43 | buffer += message_size; 44 | 45 | uint32_t buffer_size; 46 | std::memcpy(&buffer_size, buffer, sizeof(uint32_t)); 47 | buffer += sizeof(uint32_t); 48 | record.buffer.insert(record.buffer.begin(), buffer, buffer + buffer_size); 49 | buffer += buffer_size; 50 | 51 | // Skip the last zero byte 52 | ++buffer; 53 | 54 | return record; 55 | } 56 | 57 | bool CompareRecords(const Record& record1, const Record& record2) 58 | { 59 | if (record1.timestamp != record2.timestamp) 60 | return false; 61 | if (record1.thread != record2.thread) 62 | return false; 63 | if (record1.level != record2.level) 64 | return false; 65 | if (record1.logger != record2.logger) 66 | return false; 67 | if (record1.message != record2.message) 68 | return false; 69 | if (record1.buffer != record2.buffer) 70 | return false; 71 | return true; 72 | } 73 | 74 | } // namespace 75 | 76 | TEST_CASE("Binary layout", "[CppLogging]") 77 | { 78 | Record record; 79 | record.logger = "Test logger"; 80 | record.message = "Test message"; 81 | record.buffer.resize(1024, 123); 82 | 83 | BinaryLayout layout; 84 | layout.LayoutRecord(record); 85 | REQUIRE(record.raw.size() > 0); 86 | 87 | Record clone = ParseBinaryLayout(record.raw); 88 | REQUIRE(CompareRecords(clone, record)); 89 | } 90 | -------------------------------------------------------------------------------- /include/logging/appenders/file_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file file_appender.h 3 | \brief File appender definition 4 | \author Ivan Shynkarenka 5 | \date 07.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_FILE_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_FILE_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | #include "filesystem/filesystem.h" 15 | 16 | #include 17 | 18 | namespace CppLogging { 19 | 20 | //! File appender 21 | /*! 22 | File appender writes the given logging record into the file with 23 | the given file name. In case of any IO error this appender will 24 | lost the logging record, but try to recover from fail in a short 25 | interval of 100ms. 26 | 27 | Not thread-safe. 28 | */ 29 | class FileAppender : public Appender 30 | { 31 | public: 32 | //! Initialize the appender with a given file, truncate/append and auto-flush flags 33 | /*! 34 | \param file - Logging file 35 | \param truncate - Truncate flag (default is false) 36 | \param auto_flush - Auto-flush flag (default is false) 37 | \param auto_start - Auto-start flag (default is true) 38 | */ 39 | explicit FileAppender(const CppCommon::Path& file, bool truncate = false, bool auto_flush = false, bool auto_start = true); 40 | FileAppender(const FileAppender&) = delete; 41 | FileAppender(FileAppender&&) = delete; 42 | virtual ~FileAppender(); 43 | 44 | FileAppender& operator=(const FileAppender&) = delete; 45 | FileAppender& operator=(FileAppender&&) = delete; 46 | 47 | // Implementation of Appender 48 | bool IsStarted() const noexcept override { return _started; } 49 | bool Start() override; 50 | bool Stop() override; 51 | void AppendRecord(Record& record) override; 52 | void Flush() override; 53 | 54 | private: 55 | std::atomic _started{false}; 56 | CppCommon::Timestamp _retry{0}; 57 | CppCommon::File _file; 58 | bool _truncate; 59 | bool _auto_flush; 60 | 61 | //! Prepare the file for writing 62 | /* 63 | - If the file is opened and ready to write immediately returns true 64 | - If the last retry was earlier than 100ms immediately returns false 65 | - If the file is closed try to open it for writing, returns true/false 66 | 67 | \return 'true' if the file was successfully prepared, 'false' if the file failed to be prepared 68 | */ 69 | bool PrepareFile(); 70 | //! Close the file 71 | /* 72 | \return 'true' if the file was successfully closed, 'false' if the file failed to close 73 | */ 74 | bool CloseFile(); 75 | }; 76 | 77 | } // namespace CppLogging 78 | 79 | /*! \example file.cpp File logger example */ 80 | 81 | #endif // CPPLOGGING_APPENDERS_FILE_APPENDER_H 82 | -------------------------------------------------------------------------------- /include/logging/logger.inl: -------------------------------------------------------------------------------- 1 | /*! 2 | \file logger.inl 3 | \brief Logger interface inline implementation 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | namespace CppLogging { 10 | 11 | inline Logger::Logger(const std::string& name, const std::shared_ptr& sink) : _name(name), _sink(sink) 12 | { 13 | } 14 | 15 | inline Logger::~Logger() 16 | { 17 | Flush(); 18 | } 19 | 20 | template 21 | inline void Logger::Log(Level level, bool format, fmt::format_string message, T&&... args) const 22 | { 23 | // Thread local thread Id 24 | thread_local uint64_t thread = CppCommon::Thread::CurrentThreadId(); 25 | // Thread local instance of the logging record 26 | thread_local Record record; 27 | 28 | // Clear the logging record 29 | record.Clear(); 30 | 31 | // Fill necessary fields of the logging record 32 | record.timestamp = CppCommon::Timestamp::utc(); 33 | record.thread = thread; 34 | record.level = level; 35 | record.logger = _name; 36 | 37 | // Check for valid and started logging sink 38 | if (_sink && _sink->IsStarted()) 39 | { 40 | // Filter the logging record 41 | if (!_sink->FilterRecord(record)) 42 | return; 43 | 44 | // Format or serialize arguments list 45 | if (format) 46 | record.Format(message, std::forward(args)...); 47 | else 48 | record.StoreFormat(message, std::forward(args)...); 49 | 50 | // Process the logging record 51 | _sink->ProcessRecord(record); 52 | } 53 | } 54 | 55 | template 56 | inline void Logger::Debug(fmt::format_string message, T&&... args) const 57 | { 58 | #if defined(NDEBUG) 59 | // Log nothing in release mode... 60 | #else 61 | Log(Level::DEBUG, false, message, std::forward(args)...); 62 | #endif 63 | } 64 | 65 | template 66 | inline void Logger::Info(fmt::format_string message, T&&... args) const 67 | { 68 | Log(Level::INFO, false, message, std::forward(args)...); 69 | } 70 | 71 | template 72 | inline void Logger::Warn(fmt::format_string message, T&&... args) const 73 | { 74 | Log(Level::WARN, false, message, std::forward(args)...); 75 | } 76 | 77 | template 78 | inline void Logger::Error(fmt::format_string message, T&&... args) const 79 | { 80 | Log(Level::ERROR, false, message, std::forward(args)...); 81 | } 82 | 83 | template 84 | inline void Logger::Fatal(fmt::format_string message, T&&... args) const 85 | { 86 | Log(Level::FATAL, false, message, std::forward(args)...); 87 | } 88 | 89 | inline void Logger::Flush() 90 | { 91 | if (_sink && _sink->IsStarted()) 92 | _sink->Flush(); 93 | } 94 | 95 | } // namespace CppLogging 96 | -------------------------------------------------------------------------------- /performance/appender_file.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 08.09.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppCommon; 11 | using namespace CppLogging; 12 | 13 | class BinaryConfigFixture : public virtual CppBenchmark::Fixture 14 | { 15 | protected: 16 | void Initialize(CppBenchmark::Context& context) override 17 | { 18 | auto binary_sink = std::make_shared(std::make_shared()); 19 | binary_sink->appenders().push_back(std::make_shared(_file)); 20 | Config::ConfigLogger("binary", binary_sink); 21 | Config::Startup(); 22 | } 23 | 24 | void Cleanup(CppBenchmark::Context& context) override 25 | { 26 | Config::Shutdown(); 27 | if (_file.IsFileExists()) 28 | File::Remove(_file); 29 | } 30 | 31 | private: 32 | File _file{"test.bin.log"}; 33 | }; 34 | 35 | class HashConfigFixture : public virtual CppBenchmark::Fixture 36 | { 37 | protected: 38 | void Initialize(CppBenchmark::Context& context) override 39 | { 40 | auto hash_sink = std::make_shared(std::make_shared()); 41 | hash_sink->appenders().push_back(std::make_shared(_file)); 42 | Config::ConfigLogger("hash", hash_sink); 43 | Config::Startup(); 44 | } 45 | 46 | void Cleanup(CppBenchmark::Context& context) override 47 | { 48 | Config::Shutdown(); 49 | if (_file.IsFileExists()) 50 | File::Remove(_file); 51 | } 52 | 53 | private: 54 | File _file{"test.hash.log"}; 55 | }; 56 | 57 | class TextConfigFixture : public virtual CppBenchmark::Fixture 58 | { 59 | protected: 60 | void Initialize(CppBenchmark::Context& context) override 61 | { 62 | auto text_sink = std::make_shared(std::make_shared()); 63 | text_sink->appenders().push_back(std::make_shared(_file)); 64 | Config::ConfigLogger("text", text_sink); 65 | Config::Startup(); 66 | } 67 | 68 | void Cleanup(CppBenchmark::Context& context) override 69 | { 70 | Config::Shutdown(); 71 | if (_file.IsFileExists()) 72 | File::Remove(_file); 73 | } 74 | 75 | private: 76 | File _file{"test.log"}; 77 | }; 78 | 79 | BENCHMARK_FIXTURE(BinaryConfigFixture, "FileAppender-binary") 80 | { 81 | static Logger logger = Config::CreateLogger("binary"); 82 | logger.Info("Test message"); 83 | } 84 | 85 | BENCHMARK_FIXTURE(HashConfigFixture, "FileAppender-hash") 86 | { 87 | static Logger logger = Config::CreateLogger("hash"); 88 | logger.Info("Test message"); 89 | } 90 | 91 | BENCHMARK_FIXTURE(TextConfigFixture, "FileAppender-text") 92 | { 93 | static Logger logger = Config::CreateLogger("text"); 94 | logger.Info("Test message"); 95 | } 96 | 97 | BENCHMARK_MAIN() 98 | -------------------------------------------------------------------------------- /source/logging/config.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file config.cpp 3 | \brief Logger configuration implementation 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/config.h" 10 | 11 | namespace CppLogging { 12 | 13 | Config::~Config() 14 | { 15 | // Shutdown working logger processors 16 | Shutdown(); 17 | } 18 | 19 | void Config::ConfigLogger(const std::shared_ptr& sink) 20 | { 21 | Config& instance = GetInstance(); 22 | 23 | CppCommon::Locker locker(instance._lock); 24 | 25 | instance._config[""] = sink; 26 | } 27 | 28 | void Config::ConfigLogger(const std::string& name, const std::shared_ptr& sink) 29 | { 30 | Config& instance = GetInstance(); 31 | 32 | CppCommon::Locker locker(instance._lock); 33 | 34 | instance._config[name] = sink; 35 | } 36 | 37 | Logger Config::CreateLogger() 38 | { 39 | Config& instance = GetInstance(); 40 | 41 | CppCommon::Locker locker(instance._lock); 42 | 43 | auto it = instance._working.find(""); 44 | if (it != instance._working.end()) 45 | return Logger(it->first, it->second); 46 | else 47 | { 48 | auto sink = std::make_shared(std::make_shared()); 49 | sink->appenders().push_back(std::make_shared()); 50 | instance._working[""] = sink; 51 | return Logger("", sink); 52 | } 53 | } 54 | 55 | Logger Config::CreateLogger(const std::string& name) 56 | { 57 | Config& instance = GetInstance(); 58 | 59 | CppCommon::Locker locker(instance._lock); 60 | 61 | auto it = instance._working.find(name); 62 | if (it != instance._working.end()) 63 | return Logger(it->first, it->second); 64 | else 65 | return CreateLogger(); 66 | } 67 | 68 | void Config::Startup() 69 | { 70 | Config& instance = GetInstance(); 71 | 72 | CppCommon::Locker locker(instance._lock); 73 | 74 | // Update working logger processors map 75 | std::swap(instance._working, instance._config); 76 | 77 | // Start all working logger processors 78 | for (auto& processor : instance._working) 79 | if (processor.second) 80 | processor.second->Start(); 81 | 82 | // Clear config logger processors map 83 | instance._config.clear(); 84 | } 85 | 86 | void Config::Shutdown() 87 | { 88 | Config& instance = GetInstance(); 89 | 90 | CppCommon::Locker locker(instance._lock); 91 | 92 | // Flush and stop all working logger processors 93 | for (auto& processor : instance._working) 94 | { 95 | if (processor.second) 96 | { 97 | processor.second->Flush(); 98 | processor.second->Stop(); 99 | } 100 | } 101 | 102 | // Clear working logger processors map 103 | instance._working.clear(); 104 | } 105 | 106 | } // namespace CppLogging 107 | -------------------------------------------------------------------------------- /include/logging/processors/async_wait_processor.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file async_wait_processor.h 3 | \brief Asynchronous wait logging processor definition 4 | \author Ivan Shynkarenka 5 | \date 01.08.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_ASYNC_WAIT_PROCESSOR_H 10 | #define CPPLOGGING_PROCESSORS_ASYNC_WAIT_PROCESSOR_H 11 | 12 | #include "logging/processor.h" 13 | 14 | #include "threads/wait_batcher.h" 15 | 16 | #include 17 | 18 | namespace CppLogging { 19 | 20 | //! Asynchronous wait logging processor 21 | /*! 22 | Asynchronous wait logging processor stores the given logging 23 | record into thread-safe buffer and process it in the separate 24 | thread. 25 | 26 | This processor use dynamic size async buffer which cannot overflow, 27 | buy might lead to out of memory error. 28 | 29 | Please note that asynchronous logging processor moves the given 30 | logging record (ProcessRecord() method always returns false) 31 | into the buffer! 32 | 33 | Thread-safe. 34 | */ 35 | class AsyncWaitProcessor : public Processor 36 | { 37 | public: 38 | //! Initialize asynchronous processor with a given layout interface 39 | /*! 40 | \param layout - Logging layout interface 41 | \param auto_start - Auto-start the logging processor (default is true) 42 | \param capacity - Buffer capacity in logging records (0 for unlimited capacity, default is 8192) 43 | \param initial - Buffer initial capacity in logging records (default is 8192) 44 | \param on_thread_initialize - Thread initialize handler can be used to initialize priority or affinity of the logging thread (default does nothing) 45 | \param on_thread_clenup - Thread cleanup handler can be used to cleanup priority or affinity of the logging thread (default does nothing) 46 | */ 47 | explicit AsyncWaitProcessor(const std::shared_ptr& layout, bool auto_start = true, size_t capacity = 8192, size_t initial = 8192, const std::function& on_thread_initialize = [](){}, const std::function& on_thread_clenup = [](){}); 48 | AsyncWaitProcessor(const AsyncWaitProcessor&) = delete; 49 | AsyncWaitProcessor(AsyncWaitProcessor&&) = delete; 50 | virtual ~AsyncWaitProcessor(); 51 | 52 | AsyncWaitProcessor& operator=(const AsyncWaitProcessor&) = delete; 53 | AsyncWaitProcessor& operator=(AsyncWaitProcessor&&) = delete; 54 | 55 | // Implementation of Processor 56 | bool Start() override; 57 | bool Stop() override; 58 | bool ProcessRecord(Record& record) override; 59 | void Flush() override; 60 | 61 | private: 62 | CppCommon::WaitBatcher _queue; 63 | std::thread _thread; 64 | std::function _on_thread_initialize; 65 | std::function _on_thread_clenup; 66 | 67 | bool EnqueueRecord(Record& record); 68 | void ProcessThread(const std::function& on_thread_initialize, const std::function& on_thread_clenup); 69 | }; 70 | 71 | } // namespace CppLogging 72 | 73 | /*! \example async.cpp Asynchronous logger processor example */ 74 | 75 | #endif // CPPLOGGING_PROCESSORS_ASYNC_WAIT_PROCESSOR_H 76 | -------------------------------------------------------------------------------- /include/logging/processors/async_wait_free_processor.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file async_wait_free_processor.h 3 | \brief Asynchronous wait-free logging processor definition 4 | \author Ivan Shynkarenka 5 | \date 01.08.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_ASYNC_WAIT_FREE_PROCESSOR_H 10 | #define CPPLOGGING_PROCESSORS_ASYNC_WAIT_FREE_PROCESSOR_H 11 | 12 | #include "logging/processor.h" 13 | 14 | #include "logging/processors/async_wait_free_queue.h" 15 | 16 | #include 17 | 18 | namespace CppLogging { 19 | 20 | //! Asynchronous wait-free logging processor 21 | /*! 22 | Asynchronous wait-free logging processor stores the given logging 23 | record into thread-safe buffer and process it in the separate thread. 24 | 25 | This processor use fixed size async buffer which can overflow. 26 | 27 | Please note that asynchronous logging processor moves the given 28 | logging record (ProcessRecord() method always returns false) 29 | into the buffer! 30 | 31 | Thread-safe. 32 | */ 33 | class AsyncWaitFreeProcessor : public Processor 34 | { 35 | public: 36 | //! Initialize asynchronous processor with a given layout interface, overflow policy and buffer capacity 37 | /*! 38 | \param layout - Logging layout interface 39 | \param auto_start - Auto-start the logging processor (default is true) 40 | \param capacity - Buffer capacity in logging records (default is 8192) 41 | \param discard - Discard logging records on buffer overflow or block and wait (default is false) 42 | \param on_thread_initialize - Thread initialize handler can be used to initialize priority or affinity of the logging thread (default does nothing) 43 | \param on_thread_clenup - Thread cleanup handler can be used to cleanup priority or affinity of the logging thread (default does nothing) 44 | */ 45 | explicit AsyncWaitFreeProcessor(const std::shared_ptr& layout, bool auto_start = true, size_t capacity = 8192, bool discard = false, const std::function& on_thread_initialize = [](){}, const std::function& on_thread_clenup = [](){}); 46 | AsyncWaitFreeProcessor(const AsyncWaitFreeProcessor&) = delete; 47 | AsyncWaitFreeProcessor(AsyncWaitFreeProcessor&&) = delete; 48 | virtual ~AsyncWaitFreeProcessor(); 49 | 50 | AsyncWaitFreeProcessor& operator=(const AsyncWaitFreeProcessor&) = delete; 51 | AsyncWaitFreeProcessor& operator=(AsyncWaitFreeProcessor&&) = delete; 52 | 53 | // Implementation of Processor 54 | bool Start() override; 55 | bool Stop() override; 56 | bool ProcessRecord(Record& record) override; 57 | void Flush() override; 58 | 59 | private: 60 | bool _discard; 61 | AsyncWaitFreeQueue _queue; 62 | std::thread _thread; 63 | std::function _on_thread_initialize; 64 | std::function _on_thread_clenup; 65 | 66 | bool EnqueueRecord(bool discard, Record& record); 67 | void ProcessThread(const std::function& on_thread_initialize, const std::function& on_thread_clenup); 68 | }; 69 | 70 | } // namespace CppLogging 71 | 72 | #endif // CPPLOGGING_PROCESSORS_ASYNC_WAIT_FREE_PROCESSOR_H 73 | -------------------------------------------------------------------------------- /include/logging/record.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file record.h 3 | \brief Logging record definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_RECORD_H 10 | #define CPPLOGGING_RECORD_H 11 | 12 | #include "logging/level.h" 13 | #include "string/format.h" 14 | #include "threads/thread.h" 15 | 16 | #include 17 | #include 18 | 19 | namespace CppLogging { 20 | 21 | //! Logging record 22 | /*! 23 | Logging record encapsulates all required fields in a single instance: 24 | - timestamp 25 | - thread Id 26 | - level 27 | - logger 28 | - message 29 | - buffer 30 | 31 | Logging records are created inside Logger class and processed 32 | by logging appenders, filters and layouts. 33 | 34 | Not thread-safe. 35 | */ 36 | class Record 37 | { 38 | public: 39 | //! Timestamp of the logging record 40 | uint64_t timestamp; 41 | //! Thread Id of the logging record 42 | uint64_t thread; 43 | //! Level of the logging record 44 | Level level; 45 | //! Logger name of the logging record 46 | std::string logger; 47 | //! Message of the logging record 48 | std::string message; 49 | //! Buffer of the logging record 50 | std::vector buffer; 51 | 52 | //! Record content after layout 53 | std::vector raw; 54 | 55 | Record(); 56 | Record(const Record&) = default; 57 | Record(Record&&) = default; 58 | ~Record() = default; 59 | 60 | Record& operator=(const Record&) = default; 61 | Record& operator=(Record&&) = default; 62 | 63 | //! Is the record contains stored format message and its arguments 64 | bool IsFormatStored() const noexcept { return !buffer.empty(); } 65 | 66 | //! Format message and its arguments 67 | template 68 | Record& Format(fmt::format_string pattern, T&&... args); 69 | 70 | //! Store format message and its arguments 71 | template 72 | Record& StoreFormat(fmt::format_string pattern, T&&... args); 73 | 74 | //! Store custom format message and its arguments 75 | template 76 | Record& StoreCustom(const Arg& arg); 77 | template 78 | Record& StoreCustomFormat(std::string_view pattern, Args&&... args); 79 | 80 | //! Store list format message 81 | size_t StoreListBegin(); 82 | template 83 | Record& StoreList(Args&&... args); 84 | template 85 | Record& StoreListFormat(std::string_view pattern, Args&&... args); 86 | Record& StoreListEnd(size_t begin); 87 | 88 | //! Restore format message and its arguments 89 | std::string RestoreFormat() const { return RestoreFormat(message, buffer, 0, buffer.size()); } 90 | 91 | //! Restore format of the custom data type 92 | static std::string RestoreFormat(std::string_view pattern, const std::vector& buffer, size_t offset, size_t size); 93 | 94 | //! Clear logging record 95 | void Clear(); 96 | 97 | //! Swap two instances 98 | void swap(Record& record) noexcept; 99 | friend void swap(Record& record1, Record& record2) noexcept; 100 | }; 101 | 102 | } // namespace CppLogging 103 | 104 | #include "record.inl" 105 | 106 | #endif // CPPLOGGING_RECORD_H 107 | -------------------------------------------------------------------------------- /include/logging/processors/async_wait_free_queue.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file async_wait_free_queue.h 3 | \brief Asynchronous wait-free logging ring queue definition 4 | \author Ivan Shynkarenka 5 | \date 04.08.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSORS_ASYNC_WAIT_FREE_QUEUE_H 10 | #define CPPLOGGING_PROCESSORS_ASYNC_WAIT_FREE_QUEUE_H 11 | 12 | #include "logging/record.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace CppLogging { 21 | 22 | //! Asynchronous wait-free logging ring queue 23 | /*! 24 | Multiple producers / multiple consumers wait-free ring queue use only atomic operations to provide thread-safe 25 | enqueue and dequeue operations. Ring queue size is limited to the capacity provided in the constructor. 26 | 27 | FIFO order is guaranteed! 28 | 29 | Thread-safe. 30 | 31 | C++ implementation of Dmitry Vyukov's non-intrusive lock free unbound MPSC queue 32 | http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue 33 | */ 34 | template 35 | class AsyncWaitFreeQueue 36 | { 37 | public: 38 | //! Default class constructor 39 | /*! 40 | \param capacity - Ring queue capacity (must be a power of two) 41 | */ 42 | explicit AsyncWaitFreeQueue(size_t capacity); 43 | AsyncWaitFreeQueue(const AsyncWaitFreeQueue&) = delete; 44 | AsyncWaitFreeQueue(AsyncWaitFreeQueue&&) = delete; 45 | ~AsyncWaitFreeQueue() { delete[] _buffer; } 46 | 47 | AsyncWaitFreeQueue& operator=(const AsyncWaitFreeQueue&) = delete; 48 | AsyncWaitFreeQueue& operator=(AsyncWaitFreeQueue&&) = delete; 49 | 50 | //! Check if the queue is not empty 51 | explicit operator bool() const noexcept { return !empty(); } 52 | 53 | //! Is ring queue empty? 54 | bool empty() const noexcept { return (size() == 0); } 55 | //! Get ring queue capacity 56 | size_t capacity() const noexcept { return _capacity; } 57 | //! Get ring queue size 58 | size_t size() const noexcept; 59 | 60 | //! Enqueue and swap the logging record into the ring queue (multiple producers threads method) 61 | /*! 62 | \param record - Logging record to enqueue and swap 63 | \return 'true' if the item was successfully enqueue, 'false' if the ring queue is full 64 | */ 65 | bool Enqueue(Record& record); 66 | 67 | //! Dequeue and swap the logging record from the ring queue (multiple consumers threads method) 68 | /*! 69 | \param record - Logging record to dequeue and swap 70 | \return 'true' if the item was successfully dequeue, 'false' if the ring queue is empty 71 | */ 72 | bool Dequeue(Record& record); 73 | 74 | private: 75 | struct Node 76 | { 77 | std::atomic sequence; 78 | Record value; 79 | }; 80 | 81 | typedef char cache_line_pad[128]; 82 | 83 | cache_line_pad _pad0; 84 | const size_t _capacity; 85 | const size_t _mask; 86 | Node* const _buffer; 87 | 88 | cache_line_pad _pad1; 89 | std::atomic _head; 90 | cache_line_pad _pad2; 91 | std::atomic _tail; 92 | cache_line_pad _pad3; 93 | }; 94 | 95 | } // namespace CppLogging 96 | 97 | #include "async_wait_free_queue.inl" 98 | 99 | #endif // CPPLOGGING_PROCESSORS_ASYNC_WAIT_FREE_QUEUE_H 100 | -------------------------------------------------------------------------------- /include/logging/layouts/text_layout.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file text_layout.h 3 | \brief Text layout definition 4 | \author Ivan Shynkarenka 5 | \date 08.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LAYOUTS_TEXT_LAYOUT_H 10 | #define CPPLOGGING_LAYOUTS_TEXT_LAYOUT_H 11 | 12 | #include "logging/layout.h" 13 | 14 | #include 15 | #include 16 | 17 | namespace CppLogging { 18 | 19 | //! Text layout 20 | /*! 21 | Text layout converts the given logging record into the text 22 | string using a given pattern. Text layout pattern is a string 23 | with a special placeholders provided inside curly brackets ("{}"). 24 | 25 | Supported placeholders: 26 | - {UtcDateTime} / {LocalDateTime} - converted to the UTC/local date & time (e.g. "1997-07-16T19:20:30.123Z" / "1997-07-16T19:20:30.123+01:00") 27 | - {UtcDate} / {LocalDate} - converted to the UTC/local date (e.g. "1997-07-16") 28 | - {Time} / {LocalTime} - converted to the UTC/local time (e.g. "19:20:30.123Z" / "19:20:30.123+01:00") 29 | - {UtcYear} / {LocalYear} - converted to the UTC/local four-digits year (e.g. "1997") 30 | - {UtcMonth} / {LocalMonth} - converted to the UTC/local two-digits month (e.g. "07") 31 | - {UtcDay} / {LocalDay} - converted to the UTC/local two-digits day (e.g. "16") 32 | - {UtcHour} / {LocalHour} - converted to the UTC/local two-digits hour (e.g. "19") 33 | - {UtcMinute} / {LocalMinute} - converted to the UTC/local two-digits minute (e.g. "20") 34 | - {UtcSecond} / {LocalSecond} - converted to the UTC/local two-digits second (e.g. "30") 35 | - {UtcTimezone} / {LocalTimezone} - converted to the UTC/local timezone suffix (e.g. "Z" / "+01:00") 36 | - {Millisecond} - converted to the three-digits millisecond (e.g. "123") 37 | - {Microsecond} - converted to the three-digits microsecond (e.g. "123") 38 | - {Nanosecond} - converted to the three-digits nanosecond (e.g. "789") 39 | - {Thread} - converted to the thread Id (e.g. "0x0028F3D8") 40 | - {Level} - converted to the logging level 41 | - {Logger} - converted to the logger name 42 | - {Message} - converted to the log message 43 | - {EndLine} - converted to the end line suffix (e.g. Unix "\n" or Windows "\r\n") 44 | 45 | Thread-safe. 46 | */ 47 | class TextLayout : public Layout 48 | { 49 | public: 50 | //! Initialize text logging layout with a given pattern 51 | /*! 52 | \param pattern - Layout pattern 53 | */ 54 | explicit TextLayout(const std::string& pattern = "{UtcDateTime} [{Thread}] {Level} {Logger} - {Message}{EndLine}"); 55 | TextLayout(const TextLayout&) = delete; 56 | TextLayout(TextLayout&& layout) = delete; 57 | virtual ~TextLayout(); 58 | 59 | TextLayout& operator=(const TextLayout&) = delete; 60 | TextLayout& operator=(TextLayout&& layout) = delete; 61 | 62 | //! Get the text layout pattern 63 | const std::string& pattern() const noexcept; 64 | 65 | // Implementation of Layout 66 | void LayoutRecord(Record& record) override; 67 | 68 | private: 69 | class Impl; 70 | 71 | Impl& impl() noexcept { return reinterpret_cast(_storage); } 72 | const Impl& impl() const noexcept { return reinterpret_cast(_storage); } 73 | 74 | static const size_t StorageSize = 72; 75 | static const size_t StorageAlign = 8; 76 | alignas(StorageAlign) std::byte _storage[StorageSize]; 77 | }; 78 | 79 | } // namespace CppLogging 80 | 81 | #endif // CPPLOGGING_LAYOUTS_TEXT_LAYOUT_H 82 | -------------------------------------------------------------------------------- /source/logging/appenders/file_appender.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file file_appender.cpp 3 | \brief File appender implementation 4 | \author Ivan Shynkarenka 5 | \date 07.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/appenders/file_appender.h" 10 | 11 | namespace CppLogging { 12 | 13 | FileAppender::FileAppender(const CppCommon::Path& file, bool truncate, bool auto_flush, bool auto_start) 14 | : _file(file), _truncate(truncate), _auto_flush(auto_flush) 15 | { 16 | // Start the file appender 17 | if (auto_start) 18 | Start(); 19 | } 20 | 21 | FileAppender::~FileAppender() 22 | { 23 | // Stop the file appender 24 | if (IsStarted()) 25 | Stop(); 26 | } 27 | 28 | bool FileAppender::Start() 29 | { 30 | if (IsStarted()) 31 | return false; 32 | 33 | PrepareFile(); 34 | _started = true; 35 | return true; 36 | } 37 | 38 | bool FileAppender::Stop() 39 | { 40 | if (!IsStarted()) 41 | return false; 42 | 43 | CloseFile(); 44 | _started = false; 45 | return true; 46 | } 47 | 48 | void FileAppender::AppendRecord(Record& record) 49 | { 50 | // Skip logging records without layout 51 | if (record.raw.empty()) 52 | return; 53 | 54 | if (PrepareFile()) 55 | { 56 | // Try to write logging record content into the opened file 57 | try 58 | { 59 | _file.Write(record.raw.data(), record.raw.size() - 1); 60 | 61 | // Perform auto-flush if enabled 62 | if (_auto_flush) 63 | _file.Flush(); 64 | } 65 | catch (const CppCommon::FileSystemException&) 66 | { 67 | // Try to close the opened file in case of any IO error 68 | CloseFile(); 69 | } 70 | } 71 | } 72 | 73 | void FileAppender::Flush() 74 | { 75 | if (PrepareFile()) 76 | { 77 | // Try to flush the opened file 78 | try 79 | { 80 | _file.Flush(); 81 | } 82 | catch (const CppCommon::FileSystemException&) 83 | { 84 | // Try to close the opened file in case of any IO error 85 | CloseFile(); 86 | } 87 | } 88 | } 89 | 90 | bool FileAppender::PrepareFile() 91 | { 92 | try 93 | { 94 | // 1. Check if the file is already opened for writing 95 | if (_file.IsFileWriteOpened()) 96 | return true; 97 | 98 | // 2. Check retry timestamp if 100ms elapsed after the last attempt 99 | if ((CppCommon::Timestamp::utc() - _retry).milliseconds() < 100) 100 | return false; 101 | 102 | // 3. If the file is opened for reading close it 103 | if (_file.IsFileReadOpened()) 104 | _file.Close(); 105 | 106 | // 4. Open the file for writing 107 | _file.OpenOrCreate(false, true, _truncate); 108 | 109 | // 5. Reset the the retry timestamp 110 | _retry = 0; 111 | 112 | return true; 113 | } 114 | catch (const CppCommon::FileSystemException&) 115 | { 116 | // In case of any IO error reset the retry timestamp and return false! 117 | _retry = CppCommon::Timestamp::utc(); 118 | return false; 119 | } 120 | } 121 | 122 | bool FileAppender::CloseFile() 123 | { 124 | try 125 | { 126 | if (_file) 127 | _file.Close(); 128 | return true; 129 | } 130 | catch (const CppCommon::FileSystemException&) { return false; } 131 | } 132 | 133 | } // namespace CppLogging 134 | -------------------------------------------------------------------------------- /performance/file_async.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 09.09.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppCommon; 11 | using namespace CppLogging; 12 | 13 | const auto settings = CppBenchmark::Settings().ThreadsRange(1, 8, [](int from, int to, int& result) { int r = result; result *= 2; return r; }); 14 | 15 | class LogConfigFixture 16 | { 17 | protected: 18 | LogConfigFixture() 19 | { 20 | auto async_wait_binary_sink = std::make_shared(std::make_shared()); 21 | async_wait_binary_sink->appenders().push_back(std::make_shared(_binary_file_1)); 22 | Config::ConfigLogger("async-wait-binary", async_wait_binary_sink); 23 | 24 | auto async_wait_free_binary_sink = std::make_shared(std::make_shared()); 25 | async_wait_free_binary_sink->appenders().push_back(std::make_shared(_binary_file_2)); 26 | Config::ConfigLogger("async-wait-free-binary", async_wait_free_binary_sink); 27 | 28 | auto async_wait_text_sink = std::make_shared(std::make_shared()); 29 | async_wait_text_sink->appenders().push_back(std::make_shared(_text_file_1)); 30 | Config::ConfigLogger("async-wait-text", async_wait_text_sink); 31 | 32 | auto async_wait_free_text_sink = std::make_shared(std::make_shared()); 33 | async_wait_free_text_sink->appenders().push_back(std::make_shared(_text_file_2)); 34 | Config::ConfigLogger("async-wait-free-text", async_wait_free_text_sink); 35 | 36 | Config::Startup(); 37 | } 38 | 39 | ~LogConfigFixture() 40 | { 41 | Config::Shutdown(); 42 | if (_binary_file_1.IsFileExists()) 43 | File::Remove(_binary_file_1); 44 | if (_binary_file_2.IsFileExists()) 45 | File::Remove(_binary_file_2); 46 | if (_text_file_1.IsFileExists()) 47 | File::Remove(_text_file_1); 48 | if (_text_file_2.IsFileExists()) 49 | File::Remove(_text_file_2); 50 | } 51 | 52 | private: 53 | File _binary_file_1{"test1.bin.log"}; 54 | File _binary_file_2{"test2.bin.log"}; 55 | File _text_file_1{"test1.log"}; 56 | File _text_file_2{"test2.log"}; 57 | }; 58 | 59 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "FileAsyncWait-binary", settings) 60 | { 61 | thread_local Logger logger = Config::CreateLogger("async-wait-binary"); 62 | logger.Info("Test message {}-{}-{}", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 63 | } 64 | 65 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "FileAsyncWaitFree-binary", settings) 66 | { 67 | thread_local Logger logger = Config::CreateLogger("async-wait-free-binary"); 68 | logger.Info("Test message {}-{}-{}", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 69 | } 70 | 71 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "FileAsyncWait-text", settings) 72 | { 73 | thread_local Logger logger = Config::CreateLogger("async-wait-text"); 74 | logger.Info("Test message {}-{}-{}", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 75 | } 76 | 77 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "FileAsyncWaitFree-text", settings) 78 | { 79 | thread_local Logger logger = Config::CreateLogger("async-wait-free-text"); 80 | logger.Info("Test message {}-{}-{}", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 81 | } 82 | 83 | BENCHMARK_MAIN() 84 | -------------------------------------------------------------------------------- /examples/format.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file format.cpp 3 | \brief Format logger example 4 | \author Ivan Shynkarenka 5 | \date 18.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/logger.h" 10 | 11 | class Date 12 | { 13 | public: 14 | Date(int year, int month, int day) : _year(year), _month(month), _day(day) {} 15 | 16 | int year() const { return _year; } 17 | int month() const { return _month; } 18 | int day() const { return _day; } 19 | 20 | friend CppLogging::Record& operator<<(CppLogging::Record& record, const Date& date) 21 | { return record.StoreCustomFormat("{}-{}-{}", date._year, date._month, date._day); } 22 | 23 | private: 24 | int _year, _month, _day; 25 | }; 26 | 27 | template <> 28 | struct fmt::formatter 29 | { 30 | constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } 31 | 32 | auto format(const Date& date, format_context& ctx) const -> decltype(ctx.out()) 33 | { 34 | fmt::format_to(ctx.out(), "{}-{}-{}", date.year(), date.month(), date.day()); 35 | return ctx.out(); 36 | } 37 | }; 38 | 39 | class DateTime 40 | { 41 | public: 42 | DateTime(Date date, int hours, int minutes, int seconds) : _date(date), _hours(hours), _minutes(minutes), _seconds(seconds) {} 43 | 44 | Date date() const { return _date; } 45 | int hours() const { return _hours; } 46 | int minutes() const { return _minutes; } 47 | int seconds() const { return _seconds; } 48 | 49 | friend CppLogging::Record& operator<<(CppLogging::Record& record, const DateTime& datetime) 50 | { 51 | const size_t begin = record.StoreListBegin(); 52 | record.StoreList(datetime._date); 53 | record.StoreList(' '); 54 | record.StoreList(datetime._hours); 55 | record.StoreList(':'); 56 | record.StoreList(datetime._minutes); 57 | record.StoreList(':'); 58 | record.StoreList(datetime._seconds); 59 | return record.StoreListEnd(begin); 60 | } 61 | 62 | private: 63 | Date _date; 64 | int _hours, _minutes, _seconds; 65 | }; 66 | 67 | template <> 68 | struct fmt::formatter 69 | { 70 | constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } 71 | 72 | auto format(const DateTime& datetime, format_context& ctx) const -> decltype(ctx.out()) 73 | { 74 | fmt::format_to(ctx.out(), "{} {}:{}:{}", datetime.date(), datetime.hours(), datetime.minutes(), datetime.seconds()); 75 | return ctx.out(); 76 | } 77 | }; 78 | 79 | int main(int argc, char** argv) 80 | { 81 | // Create default logger 82 | CppLogging::Logger logger; 83 | 84 | // Log some messages with format 85 | logger.Info("argc: {}, argv: {}", argc, (void*)argv); 86 | logger.Info("no arguments"); 87 | logger.Info("{0}, {1}, {2}", -1, 0, 1); 88 | logger.Info("{0}, {1}, {2}", 'a', 'b', 'c'); 89 | logger.Info("{}, {}, {}", 'a', 'b', 'c'); 90 | logger.Info("{2}, {1}, {0}", 'a', 'b', 'c'); 91 | logger.Info("{0}{1}{0}", "abra", "cad"); 92 | logger.Info("{:<30}", "left aligned"); 93 | logger.Info("{:>30}", "right aligned"); 94 | logger.Info("{:^30}", "centered"); 95 | logger.Info("{:*^30}", "centered"); 96 | logger.Info("{:+f}; {:+f}", 3.14, -3.14); 97 | logger.Info("{: f}; {: f}", 3.14, -3.14); 98 | logger.Info("{:-f}; {:-f}", 3.14, -3.14); 99 | logger.Info("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); 100 | logger.Info("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42); 101 | logger.Info("The date is {}", Date(2012, 12, 9)); 102 | logger.Info("The datetime is {}", DateTime(Date(2012, 12, 9), 13, 15, 57)); 103 | logger.Info("Elapsed time: {s:.2f} seconds", "s"_a = 1.23); 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /include/logging/processor.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file processor.h 3 | \brief Logging processor interface definition 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_PROCESSOR_H 10 | #define CPPLOGGING_PROCESSOR_H 11 | 12 | #include "logging/appenders.h" 13 | #include "logging/filters.h" 14 | #include "logging/layouts.h" 15 | 16 | namespace CppLogging { 17 | 18 | //! Logging processor interface 19 | /*! 20 | Logging processor takes an instance of a single logging record 21 | and process it though all child filters, layouts and appenders. 22 | 23 | Not thread-safe. 24 | 25 | \see SyncProcessor 26 | \see AsyncProcessor 27 | \see BufferedProcessor 28 | \see ExclusiveProcessor 29 | */ 30 | class Processor : public Element 31 | { 32 | public: 33 | //! Initialize logging processor with a given layout interface 34 | /*! 35 | \param layout - Logging layout interface 36 | */ 37 | explicit Processor(const std::shared_ptr& layout) : _layout(layout) {} 38 | Processor(const Processor&) = delete; 39 | Processor(Processor&&) noexcept = delete; 40 | virtual ~Processor(); 41 | 42 | Processor& operator=(const Processor&) = delete; 43 | Processor& operator=(Processor&&) noexcept = delete; 44 | 45 | //! Get the logging processor layout 46 | std::shared_ptr& layout() noexcept { return _layout; } 47 | //! Get collection of child filters 48 | std::vector>& filters() noexcept { return _filters; } 49 | //! Get collection of child appenders 50 | std::vector>& appenders() noexcept { return _appenders; } 51 | //! Get collection of child processors 52 | std::vector>& processors() noexcept { return _processors; } 53 | 54 | //! Is the logging processor started? 55 | bool IsStarted() const noexcept override { return _started; } 56 | 57 | //! Start the logging processor 58 | /*! 59 | \return 'true' if the logging processor was successfully started, 'false' if the logging processor failed to start 60 | */ 61 | bool Start() override; 62 | //! Stop the logging processor 63 | /*! 64 | \return 'true' if the logging processor was successfully stopped, 'false' if the logging processor failed to stop 65 | */ 66 | bool Stop() override; 67 | 68 | //! Filter the given logging record 69 | /*! 70 | \param record - Logging record 71 | \return 'true' if the logging record should be processed, 'false' if the logging record was filtered out 72 | */ 73 | virtual bool FilterRecord(Record& record); 74 | 75 | //! Process the given logging record through all child filters, layouts and appenders 76 | /*! 77 | Default behavior of the method will take the given logging record and process it 78 | in the following sequence: 79 | - all filters (if any present) 80 | - all layouts (if any present) 81 | - all appenders (if any present) 82 | - all sub processors (if any present) 83 | 84 | \param record - Logging record 85 | \return 'true' if the logging record was successfully processed, 'false' if the logging record was filtered out 86 | */ 87 | virtual bool ProcessRecord(Record& record); 88 | 89 | //! Flush the current logging processor 90 | /*! 91 | Default behavior of the method will flush in the following sequence: 92 | - all appenders (if any present) 93 | - all sub processors (if any present) 94 | */ 95 | virtual void Flush(); 96 | 97 | protected: 98 | std::atomic _started{true}; 99 | std::shared_ptr _layout; 100 | std::vector> _filters; 101 | std::vector> _appenders; 102 | std::vector> _processors; 103 | }; 104 | 105 | } // namespace CppLogging 106 | 107 | #endif // CPPLOGGING_PROCESSOR_H 108 | -------------------------------------------------------------------------------- /source/logging/processor.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file processor.cpp 3 | \brief Logging processor interface implementation 4 | \author Ivan Shynkarenka 5 | \date 26.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/processor.h" 10 | 11 | namespace CppLogging { 12 | 13 | Processor::~Processor() 14 | { 15 | // Flush all appenders 16 | for (auto& appender : _appenders) 17 | if (appender && appender->IsStarted()) 18 | appender->Flush(); 19 | 20 | // Flush all sub processors 21 | for (auto& processor : _processors) 22 | if (processor && processor->IsStarted()) 23 | processor->Flush(); 24 | 25 | // Stop the logging processor 26 | if (IsStarted()) 27 | Stop(); 28 | } 29 | 30 | bool Processor::Start() 31 | { 32 | // Start logging layout 33 | if (_layout && !_layout->IsStarted()) 34 | if (!_layout->Start()) 35 | return false; 36 | 37 | // Start logging filters 38 | for (auto& filter : _filters) 39 | if (filter && !filter->IsStarted()) 40 | if (!filter->Start()) 41 | return false; 42 | 43 | // Start logging appenders 44 | for (auto& appender : _appenders) 45 | if (appender && !appender->IsStarted()) 46 | if (!appender->Start()) 47 | return false; 48 | 49 | // Start logging processors 50 | for (auto& processor : _processors) 51 | if (processor) 52 | if (!processor->Start()) 53 | return false; 54 | 55 | _started = true; 56 | 57 | return true; 58 | } 59 | 60 | bool Processor::Stop() 61 | { 62 | // Stop logging layout 63 | if (_layout && _layout->IsStarted()) 64 | if (!_layout->Stop()) 65 | return false; 66 | 67 | // Stop logging filters 68 | for (auto& filter : _filters) 69 | if (filter && filter->IsStarted()) 70 | if (!filter->Stop()) 71 | return false; 72 | 73 | // Stop logging appenders 74 | for (auto& appender : _appenders) 75 | if (appender && appender->IsStarted()) 76 | if (!appender->Stop()) 77 | return false; 78 | 79 | // Stop logging processors 80 | for (auto& processor : _processors) 81 | if (processor) 82 | if (!processor->Stop()) 83 | return false; 84 | 85 | _started = false; 86 | 87 | return true; 88 | } 89 | 90 | bool Processor::FilterRecord(Record& record) 91 | { 92 | // Filter the given logging record 93 | for (auto& filter : _filters) 94 | if (filter && filter->IsStarted() && !filter->FilterRecord(record)) 95 | return false; 96 | 97 | return true; 98 | } 99 | 100 | bool Processor::ProcessRecord(Record& record) 101 | { 102 | // Check if the logging processor started 103 | if (!IsStarted()) 104 | return true; 105 | 106 | // Filter the given logging record 107 | if (!FilterRecord(record)) 108 | return true; 109 | 110 | // Layout the given logging record 111 | if (_layout && _layout->IsStarted()) 112 | _layout->LayoutRecord(record); 113 | 114 | // Append the given logging record 115 | for (auto& appender : _appenders) 116 | if (appender && appender->IsStarted()) 117 | appender->AppendRecord(record); 118 | 119 | // Process the given logging record with sub processors 120 | for (auto& processor : _processors) 121 | if (processor && processor->IsStarted() && !processor->ProcessRecord(record)) 122 | return false; 123 | 124 | return true; 125 | } 126 | 127 | void Processor::Flush() 128 | { 129 | // Check if the logging processor started 130 | if (!IsStarted()) 131 | return; 132 | 133 | // Flush all appenders 134 | for (auto& appender : _appenders) 135 | if (appender && appender->IsStarted()) 136 | appender->Flush(); 137 | 138 | // Flush all sub processors 139 | for (auto& processor : _processors) 140 | if (processor && processor->IsStarted()) 141 | processor->Flush(); 142 | } 143 | 144 | } // namespace CppLogging 145 | -------------------------------------------------------------------------------- /performance/processor_async.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 01.08.2016 3 | // 4 | 5 | #include "benchmark/cppbenchmark.h" 6 | 7 | #include "logging/config.h" 8 | #include "logging/logger.h" 9 | 10 | using namespace CppLogging; 11 | 12 | const auto settings = CppBenchmark::Settings().ThreadsRange(1, 8, [](int from, int to, int& result) { int r = result; result *= 2; return r; }); 13 | 14 | class LogConfigFixture 15 | { 16 | protected: 17 | LogConfigFixture() 18 | { 19 | auto async_wait_null_sink = std::make_shared(std::make_shared()); 20 | async_wait_null_sink->appenders().push_back(std::make_shared()); 21 | Config::ConfigLogger("async-wait-null", async_wait_null_sink); 22 | 23 | auto async_wait_free_null_sink = std::make_shared(std::make_shared()); 24 | async_wait_free_null_sink->appenders().push_back(std::make_shared()); 25 | Config::ConfigLogger("async-wait-free-null", async_wait_free_null_sink); 26 | 27 | auto async_wait_binary_sink = std::make_shared(std::make_shared()); 28 | async_wait_binary_sink->appenders().push_back(std::make_shared()); 29 | Config::ConfigLogger("async-wait-binary", async_wait_binary_sink); 30 | 31 | auto async_wait_free_binary_sink = std::make_shared(std::make_shared()); 32 | async_wait_free_binary_sink->appenders().push_back(std::make_shared()); 33 | Config::ConfigLogger("async-wait-free-binary", async_wait_free_binary_sink); 34 | 35 | auto async_wait_text_sink = std::make_shared(std::make_shared()); 36 | async_wait_text_sink->appenders().push_back(std::make_shared()); 37 | Config::ConfigLogger("async-wait-text", async_wait_text_sink); 38 | 39 | auto async_wait_free_text_sink = std::make_shared(std::make_shared()); 40 | async_wait_free_text_sink->appenders().push_back(std::make_shared()); 41 | Config::ConfigLogger("async-wait-free-text", async_wait_free_text_sink); 42 | 43 | Config::Startup(); 44 | } 45 | }; 46 | 47 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "AsyncWaitProcessor-null", settings) 48 | { 49 | thread_local Logger logger = Config::CreateLogger("async-wait-null"); 50 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 51 | } 52 | 53 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "AsyncWaitFreeProcessor-null", settings) 54 | { 55 | thread_local Logger logger = Config::CreateLogger("async-wait-free-null"); 56 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 57 | } 58 | 59 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "AsyncWaitProcessor-binary", settings) 60 | { 61 | thread_local Logger logger = Config::CreateLogger("async-wait-binary"); 62 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 63 | } 64 | 65 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "AsyncWaitFreeProcessor-binary", settings) 66 | { 67 | thread_local Logger logger = Config::CreateLogger("async-wait-free-binary"); 68 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 69 | } 70 | 71 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "AsyncWaitProcessor-text", settings) 72 | { 73 | thread_local Logger logger = Config::CreateLogger("async-wait-text"); 74 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 75 | } 76 | 77 | BENCHMARK_THREADS_FIXTURE(LogConfigFixture, "AsyncWaitFreeProcessor-text", settings) 78 | { 79 | thread_local Logger logger = Config::CreateLogger("async-wait-free-text"); 80 | logger.Info("Test {}.{}.{} message", context.metrics().total_operations(), context.metrics().total_operations() / 1000.0, context.name()); 81 | } 82 | 83 | BENCHMARK_MAIN() 84 | -------------------------------------------------------------------------------- /include/logging/logger.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file logger.h 3 | \brief Logger interface definition 4 | \author Ivan Shynkarenka 5 | \date 29.07.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_LOGGER_H 10 | #define CPPLOGGING_LOGGER_H 11 | 12 | #include "logging/processors.h" 13 | 14 | namespace CppLogging { 15 | 16 | //! Logger interface 17 | /*! 18 | Logger is a main interface to produce logging records with a desired level. 19 | 20 | Thread-safe or not thread-safe depends on the current logging sink. 21 | */ 22 | class Logger 23 | { 24 | friend class Config; 25 | 26 | public: 27 | //! Initialize default logger 28 | Logger(); 29 | //! Initialize named logger 30 | /*! 31 | \param name - Logger name 32 | */ 33 | explicit Logger(const std::string& name); 34 | Logger(const Logger&) = default; 35 | Logger(Logger&&) = default; 36 | ~Logger(); 37 | 38 | Logger& operator=(const Logger&) = default; 39 | Logger& operator=(Logger&&) = default; 40 | 41 | //! Log debug message 42 | /*! 43 | Will log only in debug mode! 44 | 45 | \param message - Debug message 46 | */ 47 | void Debug(std::string_view message) const { Debug("{}", message); } 48 | //! Log debug message with format arguments 49 | /*! 50 | Will log only in debug mode! 51 | 52 | \param message - Debug message 53 | \param args - Format arguments 54 | */ 55 | template 56 | void Debug(fmt::format_string message, T&&... args) const; 57 | 58 | //! Log information message 59 | /*! 60 | \param message - Information message 61 | */ 62 | void Info(std::string_view message) const { Info("{}", message); } 63 | //! Log information message with format arguments 64 | /*! 65 | \param message - Information message 66 | \param args - Format arguments 67 | */ 68 | template 69 | void Info(fmt::format_string message, T&&... args) const; 70 | 71 | //! Log warning message 72 | /*! 73 | \param message - Warning message 74 | */ 75 | void Warn(std::string_view message) const { Warn("{}", message); } 76 | //! Log warning message with format arguments 77 | /*! 78 | \param message - Warning message 79 | \param args - Format arguments 80 | */ 81 | template 82 | void Warn(fmt::format_string message, T&&... args) const; 83 | 84 | //! Log error message 85 | /*! 86 | \param message - Error message 87 | */ 88 | void Error(std::string_view message) const { Error("{}", message); } 89 | //! Log error message with format arguments 90 | /*! 91 | \param message - Error message 92 | \param args - Format arguments 93 | */ 94 | template 95 | void Error(fmt::format_string message, T&&... args) const; 96 | 97 | //! Log fatal message 98 | /*! 99 | \param message - Fatal message 100 | */ 101 | void Fatal(std::string_view message) const { Fatal("{}", message); } 102 | //! Log fatal message with format arguments 103 | /*! 104 | \param message - Fatal message 105 | \param args - Format arguments 106 | */ 107 | template 108 | void Fatal(fmt::format_string message, T&&... args) const; 109 | 110 | //! Flush the current logger 111 | void Flush(); 112 | 113 | //! Update the current logger sink by taking the most recent one from configuration 114 | void Update(); 115 | 116 | private: 117 | std::string _name; 118 | std::shared_ptr _sink; 119 | 120 | //! Initialize logger 121 | /*! 122 | \param name - Logger name 123 | \param sink - Logger sink processor 124 | */ 125 | explicit Logger(const std::string& name, const std::shared_ptr& sink); 126 | 127 | //! Log the given message with a given level and format arguments list 128 | /*! 129 | \param level - Logging level 130 | \param format - Format flag 131 | \param message - Logging message 132 | \param args - Format arguments list 133 | */ 134 | template 135 | void Log(Level level, bool format, fmt::format_string message, T&&... args) const; 136 | }; 137 | 138 | } // namespace CppLogging 139 | 140 | #include "logger.inl" 141 | 142 | /*! \example default.cpp Default logger example */ 143 | /*! \example format.cpp Format logger example */ 144 | 145 | #endif // CPPLOGGING_LOGGER_H 146 | -------------------------------------------------------------------------------- /scripts/hashlog-map/hashlog-map.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Hashlog map generate tool is used to parse C++ source files for logging format messages 6 | and create the corresponding .hashlog binary file 7 | """ 8 | 9 | import codecs 10 | import glob 11 | import os 12 | import re 13 | import struct 14 | import sys 15 | 16 | __author__ = "Ivan Shynkarenka" 17 | __email__ = "chronoxor@gmail.com" 18 | __license__ = "MIT License" 19 | __url__ = "https://github.com/chronoxor/CppLogging/scripts/hashlog-map" 20 | __version__ = "1.5.0.0" 21 | 22 | 23 | class HashLogContext(object): 24 | def __init__(self, path): 25 | self.hash = dict() 26 | self.path = os.path.abspath(path) 27 | 28 | def hash_fnv1a_32(self, message): 29 | FNV_PRIME = 16777619 30 | OFFSET_BASIS = 2166136261 31 | hash = OFFSET_BASIS 32 | for char in message: 33 | hash ^= ord(char) 34 | hash *= FNV_PRIME 35 | hash %= 2**32 36 | return hash 37 | 38 | def discover(self, path): 39 | path = os.path.abspath(path) 40 | path = os.path.join(path, "**") 41 | 42 | # Recursive discover source files of required extensions 43 | sources = [] 44 | for extension in ["*.h", "*.inl", "*.cpp"]: 45 | sources.extend(glob.glob(os.path.join(path, extension), recursive=True)) 46 | 47 | # Find logging messages using pattern match 48 | pattern = re.compile('((Debug)|(Info)|(Warn)|(Error)|(Fatal))\\("(.*)", ') 49 | for source in sources: 50 | for i, line in enumerate(open(source)): 51 | for match in re.finditer(pattern, line): 52 | message = codecs.decode(match.group(7), "unicode_escape") 53 | hash = self.hash_fnv1a_32(message) 54 | if hash not in self.hash: 55 | print('Discovered logging message: "%s" with hash = 0x%08X' % (message, hash)) 56 | self.hash[hash] = message 57 | elif message != self.hash[hash]: 58 | print("Collision detected!", file=sys.stderr) 59 | print('Previous logging message: "%s" with hash = 0x%08X' % (self.hash[hash], hash), file=sys.stderr) 60 | print('Conflict logging message: "%s" with hash = 0x%08X' % (message, hash), file=sys.stderr) 61 | raise Exception("Collision detected!") 62 | 63 | def generate(self, path): 64 | path = os.path.abspath(path) 65 | path = os.path.join(path, ".hashlog") 66 | 67 | # Write the current hash map into the binary file 68 | print("Generating .hashlog file", end="...") 69 | with open(path, "wb") as hashlog: 70 | hashlog.write(struct.pack(" 17 | inline AsyncWaitFreeQueue::AsyncWaitFreeQueue(size_t capacity) : _capacity(capacity), _mask(capacity - 1), _buffer(new Node[capacity]), _head(0), _tail(0) 18 | { 19 | assert((capacity > 1) && "Ring queue capacity must be greater than one!"); 20 | assert(((capacity & (capacity - 1)) == 0) && "Ring queue capacity must be a power of two!"); 21 | 22 | memset(_pad0, 0, sizeof(cache_line_pad)); 23 | memset(_pad1, 0, sizeof(cache_line_pad)); 24 | memset(_pad2, 0, sizeof(cache_line_pad)); 25 | memset(_pad3, 0, sizeof(cache_line_pad)); 26 | 27 | // Populate the sequence initial values 28 | for (size_t i = 0; i < capacity; ++i) 29 | _buffer[i].sequence.store(i, std::memory_order_relaxed); 30 | } 31 | 32 | template 33 | inline size_t AsyncWaitFreeQueue::size() const noexcept 34 | { 35 | const size_t head = _head.load(std::memory_order_acquire); 36 | const size_t tail = _tail.load(std::memory_order_acquire); 37 | 38 | return head - tail; 39 | } 40 | 41 | template 42 | inline bool AsyncWaitFreeQueue::Enqueue(Record& record) 43 | { 44 | size_t head_sequence = _head.load(std::memory_order_relaxed); 45 | 46 | for (;;) 47 | { 48 | Node* node = &_buffer[head_sequence & _mask]; 49 | size_t node_sequence = node->sequence.load(std::memory_order_acquire); 50 | 51 | // If node sequence and head sequence are the same then it means this slot is empty 52 | int64_t diff = (int64_t)node_sequence - (int64_t)head_sequence; 53 | if (diff == 0) 54 | { 55 | // Claim our spot by moving head. If head isn't the same 56 | // as we last checked then that means someone beat us to 57 | // the punch weak compare is faster, but can return spurious 58 | // results which in this instance is OK, because it's in the loop 59 | if (_head.compare_exchange_weak(head_sequence, head_sequence + 1, std::memory_order_relaxed)) 60 | { 61 | // Store and swap the item value 62 | swap(node->value, record); 63 | 64 | // Increment the sequence so that the tail knows it's accessible 65 | node->sequence.store(head_sequence + 1, std::memory_order_release); 66 | return true; 67 | } 68 | } 69 | else if (diff < 0) 70 | { 71 | // If node sequence is less than head sequence then it means this slot is full 72 | // and therefore buffer is full 73 | return false; 74 | } 75 | else 76 | { 77 | // Under normal circumstances this branch should never be taken 78 | head_sequence = _head.load(std::memory_order_relaxed); 79 | } 80 | } 81 | 82 | // Never happens... 83 | return false; 84 | } 85 | 86 | template 87 | inline bool AsyncWaitFreeQueue::Dequeue(Record& record) 88 | { 89 | size_t tail_sequence = _tail.load(std::memory_order_relaxed); 90 | 91 | for (;;) 92 | { 93 | Node* node = &_buffer[tail_sequence & _mask]; 94 | size_t node_sequence = node->sequence.load(std::memory_order_acquire); 95 | 96 | // If node sequence and head sequence are the same then it means this slot is empty 97 | int64_t diff = (int64_t)node_sequence - (int64_t)(tail_sequence + 1); 98 | if (diff == 0) 99 | { 100 | // Claim our spot by moving head. If head isn't the same 101 | // as we last checked then that means someone beat us to 102 | // the punch weak compare is faster, but can return spurious 103 | // results which in this instance is OK, because it's in the loop 104 | if (_tail.compare_exchange_weak(tail_sequence, tail_sequence + 1, std::memory_order_relaxed)) 105 | { 106 | // Swap and get the item value 107 | swap(record, node->value); 108 | 109 | // Set the sequence to what the head sequence should be next time around 110 | node->sequence.store(tail_sequence + _mask + 1, std::memory_order_release); 111 | return true; 112 | } 113 | } 114 | else if (diff < 0) 115 | { 116 | // If seq is less than head seq then it means this slot is full and therefore the buffer is full 117 | return false; 118 | } 119 | else 120 | { 121 | // Under normal circumstances this branch should never be taken 122 | tail_sequence = _tail.load(std::memory_order_relaxed); 123 | } 124 | } 125 | 126 | // Never happens... 127 | return false; 128 | } 129 | 130 | } // namespace CppLogging 131 | 132 | #if defined(_MSC_VER) 133 | #pragma warning(pop) 134 | #endif 135 | -------------------------------------------------------------------------------- /source/logging/processors/async_wait_processor.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file async_wait_processor.cpp 3 | \brief Asynchronous wait logging processor implementation 4 | \author Ivan Shynkarenka 5 | \date 01.08.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/processors/async_wait_processor.h" 10 | 11 | #include "errors/fatal.h" 12 | #include "threads/thread.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | AsyncWaitProcessor::AsyncWaitProcessor(const std::shared_ptr& layout, bool auto_start, size_t capacity, size_t initial, const std::function& on_thread_initialize, const std::function& on_thread_clenup) 19 | : Processor(layout), 20 | _queue(capacity, initial), 21 | _on_thread_initialize(on_thread_initialize), 22 | _on_thread_clenup(on_thread_clenup) 23 | { 24 | _started = false; 25 | 26 | // Start the logging processor 27 | if (auto_start) 28 | Start(); 29 | } 30 | 31 | AsyncWaitProcessor::~AsyncWaitProcessor() 32 | { 33 | // Stop the logging processor 34 | if (IsStarted()) 35 | Stop(); 36 | } 37 | 38 | bool AsyncWaitProcessor::Start() 39 | { 40 | bool started = IsStarted(); 41 | 42 | if (!Processor::Start()) 43 | return false; 44 | 45 | if (!started) 46 | { 47 | // Start processing thread 48 | _thread = CppCommon::Thread::Start([this]() { ProcessThread(_on_thread_initialize, _on_thread_clenup); }); 49 | } 50 | 51 | return true; 52 | } 53 | 54 | bool AsyncWaitProcessor::Stop() 55 | { 56 | if (IsStarted()) 57 | { 58 | // Thread local stop operation record 59 | thread_local Record stop; 60 | 61 | // Enqueue stop operation record 62 | stop.timestamp = 0; 63 | EnqueueRecord(stop); 64 | 65 | // Wait for processing thread 66 | _thread.join(); 67 | } 68 | 69 | return Processor::Stop(); 70 | } 71 | 72 | bool AsyncWaitProcessor::ProcessRecord(Record& record) 73 | { 74 | // Check if the logging processor started 75 | if (!IsStarted()) 76 | return true; 77 | 78 | // Enqueue the given logger record 79 | return EnqueueRecord(record); 80 | } 81 | 82 | bool AsyncWaitProcessor::EnqueueRecord(Record& record) 83 | { 84 | // Try to enqueue the given logger record 85 | return _queue.Enqueue(record); 86 | } 87 | 88 | void AsyncWaitProcessor::ProcessThread(const std::function& on_thread_initialize, const std::function& on_thread_clenup) 89 | { 90 | // Call the thread initialize handler 91 | assert((on_thread_initialize) && "Thread initialize handler must be valid!"); 92 | if (on_thread_initialize) 93 | on_thread_initialize(); 94 | 95 | try 96 | { 97 | // Thread local logger records to process 98 | thread_local std::vector records; 99 | thread_local uint64_t previous = 0; 100 | 101 | // Reserve initial space for logging records 102 | records.reserve(_queue.capacity()); 103 | 104 | while (_started) 105 | { 106 | // Dequeue the next logging record 107 | if (!_queue.Dequeue(records)) 108 | return; 109 | 110 | // Current timestamp 111 | uint64_t current = 0; 112 | 113 | // Process all logging records 114 | for (auto& record : records) 115 | { 116 | // Handle stop operation record 117 | if (record.timestamp == 0) 118 | return; 119 | 120 | // Handle flush operation record 121 | if (record.timestamp == 1) 122 | { 123 | // Flush the logging processor 124 | Processor::Flush(); 125 | continue; 126 | } 127 | 128 | // Process logging record 129 | Processor::ProcessRecord(record); 130 | 131 | // Find the latest record timestamp 132 | if (record.timestamp > current) 133 | current = record.timestamp; 134 | } 135 | 136 | // Handle auto-flush period 137 | if (CppCommon::Timespan((int64_t)(current - previous)).seconds() > 1) 138 | { 139 | // Flush the logging processor 140 | Processor::Flush(); 141 | 142 | // Update the previous timestamp 143 | previous = current; 144 | } 145 | } 146 | } 147 | catch (const std::exception& ex) 148 | { 149 | fatality(ex); 150 | } 151 | catch (...) 152 | { 153 | fatality("Asynchronous wait logging processor terminated!"); 154 | } 155 | 156 | // Call the thread cleanup handler 157 | assert((on_thread_clenup) && "Thread cleanup handler must be valid!"); 158 | if (on_thread_clenup) 159 | on_thread_clenup(); 160 | } 161 | 162 | void AsyncWaitProcessor::Flush() 163 | { 164 | // Check if the logging processor started 165 | if (!IsStarted()) 166 | return; 167 | 168 | // Thread local flush operation record 169 | thread_local Record flush; 170 | 171 | // Enqueue flush operation record 172 | flush.timestamp = 1; 173 | EnqueueRecord(flush); 174 | } 175 | 176 | } // namespace CppLogging 177 | -------------------------------------------------------------------------------- /source/logging/appenders/minizip/crypt.h: -------------------------------------------------------------------------------- 1 | /* crypt.h -- base code for crypt/uncrypt ZIPfile 2 | 3 | 4 | Version 1.01e, February 12th, 2005 5 | 6 | Copyright (C) 1998-2005 Gilles Vollant 7 | 8 | This code is a modified version of crypting code in Infozip distribution 9 | 10 | The encryption/decryption parts of this source code (as opposed to the 11 | non-echoing password parts) were originally written in Europe. The 12 | whole source package can be freely distributed, including from the USA. 13 | (Prior to January 2000, re-export from the US was a violation of US law.) 14 | 15 | This encryption code is a direct transcription of the algorithm from 16 | Roger Schlafly, described by Phil Katz in the file appnote.txt. This 17 | file (appnote.txt) is distributed with the PKZIP program (even in the 18 | version without encryption capabilities). 19 | 20 | If you don't need crypting in your application, just define symbols 21 | NOCRYPT and NOUNCRYPT. 22 | 23 | This code support the "Traditional PKWARE Encryption". 24 | 25 | The new AES encryption added on Zip format by Winzip (see the page 26 | http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong 27 | Encryption is not supported. 28 | */ 29 | 30 | #define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) 31 | 32 | /*********************************************************************** 33 | * Return the next byte in the pseudo-random sequence 34 | */ 35 | static int decrypt_byte(unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { 36 | unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an 37 | * unpredictable manner on 16-bit systems; not a problem 38 | * with any known compiler so far, though */ 39 | 40 | (void)pcrc_32_tab; 41 | temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; 42 | return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); 43 | } 44 | 45 | /*********************************************************************** 46 | * Update the encryption keys with the next byte of plain text 47 | */ 48 | static int update_keys(unsigned long* pkeys, const z_crc_t* pcrc_32_tab, int c) { 49 | (*(pkeys+0)) = CRC32((*(pkeys+0)), c); 50 | (*(pkeys+1)) += (*(pkeys+0)) & 0xff; 51 | (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; 52 | { 53 | register int keyshift = (int)((*(pkeys+1)) >> 24); 54 | (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); 55 | } 56 | return c; 57 | } 58 | 59 | 60 | /*********************************************************************** 61 | * Initialize the encryption keys and the random header according to 62 | * the given password. 63 | */ 64 | static void init_keys(const char* passwd, unsigned long* pkeys, const z_crc_t* pcrc_32_tab) { 65 | *(pkeys+0) = 305419896L; 66 | *(pkeys+1) = 591751049L; 67 | *(pkeys+2) = 878082192L; 68 | while (*passwd != '\0') { 69 | update_keys(pkeys,pcrc_32_tab,(int)*passwd); 70 | passwd++; 71 | } 72 | } 73 | 74 | #define zdecode(pkeys,pcrc_32_tab,c) \ 75 | (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) 76 | 77 | #define zencode(pkeys,pcrc_32_tab,c,t) \ 78 | (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), (Byte)t^(c)) 79 | 80 | #ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED 81 | 82 | #define RAND_HEAD_LEN 12 83 | /* "last resort" source for second part of crypt seed pattern */ 84 | # ifndef ZCR_SEED2 85 | # define ZCR_SEED2 3141592654UL /* use PI as default pattern */ 86 | # endif 87 | 88 | static unsigned crypthead(const char* passwd, /* password string */ 89 | unsigned char* buf, /* where to write header */ 90 | int bufSize, 91 | unsigned long* pkeys, 92 | const z_crc_t* pcrc_32_tab, 93 | unsigned long crcForCrypting) { 94 | unsigned n; /* index in random header */ 95 | int t; /* temporary */ 96 | int c; /* random byte */ 97 | unsigned char header[RAND_HEAD_LEN-2]; /* random header */ 98 | static unsigned calls = 0; /* ensure different random header each time */ 99 | 100 | if (bufSize> 7) & 0xff; 115 | header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); 116 | } 117 | /* Encrypt random header (last two bytes is high word of crc) */ 118 | init_keys(passwd, pkeys, pcrc_32_tab); 119 | for (n = 0; n < RAND_HEAD_LEN-2; n++) 120 | { 121 | buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); 122 | } 123 | buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); 124 | buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); 125 | return n; 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /source/logging/processors/async_wait_free_processor.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | \file async_wait_free_processor.cpp 3 | \brief Asynchronous wait-free logging processor implementation 4 | \author Ivan Shynkarenka 5 | \date 01.08.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #include "logging/processors/async_wait_free_processor.h" 10 | 11 | #include "errors/fatal.h" 12 | #include "threads/thread.h" 13 | 14 | #include 15 | 16 | namespace CppLogging { 17 | 18 | AsyncWaitFreeProcessor::AsyncWaitFreeProcessor(const std::shared_ptr& layout, bool auto_start, size_t capacity, bool discard, const std::function& on_thread_initialize, const std::function& on_thread_clenup) 19 | : Processor(layout), 20 | _discard(discard), 21 | _queue(capacity), 22 | _on_thread_initialize(on_thread_initialize), 23 | _on_thread_clenup(on_thread_clenup) 24 | { 25 | _started = false; 26 | 27 | // Start the logging processor 28 | if (auto_start) 29 | Start(); 30 | } 31 | 32 | AsyncWaitFreeProcessor::~AsyncWaitFreeProcessor() 33 | { 34 | // Stop the logging processor 35 | if (IsStarted()) 36 | Stop(); 37 | } 38 | 39 | bool AsyncWaitFreeProcessor::Start() 40 | { 41 | bool started = IsStarted(); 42 | 43 | if (!Processor::Start()) 44 | return false; 45 | 46 | if (!started) 47 | { 48 | // Start processing thread 49 | _thread = CppCommon::Thread::Start([this]() { ProcessThread(_on_thread_initialize, _on_thread_clenup); }); 50 | } 51 | 52 | return true; 53 | } 54 | 55 | bool AsyncWaitFreeProcessor::Stop() 56 | { 57 | if (IsStarted()) 58 | { 59 | // Thread local stop operation record 60 | thread_local Record stop; 61 | 62 | // Enqueue stop operation record 63 | stop.timestamp = 0; 64 | EnqueueRecord(false, stop); 65 | 66 | // Wait for processing thread 67 | _thread.join(); 68 | } 69 | 70 | return Processor::Stop(); 71 | } 72 | 73 | bool AsyncWaitFreeProcessor::ProcessRecord(Record& record) 74 | { 75 | // Check if the logging processor started 76 | if (!IsStarted()) 77 | return true; 78 | 79 | // Enqueue the given logger record 80 | return EnqueueRecord(_discard, record); 81 | } 82 | 83 | bool AsyncWaitFreeProcessor::EnqueueRecord(bool discard, Record& record) 84 | { 85 | // Try to enqueue the given logger record 86 | if (!_queue.Enqueue(record)) 87 | { 88 | // If the overflow policy is discard logging record, return immediately 89 | if (discard) 90 | return false; 91 | 92 | // If the overflow policy is blocking then yield if the queue is full 93 | while (!_queue.Enqueue(record)) 94 | CppCommon::Thread::Yield(); 95 | } 96 | 97 | return true; 98 | } 99 | 100 | void AsyncWaitFreeProcessor::ProcessThread(const std::function& on_thread_initialize, const std::function& on_thread_clenup) 101 | { 102 | // Call the thread initialize handler 103 | assert((on_thread_initialize) && "Thread initialize handler must be valid!"); 104 | if (on_thread_initialize) 105 | on_thread_initialize(); 106 | 107 | try 108 | { 109 | // Thread local logger record to process 110 | thread_local Record record; 111 | thread_local uint64_t previous = CppCommon::Timestamp::utc(); 112 | 113 | while (_started) 114 | { 115 | // Try to dequeue the next logging record 116 | bool empty = !_queue.Dequeue(record); 117 | 118 | // Current timestamp 119 | uint64_t current; 120 | 121 | if (!empty) 122 | { 123 | // Handle stop operation record 124 | if (record.timestamp == 0) 125 | return; 126 | 127 | // Handle flush operation record 128 | if (record.timestamp == 1) 129 | { 130 | // Flush the logging processor 131 | Processor::Flush(); 132 | continue; 133 | } 134 | 135 | // Process logging record 136 | Processor::ProcessRecord(record); 137 | 138 | // Update the current timestamp 139 | current = record.timestamp; 140 | } 141 | else 142 | { 143 | // Update the current timestamp 144 | current = CppCommon::Timestamp::utc(); 145 | } 146 | 147 | // Handle auto-flush period 148 | if (CppCommon::Timespan((int64_t)(current - previous)).seconds() > 1) 149 | { 150 | // Flush the logging processor 151 | Processor::Flush(); 152 | 153 | // Update the previous timestamp 154 | previous = current; 155 | } 156 | 157 | // Sleep for a while if the queue was empty 158 | if (empty) 159 | CppCommon::Thread::Sleep(100); 160 | } 161 | } 162 | catch (const std::exception& ex) 163 | { 164 | fatality(ex); 165 | } 166 | catch (...) 167 | { 168 | fatality("Asynchronous wait-free logging processor terminated!"); 169 | } 170 | 171 | // Call the thread cleanup handler 172 | assert((on_thread_clenup) && "Thread cleanup handler must be valid!"); 173 | if (on_thread_clenup) 174 | on_thread_clenup(); 175 | } 176 | 177 | void AsyncWaitFreeProcessor::Flush() 178 | { 179 | // Check if the logging processor started 180 | if (!IsStarted()) 181 | return; 182 | 183 | // Thread local flush operation record 184 | thread_local Record flush; 185 | 186 | // Enqueue flush operation record 187 | flush.timestamp = 1; 188 | EnqueueRecord(false, flush); 189 | } 190 | 191 | } // namespace CppLogging 192 | -------------------------------------------------------------------------------- /tests/test_record_format.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Ivan Shynkarenka on 18.09.2016 3 | // 4 | 5 | #include "test.h" 6 | 7 | #include "logging/record.h" 8 | 9 | using namespace CppLogging; 10 | 11 | namespace { 12 | 13 | class Date 14 | { 15 | public: 16 | Date(int year, int month, int day) : _year(year), _month(month), _day(day) {} 17 | 18 | int year() const { return _year; } 19 | int month() const { return _month; } 20 | int day() const { return _day; } 21 | 22 | friend CppLogging::Record& operator<<(CppLogging::Record& record, const Date& date) 23 | { return record.StoreCustomFormat("{}-{}-{}", date._year, date._month, date._day); } 24 | 25 | private: 26 | int _year, _month, _day; 27 | }; 28 | 29 | } // namespace 30 | 31 | template <> 32 | struct fmt::formatter 33 | { 34 | constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } 35 | 36 | auto format(const Date& date, format_context& ctx) const -> decltype(ctx.out()) 37 | { 38 | fmt::format_to(ctx.out(), "{}-{}-{}", date.year(), date.month(), date.day()); 39 | return ctx.out(); 40 | } 41 | }; 42 | 43 | namespace { 44 | 45 | class DateTime 46 | { 47 | public: 48 | DateTime(Date date, int hours, int minutes, int seconds) : _date(date), _hours(hours), _minutes(minutes), _seconds(seconds) {} 49 | 50 | Date date() const { return _date; } 51 | int hours() const { return _hours; } 52 | int minutes() const { return _minutes; } 53 | int seconds() const { return _seconds; } 54 | 55 | friend CppLogging::Record& operator<<(CppLogging::Record& record, const DateTime& datetime) 56 | { 57 | const size_t begin = record.StoreListBegin(); 58 | record.StoreList(datetime._date); 59 | record.StoreList(' '); 60 | record.StoreList(datetime._hours); 61 | record.StoreList(':'); 62 | record.StoreList(datetime._minutes); 63 | record.StoreList(':'); 64 | record.StoreList(datetime._seconds); 65 | return record.StoreListEnd(begin); 66 | } 67 | 68 | private: 69 | Date _date; 70 | int _hours, _minutes, _seconds; 71 | }; 72 | 73 | } // namespace 74 | 75 | template <> 76 | struct fmt::formatter 77 | { 78 | constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } 79 | 80 | auto format(const DateTime& datetime, format_context& ctx) const -> decltype(ctx.out()) 81 | { 82 | fmt::format_to(ctx.out(), "{} {}:{}:{}", datetime.date(), datetime.hours(), datetime.minutes(), datetime.seconds()); 83 | return ctx.out(); 84 | } 85 | }; 86 | 87 | namespace { 88 | 89 | template 90 | std::string format(fmt::format_string pattern, T&&... args) 91 | { 92 | Record record; 93 | record.Format(pattern, std::forward(args)...); 94 | return record.message; 95 | } 96 | 97 | template 98 | std::string store(fmt::format_string pattern, T&&... args) 99 | { 100 | Record record; 101 | record.StoreFormat(pattern, std::forward(args)...); 102 | record.message = record.RestoreFormat(); 103 | return record.message; 104 | } 105 | 106 | } // namespace 107 | 108 | TEST_CASE("Format message", "[CppLogging]") 109 | { 110 | REQUIRE(format("no arguments") == "no arguments"); 111 | REQUIRE(format("{0}, {1}, {2}", -1, 0, 1) == "-1, 0, 1"); 112 | REQUIRE(format("{0}, {1}, {2}", 'a', 'b', 'c') == "a, b, c"); 113 | REQUIRE(format("{}, {}, {}", 'a', 'b', 'c') == "a, b, c"); 114 | REQUIRE(format("{2}, {1}, {0}", 'a', 'b', 'c') == "c, b, a"); 115 | REQUIRE(format("{0}{1}{0}", "abra", "cad") == "abracadabra"); 116 | REQUIRE(format("{:<30}", "left aligned") == "left aligned "); 117 | REQUIRE(format("{:>30}", "right aligned") == " right aligned"); 118 | REQUIRE(format("{:^30}", "centered") == " centered "); 119 | REQUIRE(format("{:*^30}", "centered") == "***********centered***********"); 120 | REQUIRE(format("{:+f}; {:+f}", 3.14, -3.14) == "+3.140000; -3.140000"); 121 | REQUIRE(format("{: f}; {: f}", 3.14, -3.14) == " 3.140000; -3.140000"); 122 | REQUIRE(format("{:-f}; {:-f}", 3.14, -3.14) == "3.140000; -3.140000"); 123 | REQUIRE(format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42) == "int: 42; hex: 2a; oct: 52; bin: 101010"); 124 | REQUIRE(format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42) == "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"); 125 | REQUIRE(format("The date is {}", Date(2012, 12, 9)) == "The date is 2012-12-9"); 126 | REQUIRE(format("The datetime is {}", DateTime(Date(2012, 12, 9), 13, 15, 57)) == "The datetime is 2012-12-9 13:15:57"); 127 | REQUIRE(format("Elapsed time: {s:.2f} seconds", "s"_a = 1.23) == "Elapsed time: 1.23 seconds"); 128 | } 129 | 130 | TEST_CASE("Store message", "[CppLogging]") 131 | { 132 | REQUIRE(store("no arguments") == "no arguments"); 133 | REQUIRE(store("{0}, {1}, {2}", -1, 0, 1) == "-1, 0, 1"); 134 | REQUIRE(store("{0}, {1}, {2}", 'a', 'b', 'c') == "a, b, c"); 135 | REQUIRE(store("{}, {}, {}", 'a', 'b', 'c') == "a, b, c"); 136 | REQUIRE(store("{2}, {1}, {0}", 'a', 'b', 'c') == "c, b, a"); 137 | REQUIRE(store("{0}{1}{0}", "abra", "cad") == "abracadabra"); 138 | REQUIRE(store("{:<30}", "left aligned") == "left aligned "); 139 | REQUIRE(store("{:>30}", "right aligned") == " right aligned"); 140 | REQUIRE(store("{:^30}", "centered") == " centered "); 141 | REQUIRE(store("{:*^30}", "centered") == "***********centered***********"); 142 | REQUIRE(store("{:+f}; {:+f}", 3.14, -3.14) == "+3.140000; -3.140000"); 143 | REQUIRE(store("{: f}; {: f}", 3.14, -3.14) == " 3.140000; -3.140000"); 144 | REQUIRE(store("{:-f}; {:-f}", 3.14, -3.14) == "3.140000; -3.140000"); 145 | REQUIRE(store("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42) == "int: 42; hex: 2a; oct: 52; bin: 101010"); 146 | REQUIRE(store("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42) == "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"); 147 | REQUIRE(store("The date is {}", Date(2012, 12, 9)) == "The date is 2012-12-9"); 148 | REQUIRE(store("The datetime is {}", DateTime(Date(2012, 12, 9), 13, 15, 57)) == "The datetime is 2012-12-9 13:15:57"); 149 | REQUIRE(store("Elapsed time: {s:.2f} seconds", "s"_a = 1.23) == "Elapsed time: 1.23 seconds"); 150 | } 151 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | # Global properties 4 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 5 | 6 | # Project name 7 | project(cpplogging) 8 | 9 | # Doxygen 10 | find_package(Doxygen) 11 | if(DOXYGEN_FOUND) 12 | set(DOXYGEN "doxygen") 13 | if(NOT TARGET ${DOXYGEN}) 14 | add_custom_command(OUTPUT "Doxyfile" COMMAND ${DOXYGEN_EXECUTABLE} "Doxyfile" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/documents") 15 | add_custom_target(${DOXYGEN} DEPENDS "Doxyfile") 16 | set_target_properties(${DOXYGEN} PROPERTIES FOLDER "doxygen") 17 | endif() 18 | endif() 19 | 20 | # CMake module path 21 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 22 | 23 | # Compiler features 24 | include(SetCompilerFeatures) 25 | include(SetCompilerWarnings) 26 | include(SetPlatformFeatures) 27 | include(SystemInformation) 28 | 29 | # Modules 30 | add_subdirectory("modules") 31 | 32 | # Link libraries 33 | list(APPEND LINKLIBS cppcommon) 34 | 35 | # Support zlib/contrib/minizip 36 | file(GLOB_RECURSE MINIZIP_FILES "source/logging/appenders/minizip/*.c") 37 | if(NOT WIN32) 38 | list(REMOVE_ITEM MINIZIP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/source/logging/appenders/minizip/iowin32.c") 39 | endif() 40 | 41 | # System directories 42 | include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/modules") 43 | 44 | # Library 45 | file(GLOB_RECURSE LIB_HEADER_FILES "include/*.h" "source/*.h") 46 | file(GLOB_RECURSE LIB_INLINE_FILES "include/*.inl" "source/*.inl") 47 | file(GLOB_RECURSE LIB_SOURCE_FILES "include/*.cpp" "source/*.cpp") 48 | add_library(cpplogging ${LIB_HEADER_FILES} ${LIB_INLINE_FILES} ${LIB_SOURCE_FILES} ${MINIZIP_FILES}) 49 | if(MSVC) 50 | # C4067: unexpected tokens following preprocessor directive - expected a newline 51 | # C4131: 'function' : uses old-style declarator 52 | # C4189: 'identifier' : local variable is initialized but not referenced 53 | # C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data 54 | # C4456: declaration of 'identifier' hides previous local declaration 55 | set_target_properties(cpplogging PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS} /wd4067 /wd4131 /wd4189 /wd4244 /wd4456" FOLDER "libraries") 56 | else() 57 | set_target_properties(cpplogging PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS} -Wno-shadow -Wno-unused-variable" FOLDER "libraries") 58 | endif() 59 | if(CYGWIN OR MINGW) 60 | target_compile_definitions(cpplogging PRIVATE USE_FILE32API=1) 61 | endif() 62 | target_include_directories(cpplogging PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") 63 | target_link_libraries(cpplogging ${LINKLIBS} zlib) 64 | list(APPEND INSTALL_TARGETS cpplogging) 65 | list(APPEND LINKLIBS cpplogging) 66 | 67 | # Additional module components: benchmarks, examples, plugins, tests, tools and install 68 | if(NOT CPPLOGGING_MODULE) 69 | 70 | # Examples 71 | file(GLOB EXAMPLE_HEADER_FILES "examples/*.h") 72 | file(GLOB EXAMPLE_INLINE_FILES "examples/*.inl") 73 | file(GLOB EXAMPLE_SOURCE_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/examples" "examples/*.cpp") 74 | foreach(EXAMPLE_SOURCE_FILE ${EXAMPLE_SOURCE_FILES}) 75 | string(REGEX REPLACE "(.*)\\.cpp" "\\1" EXAMPLE_NAME ${EXAMPLE_SOURCE_FILE}) 76 | set(EXAMPLE_TARGET "cpplogging-example-${EXAMPLE_NAME}") 77 | add_executable(${EXAMPLE_TARGET} ${EXAMPLE_HEADER_FILES} ${EXAMPLE_INLINE_FILES} "examples/${EXAMPLE_SOURCE_FILE}") 78 | set_target_properties(${EXAMPLE_TARGET} PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS}" FOLDER "examples") 79 | target_link_libraries(${EXAMPLE_TARGET} ${LINKLIBS}) 80 | list(APPEND INSTALL_TARGETS ${EXAMPLE_TARGET}) 81 | list(APPEND INSTALL_TARGETS_PDB ${EXAMPLE_TARGET}) 82 | endforeach() 83 | 84 | # Benchmarks 85 | file(GLOB BENCHMARK_HEADER_FILES "performance/*.h") 86 | file(GLOB BENCHMARK_INLINE_FILES "performance/*.inl") 87 | file(GLOB BENCHMARK_SOURCE_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/performance" "performance/*.cpp") 88 | foreach(BENCHMARK_SOURCE_FILE ${BENCHMARK_SOURCE_FILES}) 89 | string(REGEX REPLACE "(.*)\\.cpp" "\\1" BENCHMARK_NAME ${BENCHMARK_SOURCE_FILE}) 90 | set(BENCHMARK_TARGET "cpplogging-performance-${BENCHMARK_NAME}") 91 | add_executable(${BENCHMARK_TARGET} ${BENCHMARK_HEADER_FILES} ${BENCHMARK_INLINE_FILES} "performance/${BENCHMARK_SOURCE_FILE}") 92 | set_target_properties(${BENCHMARK_TARGET} PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS}" FOLDER "performance") 93 | target_link_libraries(${BENCHMARK_TARGET} ${LINKLIBS} cppbenchmark) 94 | list(APPEND INSTALL_TARGETS ${BENCHMARK_TARGET}) 95 | list(APPEND INSTALL_TARGETS_PDB ${BENCHMARK_TARGET}) 96 | endforeach() 97 | 98 | # Tools 99 | file(GLOB TOOLS_DIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/tools" "tools/*") 100 | foreach(TOOLS_DIR ${TOOLS_DIRS}) 101 | file(GLOB_RECURSE TOOLS_HEADER_FILES "tools/${TOOLS_DIR}/*.h") 102 | file(GLOB_RECURSE TOOLS_INLINE_FILES "tools/${TOOLS_DIR}/*.inl") 103 | file(GLOB_RECURSE TOOLS_SOURCE_FILES "tools/${TOOLS_DIR}/*.cpp") 104 | set(TOOL_TARGET "${TOOLS_DIR}") 105 | add_executable(${TOOL_TARGET} ${TOOLS_HEADER_FILES} ${TOOLS_INLINE_FILES} ${TOOLS_SOURCE_FILES}) 106 | set_target_properties(${TOOL_TARGET} PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS}" FOLDER "tools") 107 | target_link_libraries(${TOOL_TARGET} ${LINKLIBS} cpp-optparse) 108 | list(APPEND INSTALL_TARGETS ${TOOL_TARGET}) 109 | list(APPEND INSTALL_TARGETS_PDB ${TOOL_TARGET}) 110 | endforeach() 111 | 112 | # Tests 113 | file(GLOB TESTS_HEADER_FILES "tests/*.h") 114 | file(GLOB TESTS_INLINE_FILES "tests/*.inl") 115 | file(GLOB TESTS_SOURCE_FILES "tests/*.cpp") 116 | add_executable(cpplogging-tests ${TESTS_HEADER_FILES} ${TESTS_INLINE_FILES} ${TESTS_SOURCE_FILES}) 117 | set_target_properties(cpplogging-tests PROPERTIES COMPILE_FLAGS "${PEDANTIC_COMPILE_FLAGS}" FOLDER "tests") 118 | target_include_directories(cpplogging-tests PRIVATE Catch2) 119 | target_link_libraries(cpplogging-tests ${LINKLIBS} Catch2) 120 | list(APPEND INSTALL_TARGETS cpplogging-tests) 121 | list(APPEND INSTALL_TARGETS_PDB cpplogging-tests) 122 | 123 | # CTest 124 | enable_testing() 125 | add_test(cpplogging-tests cpplogging-tests --durations yes --order lex) 126 | 127 | # Install 128 | install(TARGETS ${INSTALL_TARGETS} 129 | RUNTIME DESTINATION "${PROJECT_SOURCE_DIR}/bin" 130 | LIBRARY DESTINATION "${PROJECT_SOURCE_DIR}/bin" 131 | ARCHIVE DESTINATION "${PROJECT_SOURCE_DIR}/bin") 132 | 133 | # Install *.pdb files 134 | if(MSVC) 135 | foreach(INSTALL_TARGET_PDB ${INSTALL_TARGETS_PDB}) 136 | install(FILES $ DESTINATION "${PROJECT_SOURCE_DIR}/bin") 137 | endforeach() 138 | endif() 139 | 140 | endif() 141 | -------------------------------------------------------------------------------- /include/logging/appenders/rolling_file_appender.h: -------------------------------------------------------------------------------- 1 | /*! 2 | \file rolling_file_appender.h 3 | \brief Rolling file appender definition 4 | \author Ivan Shynkarenka 5 | \date 12.09.2016 6 | \copyright MIT License 7 | */ 8 | 9 | #ifndef CPPLOGGING_APPENDERS_ROLLING_FILE_APPENDER_H 10 | #define CPPLOGGING_APPENDERS_ROLLING_FILE_APPENDER_H 11 | 12 | #include "logging/appender.h" 13 | 14 | #include "filesystem/filesystem.h" 15 | 16 | #include 17 | 18 | namespace CppLogging { 19 | 20 | //! Time rolling policy 21 | enum class TimeRollingPolicy 22 | { 23 | YEAR, //!< Year rolling policy 24 | MONTH, //!< Monthly rolling policy 25 | DAY, //!< Daily rolling policy 26 | HOUR, //!< Hour rolling policy 27 | MINUTE, //!< Minute rolling policy 28 | SECOND //!< Second rolling policy 29 | }; 30 | 31 | //! Stream output: Time rolling policy 32 | /*! 33 | \param stream - Output stream 34 | \param policy - Time rolling policy 35 | \return Output stream 36 | */ 37 | template 38 | TOutputStream& operator<<(TOutputStream& stream, TimeRollingPolicy policy); 39 | 40 | //! Rolling file appender 41 | /*! 42 | Rolling file appender writes the given logging record into the file 43 | and performs file rolling operation depends on the given policy. 44 | In case of any IO error this appender will lost the logging record, 45 | but try to recover from fail in a short interval of 100ms. 46 | 47 | Time-based rolling policy will create a new logging file to write 48 | into using a special pattern (contains date & time placeholders). 49 | 50 | Size-based rolling policy will create a new logging file to write 51 | when the current file size exceeded size limit. Logging backups 52 | are indexed and its count could be limited as well. 53 | 54 | It is possible to enable archivation of the logging backups in a 55 | background thread. 56 | 57 | Not thread-safe. 58 | */ 59 | class RollingFileAppender : public Appender 60 | { 61 | friend class SizePolicyImpl; 62 | friend class TimePolicyImpl; 63 | 64 | public: 65 | //! Initialize the rolling file appender with a time-based policy 66 | /*! 67 | Time-based policy composes logging filename from the given pattern 68 | using the following placeholders: 69 | - {UtcDateTime} / {LocalDateTime} - converted to the UTC/local date & time (e.g. "1997-07-16T192030Z" / "1997-07-16T192030+0100") 70 | - {UtcDate} / {LocalDate} - converted to the UTC/local date (e.g. "1997-07-16") 71 | - {UtcTime} / {LocalTime} - converted to the UTC/local time (e.g. "192030Z" / "192030+0100") 72 | - {UtcYear} / {LocalYear} - converted to the UTC/local four-digits year (e.g. "1997") 73 | - {UtcMonth} / {LocalMonth} - converted to the UTC/local two-digits month (e.g. "07") 74 | - {UtcDay} / {LocalDay} - converted to the UTC/local two-digits day (e.g. "16") 75 | - {UtcHour} / {LocalHour} - converted to the UTC/local two-digits hour (e.g. "19") 76 | - {UtcMinute} / {LocalMinute} - converted to the UTC/local two-digits minute (e.g. "20") 77 | - {UtcSecond} / {LocalSecond} - converted to the UTC/local two-digits second (e.g. "30") 78 | - {UtcTimezone} / {LocalTimezone} - converted to the UTC/local timezone suffix (e.g. "Z" / "+0100") 79 | 80 | \param path - Logging path 81 | \param policy - Time-based rolling policy (default is TimeRollingPolicy::DAY) 82 | \param pattern - Logging pattern (default is "{UtcDateTime}.log") 83 | \param archive - Archivation flag (default is false) 84 | \param truncate - Truncate flag (default is false) 85 | \param auto_flush - Auto-flush flag (default is false) 86 | \param auto_start - Auto-start flag (default is true) 87 | */ 88 | explicit RollingFileAppender(const CppCommon::Path& path, TimeRollingPolicy policy = TimeRollingPolicy::DAY, const std::string& pattern = "{UtcDateTime}.log", bool archive = false, bool truncate = false, bool auto_flush = false, bool auto_start = true); 89 | //! Initialize the rolling file appender with a size-based policy 90 | /*! 91 | Size-based policy for 5 backups works in a following way: 92 | 93 | example.log -> example.1.log 94 | example.1.log -> example.2.log 95 | example.2.log -> example.3.log 96 | example.3.log -> example.4.log 97 | example.4.log -> example.5.log 98 | example.5.log -> remove 99 | 100 | \param path - Logging path 101 | \param filename - Logging filename 102 | \param extension - Logging extension 103 | \param size - Rolling size limit in bytes (default is 100 megabytes) 104 | \param backups - Rolling backups count (default is 10) 105 | \param archive - Archivation flag (default is false) 106 | \param truncate - Truncate flag (default is false) 107 | \param auto_flush - Auto-flush flag (default is false) 108 | \param auto_start - Auto-start flag (default is true) 109 | */ 110 | explicit RollingFileAppender(const CppCommon::Path& path, const std::string& filename, const std::string& extension, size_t size = 104857600, size_t backups = 10, bool archive = false, bool truncate = false, bool auto_flush = false, bool auto_start = true); 111 | RollingFileAppender(const RollingFileAppender&) = delete; 112 | RollingFileAppender(RollingFileAppender&& appender) = delete; 113 | virtual ~RollingFileAppender(); 114 | 115 | RollingFileAppender& operator=(const RollingFileAppender&) = delete; 116 | RollingFileAppender& operator=(RollingFileAppender&& appender) = delete; 117 | 118 | // Implementation of Appender 119 | bool IsStarted() const noexcept override; 120 | bool Start() override; 121 | bool Stop() override; 122 | void AppendRecord(Record& record) override; 123 | void Flush() override; 124 | 125 | protected: 126 | //! Initialize archivation thread handler 127 | /*! 128 | This handler can be used to initialize priority or affinity of the archivation thread. 129 | */ 130 | virtual void onArchiveThreadInitialize() {} 131 | //! Cleanup archivation thread handler 132 | /*! 133 | This handler can be used to cleanup priority or affinity of the archivation thread. 134 | */ 135 | virtual void onArchiveThreadCleanup() {} 136 | 137 | private: 138 | class Impl; 139 | 140 | Impl& impl() noexcept { return reinterpret_cast(_storage); } 141 | const Impl& impl() const noexcept { return reinterpret_cast(_storage); } 142 | 143 | static const size_t StorageSize = 608; 144 | static const size_t StorageAlign = 8; 145 | alignas(StorageAlign) std::byte _storage[StorageSize]; 146 | }; 147 | 148 | } // namespace CppLogging 149 | 150 | #include "rolling_file_appender.inl" 151 | 152 | #endif // CPPLOGGING_APPENDERS_ROLLING_FILE_APPENDER_H 153 | --------------------------------------------------------------------------------