├── .appveyor.yml ├── .circleci └── config.yml ├── .cirrus.yml ├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── nuget.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── doc └── color-console.png ├── include └── plog │ ├── Appenders │ ├── AndroidAppender.h │ ├── ArduinoAppender.h │ ├── ColorConsoleAppender.h │ ├── ConsoleAppender.h │ ├── DebugOutputAppender.h │ ├── DynamicAppender.h │ ├── EventLogAppender.h │ ├── IAppender.h │ └── RollingFileAppender.h │ ├── Converters │ ├── NativeEOLConverter.h │ └── UTF8Converter.h │ ├── Formatters │ ├── CsvFormatter.h │ ├── FuncMessageFormatter.h │ ├── MessageOnlyFormatter.h │ └── TxtFormatter.h │ ├── Helpers │ ├── AscDump.h │ ├── HexDump.h │ └── PrintVar.h │ ├── Init.h │ ├── Initializers │ ├── ConsoleInitializer.h │ └── RollingFileInitializer.h │ ├── Log.h │ ├── Logger.h │ ├── Record.h │ ├── Severity.h │ ├── Util.h │ └── WinApi.h ├── library.json ├── plog.nuspec ├── plog.targets ├── samples ├── Android │ ├── AndroidJNI │ │ └── Main.cpp │ ├── AndroidNative │ │ └── Main.cpp │ └── CMakeLists.txt ├── Arduino │ ├── platformio.ini │ └── src │ │ └── main.cpp ├── AscDump │ ├── CMakeLists.txt │ └── Main.cpp ├── CMakeLists.txt ├── CXX11 │ ├── CMakeLists.txt │ └── Main.cpp ├── CXX17 │ ├── CMakeLists.txt │ └── Main.cpp ├── Chained │ ├── CMakeLists.txt │ ├── ChainedApp │ │ └── Main.cpp │ └── ChainedLib │ │ └── Main.cpp ├── ColorConsole │ ├── CMakeLists.txt │ └── Main.cpp ├── CustomAppender │ ├── CMakeLists.txt │ └── Main.cpp ├── CustomConverter │ ├── CMakeLists.txt │ └── Main.cpp ├── CustomFormatter │ ├── CMakeLists.txt │ └── Main.cpp ├── CustomType │ ├── CMakeLists.txt │ └── Main.cpp ├── DebugOutput │ ├── CMakeLists.txt │ └── Main.cpp ├── Demo │ ├── CMakeLists.txt │ ├── Customer.h │ ├── Main.cpp │ ├── MyClass.cpp │ └── MyClass.h ├── DisableLogging │ ├── CMakeLists.txt │ └── Main.cpp ├── DynamicAppender │ ├── CMakeLists.txt │ └── Main.cpp ├── EventLog │ ├── CMakeLists.txt │ └── Main.cpp ├── Facilities │ ├── CMakeLists.txt │ └── Main.cpp ├── FreeRTOS │ ├── CMakeLists.txt │ ├── freertos_config │ │ ├── CMakeLists.txt │ │ └── FreeRTOSConfig.h │ └── main.cpp ├── Hello │ ├── CMakeLists.txt │ └── Main.cpp ├── HexDump │ ├── CMakeLists.txt │ └── Main.cpp ├── Library │ ├── CMakeLists.txt │ ├── LibraryApp │ │ └── Main.cpp │ └── LibraryLib │ │ └── Lib.cpp ├── MultiAppender │ ├── CMakeLists.txt │ └── Main.cpp ├── MultiInstance │ ├── CMakeLists.txt │ └── Main.cpp ├── NotShared │ ├── CMakeLists.txt │ ├── NotSharedApp │ │ └── Main.cpp │ ├── NotSharedLib1 │ │ └── Main.cpp │ └── NotSharedLib2 │ │ └── Main.cpp ├── ObjectiveC │ ├── CMakeLists.txt │ └── Main.mm ├── Path │ ├── CMakeLists.txt │ └── Main.cpp ├── Performance │ ├── CMakeLists.txt │ └── Main.cpp ├── PrintVar │ ├── CMakeLists.txt │ └── Main.cpp ├── SetFileName │ ├── CMakeLists.txt │ └── Main.cpp ├── Shared │ ├── CMakeLists.txt │ ├── SharedApp │ │ └── Main.cpp │ ├── SharedLib1 │ │ └── Main.cpp │ └── SharedLib2 │ │ └── Main.cpp ├── SkipNativeEOL │ ├── CMakeLists.txt │ └── Main.cpp ├── UtcTime │ ├── CMakeLists.txt │ └── Main.cpp └── Utf8Everywhere │ ├── CMakeLists.txt │ └── Main.cpp └── test ├── CMakeLists.txt ├── CastToString.cpp ├── Common.h ├── Conditional.cpp ├── Main.cpp ├── NullCharPointer.cpp ├── Path.cpp ├── Printf.cpp ├── SimpleTypes.cpp ├── StdContainers.cpp ├── StdManipulators.cpp ├── StdStreamable.cpp ├── StringTypes.cpp ├── TestAppender.h └── doctest ├── 1.2.9 └── doctest.h └── 2.4.11 └── doctest.h /.appveyor.yml: -------------------------------------------------------------------------------- 1 | configuration: 2 | - Release 3 | 4 | environment: 5 | matrix: 6 | - generator: "Visual Studio 14 2015" 7 | - generator: "Visual Studio 14 2015 Win64" 8 | - generator: "Visual Studio 10 2010" 9 | - generator: "Visual Studio 10 2010 Win64" 10 | - generator: "MinGW Makefiles" 11 | dialect: mingw 12 | - generator: "MinGW Makefiles" 13 | dialect: mingw-w64 14 | 15 | matrix: 16 | fast_finish: true 17 | 18 | shallow_clone: true 19 | 20 | skip_commits: 21 | files: 22 | - .circleci/ 23 | - .github/ 24 | - .editorconfig 25 | - .gitignore 26 | - .cirrus.yml 27 | - LICENSE 28 | - plog.nuspec 29 | - plog.targets 30 | - README.md 31 | 32 | before_build: 33 | # Workaround for CMake not wanting sh.exe on PATH for MinGW 34 | - set PATH=%PATH:C:\Program Files\Git\usr\bin;=% 35 | - if "%dialect%"=="mingw" set PATH=c:\MinGW\bin;%PATH% 36 | - if "%dialect%"=="mingw-w64" set PATH=c:\msys64\mingw64\bin;%PATH% 37 | - cmake -H. -Bbuild -G"%generator%" -DCMAKE_BUILD_TYPE=%configuration% -DPLOG_BUILD_TESTS=1 38 | 39 | build_script: 40 | - if "%generator:~0,6%"=="Visual" set CMAKE_BUILD_FLAGS=--config %configuration% -- /m /v:m 41 | - if "%generator:~0,5%"=="MinGW" set CMAKE_BUILD_FLAGS=-- -j 42 | - cmake --build build %CMAKE_BUILD_FLAGS% 43 | 44 | test_script: 45 | - cd build && ctest -V 46 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | jobs: 3 | rtems: 4 | docker: 5 | - image: sergiusthebest/rtems-arm-rasberrypi-ci:latest 6 | steps: 7 | - checkout 8 | - run: cmake -H. -Bbuild -DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE && cd build && make -j 9 | platformio: 10 | docker: 11 | - image: ghcr.io/sergiusthebest/platformio-ci:latest 12 | steps: 13 | - checkout 14 | - run: pio run -d samples/Arduino 15 | android-ndk: 16 | docker: 17 | - image: sergiusthebest/android-ndk-ci:latest 18 | steps: 19 | - checkout 20 | - run: cmake -H. -Bbuild -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake && cd build && make -j 21 | workflows: 22 | version: 2 23 | workflow: 24 | jobs: 25 | - rtems 26 | - platformio 27 | - android-ndk 28 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | task: 2 | name: FreeBSD 3 | freebsd_instance: 4 | matrix: 5 | - image_family: freebsd-13-3 6 | - image_family: freebsd-14-0 7 | - image_family: freebsd-15-0-snap 8 | matrix: 9 | - BUILD_TYPE: Release 10 | install_script: pkg install -y cmake 11 | compile_script: cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPLOG_BUILD_TESTS=1 && cmake --build build -- -j4 12 | test_script: cd build && ctest -V 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 4 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.yml] 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - .circleci/ 7 | - .appveyor.yml 8 | - .cirrus.yml 9 | - .editorconfig 10 | - .gitignore 11 | - LICENSE 12 | - plog.nuspec 13 | - plog.targets 14 | - README.md 15 | pull_request: 16 | paths-ignore: 17 | - .circleci/ 18 | - .appveyor.yml 19 | - .cirrus.yml 20 | - .editorconfig 21 | - .gitignore 22 | - LICENSE 23 | - plog.nuspec 24 | - plog.targets 25 | - README.md 26 | 27 | permissions: 28 | contents: read 29 | 30 | jobs: 31 | build: 32 | name: ${{ matrix.cxx }}, ${{ matrix.os }} 33 | 34 | strategy: 35 | fail-fast: true 36 | matrix: 37 | include: [ 38 | # You can access the following values via ${{ matrix.??? }} 39 | # 40 | # pkgs : apt-get package names separated by space 41 | # cxx : C++ compiler executable 42 | # os : GitHub Actions YAML workflow label. See https://github.com/actions/virtual-environments#available-environments 43 | 44 | # linux: gcc 45 | { os: ubuntu-24.04, cxx: g++-14, pkgs: '' }, # (default on Noble 24.04) 46 | { os: ubuntu-22.04, cxx: g++-11, pkgs: '' }, # (default on Jammy 22.04) 47 | { os: ubuntu-22.04, cxx: g++-9, pkgs: '' }, # (default on Focal 20.04) 48 | { os: ubuntu-20.04, cxx: g++-7, pkgs: 'g++-7' }, # (default on Bionic 18.04) 49 | { os: ubuntu-20.04, cxx: g++-5, pkgs: 'g++-5', repo: 'xenial' }, # (default on Xenial 16.04) 50 | { os: ubuntu-20.04, cxx: g++-4.8, pkgs: 'g++-4.8', repo: 'trusty' }, # (default on Trusty 14.04) 51 | # linux: clang 52 | { os: ubuntu-22.04, cxx: clang++-14, pkgs: '' }, 53 | { os: ubuntu-20.04, cxx: clang++-10, pkgs: '' }, 54 | { os: ubuntu-20.04, cxx: clang++-6.0, pkgs: 'clang-6.0' }, 55 | # windows: msvc 56 | { os: windows-2019, cxx: 'vs2019' }, 57 | { os: windows-2022, cxx: 'vs2022' }, 58 | # macos: clang 59 | { os: macos-13, cxx: 'clang++' } 60 | ] 61 | 62 | runs-on: ${{ matrix.os }} 63 | 64 | env: 65 | CXX: ${{ matrix.cxx }} 66 | 67 | steps: 68 | - name: checkout 69 | uses: actions/checkout@v3 70 | with: 71 | fetch-depth: 0 72 | 73 | # workaround for broken clang on ubuntu runner until https://github.com/actions/runner-images/issues/8659 get fixed 74 | - uses: mjp41/workaround8649@c8550b715ccdc17f89c8d5c28d7a48eeff9c94a8 75 | with: 76 | os: ${{ matrix.os }} 77 | 78 | - name: apt/sources.list.d 79 | if: ${{ matrix.repo != '' }} 80 | run: | 81 | echo "deb http://archive.ubuntu.com/ubuntu ${{ matrix.repo }} main" | sudo tee /etc/apt/sources.list.d/${{ matrix.repo }}.list 82 | 83 | - name: apt-get install 84 | if: ${{ matrix.pkgs != '' }} 85 | run: | 86 | sudo apt-get update 87 | sudo apt-get install ${{ matrix.pkgs }} 88 | 89 | - name: build 90 | run: | 91 | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DPLOG_BUILD_TESTS=1 . 92 | cmake --build build --parallel 93 | 94 | - name: test 95 | run: cd build && ctest -V 96 | -------------------------------------------------------------------------------- /.github/workflows/nuget.yml: -------------------------------------------------------------------------------- 1 | name: nuget 2 | 3 | on: 4 | push: 5 | tags: 6 | - '[0-9]*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Setup NuGet 19 | uses: nuget/setup-nuget@v1 20 | with: 21 | nuget-version: 'latest' 22 | 23 | - name: Pack NuGet 24 | run: nuget pack plog.nuspec -p version=${{github.ref_name}} 25 | 26 | - name: Publish 27 | run: nuget push *.nupkg -Source "https://nuget.pkg.github.com/SergiusTheBest/index.json" -SkipDuplicate -ApiKey "${{secrets.GITHUB_TOKEN}}" 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(${CMAKE_VERSION} VERSION_LESS 3.27.0) 2 | cmake_minimum_required(VERSION 3.0) 3 | elseif(${CMAKE_VERSION} VERSION_LESS 3.31.0) 4 | cmake_minimum_required(VERSION 3.6) 5 | else() 6 | cmake_minimum_required(VERSION 3.10) 7 | endif() 8 | 9 | project(plog VERSION 1.1.10 LANGUAGES CXX) 10 | 11 | # check if building as a stand-alone project 12 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 13 | set(IS_TOPLEVEL_PROJECT TRUE) 14 | else() 15 | set(IS_TOPLEVEL_PROJECT FALSE) 16 | endif() 17 | 18 | # options 19 | option(PLOG_BUILD_SAMPLES "Build ${PROJECT_NAME} samples" ${IS_TOPLEVEL_PROJECT}) 20 | option(PLOG_INSTALL "Generate ${PROJECT_NAME} install target" ${IS_TOPLEVEL_PROJECT}) 21 | option(PLOG_BUILD_TESTS "Build tests" OFF) 22 | 23 | # make sure install paths work on all platforms 24 | include(GNUInstallDirs) 25 | 26 | add_library(${PROJECT_NAME} INTERFACE) 27 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 28 | 29 | target_include_directories(${PROJECT_NAME} 30 | INTERFACE 31 | $ 32 | $ 33 | ) 34 | 35 | if(ANDROID) 36 | target_link_libraries(${PROJECT_NAME} INTERFACE log) 37 | endif() 38 | 39 | if(PLOG_BUILD_SAMPLES) 40 | # add a pseudo-project to make plog headers visible in IDE 41 | file(GLOB_RECURSE ${PROJECT_NAME}_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) 42 | add_custom_target(${PROJECT_NAME}-headers SOURCES ${${PROJECT_NAME}_HEADERS}) 43 | set_target_properties(${PROJECT_NAME}-headers PROPERTIES FOLDER Include) 44 | 45 | # add samples 46 | add_subdirectory(samples) 47 | endif() 48 | 49 | if(PLOG_BUILD_TESTS) 50 | enable_testing() 51 | add_subdirectory(test) 52 | endif() 53 | 54 | if(PLOG_INSTALL) 55 | install( 56 | TARGETS ${PROJECT_NAME} 57 | EXPORT ${PROJECT_NAME}Config 58 | ) 59 | 60 | install( 61 | EXPORT ${PROJECT_NAME}Config 62 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 63 | NAMESPACE ${PROJECT_NAME}:: 64 | ) 65 | 66 | install( 67 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME} 68 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 69 | ) 70 | 71 | install( 72 | FILES 73 | ${CMAKE_CURRENT_SOURCE_DIR}/README.md 74 | ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE 75 | DESTINATION ${CMAKE_INSTALL_DOCDIR} 76 | ) 77 | 78 | include(CMakePackageConfigHelpers) 79 | write_basic_package_version_file( 80 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 81 | COMPATIBILITY AnyNewerVersion 82 | ) 83 | 84 | install( 85 | FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 86 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 87 | ) 88 | endif() 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sergey Podobry 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 | -------------------------------------------------------------------------------- /doc/color-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SergiusTheBest/plog/94899e0b926ac1b0f4750bfbd495167b4a6ae9ef/doc/color-console.png -------------------------------------------------------------------------------- /include/plog/Appenders/AndroidAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | template 8 | class PLOG_LINKAGE_HIDDEN AndroidAppender : public IAppender 9 | { 10 | public: 11 | AndroidAppender(const char* tag) : m_tag(tag) 12 | { 13 | } 14 | 15 | virtual void write(const Record& record) PLOG_OVERRIDE 16 | { 17 | std::string str = Formatter::format(record); 18 | 19 | __android_log_print(toPriority(record.getSeverity()), m_tag, "%s", str.c_str()); 20 | } 21 | 22 | private: 23 | static android_LogPriority toPriority(Severity severity) 24 | { 25 | switch (severity) 26 | { 27 | case fatal: 28 | return ANDROID_LOG_FATAL; 29 | case error: 30 | return ANDROID_LOG_ERROR; 31 | case warning: 32 | return ANDROID_LOG_WARN; 33 | case info: 34 | return ANDROID_LOG_INFO; 35 | case debug: 36 | return ANDROID_LOG_DEBUG; 37 | case verbose: 38 | return ANDROID_LOG_VERBOSE; 39 | default: 40 | return ANDROID_LOG_UNKNOWN; 41 | } 42 | } 43 | 44 | private: 45 | const char* const m_tag; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /include/plog/Appenders/ArduinoAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | template 8 | class PLOG_LINKAGE_HIDDEN ArduinoAppender : public IAppender 9 | { 10 | public: 11 | ArduinoAppender(Stream &stream) : m_stream(stream) 12 | { 13 | } 14 | 15 | virtual void write(const Record &record) PLOG_OVERRIDE 16 | { 17 | m_stream.print(Formatter::format(record).c_str()); 18 | } 19 | 20 | private: 21 | Stream &m_stream; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /include/plog/Appenders/ColorConsoleAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | template 8 | class PLOG_LINKAGE_HIDDEN ColorConsoleAppender : public ConsoleAppender 9 | { 10 | public: 11 | #ifdef _WIN32 12 | # ifdef _MSC_VER 13 | # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' 14 | # endif 15 | ColorConsoleAppender(OutputStream outStream = streamStdOut) 16 | : ConsoleAppender(outStream) 17 | , m_originalAttr() 18 | { 19 | if (this->m_isatty) 20 | { 21 | CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 22 | GetConsoleScreenBufferInfo(this->m_outputHandle, &csbiInfo); 23 | 24 | m_originalAttr = csbiInfo.wAttributes; 25 | } 26 | } 27 | #else 28 | ColorConsoleAppender(OutputStream outStream = streamStdOut) 29 | : ConsoleAppender(outStream) 30 | {} 31 | #endif 32 | 33 | virtual void write(const Record& record) PLOG_OVERRIDE 34 | { 35 | util::nstring str = Formatter::format(record); 36 | util::MutexLock lock(this->m_mutex); 37 | 38 | setColor(record.getSeverity()); 39 | this->writestr(str); 40 | resetColor(); 41 | } 42 | 43 | protected: 44 | void setColor(Severity severity) 45 | { 46 | if (this->m_isatty) 47 | { 48 | switch (severity) 49 | { 50 | #ifdef _WIN32 51 | case fatal: 52 | SetConsoleTextAttribute(this->m_outputHandle, foreground::kRed | foreground::kGreen | foreground::kBlue | foreground::kIntensity | background::kRed); // white on red background 53 | break; 54 | 55 | case error: 56 | SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kRed | foreground::kIntensity | (m_originalAttr & 0xf0))); // red 57 | break; 58 | 59 | case warning: 60 | SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kRed | foreground::kGreen | foreground::kIntensity | (m_originalAttr & 0xf0))); // yellow 61 | break; 62 | 63 | case debug: 64 | case verbose: 65 | SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kGreen | foreground::kBlue | foreground::kIntensity | (m_originalAttr & 0xf0))); // cyan 66 | break; 67 | #else 68 | case fatal: 69 | this->m_outputStream << "\x1B[97m\x1B[41m"; // white on red background 70 | break; 71 | 72 | case error: 73 | this->m_outputStream << "\x1B[91m"; // red 74 | break; 75 | 76 | case warning: 77 | this->m_outputStream << "\x1B[93m"; // yellow 78 | break; 79 | 80 | case debug: 81 | case verbose: 82 | this->m_outputStream << "\x1B[96m"; // cyan 83 | break; 84 | #endif 85 | default: 86 | break; 87 | } 88 | } 89 | } 90 | 91 | void resetColor() 92 | { 93 | if (this->m_isatty) 94 | { 95 | #ifdef _WIN32 96 | SetConsoleTextAttribute(this->m_outputHandle, m_originalAttr); 97 | #else 98 | this->m_outputStream << "\x1B[0m\x1B[0K"; 99 | #endif 100 | } 101 | } 102 | 103 | private: 104 | #ifdef _WIN32 105 | WORD m_originalAttr; 106 | #endif 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /include/plog/Appenders/ConsoleAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace plog 8 | { 9 | enum OutputStream 10 | { 11 | streamStdOut, 12 | streamStdErr 13 | }; 14 | 15 | template 16 | class PLOG_LINKAGE_HIDDEN ConsoleAppender : public IAppender 17 | { 18 | public: 19 | #ifdef _WIN32 20 | # ifdef _MSC_VER 21 | # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' 22 | # endif 23 | ConsoleAppender(OutputStream outStream = streamStdOut) 24 | : m_isatty(!!_isatty(_fileno(outStream == streamStdOut ? stdout : stderr))) 25 | , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr) 26 | , m_outputHandle() 27 | { 28 | if (m_isatty) 29 | { 30 | m_outputHandle = GetStdHandle(outStream == streamStdOut ? stdHandle::kOutput : stdHandle::kErrorOutput); 31 | } 32 | } 33 | #else 34 | ConsoleAppender(OutputStream outStream = streamStdOut) 35 | : m_isatty(!!isatty(fileno(outStream == streamStdOut ? stdout : stderr))) 36 | , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr) 37 | {} 38 | #endif 39 | 40 | virtual void write(const Record& record) PLOG_OVERRIDE 41 | { 42 | util::nstring str = Formatter::format(record); 43 | util::MutexLock lock(m_mutex); 44 | 45 | writestr(str); 46 | } 47 | 48 | protected: 49 | void writestr(const util::nstring& str) 50 | { 51 | #ifdef _WIN32 52 | if (m_isatty) 53 | { 54 | const std::wstring& wstr = util::toWide(str); 55 | WriteConsoleW(m_outputHandle, wstr.c_str(), static_cast(wstr.size()), NULL, NULL); 56 | } 57 | else 58 | { 59 | # if PLOG_CHAR_IS_UTF8 60 | m_outputStream << str << std::flush; 61 | # else 62 | m_outputStream << util::toNarrow(str, codePage::kActive) << std::flush; 63 | # endif 64 | } 65 | #else 66 | m_outputStream << str << std::flush; 67 | #endif 68 | } 69 | 70 | private: 71 | #ifdef __BORLANDC__ 72 | static int _isatty(int fd) { return ::isatty(fd); } 73 | #endif 74 | 75 | protected: 76 | util::Mutex m_mutex; 77 | const bool m_isatty; 78 | std::ostream& m_outputStream; 79 | #ifdef _WIN32 80 | HANDLE m_outputHandle; 81 | #endif 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /include/plog/Appenders/DebugOutputAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | template 8 | class PLOG_LINKAGE_HIDDEN DebugOutputAppender : public IAppender 9 | { 10 | public: 11 | virtual void write(const Record& record) PLOG_OVERRIDE 12 | { 13 | OutputDebugStringW(util::toWide(Formatter::format(record)).c_str()); 14 | } 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /include/plog/Appenders/DynamicAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | class PLOG_LINKAGE_HIDDEN DynamicAppender : public IAppender 8 | { 9 | public: 10 | DynamicAppender& addAppender(IAppender* appender) 11 | { 12 | assert(appender != this); 13 | 14 | util::MutexLock lock(m_mutex); 15 | m_appenders.insert(appender); 16 | 17 | return *this; 18 | } 19 | 20 | DynamicAppender& removeAppender(IAppender* appender) 21 | { 22 | util::MutexLock lock(m_mutex); 23 | m_appenders.erase(appender); 24 | 25 | return *this; 26 | } 27 | 28 | virtual void write(const Record& record) PLOG_OVERRIDE 29 | { 30 | util::MutexLock lock(m_mutex); 31 | 32 | for (std::set::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it) 33 | { 34 | (*it)->write(record); 35 | } 36 | } 37 | 38 | private: 39 | mutable util::Mutex m_mutex; 40 | std::set m_appenders; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /include/plog/Appenders/EventLogAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | template 8 | class PLOG_LINKAGE_HIDDEN EventLogAppender : public IAppender 9 | { 10 | public: 11 | EventLogAppender(const util::nchar* sourceName) : m_eventSource(RegisterEventSourceW(NULL, util::toWide(sourceName).c_str())) 12 | { 13 | } 14 | 15 | ~EventLogAppender() 16 | { 17 | DeregisterEventSource(m_eventSource); 18 | } 19 | 20 | virtual void write(const Record& record) PLOG_OVERRIDE 21 | { 22 | util::nstring str = Formatter::format(record); 23 | 24 | write(record.getSeverity(), util::toWide(str).c_str()); 25 | } 26 | 27 | private: 28 | void write(Severity severity, const wchar_t* str) 29 | { 30 | const wchar_t* logMessagePtr[] = { str }; 31 | 32 | ReportEventW(m_eventSource, logSeverityToType(severity), static_cast(severity), 0, NULL, 1, 0, logMessagePtr, NULL); 33 | } 34 | 35 | static WORD logSeverityToType(plog::Severity severity) 36 | { 37 | switch (severity) 38 | { 39 | case plog::fatal: 40 | case plog::error: 41 | return eventLog::kErrorType; 42 | 43 | case plog::warning: 44 | return eventLog::kWarningType; 45 | 46 | case plog::info: 47 | case plog::debug: 48 | case plog::verbose: 49 | default: 50 | return eventLog::kInformationType; 51 | } 52 | } 53 | 54 | private: 55 | HANDLE m_eventSource; 56 | }; 57 | 58 | class EventLogAppenderRegistry 59 | { 60 | public: 61 | static bool add(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) 62 | { 63 | std::wstring logKeyName; 64 | std::wstring sourceKeyName; 65 | getKeyNames(sourceName, logName, sourceKeyName, logKeyName); 66 | 67 | HKEY sourceKey; 68 | if (0 != RegCreateKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, NULL, 0, regSam::kSetValue, NULL, &sourceKey, NULL)) 69 | { 70 | return false; 71 | } 72 | 73 | const DWORD kTypesSupported = eventLog::kErrorType | eventLog::kWarningType | eventLog::kInformationType; 74 | RegSetValueExW(sourceKey, L"TypesSupported", 0, regType::kDword, reinterpret_cast(&kTypesSupported), sizeof(kTypesSupported)); 75 | 76 | const wchar_t kEventMessageFile[] = L"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\EventLogMessages.dll;%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll"; 77 | RegSetValueExW(sourceKey, L"EventMessageFile", 0, regType::kExpandSz, reinterpret_cast(kEventMessageFile), sizeof(kEventMessageFile) - sizeof(*kEventMessageFile)); 78 | 79 | RegCloseKey(sourceKey); 80 | return true; 81 | } 82 | 83 | static bool exists(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) 84 | { 85 | std::wstring logKeyName; 86 | std::wstring sourceKeyName; 87 | getKeyNames(sourceName, logName, sourceKeyName, logKeyName); 88 | 89 | HKEY sourceKey; 90 | if (0 != RegOpenKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, regSam::kQueryValue, &sourceKey)) 91 | { 92 | return false; 93 | } 94 | 95 | RegCloseKey(sourceKey); 96 | return true; 97 | } 98 | 99 | static void remove(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) 100 | { 101 | std::wstring logKeyName; 102 | std::wstring sourceKeyName; 103 | getKeyNames(sourceName, logName, sourceKeyName, logKeyName); 104 | 105 | RegDeleteKeyW(hkey::kLocalMachine, sourceKeyName.c_str()); 106 | RegDeleteKeyW(hkey::kLocalMachine, logKeyName.c_str()); 107 | } 108 | 109 | private: 110 | static void getKeyNames(const util::nchar* sourceName, const util::nchar* logName, std::wstring& sourceKeyName, std::wstring& logKeyName) 111 | { 112 | const std::wstring kPrefix = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\"; 113 | logKeyName = kPrefix + util::toWide(logName); 114 | sourceKeyName = logKeyName + L"\\" + util::toWide(sourceName); 115 | } 116 | }; 117 | } 118 | -------------------------------------------------------------------------------- /include/plog/Appenders/IAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | class PLOG_LINKAGE IAppender 8 | { 9 | public: 10 | virtual ~IAppender() 11 | { 12 | } 13 | 14 | virtual void write(const Record& record) = 0; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /include/plog/Appenders/RollingFileAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace plog 9 | { 10 | template > 11 | class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender 12 | { 13 | public: 14 | RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) 15 | : m_fileSize() 16 | , m_maxFileSize() 17 | , m_maxFiles(maxFiles) 18 | , m_firstWrite(true) 19 | { 20 | setFileName(fileName); 21 | setMaxFileSize(maxFileSize); 22 | } 23 | 24 | #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 25 | RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) 26 | : m_fileSize() 27 | , m_maxFileSize() 28 | , m_maxFiles(maxFiles) 29 | , m_firstWrite(true) 30 | { 31 | setFileName(fileName); 32 | setMaxFileSize(maxFileSize); 33 | } 34 | #endif 35 | 36 | virtual void write(const Record& record) PLOG_OVERRIDE 37 | { 38 | util::MutexLock lock(m_mutex); 39 | 40 | if (m_firstWrite) 41 | { 42 | openLogFile(); 43 | m_firstWrite = false; 44 | } 45 | else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast(-1) != m_fileSize) 46 | { 47 | rollLogFiles(); 48 | } 49 | 50 | size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record))); 51 | 52 | if (static_cast(-1) != bytesWritten) 53 | { 54 | m_fileSize += bytesWritten; 55 | } 56 | } 57 | 58 | void setFileName(const util::nchar* fileName) 59 | { 60 | util::MutexLock lock(m_mutex); 61 | 62 | util::splitFileName(fileName, m_fileNameNoExt, m_fileExt); 63 | 64 | m_file.close(); 65 | m_firstWrite = true; 66 | } 67 | 68 | #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 69 | void setFileName(const char* fileName) 70 | { 71 | setFileName(util::toWide(fileName).c_str()); 72 | } 73 | #endif 74 | 75 | void setMaxFiles(int maxFiles) 76 | { 77 | m_maxFiles = maxFiles; 78 | } 79 | 80 | void setMaxFileSize(size_t maxFileSize) 81 | { 82 | m_maxFileSize = (std::max)(maxFileSize, static_cast(1000)); // set a lower limit for the maxFileSize 83 | } 84 | 85 | void rollLogFiles() 86 | { 87 | m_file.close(); 88 | 89 | util::nstring lastFileName = buildFileName(m_maxFiles - 1); 90 | util::File::unlink(lastFileName); 91 | 92 | for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber) 93 | { 94 | util::nstring currentFileName = buildFileName(fileNumber); 95 | util::nstring nextFileName = buildFileName(fileNumber + 1); 96 | 97 | util::File::rename(currentFileName, nextFileName); 98 | } 99 | 100 | openLogFile(); 101 | m_firstWrite = false; 102 | } 103 | 104 | private: 105 | void openLogFile() 106 | { 107 | m_fileSize = m_file.open(buildFileName()); 108 | 109 | if (0 == m_fileSize) 110 | { 111 | size_t bytesWritten = m_file.write(Converter::header(Formatter::header())); 112 | 113 | if (static_cast(-1) != bytesWritten) 114 | { 115 | m_fileSize += bytesWritten; 116 | } 117 | } 118 | } 119 | 120 | util::nstring buildFileName(int fileNumber = 0) 121 | { 122 | util::nostringstream ss; 123 | ss << m_fileNameNoExt; 124 | 125 | if (fileNumber > 0) 126 | { 127 | ss << '.' << fileNumber; 128 | } 129 | 130 | if (!m_fileExt.empty()) 131 | { 132 | ss << '.' << m_fileExt; 133 | } 134 | 135 | return ss.str(); 136 | } 137 | 138 | private: 139 | util::Mutex m_mutex; 140 | util::File m_file; 141 | size_t m_fileSize; 142 | size_t m_maxFileSize; 143 | int m_maxFiles; 144 | util::nstring m_fileExt; 145 | util::nstring m_fileNameNoExt; 146 | bool m_firstWrite; 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /include/plog/Converters/NativeEOLConverter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | template 8 | class NativeEOLConverter : public NextConverter 9 | { 10 | #ifdef _WIN32 11 | public: 12 | static std::string header(const util::nstring& str) 13 | { 14 | return NextConverter::header(fixLineEndings(str)); 15 | } 16 | 17 | static std::string convert(const util::nstring& str) 18 | { 19 | return NextConverter::convert(fixLineEndings(str)); 20 | } 21 | 22 | private: 23 | static util::nstring fixLineEndings(const util::nstring& str) 24 | { 25 | util::nstring output; 26 | output.reserve(str.length() * 2); // the worst case requires 2x chars 27 | 28 | for (size_t i = 0; i < str.size(); ++i) 29 | { 30 | util::nchar ch = str[i]; 31 | 32 | if (ch == PLOG_NSTR('\n')) 33 | { 34 | output.push_back(PLOG_NSTR('\r')); 35 | } 36 | 37 | output.push_back(ch); 38 | } 39 | 40 | return output; 41 | } 42 | #endif 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /include/plog/Converters/UTF8Converter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace plog 5 | { 6 | class UTF8Converter 7 | { 8 | public: 9 | static std::string header(const util::nstring& str) 10 | { 11 | const char kBOM[] = "\xEF\xBB\xBF"; 12 | 13 | return std::string(kBOM) + convert(str); 14 | } 15 | 16 | #if PLOG_CHAR_IS_UTF8 17 | static const std::string& convert(const util::nstring& str) 18 | { 19 | return str; 20 | } 21 | #else 22 | static std::string convert(const util::nstring& str) 23 | { 24 | return util::toNarrow(str, codePage::kUTF8); 25 | } 26 | #endif 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /include/plog/Formatters/CsvFormatter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace plog 7 | { 8 | template 9 | class CsvFormatterImpl 10 | { 11 | public: 12 | static util::nstring header() 13 | { 14 | return PLOG_NSTR("Date;Time;Severity;TID;This;Function;Message\n"); 15 | } 16 | 17 | static util::nstring format(const Record& record) 18 | { 19 | tm t; 20 | useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time); 21 | 22 | util::nostringstream ss; 23 | ss << t.tm_year + 1900 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(";"); 24 | ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast (record.getTime().millitm) << PLOG_NSTR(";"); 25 | ss << severityToString(record.getSeverity()) << PLOG_NSTR(";"); 26 | ss << record.getTid() << PLOG_NSTR(";"); 27 | ss << record.getObject() << PLOG_NSTR(";"); 28 | ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(";"); 29 | 30 | util::nstring message = record.getMessage(); 31 | 32 | if (message.size() > kMaxMessageSize) 33 | { 34 | message.resize(kMaxMessageSize); 35 | message.append(PLOG_NSTR("...")); 36 | } 37 | 38 | util::nistringstream split(message); 39 | util::nstring token; 40 | 41 | while (!split.eof()) 42 | { 43 | std::getline(split, token, PLOG_NSTR('"')); 44 | ss << PLOG_NSTR("\"") << token << PLOG_NSTR("\""); 45 | } 46 | 47 | ss << PLOG_NSTR("\n"); 48 | 49 | return ss.str(); 50 | } 51 | 52 | static const size_t kMaxMessageSize = 32000; 53 | }; 54 | 55 | class CsvFormatter : public CsvFormatterImpl {}; 56 | class CsvFormatterUtcTime : public CsvFormatterImpl {}; 57 | } 58 | -------------------------------------------------------------------------------- /include/plog/Formatters/FuncMessageFormatter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | class FuncMessageFormatter 8 | { 9 | public: 10 | static util::nstring header() 11 | { 12 | return util::nstring(); 13 | } 14 | 15 | static util::nstring format(const Record& record) 16 | { 17 | util::nostringstream ss; 18 | ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(": ") << record.getMessage() << PLOG_NSTR("\n"); 19 | 20 | return ss.str(); 21 | } 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /include/plog/Formatters/MessageOnlyFormatter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | class MessageOnlyFormatter 8 | { 9 | public: 10 | static util::nstring header() 11 | { 12 | return util::nstring(); 13 | } 14 | 15 | static util::nstring format(const Record& record) 16 | { 17 | util::nostringstream ss; 18 | ss << record.getMessage() << PLOG_NSTR("\n"); 19 | 20 | return ss.str(); 21 | } 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /include/plog/Formatters/TxtFormatter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace plog 7 | { 8 | template 9 | class TxtFormatterImpl 10 | { 11 | public: 12 | static util::nstring header() 13 | { 14 | return util::nstring(); 15 | } 16 | 17 | static util::nstring format(const Record& record) 18 | { 19 | tm t; 20 | useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time); 21 | 22 | util::nostringstream ss; 23 | ss << t.tm_year + 1900 << "-" << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("-") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(" "); 24 | ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast (record.getTime().millitm) << PLOG_NSTR(" "); 25 | ss << std::setfill(PLOG_NSTR(' ')) << std::setw(5) << std::left << severityToString(record.getSeverity()) << PLOG_NSTR(" "); 26 | ss << PLOG_NSTR("[") << record.getTid() << PLOG_NSTR("] "); 27 | ss << PLOG_NSTR("[") << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR("] "); 28 | ss << record.getMessage() << PLOG_NSTR("\n"); 29 | 30 | return ss.str(); 31 | } 32 | }; 33 | 34 | class TxtFormatter : public TxtFormatterImpl {}; 35 | class TxtFormatterUtcTime : public TxtFormatterImpl {}; 36 | } 37 | -------------------------------------------------------------------------------- /include/plog/Helpers/AscDump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | class AscDump 8 | { 9 | public: 10 | AscDump(const void* ptr, size_t size) 11 | : m_ptr(static_cast(ptr)) 12 | , m_size(size) 13 | { 14 | } 15 | 16 | friend util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump); 17 | 18 | private: 19 | const char* m_ptr; 20 | size_t m_size; 21 | }; 22 | 23 | inline util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump) 24 | { 25 | for (size_t i = 0; i < ascDump.m_size; ++i) 26 | { 27 | stream << (std::isprint(ascDump.m_ptr[i]) ? ascDump.m_ptr[i] : '.'); 28 | } 29 | 30 | return stream; 31 | } 32 | 33 | inline AscDump ascdump(const void* ptr, size_t size) { return AscDump(ptr, size); } 34 | 35 | template 36 | inline AscDump ascdump(const Container& container) { return AscDump(container.data(), container.size() * sizeof(*container.data())); } 37 | 38 | template 39 | inline AscDump ascdump(const T (&arr)[N]) { return AscDump(arr, N * sizeof(*arr)); } 40 | } 41 | -------------------------------------------------------------------------------- /include/plog/Helpers/HexDump.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | class HexDump 8 | { 9 | public: 10 | HexDump(const void* ptr, size_t size) 11 | : m_ptr(static_cast(ptr)) 12 | , m_size(size) 13 | , m_group(8) 14 | , m_digitSeparator(" ") 15 | , m_groupSeparator(" ") 16 | { 17 | } 18 | 19 | HexDump& group(size_t group) 20 | { 21 | m_group = group; 22 | return *this; 23 | } 24 | 25 | HexDump& separator(const char* digitSeparator) 26 | { 27 | m_digitSeparator = digitSeparator; 28 | return *this; 29 | } 30 | 31 | HexDump& separator(const char* digitSeparator, const char* groupSeparator) 32 | { 33 | m_digitSeparator = digitSeparator; 34 | m_groupSeparator = groupSeparator; 35 | return *this; 36 | } 37 | 38 | friend util::nostringstream& operator<<(util::nostringstream& stream, const HexDump&); 39 | 40 | private: 41 | const unsigned char* m_ptr; 42 | size_t m_size; 43 | size_t m_group; 44 | const char* m_digitSeparator; 45 | const char* m_groupSeparator; 46 | }; 47 | 48 | inline util::nostringstream& operator<<(util::nostringstream& stream, const HexDump& hexDump) 49 | { 50 | stream << std::hex << std::setfill(PLOG_NSTR('0')); 51 | 52 | for (size_t i = 0; i < hexDump.m_size;) 53 | { 54 | stream << std::setw(2) << static_cast(hexDump.m_ptr[i]); 55 | 56 | if (++i < hexDump.m_size) 57 | { 58 | if (hexDump.m_group > 0 && i % hexDump.m_group == 0) 59 | { 60 | stream << hexDump.m_groupSeparator; 61 | } 62 | else 63 | { 64 | stream << hexDump.m_digitSeparator; 65 | } 66 | } 67 | } 68 | 69 | return stream; 70 | } 71 | 72 | inline HexDump hexdump(const void* ptr, size_t size) { return HexDump(ptr, size); } 73 | 74 | template 75 | inline HexDump hexdump(const Container& container) { return HexDump(container.data(), container.size() * sizeof(*container.data())); } 76 | 77 | template 78 | inline HexDump hexdump(const T (&arr)[N]) { return HexDump(arr, N * sizeof(*arr)); } 79 | } 80 | -------------------------------------------------------------------------------- /include/plog/Helpers/PrintVar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PLOG_IMPL_PRINT_VAR_1(a1) #a1 ": " << a1 4 | #define PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_1(a1) PLOG_IMPL_PRINT_VAR_TAIL(a2) 5 | #define PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_TAIL(a3) 6 | #define PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_TAIL(a4) 7 | #define PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_TAIL(a5) 8 | #define PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_TAIL(a6) 9 | #define PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_TAIL(a7) 10 | #define PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_TAIL(a8) 11 | #define PLOG_IMPL_PRINT_VAR_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_TAIL(a9) 12 | #define PLOG_IMPL_PRINT_VAR_TAIL(a) << ", " PLOG_IMPL_PRINT_VAR_1(a) 13 | 14 | #define PLOG_IMPL_PRINT_VAR_EXPAND(x) x 15 | 16 | #ifdef __GNUC__ 17 | #pragma GCC system_header // disable warning: variadic macros are a C99 feature 18 | #endif 19 | 20 | #define PLOG_IMPL_PRINT_VAR_GET_MACRO(x1, x2, x3, x4, x5, x6, x7, x8, x9, NAME, ...) NAME 21 | 22 | #define PLOG_PRINT_VAR(...) PLOG_IMPL_PRINT_VAR_EXPAND(PLOG_IMPL_PRINT_VAR_GET_MACRO(__VA_ARGS__,\ 23 | PLOG_IMPL_PRINT_VAR_9, PLOG_IMPL_PRINT_VAR_8, PLOG_IMPL_PRINT_VAR_7, PLOG_IMPL_PRINT_VAR_6, PLOG_IMPL_PRINT_VAR_5,\ 24 | PLOG_IMPL_PRINT_VAR_4, PLOG_IMPL_PRINT_VAR_3, PLOG_IMPL_PRINT_VAR_2, PLOG_IMPL_PRINT_VAR_1, UNUSED)(__VA_ARGS__)) 25 | -------------------------------------------------------------------------------- /include/plog/Init.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace plog 5 | { 6 | template 7 | PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity = none, IAppender* appender = NULL) 8 | { 9 | static Logger logger(maxSeverity); 10 | return appender ? logger.addAppender(appender) : logger; 11 | } 12 | 13 | inline Logger& init(Severity maxSeverity = none, IAppender* appender = NULL) 14 | { 15 | return init(maxSeverity, appender); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /include/plog/Initializers/ConsoleInitializer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace plog 6 | { 7 | ////////////////////////////////////////////////////////////////////////// 8 | // ColorConsoleAppender with any Formatter 9 | 10 | template 11 | PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity, OutputStream outputStream) 12 | { 13 | static ColorConsoleAppender appender(outputStream); 14 | return init(maxSeverity, &appender); 15 | } 16 | 17 | template 18 | inline Logger& init(Severity maxSeverity, OutputStream outputStream) 19 | { 20 | return init(maxSeverity, outputStream); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /include/plog/Initializers/RollingFileInitializer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace plog 9 | { 10 | ////////////////////////////////////////////////////////////////////////// 11 | // RollingFileAppender with any Formatter 12 | 13 | template 14 | PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) 15 | { 16 | static RollingFileAppender rollingFileAppender(fileName, maxFileSize, maxFiles); 17 | return init(maxSeverity, &rollingFileAppender); 18 | } 19 | 20 | template 21 | inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) 22 | { 23 | return init(maxSeverity, fileName, maxFileSize, maxFiles); 24 | } 25 | 26 | ////////////////////////////////////////////////////////////////////////// 27 | // RollingFileAppender with TXT/CSV chosen by file extension 28 | 29 | namespace 30 | { 31 | inline bool isCsv(const util::nchar* fileName) 32 | { 33 | const util::nchar* dot = util::findExtensionDot(fileName); 34 | #if PLOG_CHAR_IS_UTF8 35 | return dot && 0 == std::strcmp(dot, ".csv"); 36 | #else 37 | return dot && 0 == std::wcscmp(dot, L".csv"); 38 | #endif 39 | } 40 | } 41 | 42 | template 43 | inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) 44 | { 45 | return isCsv(fileName) ? init(maxSeverity, fileName, maxFileSize, maxFiles) : init(maxSeverity, fileName, maxFileSize, maxFiles); 46 | } 47 | 48 | inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) 49 | { 50 | return init(maxSeverity, fileName, maxFileSize, maxFiles); 51 | } 52 | 53 | ////////////////////////////////////////////////////////////////////////// 54 | // CHAR variants for Windows 55 | 56 | #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 57 | template 58 | inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) 59 | { 60 | return init(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles); 61 | } 62 | 63 | template 64 | inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) 65 | { 66 | return init(maxSeverity, fileName, maxFileSize, maxFiles); 67 | } 68 | 69 | template 70 | inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) 71 | { 72 | return init(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles); 73 | } 74 | 75 | inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) 76 | { 77 | return init(maxSeverity, fileName, maxFileSize, maxFiles); 78 | } 79 | #endif 80 | } 81 | -------------------------------------------------------------------------------- /include/plog/Log.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // Plog - portable and simple log for C++ 3 | // Documentation and sources: https://github.com/SergiusTheBest/plog 4 | // License: MIT, https://choosealicense.com/licenses/mit 5 | 6 | #pragma once 7 | #include 8 | 9 | ////////////////////////////////////////////////////////////////////////// 10 | // Helper macros that get context info 11 | 12 | #if defined(PLOG_ENABLE_GET_THIS) && defined(_MSC_VER) && _MSC_VER >= 1600 && !defined(__INTELLISENSE__) && !defined(__INTEL_COMPILER) && !defined(__llvm__) && !defined(__RESHARPER__) // >= Visual Studio 2010, skip IntelliSense, Intel Compiler, Clang Code Model and ReSharper 13 | # define PLOG_GET_THIS() __if_exists(this) { this } __if_not_exists(this) { 0 } 14 | #else 15 | # define PLOG_GET_THIS() reinterpret_cast(0) 16 | #endif 17 | 18 | #ifdef _MSC_VER 19 | # define PLOG_GET_FUNC() __FUNCTION__ 20 | #elif defined(__BORLANDC__) 21 | # define PLOG_GET_FUNC() __FUNC__ 22 | #else 23 | # define PLOG_GET_FUNC() __PRETTY_FUNCTION__ 24 | #endif 25 | 26 | #ifdef PLOG_CAPTURE_FILE 27 | # define PLOG_GET_FILE() __FILE__ 28 | #else 29 | # define PLOG_GET_FILE() "" 30 | #endif 31 | 32 | ////////////////////////////////////////////////////////////////////////// 33 | // Log severity level checker 34 | 35 | #ifdef PLOG_DISABLE_LOGGING 36 | # ifdef _MSC_VER 37 | # define IF_PLOG_(instanceId, severity) __pragma(warning(push)) __pragma(warning(disable:4127)) if (true) {;} else __pragma(warning(pop)) // conditional expression is constant 38 | # else 39 | # define IF_PLOG_(instanceId, severity) if (true) {;} else 40 | # endif 41 | #else 42 | # define IF_PLOG_(instanceId, severity) if (!plog::get() || !plog::get()->checkSeverity(severity)) {;} else 43 | #endif 44 | 45 | #define IF_PLOG(severity) IF_PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) 46 | 47 | ////////////////////////////////////////////////////////////////////////// 48 | // Main logging macros 49 | 50 | #define PLOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref() 51 | #define PLOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) 52 | 53 | #define PLOG_VERBOSE PLOG(plog::verbose) 54 | #define PLOG_DEBUG PLOG(plog::debug) 55 | #define PLOG_INFO PLOG(plog::info) 56 | #define PLOG_WARNING PLOG(plog::warning) 57 | #define PLOG_ERROR PLOG(plog::error) 58 | #define PLOG_FATAL PLOG(plog::fatal) 59 | #define PLOG_NONE PLOG(plog::none) 60 | 61 | #define PLOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose) 62 | #define PLOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug) 63 | #define PLOG_INFO_(instanceId) PLOG_(instanceId, plog::info) 64 | #define PLOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning) 65 | #define PLOG_ERROR_(instanceId) PLOG_(instanceId, plog::error) 66 | #define PLOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal) 67 | #define PLOG_NONE_(instanceId) PLOG_(instanceId, plog::none) 68 | 69 | #define PLOGV PLOG_VERBOSE 70 | #define PLOGD PLOG_DEBUG 71 | #define PLOGI PLOG_INFO 72 | #define PLOGW PLOG_WARNING 73 | #define PLOGE PLOG_ERROR 74 | #define PLOGF PLOG_FATAL 75 | #define PLOGN PLOG_NONE 76 | 77 | #define PLOGV_(instanceId) PLOG_VERBOSE_(instanceId) 78 | #define PLOGD_(instanceId) PLOG_DEBUG_(instanceId) 79 | #define PLOGI_(instanceId) PLOG_INFO_(instanceId) 80 | #define PLOGW_(instanceId) PLOG_WARNING_(instanceId) 81 | #define PLOGE_(instanceId) PLOG_ERROR_(instanceId) 82 | #define PLOGF_(instanceId) PLOG_FATAL_(instanceId) 83 | #define PLOGN_(instanceId) PLOG_NONE_(instanceId) 84 | 85 | ////////////////////////////////////////////////////////////////////////// 86 | // Conditional logging macros 87 | 88 | #define PLOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity) 89 | #define PLOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition) 90 | 91 | #define PLOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition) 92 | #define PLOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition) 93 | #define PLOG_INFO_IF(condition) PLOG_IF(plog::info, condition) 94 | #define PLOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition) 95 | #define PLOG_ERROR_IF(condition) PLOG_IF(plog::error, condition) 96 | #define PLOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition) 97 | #define PLOG_NONE_IF(condition) PLOG_IF(plog::none, condition) 98 | 99 | #define PLOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition) 100 | #define PLOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition) 101 | #define PLOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition) 102 | #define PLOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition) 103 | #define PLOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition) 104 | #define PLOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition) 105 | #define PLOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition) 106 | 107 | #define PLOGV_IF(condition) PLOG_VERBOSE_IF(condition) 108 | #define PLOGD_IF(condition) PLOG_DEBUG_IF(condition) 109 | #define PLOGI_IF(condition) PLOG_INFO_IF(condition) 110 | #define PLOGW_IF(condition) PLOG_WARNING_IF(condition) 111 | #define PLOGE_IF(condition) PLOG_ERROR_IF(condition) 112 | #define PLOGF_IF(condition) PLOG_FATAL_IF(condition) 113 | #define PLOGN_IF(condition) PLOG_NONE_IF(condition) 114 | 115 | #define PLOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition) 116 | #define PLOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition) 117 | #define PLOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition) 118 | #define PLOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition) 119 | #define PLOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition) 120 | #define PLOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition) 121 | #define PLOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition) 122 | 123 | // Old macro names for downward compatibility. To bypass including these macro names, add 124 | // #define PLOG_OMIT_LOG_DEFINES before #include 125 | #ifndef PLOG_OMIT_LOG_DEFINES 126 | 127 | ////////////////////////////////////////////////////////////////////////// 128 | // Main logging macros - can be changed later to point at macros for a different logging package 129 | 130 | #define LOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref() 131 | #define LOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) 132 | 133 | #define LOG_VERBOSE PLOG(plog::verbose) 134 | #define LOG_DEBUG PLOG(plog::debug) 135 | #define LOG_INFO PLOG(plog::info) 136 | #define LOG_WARNING PLOG(plog::warning) 137 | #define LOG_ERROR PLOG(plog::error) 138 | #define LOG_FATAL PLOG(plog::fatal) 139 | #define LOG_NONE PLOG(plog::none) 140 | 141 | #define LOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose) 142 | #define LOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug) 143 | #define LOG_INFO_(instanceId) PLOG_(instanceId, plog::info) 144 | #define LOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning) 145 | #define LOG_ERROR_(instanceId) PLOG_(instanceId, plog::error) 146 | #define LOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal) 147 | #define LOG_NONE_(instanceId) PLOG_(instanceId, plog::none) 148 | 149 | #define LOGV PLOG_VERBOSE 150 | #define LOGD PLOG_DEBUG 151 | #define LOGI PLOG_INFO 152 | #define LOGW PLOG_WARNING 153 | #define LOGE PLOG_ERROR 154 | #define LOGF PLOG_FATAL 155 | #define LOGN PLOG_NONE 156 | 157 | #define LOGV_(instanceId) PLOG_VERBOSE_(instanceId) 158 | #define LOGD_(instanceId) PLOG_DEBUG_(instanceId) 159 | #define LOGI_(instanceId) PLOG_INFO_(instanceId) 160 | #define LOGW_(instanceId) PLOG_WARNING_(instanceId) 161 | #define LOGE_(instanceId) PLOG_ERROR_(instanceId) 162 | #define LOGF_(instanceId) PLOG_FATAL_(instanceId) 163 | #define LOGN_(instanceId) PLOG_NONE_(instanceId) 164 | 165 | ////////////////////////////////////////////////////////////////////////// 166 | // Conditional logging macros 167 | 168 | #define LOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity) 169 | #define LOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition) 170 | 171 | #define LOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition) 172 | #define LOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition) 173 | #define LOG_INFO_IF(condition) PLOG_IF(plog::info, condition) 174 | #define LOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition) 175 | #define LOG_ERROR_IF(condition) PLOG_IF(plog::error, condition) 176 | #define LOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition) 177 | #define LOG_NONE_IF(condition) PLOG_IF(plog::none, condition) 178 | 179 | #define LOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition) 180 | #define LOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition) 181 | #define LOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition) 182 | #define LOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition) 183 | #define LOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition) 184 | #define LOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition) 185 | #define LOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition) 186 | 187 | #define LOGV_IF(condition) PLOG_VERBOSE_IF(condition) 188 | #define LOGD_IF(condition) PLOG_DEBUG_IF(condition) 189 | #define LOGI_IF(condition) PLOG_INFO_IF(condition) 190 | #define LOGW_IF(condition) PLOG_WARNING_IF(condition) 191 | #define LOGE_IF(condition) PLOG_ERROR_IF(condition) 192 | #define LOGF_IF(condition) PLOG_FATAL_IF(condition) 193 | #define LOGN_IF(condition) PLOG_NONE_IF(condition) 194 | 195 | #define LOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition) 196 | #define LOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition) 197 | #define LOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition) 198 | #define LOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition) 199 | #define LOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition) 200 | #define LOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition) 201 | #define LOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition) 202 | #endif 203 | -------------------------------------------------------------------------------- /include/plog/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef PLOG_DEFAULT_INSTANCE // for backward compatibility 7 | # define PLOG_DEFAULT_INSTANCE_ID PLOG_DEFAULT_INSTANCE 8 | #endif 9 | 10 | #ifndef PLOG_DEFAULT_INSTANCE_ID 11 | # define PLOG_DEFAULT_INSTANCE_ID 0 12 | #endif 13 | 14 | namespace plog 15 | { 16 | template 17 | class PLOG_LINKAGE Logger : public util::Singleton >, public IAppender 18 | { 19 | public: 20 | Logger(Severity maxSeverity = none) : m_maxSeverity(maxSeverity) 21 | { 22 | } 23 | 24 | Logger& addAppender(IAppender* appender) 25 | { 26 | assert(appender != this); 27 | m_appenders.push_back(appender); 28 | return *this; 29 | } 30 | 31 | Severity getMaxSeverity() const 32 | { 33 | return m_maxSeverity; 34 | } 35 | 36 | void setMaxSeverity(Severity severity) 37 | { 38 | m_maxSeverity = severity; 39 | } 40 | 41 | bool checkSeverity(Severity severity) const 42 | { 43 | return severity <= m_maxSeverity; 44 | } 45 | 46 | virtual void write(const Record& record) PLOG_OVERRIDE 47 | { 48 | if (checkSeverity(record.getSeverity())) 49 | { 50 | *this += record; 51 | } 52 | } 53 | 54 | void operator+=(const Record& record) 55 | { 56 | for (std::vector::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it) 57 | { 58 | (*it)->write(record); 59 | } 60 | } 61 | 62 | private: 63 | Severity m_maxSeverity; 64 | #ifdef _MSC_VER 65 | # pragma warning(push) 66 | # pragma warning(disable:4251) // needs to have dll-interface to be used by clients of class 67 | #endif 68 | std::vector m_appenders; 69 | #ifdef _MSC_VER 70 | # pragma warning(pop) 71 | #endif 72 | }; 73 | 74 | template 75 | inline Logger* get() 76 | { 77 | return Logger::getInstance(); 78 | } 79 | 80 | inline Logger* get() 81 | { 82 | return Logger::getInstance(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /include/plog/Record.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef __cplusplus_cli 7 | #include // For PtrToStringChars 8 | #endif 9 | 10 | namespace plog 11 | { 12 | namespace detail 13 | { 14 | #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` 15 | namespace meta 16 | { 17 | template 18 | inline T& declval() 19 | { 20 | #ifdef __INTEL_COMPILER 21 | # pragma warning(suppress: 327) // NULL reference is not allowed 22 | #endif 23 | return *reinterpret_cast(0); 24 | } 25 | 26 | template 27 | struct enableIf {}; 28 | 29 | template 30 | struct enableIf { typedef T type; }; 31 | 32 | struct No { char a[1]; }; 33 | struct Yes { char a[2]; }; 34 | 35 | template 36 | struct isConvertible 37 | { 38 | // `+ sizeof(U*)` is required for GCC 4.5-4.7 39 | template 40 | static typename enableIf(meta::declval())) + sizeof(U*)), Yes>::type test(int); 41 | 42 | template 43 | static No test(...); 44 | 45 | enum { value = sizeof(test(0)) == sizeof(Yes) }; 46 | }; 47 | 48 | template 49 | struct isConvertibleToString : isConvertible {}; 50 | 51 | #if PLOG_ENABLE_WCHAR_INPUT 52 | template 53 | struct isConvertibleToWString : isConvertible {}; 54 | #endif 55 | 56 | template 57 | struct isContainer 58 | { 59 | template 60 | static typename meta::enableIf().begin()) + sizeof(meta::declval().end() 63 | #else 64 | typename U::const_iterator 65 | #endif 66 | )), Yes>::type test(int); 67 | 68 | template 69 | static No test(...); 70 | 71 | enum { value = sizeof(test(0)) == sizeof(Yes) }; 72 | }; 73 | 74 | // Detects `std::filesystem::path` and `boost::filesystem::path`. They look like containers 75 | // but we don't want to treat them as containers, so we use this detector to filter them out. 76 | template 77 | struct isFilesystemPath 78 | { 79 | template 80 | static typename meta::enableIf().preferred_separator)), Yes>::type test(int); 81 | 82 | template 83 | static No test(...); 84 | 85 | enum { value = sizeof(test(0)) == sizeof(Yes) }; 86 | }; 87 | } 88 | #endif 89 | 90 | ////////////////////////////////////////////////////////////////////////// 91 | // Stream output operators as free functions 92 | 93 | #if PLOG_ENABLE_WCHAR_INPUT 94 | inline void operator<<(util::nostringstream& stream, const wchar_t* data) 95 | { 96 | data = data ? data : L"(null)"; 97 | 98 | # ifdef _WIN32 99 | # if PLOG_CHAR_IS_UTF8 100 | std::operator<<(stream, util::toNarrow(data, codePage::kUTF8)); 101 | # else 102 | std::operator<<(stream, data); 103 | # endif 104 | # else 105 | std::operator<<(stream, util::toNarrow(data)); 106 | # endif 107 | } 108 | 109 | inline void operator<<(util::nostringstream& stream, wchar_t* data) 110 | { 111 | plog::detail::operator<<(stream, const_cast(data)); 112 | } 113 | 114 | inline void operator<<(util::nostringstream& stream, const std::wstring& data) 115 | { 116 | plog::detail::operator<<(stream, data.c_str()); 117 | } 118 | #endif 119 | 120 | inline void operator<<(util::nostringstream& stream, const char* data) 121 | { 122 | data = data ? data : "(null)"; 123 | 124 | #if defined(_WIN32) && defined(__BORLANDC__) 125 | # if PLOG_CHAR_IS_UTF8 126 | stream << data; 127 | # else 128 | stream << util::toWide(data); 129 | # endif 130 | #elif defined(_WIN32) 131 | # if PLOG_CHAR_IS_UTF8 132 | std::operator<<(stream, data); 133 | # else 134 | std::operator<<(stream, util::toWide(data)); 135 | # endif 136 | #else 137 | std::operator<<(stream, data); 138 | #endif 139 | } 140 | 141 | inline void operator<<(util::nostringstream& stream, char* data) 142 | { 143 | plog::detail::operator<<(stream, const_cast(data)); 144 | } 145 | 146 | inline void operator<<(util::nostringstream& stream, const std::string& data) 147 | { 148 | plog::detail::operator<<(stream, data.c_str()); 149 | } 150 | 151 | #ifdef __cpp_char8_t 152 | inline void operator<<(util::nostringstream& stream, const char8_t* data) 153 | { 154 | # if PLOG_CHAR_IS_UTF8 155 | plog::detail::operator<<(stream, reinterpret_cast(data)); 156 | # else 157 | plog::detail::operator<<(stream, util::toWide(reinterpret_cast(data), codePage::kUTF8)); 158 | # endif 159 | } 160 | #endif //__cpp_char8_t 161 | 162 | // Print `std::pair` 163 | template 164 | inline void operator<<(util::nostringstream& stream, const std::pair& data) 165 | { 166 | stream << data.first; 167 | stream << ":"; 168 | stream << data.second; 169 | } 170 | 171 | #if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600 172 | #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` 173 | // Print data that can be casted to `std::string` 174 | template 175 | inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) 176 | { 177 | plog::detail::operator<<(stream, static_cast(data)); 178 | } 179 | 180 | #if PLOG_ENABLE_WCHAR_INPUT 181 | // Print data that can be casted to `std::wstring` 182 | template 183 | inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) 184 | { 185 | plog::detail::operator<<(stream, static_cast(data)); 186 | } 187 | #endif 188 | 189 | // Print std containers 190 | template 191 | inline typename meta::enableIf::value && 192 | !meta::isConvertibleToString::value && 193 | #if PLOG_ENABLE_WCHAR_INPUT 194 | !meta::isConvertibleToWString::value && 195 | #endif 196 | !meta::isFilesystemPath::value, void>::type operator<<(util::nostringstream& stream, const T& data) 197 | { 198 | stream << "["; 199 | 200 | for (typename T::const_iterator it = data.begin(); it != data.end();) 201 | { 202 | stream << *it; 203 | 204 | if (++it == data.end()) 205 | { 206 | break; 207 | } 208 | 209 | stream << ", "; 210 | } 211 | 212 | stream << "]"; 213 | } 214 | #endif 215 | #endif 216 | 217 | #ifdef __cplusplus_cli 218 | // Print managed C++ `System::String^` 219 | inline void operator<<(util::nostringstream& stream, System::String^ data) 220 | { 221 | cli::pin_ptr ptr = PtrToStringChars(data); 222 | plog::detail::operator<<(stream, static_cast(ptr)); 223 | } 224 | #endif 225 | 226 | #if PLOG_ENABLE_WCHAR_INPUT && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` 227 | namespace meta 228 | { 229 | template 230 | struct valueType { enum { value = Value }; }; 231 | 232 | template 233 | inline No operator<<(Stream&, const T&); 234 | 235 | template 236 | struct isStreamable : valueType(), meta::declval())) != sizeof(No)> {}; 237 | 238 | template 239 | struct isStreamable : valueType {}; 240 | 241 | template 242 | struct isStreamable : valueType {}; 243 | 244 | template 245 | struct isStreamable : valueType {}; 246 | 247 | // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const wchar_t*) = delete` so explicitly define it 248 | template<> 249 | struct isStreamable : valueType {}; 250 | 251 | # ifdef __cpp_char8_t 252 | // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const char8_t*) = delete` so explicitly define it 253 | template 254 | struct isStreamable : valueType {}; 255 | 256 | template 257 | struct isStreamable : valueType {}; 258 | # endif //__cpp_char8_t 259 | } 260 | 261 | # if PLOG_CHAR_IS_UTF8 262 | // Print types that can be streamed into `std::owstringstream` but not into `std::ostringstream` when we use UTF-8 on Windows 263 | template 264 | inline typename meta::enableIf::value && 265 | !meta::isStreamable::value && 266 | !meta::isConvertibleToWString::value, void>::type operator<<(std::ostringstream& stream, const T& data) 267 | { 268 | std::wostringstream ss; 269 | ss << data; 270 | stream << ss.str(); 271 | } 272 | # else 273 | // Print types that can be streamed into `std::ostringstream` but not into `std::owstringstream` when we use `wchar_t` on Windows 274 | template 275 | inline typename meta::enableIf::value && 276 | !meta::isStreamable::value && 277 | !meta::isConvertibleToString::value, void>::type operator<<(std::wostringstream& stream, const T& data) 278 | { 279 | std::ostringstream ss; 280 | ss << data; 281 | stream << ss.str(); 282 | } 283 | # endif 284 | #endif 285 | } 286 | 287 | class Record 288 | { 289 | public: 290 | Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId) 291 | : m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file), m_instanceId(instanceId) 292 | { 293 | util::ftime(&m_time); 294 | } 295 | 296 | Record& ref() 297 | { 298 | return *this; 299 | } 300 | 301 | ////////////////////////////////////////////////////////////////////////// 302 | // Stream output operators 303 | 304 | Record& operator<<(char data) 305 | { 306 | char str[] = { data, 0 }; 307 | return *this << str; 308 | } 309 | 310 | #if PLOG_ENABLE_WCHAR_INPUT 311 | Record& operator<<(wchar_t data) 312 | { 313 | wchar_t str[] = { data, 0 }; 314 | return *this << str; 315 | } 316 | #endif 317 | 318 | Record& operator<<(util::nostream& (PLOG_CDECL *data)(util::nostream&)) 319 | { 320 | m_message << data; 321 | return *this; 322 | } 323 | 324 | #ifdef QT_VERSION 325 | Record& operator<<(const QString& data) 326 | { 327 | # if PLOG_CHAR_IS_UTF8 328 | return *this << data.toStdString(); 329 | # else 330 | return *this << data.toStdWString(); 331 | # endif 332 | } 333 | 334 | # if QT_VERSION < 0x060000 335 | Record& operator<<(const QStringRef& data) 336 | { 337 | return *this << data.toString(); 338 | } 339 | # endif 340 | 341 | # ifdef QSTRINGVIEW_H 342 | Record& operator<<(QStringView data) 343 | { 344 | return *this << data.toString(); 345 | } 346 | # endif 347 | #endif 348 | 349 | template 350 | Record& operator<<(const T& data) 351 | { 352 | using namespace plog::detail; 353 | 354 | m_message << data; 355 | return *this; 356 | } 357 | 358 | #ifndef __cplusplus_cli 359 | Record& printf(const char* format, ...) 360 | { 361 | using namespace util; 362 | 363 | char* str = NULL; 364 | va_list ap; 365 | 366 | va_start(ap, format); 367 | int len = vasprintf(&str, format, ap); 368 | static_cast(len); 369 | va_end(ap); 370 | 371 | *this << str; 372 | free(str); 373 | 374 | return *this; 375 | } 376 | 377 | #ifdef _WIN32 378 | Record& printf(const wchar_t* format, ...) 379 | { 380 | using namespace util; 381 | 382 | wchar_t* str = NULL; 383 | va_list ap; 384 | 385 | va_start(ap, format); 386 | int len = vaswprintf(&str, format, ap); 387 | static_cast(len); 388 | va_end(ap); 389 | 390 | *this << str; 391 | free(str); 392 | 393 | return *this; 394 | } 395 | #endif 396 | #endif //__cplusplus_cli 397 | 398 | ////////////////////////////////////////////////////////////////////////// 399 | // Getters 400 | 401 | virtual const util::Time& getTime() const 402 | { 403 | return m_time; 404 | } 405 | 406 | virtual Severity getSeverity() const 407 | { 408 | return m_severity; 409 | } 410 | 411 | virtual unsigned int getTid() const 412 | { 413 | return m_tid; 414 | } 415 | 416 | virtual const void* getObject() const 417 | { 418 | return m_object; 419 | } 420 | 421 | virtual size_t getLine() const 422 | { 423 | return m_line; 424 | } 425 | 426 | virtual const util::nchar* getMessage() const 427 | { 428 | m_messageStr = m_message.str(); 429 | return m_messageStr.c_str(); 430 | } 431 | 432 | virtual const char* getFunc() const 433 | { 434 | m_funcStr = util::processFuncName(m_func); 435 | return m_funcStr.c_str(); 436 | } 437 | 438 | virtual const char* getFile() const 439 | { 440 | return m_file; 441 | } 442 | 443 | virtual ~Record() // virtual destructor to satisfy -Wnon-virtual-dtor warning 444 | { 445 | } 446 | 447 | virtual int getInstanceId() const 448 | { 449 | return m_instanceId; 450 | } 451 | 452 | private: 453 | util::Time m_time; 454 | const Severity m_severity; 455 | const unsigned int m_tid; 456 | const void* const m_object; 457 | const size_t m_line; 458 | util::nostringstream m_message; 459 | const char* const m_func; 460 | const char* const m_file; 461 | const int m_instanceId; 462 | mutable std::string m_funcStr; 463 | mutable util::nstring m_messageStr; 464 | }; 465 | } 466 | -------------------------------------------------------------------------------- /include/plog/Severity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace plog 5 | { 6 | enum Severity 7 | { 8 | none = 0, 9 | fatal = 1, 10 | error = 2, 11 | warning = 3, 12 | info = 4, 13 | debug = 5, 14 | verbose = 6 15 | }; 16 | 17 | #ifdef _MSC_VER 18 | # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' 19 | #endif 20 | inline const char* severityToString(Severity severity) 21 | { 22 | switch (severity) 23 | { 24 | case fatal: 25 | return "FATAL"; 26 | case error: 27 | return "ERROR"; 28 | case warning: 29 | return "WARN"; 30 | case info: 31 | return "INFO"; 32 | case debug: 33 | return "DEBUG"; 34 | case verbose: 35 | return "VERB"; 36 | default: 37 | return "NONE"; 38 | } 39 | } 40 | 41 | inline Severity severityFromString(const char* str) 42 | { 43 | switch (std::toupper(str[0])) 44 | { 45 | case 'F': 46 | return fatal; 47 | case 'E': 48 | return error; 49 | case 'W': 50 | return warning; 51 | case 'I': 52 | return info; 53 | case 'D': 54 | return debug; 55 | case 'V': 56 | return verbose; 57 | default: 58 | return none; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /include/plog/Util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef PLOG_ENABLE_WCHAR_INPUT 11 | # ifdef _WIN32 12 | # define PLOG_ENABLE_WCHAR_INPUT 1 13 | # else 14 | # define PLOG_ENABLE_WCHAR_INPUT 0 15 | # endif 16 | #endif 17 | 18 | ////////////////////////////////////////////////////////////////////////// 19 | // PLOG_CHAR_IS_UTF8 specifies character encoding of `char` type. On *nix 20 | // systems it's set to UTF-8 while on Windows in can be ANSI or UTF-8. It 21 | // automatically detects `/utf-8` command line option in MSVC. Also it can 22 | // be set manually if required. 23 | // This option allows to support http://utf8everywhere.org approach. 24 | 25 | #ifndef PLOG_CHAR_IS_UTF8 26 | # if defined(_WIN32) && !defined(_UTF8) 27 | # define PLOG_CHAR_IS_UTF8 0 28 | # else 29 | # define PLOG_CHAR_IS_UTF8 1 30 | # endif 31 | #endif 32 | 33 | #ifdef _WIN32 34 | # if defined(PLOG_EXPORT) 35 | # define PLOG_LINKAGE __declspec(dllexport) 36 | # elif defined(PLOG_IMPORT) 37 | # define PLOG_LINKAGE __declspec(dllimport) 38 | # endif 39 | # if defined(PLOG_GLOBAL) 40 | # error "PLOG_GLOBAL isn't supported on Windows" 41 | # endif 42 | #else 43 | # if defined(PLOG_GLOBAL) 44 | # define PLOG_LINKAGE __attribute__ ((visibility ("default"))) 45 | # elif defined(PLOG_LOCAL) 46 | # define PLOG_LINKAGE __attribute__ ((visibility ("hidden"))) 47 | # define PLOG_LINKAGE_HIDDEN PLOG_LINKAGE 48 | # endif 49 | # if defined(PLOG_EXPORT) || defined(PLOG_IMPORT) 50 | # error "PLOG_EXPORT/PLOG_IMPORT is supported only on Windows" 51 | # endif 52 | #endif 53 | 54 | #ifndef PLOG_LINKAGE 55 | # define PLOG_LINKAGE 56 | #endif 57 | 58 | #ifndef PLOG_LINKAGE_HIDDEN 59 | # define PLOG_LINKAGE_HIDDEN 60 | #endif 61 | 62 | #ifdef _WIN32 63 | # include 64 | # include 65 | # include 66 | # include 67 | # include 68 | #else 69 | # include 70 | # include 71 | # if defined(__linux__) || defined(__FreeBSD__) 72 | # include 73 | # elif defined(__rtems__) 74 | # include 75 | # endif 76 | # if defined(_POSIX_THREADS) 77 | # include 78 | # endif 79 | # if PLOG_ENABLE_WCHAR_INPUT 80 | # include 81 | # endif 82 | #endif 83 | 84 | #ifdef __FREERTOS__ // There is no standard way to know if the code is compiled for FreeRTOS. We expect __FREERTOS__ macro to be defined in such case. 85 | # include 86 | # include 87 | # include 88 | #endif 89 | 90 | #if PLOG_CHAR_IS_UTF8 91 | # define PLOG_NSTR(x) x 92 | #else 93 | # define _PLOG_NSTR(x) L##x 94 | # define PLOG_NSTR(x) _PLOG_NSTR(x) 95 | #endif 96 | 97 | #ifdef _WIN32 98 | # define PLOG_CDECL __cdecl 99 | #else 100 | # define PLOG_CDECL 101 | #endif 102 | 103 | #if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700 104 | # define PLOG_OVERRIDE override 105 | #else 106 | # define PLOG_OVERRIDE 107 | #endif 108 | 109 | namespace plog 110 | { 111 | namespace util 112 | { 113 | #if PLOG_CHAR_IS_UTF8 114 | typedef std::string nstring; 115 | typedef std::ostringstream nostringstream; 116 | typedef std::istringstream nistringstream; 117 | typedef std::ostream nostream; 118 | typedef char nchar; 119 | #else 120 | typedef std::wstring nstring; 121 | typedef std::wostringstream nostringstream; 122 | typedef std::wistringstream nistringstream; 123 | typedef std::wostream nostream; 124 | typedef wchar_t nchar; 125 | #endif 126 | 127 | inline void localtime_s(struct tm* t, const time_t* time) 128 | { 129 | #if defined(_WIN32) && defined(__BORLANDC__) 130 | ::localtime_s(time, t); 131 | #elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) 132 | *t = *::localtime(time); 133 | #elif defined(_WIN32) 134 | ::localtime_s(t, time); 135 | #else 136 | ::localtime_r(time, t); 137 | #endif 138 | } 139 | 140 | inline void gmtime_s(struct tm* t, const time_t* time) 141 | { 142 | #if defined(_WIN32) && defined(__BORLANDC__) 143 | ::gmtime_s(time, t); 144 | #elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) 145 | *t = *::gmtime(time); 146 | #elif defined(_WIN32) 147 | ::gmtime_s(t, time); 148 | #else 149 | ::gmtime_r(time, t); 150 | #endif 151 | } 152 | 153 | #ifdef _WIN32 154 | typedef timeb Time; 155 | 156 | inline void ftime(Time* t) 157 | { 158 | ::ftime(t); 159 | } 160 | #else 161 | struct Time 162 | { 163 | time_t time; 164 | unsigned short millitm; 165 | }; 166 | 167 | inline void ftime(Time* t) 168 | { 169 | timeval tv; 170 | ::gettimeofday(&tv, NULL); 171 | 172 | t->time = tv.tv_sec; 173 | t->millitm = static_cast(tv.tv_usec / 1000); 174 | } 175 | #endif 176 | 177 | inline unsigned int gettid() 178 | { 179 | #if defined(__FREERTOS__) && defined(INCLUDE_xTaskGetCurrentTaskHandle) 180 | return static_cast(reinterpret_cast(xTaskGetCurrentTaskHandle())); 181 | #elif defined(_WIN32) 182 | return GetCurrentThreadId(); 183 | #elif defined(__linux__) 184 | return static_cast(::syscall(__NR_gettid)); 185 | #elif defined(__FreeBSD__) 186 | long tid; 187 | syscall(SYS_thr_self, &tid); 188 | return static_cast(tid); 189 | #elif defined(__rtems__) 190 | return rtems_task_self(); 191 | #elif defined(__APPLE__) 192 | uint64_t tid64; 193 | pthread_threadid_np(NULL, &tid64); 194 | return static_cast(tid64); 195 | #else 196 | return 0; 197 | #endif 198 | } 199 | 200 | #ifndef _GNU_SOURCE 201 | inline int vasprintf(char** strp, const char* format, va_list ap) 202 | { 203 | va_list ap_copy; 204 | #if defined(_MSC_VER) && _MSC_VER <= 1600 205 | ap_copy = ap; // there is no va_copy on Visual Studio 2010 206 | #else 207 | va_copy(ap_copy, ap); 208 | #endif 209 | #ifndef __STDC_SECURE_LIB__ 210 | int charCount = vsnprintf(NULL, 0, format, ap_copy); 211 | #else 212 | int charCount = _vscprintf(format, ap_copy); 213 | #endif 214 | va_end(ap_copy); 215 | if (charCount < 0) 216 | { 217 | return -1; 218 | } 219 | 220 | size_t bufferCharCount = static_cast(charCount) + 1; 221 | 222 | char* str = static_cast(malloc(bufferCharCount)); 223 | if (!str) 224 | { 225 | return -1; 226 | } 227 | 228 | #ifndef __STDC_SECURE_LIB__ 229 | int retval = vsnprintf(str, bufferCharCount, format, ap); 230 | #else 231 | int retval = vsnprintf_s(str, bufferCharCount, static_cast(-1), format, ap); 232 | #endif 233 | if (retval < 0) 234 | { 235 | free(str); 236 | return -1; 237 | } 238 | 239 | *strp = str; 240 | return retval; 241 | } 242 | #endif 243 | 244 | #ifdef _WIN32 245 | inline int vaswprintf(wchar_t** strp, const wchar_t* format, va_list ap) 246 | { 247 | #if defined(__BORLANDC__) 248 | int charCount = 0x1000; // there is no _vscwprintf on Borland/Embarcadero 249 | #else 250 | int charCount = _vscwprintf(format, ap); 251 | if (charCount < 0) 252 | { 253 | return -1; 254 | } 255 | #endif 256 | 257 | size_t bufferCharCount = static_cast(charCount) + 1; 258 | 259 | wchar_t* str = static_cast(malloc(bufferCharCount * sizeof(wchar_t))); 260 | if (!str) 261 | { 262 | return -1; 263 | } 264 | 265 | #if defined(__BORLANDC__) 266 | int retval = vsnwprintf_s(str, bufferCharCount, format, ap); 267 | #elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) 268 | int retval = _vsnwprintf(str, bufferCharCount, format, ap); 269 | #else 270 | int retval = _vsnwprintf_s(str, bufferCharCount, charCount, format, ap); 271 | #endif 272 | if (retval < 0) 273 | { 274 | free(str); 275 | return -1; 276 | } 277 | 278 | *strp = str; 279 | return retval; 280 | } 281 | #endif 282 | 283 | #ifdef _WIN32 284 | inline std::wstring toWide(const char* str, UINT cp = codePage::kChar) 285 | { 286 | size_t len = ::strlen(str); 287 | std::wstring wstr(len, 0); 288 | 289 | if (!wstr.empty()) 290 | { 291 | int wlen = MultiByteToWideChar(cp, 0, str, static_cast(len), &wstr[0], static_cast(wstr.size())); 292 | wstr.resize(wlen); 293 | } 294 | 295 | return wstr; 296 | } 297 | 298 | inline std::wstring toWide(const std::string& str, UINT cp = codePage::kChar) 299 | { 300 | return toWide(str.c_str(), cp); 301 | } 302 | 303 | inline const std::wstring& toWide(const std::wstring& str) // do nothing for already wide string 304 | { 305 | return str; 306 | } 307 | 308 | inline std::string toNarrow(const std::wstring& wstr, long page) 309 | { 310 | int len = WideCharToMultiByte(page, 0, wstr.c_str(), static_cast(wstr.size()), 0, 0, 0, 0); 311 | std::string str(len, 0); 312 | 313 | if (!str.empty()) 314 | { 315 | WideCharToMultiByte(page, 0, wstr.c_str(), static_cast(wstr.size()), &str[0], len, 0, 0); 316 | } 317 | 318 | return str; 319 | } 320 | #elif PLOG_ENABLE_WCHAR_INPUT 321 | inline std::string toNarrow(const wchar_t* wstr) 322 | { 323 | size_t wlen = ::wcslen(wstr); 324 | std::string str(wlen * sizeof(wchar_t), 0); 325 | 326 | if (!str.empty()) 327 | { 328 | const char* in = reinterpret_cast(&wstr[0]); 329 | char* out = &str[0]; 330 | size_t inBytes = wlen * sizeof(wchar_t); 331 | size_t outBytes = str.size(); 332 | 333 | iconv_t cd = ::iconv_open("UTF-8", "WCHAR_T"); 334 | ::iconv(cd, const_cast(&in), &inBytes, &out, &outBytes); 335 | ::iconv_close(cd); 336 | 337 | str.resize(str.size() - outBytes); 338 | } 339 | 340 | return str; 341 | } 342 | #endif 343 | 344 | inline std::string processFuncName(const char* func) 345 | { 346 | #if (defined(_WIN32) && !defined(__MINGW32__)) || defined(__OBJC__) 347 | return std::string(func); 348 | #else 349 | const char* funcBegin = func; 350 | const char* funcEnd = ::strchr(funcBegin, '('); 351 | int foundTemplate = 0; 352 | 353 | if (!funcEnd) 354 | { 355 | return std::string(func); 356 | } 357 | 358 | for (const char* i = funcEnd - 1; i >= funcBegin; --i) // search backwards for the first space char 359 | { 360 | if (*i == '>') 361 | { 362 | foundTemplate++; 363 | } 364 | else if (*i == '<') 365 | { 366 | foundTemplate--; 367 | } 368 | else if (*i == ' ' && foundTemplate == 0) 369 | { 370 | funcBegin = i + 1; 371 | break; 372 | } 373 | } 374 | 375 | return std::string(funcBegin, funcEnd); 376 | #endif 377 | } 378 | 379 | inline const nchar* findExtensionDot(const nchar* fileName) 380 | { 381 | #if PLOG_CHAR_IS_UTF8 382 | return std::strrchr(fileName, '.'); 383 | #else 384 | return std::wcsrchr(fileName, L'.'); 385 | #endif 386 | } 387 | 388 | inline void splitFileName(const nchar* fileName, nstring& fileNameNoExt, nstring& fileExt) 389 | { 390 | const nchar* dot = findExtensionDot(fileName); 391 | 392 | if (dot) 393 | { 394 | fileNameNoExt.assign(fileName, dot); 395 | fileExt.assign(dot + 1); 396 | } 397 | else 398 | { 399 | fileNameNoExt.assign(fileName); 400 | fileExt.clear(); 401 | } 402 | } 403 | 404 | class PLOG_LINKAGE NonCopyable 405 | { 406 | protected: 407 | NonCopyable() 408 | { 409 | } 410 | 411 | private: 412 | NonCopyable(const NonCopyable&); 413 | NonCopyable& operator=(const NonCopyable&); 414 | }; 415 | 416 | class PLOG_LINKAGE_HIDDEN File : NonCopyable 417 | { 418 | public: 419 | File() : m_file(-1) 420 | { 421 | } 422 | 423 | ~File() 424 | { 425 | close(); 426 | } 427 | 428 | size_t open(const nstring& fileName) 429 | { 430 | #if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__)) 431 | m_file = ::_wsopen(toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, SH_DENYWR, _S_IREAD | _S_IWRITE); 432 | #elif defined(_WIN32) 433 | ::_wsopen_s(&m_file, toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, _SH_DENYWR, _S_IREAD | _S_IWRITE); 434 | #elif defined(O_CLOEXEC) 435 | m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 436 | #else 437 | m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 438 | #endif 439 | return seek(0, SEEK_END); 440 | } 441 | 442 | size_t write(const void* buf, size_t count) 443 | { 444 | return m_file != -1 ? static_cast( 445 | #ifdef _WIN32 446 | ::_write(m_file, buf, static_cast(count)) 447 | #else 448 | ::write(m_file, buf, count) 449 | #endif 450 | ) : static_cast(-1); 451 | } 452 | 453 | template 454 | size_t write(const std::basic_string& str) 455 | { 456 | return write(str.data(), str.size() * sizeof(CharType)); 457 | } 458 | 459 | size_t seek(size_t offset, int whence) 460 | { 461 | return m_file != -1 ? static_cast( 462 | #if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__)) 463 | ::_lseek(m_file, static_cast(offset), whence) 464 | #elif defined(_WIN32) 465 | ::_lseeki64(m_file, static_cast(offset), whence) 466 | #else 467 | ::lseek(m_file, static_cast(offset), whence) 468 | #endif 469 | ) : static_cast(-1); 470 | } 471 | 472 | void close() 473 | { 474 | if (m_file != -1) 475 | { 476 | #ifdef _WIN32 477 | ::_close(m_file); 478 | #else 479 | ::close(m_file); 480 | #endif 481 | m_file = -1; 482 | } 483 | } 484 | 485 | static int unlink(const nstring& fileName) 486 | { 487 | #ifdef _WIN32 488 | return ::_wunlink(toWide(fileName).c_str()); 489 | #else 490 | return ::unlink(fileName.c_str()); 491 | #endif 492 | } 493 | 494 | static int rename(const nstring& oldFilename, const nstring& newFilename) 495 | { 496 | #ifdef _WIN32 497 | return MoveFileW(toWide(oldFilename).c_str(), toWide(newFilename).c_str()); 498 | #else 499 | return ::rename(oldFilename.c_str(), newFilename.c_str()); 500 | #endif 501 | } 502 | 503 | private: 504 | int m_file; 505 | }; 506 | 507 | class PLOG_LINKAGE_HIDDEN Mutex : NonCopyable 508 | { 509 | public: 510 | Mutex() 511 | { 512 | #ifdef __FREERTOS__ 513 | m_sync = xSemaphoreCreateBinary(); 514 | xSemaphoreGive(m_sync); 515 | #elif defined(_WIN32) 516 | InitializeCriticalSection(&m_sync); 517 | #elif defined(__rtems__) 518 | rtems_semaphore_create(0, 1, 519 | RTEMS_PRIORITY | 520 | RTEMS_BINARY_SEMAPHORE | 521 | RTEMS_INHERIT_PRIORITY, 1, &m_sync); 522 | #elif defined(_POSIX_THREADS) 523 | ::pthread_mutex_init(&m_sync, 0); 524 | #endif 525 | } 526 | 527 | ~Mutex() 528 | { 529 | #ifdef __FREERTOS__ 530 | vSemaphoreDelete(m_sync); 531 | #elif defined(_WIN32) 532 | DeleteCriticalSection(&m_sync); 533 | #elif defined(__rtems__) 534 | rtems_semaphore_delete(m_sync); 535 | #elif defined(_POSIX_THREADS) 536 | ::pthread_mutex_destroy(&m_sync); 537 | #endif 538 | } 539 | 540 | friend class MutexLock; 541 | 542 | private: 543 | void lock() 544 | { 545 | #ifdef __FREERTOS__ 546 | xSemaphoreTake(m_sync, portMAX_DELAY); 547 | #elif defined(_WIN32) 548 | EnterCriticalSection(&m_sync); 549 | #elif defined(__rtems__) 550 | rtems_semaphore_obtain(m_sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT); 551 | #elif defined(_POSIX_THREADS) 552 | ::pthread_mutex_lock(&m_sync); 553 | #endif 554 | } 555 | 556 | void unlock() 557 | { 558 | #ifdef __FREERTOS__ 559 | xSemaphoreGive(m_sync); 560 | #elif defined(_WIN32) 561 | LeaveCriticalSection(&m_sync); 562 | #elif defined(__rtems__) 563 | rtems_semaphore_release(m_sync); 564 | #elif defined(_POSIX_THREADS) 565 | ::pthread_mutex_unlock(&m_sync); 566 | #endif 567 | } 568 | 569 | private: 570 | #ifdef __FREERTOS__ 571 | SemaphoreHandle_t m_sync; 572 | #elif defined(_WIN32) 573 | CRITICAL_SECTION m_sync; 574 | #elif defined(__rtems__) 575 | rtems_id m_sync; 576 | #elif defined(_POSIX_THREADS) 577 | pthread_mutex_t m_sync; 578 | #endif 579 | }; 580 | 581 | class PLOG_LINKAGE_HIDDEN MutexLock : NonCopyable 582 | { 583 | public: 584 | MutexLock(Mutex& mutex) : m_mutex(mutex) 585 | { 586 | m_mutex.lock(); 587 | } 588 | 589 | ~MutexLock() 590 | { 591 | m_mutex.unlock(); 592 | } 593 | 594 | private: 595 | Mutex& m_mutex; 596 | }; 597 | 598 | template 599 | #ifdef _WIN32 600 | class Singleton : NonCopyable 601 | #else 602 | class PLOG_LINKAGE Singleton : NonCopyable 603 | #endif 604 | { 605 | public: 606 | #if (defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 8) && !defined(__BORLANDC__) 607 | // This constructor is called before the `T` object is fully constructed, and 608 | // pointers are not dereferenced anyway, so UBSan shouldn't check vptrs. 609 | __attribute__((no_sanitize("vptr"))) 610 | #endif 611 | Singleton() 612 | { 613 | assert(!m_instance); 614 | m_instance = static_cast(this); 615 | } 616 | 617 | ~Singleton() 618 | { 619 | assert(m_instance); 620 | m_instance = 0; 621 | } 622 | 623 | static T* getInstance() 624 | { 625 | return m_instance; 626 | } 627 | 628 | private: 629 | static T* m_instance; 630 | }; 631 | 632 | template 633 | T* Singleton::m_instance = NULL; 634 | } 635 | } 636 | -------------------------------------------------------------------------------- /include/plog/WinApi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | 5 | // These windows structs must be in a global namespace 6 | struct HKEY__; 7 | struct _SECURITY_ATTRIBUTES; 8 | struct _CONSOLE_SCREEN_BUFFER_INFO; 9 | struct _RTL_CRITICAL_SECTION; 10 | 11 | namespace plog 12 | { 13 | typedef unsigned long DWORD; 14 | typedef unsigned short WORD; 15 | typedef unsigned char BYTE; 16 | typedef unsigned int UINT; 17 | typedef int BOOL; 18 | typedef long LSTATUS; 19 | typedef char* LPSTR; 20 | typedef wchar_t* LPWSTR; 21 | typedef const char* LPCSTR; 22 | typedef const wchar_t* LPCWSTR; 23 | typedef void* HANDLE; 24 | typedef HKEY__* HKEY; 25 | typedef size_t ULONG_PTR; 26 | 27 | struct CRITICAL_SECTION 28 | { 29 | void* DebugInfo; 30 | long LockCount; 31 | long RecursionCount; 32 | HANDLE OwningThread; 33 | HANDLE LockSemaphore; 34 | ULONG_PTR SpinCount; 35 | }; 36 | 37 | struct COORD 38 | { 39 | short X; 40 | short Y; 41 | }; 42 | 43 | struct SMALL_RECT 44 | { 45 | short Left; 46 | short Top; 47 | short Right; 48 | short Bottom; 49 | }; 50 | 51 | struct CONSOLE_SCREEN_BUFFER_INFO 52 | { 53 | COORD dwSize; 54 | COORD dwCursorPosition; 55 | WORD wAttributes; 56 | SMALL_RECT srWindow; 57 | COORD dwMaximumWindowSize; 58 | }; 59 | 60 | namespace codePage 61 | { 62 | const UINT kActive = 0; 63 | const UINT kUTF8 = 65001; 64 | #if PLOG_CHAR_IS_UTF8 65 | const UINT kChar = kUTF8; 66 | #else 67 | const UINT kChar = kActive; 68 | #endif 69 | } 70 | 71 | namespace eventLog 72 | { 73 | const WORD kErrorType = 0x0001; 74 | const WORD kWarningType = 0x0002; 75 | const WORD kInformationType = 0x0004; 76 | } 77 | 78 | namespace hkey 79 | { 80 | const HKEY kLocalMachine = reinterpret_cast(static_cast(0x80000002)); 81 | } 82 | 83 | namespace regSam 84 | { 85 | const DWORD kQueryValue = 0x0001; 86 | const DWORD kSetValue = 0x0002; 87 | } 88 | 89 | namespace regType 90 | { 91 | const DWORD kExpandSz = 2; 92 | const DWORD kDword = 4; 93 | } 94 | 95 | namespace stdHandle 96 | { 97 | const DWORD kOutput = static_cast(-11); 98 | const DWORD kErrorOutput = static_cast(-12); 99 | } 100 | 101 | namespace foreground 102 | { 103 | const WORD kBlue = 0x0001; 104 | const WORD kGreen = 0x0002; 105 | const WORD kRed = 0x0004; 106 | const WORD kIntensity = 0x0008; 107 | } 108 | 109 | namespace background 110 | { 111 | const WORD kBlue = 0x0010; 112 | const WORD kGreen = 0x0020; 113 | const WORD kRed = 0x0040; 114 | const WORD kIntensity = 0x0080; 115 | } 116 | 117 | extern "C" 118 | { 119 | __declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); 120 | __declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, BOOL* lpUsedDefaultChar); 121 | 122 | __declspec(dllimport) DWORD __stdcall GetCurrentThreadId(); 123 | 124 | __declspec(dllimport) BOOL __stdcall MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName); 125 | 126 | __declspec(dllimport) void __stdcall InitializeCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); 127 | __declspec(dllimport) void __stdcall EnterCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); 128 | __declspec(dllimport) void __stdcall LeaveCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); 129 | __declspec(dllimport) void __stdcall DeleteCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); 130 | 131 | __declspec(dllimport) HANDLE __stdcall RegisterEventSourceW(LPCWSTR lpUNCServerName, LPCWSTR lpSourceName); 132 | __declspec(dllimport) BOOL __stdcall DeregisterEventSource(HANDLE hEventLog); 133 | __declspec(dllimport) BOOL __stdcall ReportEventW(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, void* lpUserSid, WORD wNumStrings, DWORD dwDataSize, LPCWSTR* lpStrings, void* lpRawData); 134 | 135 | __declspec(dllimport) LSTATUS __stdcall RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, DWORD samDesired, _SECURITY_ATTRIBUTES* lpSecurityAttributes, HKEY* phkResult, DWORD* lpdwDisposition); 136 | __declspec(dllimport) LSTATUS __stdcall RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData); 137 | __declspec(dllimport) LSTATUS __stdcall RegCloseKey(HKEY hKey); 138 | __declspec(dllimport) LSTATUS __stdcall RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); 139 | __declspec(dllimport) LSTATUS __stdcall RegDeleteKeyW(HKEY hKey, LPCWSTR lpSubKey); 140 | 141 | __declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle); 142 | 143 | __declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const void* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten, void* lpReserved); 144 | __declspec(dllimport) BOOL __stdcall GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, _CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo); 145 | __declspec(dllimport) BOOL __stdcall SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes); 146 | 147 | __declspec(dllimport) void __stdcall OutputDebugStringW(LPCWSTR lpOutputString); 148 | } 149 | 150 | inline void InitializeCriticalSection(CRITICAL_SECTION* criticalSection) 151 | { 152 | plog::InitializeCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); 153 | } 154 | 155 | inline void EnterCriticalSection(CRITICAL_SECTION* criticalSection) 156 | { 157 | plog::EnterCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); 158 | } 159 | 160 | inline void LeaveCriticalSection(CRITICAL_SECTION* criticalSection) 161 | { 162 | plog::LeaveCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); 163 | } 164 | 165 | inline void DeleteCriticalSection(CRITICAL_SECTION* criticalSection) 166 | { 167 | plog::DeleteCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); 168 | } 169 | 170 | inline BOOL GetConsoleScreenBufferInfo(HANDLE consoleOutput, CONSOLE_SCREEN_BUFFER_INFO* consoleScreenBufferInfo) 171 | { 172 | return plog::GetConsoleScreenBufferInfo(consoleOutput, reinterpret_cast<_CONSOLE_SCREEN_BUFFER_INFO*>(consoleScreenBufferInfo)); 173 | } 174 | } 175 | #endif // _WIN32 176 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", 3 | "name": "plog", 4 | "version": "1.1.10", 5 | "description": "Portable, simple and extensible C++ logging library", 6 | "keywords": [ 7 | "plog", 8 | "log", 9 | "logger", 10 | "logging" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/SergiusTheBest/plog.git" 15 | }, 16 | "authors": { 17 | "name": "Sergey Podobry", 18 | "email": "sergey.podobry@gmail.com", 19 | "maintainer": true 20 | }, 21 | "license": "MIT", 22 | "frameworks": "*", 23 | "platforms": "*", 24 | "headers": [ 25 | "plog/Log.h", 26 | "plog/Init.h", 27 | "plog/Appenders/ArduinoAppender.h", 28 | "plog/Formatters/CsvFormatter.h", 29 | "plog/Formatters/FuncMessageFormatter.h", 30 | "plog/Formatters/MessageOnlyFormatter.h", 31 | "plog/Formatters/TxtFormatter.h" 32 | ], 33 | "examples": [ 34 | { 35 | "name": "Arduino", 36 | "base": "samples/Arduino", 37 | "files": [ 38 | "platformio.ini", 39 | "src/main.cpp" 40 | ] 41 | } 42 | ], 43 | "export": { 44 | "include": [ 45 | "doc", 46 | "include", 47 | "samples/Arduino", 48 | "LICENSE", 49 | "README.md" 50 | ] 51 | }, 52 | "build": { 53 | "includeDir": "include" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /plog.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | plog 5 | $version$ 6 | Pretty powerful logging library in about 1000 lines of code 7 | SergiusTheBest 8 | https://github.com/SergiusTheBest/plog 9 | MIT 10 | false 11 | native log logging c++ cpp 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /plog.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)../../lib/native/include;%(AdditionalIncludeDirectories) 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/Android/AndroidJNI/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // AndroidJNI - shows how to use the Android appender in JNI. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" jint JNI_Onload(JavaVM*, void*) 12 | { 13 | static plog::AndroidAppender appender("MyApp"); // Create an appender and set a log tag. 14 | plog::init(plog::debug, &appender); // Initialize the logger with the appender. 15 | 16 | return JNI_VERSION_1_6; 17 | } 18 | 19 | extern "C" void Java_com_github_sergius_myapp_Sample_foo(JNIEnv*, jobject) 20 | { 21 | PLOGD << "Hello Android!"; 22 | } 23 | -------------------------------------------------------------------------------- /samples/Android/AndroidNative/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // AndroidNative - shows how to use an Android appender. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | static plog::AndroidAppender androidAppender("app"); 13 | plog::init(plog::verbose, &androidAppender); 14 | 15 | PLOG_VERBOSE << "This is a VERBOSE message"; 16 | PLOG_DEBUG << "This is a DEBUG message"; 17 | PLOG_INFO << "This is an INFO message"; 18 | PLOG_WARNING << "This is a WARNING message"; 19 | PLOG_ERROR << "This is an ERROR message"; 20 | PLOG_FATAL << "This is a FATAL message"; 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /samples/Android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(ANDROID) 2 | add_library(AndroidJNI SHARED AndroidJNI/Main.cpp) 3 | target_link_libraries(AndroidJNI plog) 4 | 5 | add_executable(AndroidNative AndroidNative/Main.cpp) 6 | target_link_libraries(AndroidNative plog) 7 | else() 8 | add_custom_target(AndroidJNI SOURCES AndroidJNI/Main.cpp) 9 | add_custom_target(AndroidNative SOURCES AndroidNative/Main.cpp) 10 | endif() 11 | 12 | set_target_properties(AndroidJNI PROPERTIES FOLDER Samples/Android) 13 | set_target_properties(AndroidNative PROPERTIES FOLDER Samples/Android) 14 | -------------------------------------------------------------------------------- /samples/Arduino/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | include_dir = ../../include 13 | 14 | [env:esp8266] 15 | platform = espressif8266 16 | board = nodemcuv2 17 | framework = arduino 18 | 19 | [env:esp32] 20 | platform = espressif32 21 | board = esp32dev 22 | framework = arduino 23 | 24 | [env:pico] 25 | platform = raspberrypi 26 | board = pico 27 | framework = arduino 28 | -------------------------------------------------------------------------------- /samples/Arduino/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static plog::ArduinoAppender arduinoAppender(Serial); 9 | 10 | void setup() 11 | { 12 | plog::init(plog::verbose, &arduinoAppender); 13 | 14 | PLOG_VERBOSE << "verbose"; 15 | PLOG_DEBUG << "debug"; 16 | PLOG_INFO << "info"; 17 | PLOG_WARNING << "warning"; 18 | PLOG_ERROR << "error"; 19 | PLOG_FATAL << "fatal"; 20 | } 21 | 22 | void loop() 23 | { 24 | delay(1000); // Wait for a second 25 | } 26 | -------------------------------------------------------------------------------- /samples/AscDump/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(AscDump Main.cpp) 2 | target_link_libraries(AscDump plog) 3 | set_target_properties(AscDump PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/AscDump/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // AscDump - shows how to use plog::ascdump to dump binary buffers into ASCII. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | int main() 13 | { 14 | plog::init(plog::verbose, plog::streamStdOut); 15 | 16 | std::vector v; 17 | v.push_back(0x6548); 18 | v.push_back(0x6c6c); 19 | v.push_back(0x216f); 20 | v.push_back(0); 21 | v.push_back(-1); 22 | 23 | PLOGI << "v: " << plog::ascdump(v); 24 | 25 | unsigned char arr[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0xff}; 26 | PLOGI << "arr: " << plog::ascdump(arr); 27 | 28 | void* p = malloc(100); 29 | PLOGI << "p: " << plog::ascdump(p, 100); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(${CMAKE_VERSION} VERSION_LESS 3.27.0) 2 | cmake_minimum_required(VERSION 3.0) 3 | elseif(${CMAKE_VERSION} VERSION_LESS 3.31.0) 4 | cmake_minimum_required(VERSION 3.6) 5 | else() 6 | cmake_minimum_required(VERSION 3.10) 7 | endif() 8 | 9 | if(POLICY CMP0063) #Honor visibility properties for all target types 10 | cmake_policy(SET CMP0063 NEW) 11 | endif() 12 | 13 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 14 | 15 | macro(checkObjCXX) 16 | file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy.mm" "int main(){return 0;}\n") 17 | execute_process( 18 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/CMakeFiles 19 | COMMAND ${CMAKE_CXX_COMPILER} dummy.mm 20 | RESULT_VARIABLE result 21 | ERROR_QUIET 22 | OUTPUT_QUIET 23 | ) 24 | file(REMOVE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy.mm") 25 | 26 | if("${result}" STREQUAL "0") 27 | set(CMAKE_OBJCXX_AVAILABLE 1) 28 | message("-- ObjectiveC++ support is detected") 29 | endif() 30 | endmacro() 31 | 32 | project(Samples) 33 | 34 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 35 | set(CMAKE_COMPILER_IS_CLANGXX 1) 36 | endif() 37 | 38 | if(MSVC) 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX") 40 | string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") 41 | elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) 42 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -Wnon-virtual-dtor -Wundef -pedantic -Werror") 43 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 44 | set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) 45 | checkObjCXX() 46 | if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) 47 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") 48 | endif() 49 | if(CMAKE_COMPILER_IS_CLANGXX) 50 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnewline-eof") 51 | endif() 52 | endif() 53 | 54 | add_subdirectory(Android) 55 | add_subdirectory(AscDump) 56 | add_subdirectory(Chained) 57 | add_subdirectory(ColorConsole) 58 | add_subdirectory(CustomAppender) 59 | add_subdirectory(CustomConverter) 60 | add_subdirectory(CustomFormatter) 61 | add_subdirectory(CustomType) 62 | add_subdirectory(CXX11) 63 | add_subdirectory(CXX17) 64 | add_subdirectory(DebugOutput) 65 | add_subdirectory(Demo) 66 | add_subdirectory(DisableLogging) 67 | add_subdirectory(DynamicAppender) 68 | add_subdirectory(EventLog) 69 | add_subdirectory(Facilities) 70 | add_subdirectory(FreeRTOS) 71 | add_subdirectory(Hello) 72 | add_subdirectory(HexDump) 73 | add_subdirectory(Library) 74 | add_subdirectory(MultiAppender) 75 | add_subdirectory(MultiInstance) 76 | add_subdirectory(NotShared) 77 | add_subdirectory(ObjectiveC) 78 | add_subdirectory(Path) 79 | add_subdirectory(Performance) 80 | add_subdirectory(PrintVar) 81 | add_subdirectory(SetFileName) 82 | add_subdirectory(Shared) 83 | add_subdirectory(SkipNativeEOL) 84 | add_subdirectory(UtcTime) 85 | add_subdirectory(Utf8Everywhere) 86 | -------------------------------------------------------------------------------- /samples/CXX11/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT CMAKE_VERSION VERSION_LESS 3.3.0) 2 | if(${CMAKE_VERSION} VERSION_LESS 3.27.0) 3 | cmake_minimum_required(VERSION 3.3) 4 | elseif(${CMAKE_VERSION} VERSION_LESS 3.31.0) 5 | cmake_minimum_required(VERSION 3.6) 6 | else() 7 | cmake_minimum_required(VERSION 3.10) 8 | endif() 9 | 10 | if(cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) 11 | add_executable(CXX11 Main.cpp) 12 | target_link_libraries(CXX11 plog::plog) 13 | set_target_properties(CXX11 PROPERTIES FOLDER Samples CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON) 14 | endif() 15 | endif() 16 | -------------------------------------------------------------------------------- /samples/CXX11/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CXX11 - demonstrates log stream abilities for C++11 features. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | int main() 14 | { 15 | plog::init(plog::debug, plog::streamStdOut); // Initialize logging 16 | 17 | std::unordered_map unorderedMap; 18 | unorderedMap["red"] = 1; 19 | unorderedMap["green"] = 2; 20 | unorderedMap["blue"] = 4; 21 | PLOG_INFO << unorderedMap; 22 | 23 | std::unordered_set unorderedSet; 24 | unorderedSet.insert("red"); 25 | unorderedSet.insert("green"); 26 | unorderedSet.insert("blue"); 27 | PLOG_INFO << unorderedSet; 28 | 29 | std::array array = {{1, 2, 3, 4}}; 30 | PLOG_INFO << array; 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /samples/CXX17/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT CMAKE_VERSION VERSION_LESS 3.3.0) 2 | if(${CMAKE_VERSION} VERSION_LESS 3.27.0) 3 | cmake_minimum_required(VERSION 3.3) 4 | elseif(${CMAKE_VERSION} VERSION_LESS 3.31.0) 5 | cmake_minimum_required(VERSION 3.6) 6 | else() 7 | cmake_minimum_required(VERSION 3.10) 8 | endif() 9 | 10 | # Unfortunately cxx_std_17 in CMAKE_CXX_COMPILE_FEATURES is not reliable, so check include files 11 | include(CheckIncludeFileCXX) 12 | CHECK_INCLUDE_FILE_CXX("string_view" HAS_STRING_VIEW) 13 | CHECK_INCLUDE_FILE_CXX("filesystem" HAS_FILESYSTEM) 14 | 15 | if(HAS_STRING_VIEW AND HAS_FILESYSTEM) 16 | add_executable(CXX17 Main.cpp) 17 | target_link_libraries(CXX17 plog::plog) 18 | set_target_properties(CXX17 PROPERTIES FOLDER Samples CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) 19 | if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) 20 | target_link_libraries(CXX17 stdc++fs) 21 | endif() 22 | endif() 23 | endif() 24 | -------------------------------------------------------------------------------- /samples/CXX17/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CXX17 - demonstrates log stream abilities for C++17 features. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | plog::init(plog::debug, plog::streamStdOut); // Initialize logging 15 | 16 | std::string_view strView = "string view"; 17 | PLOG_INFO << strView; 18 | 19 | #if PLOG_ENABLE_WCHAR_INPUT 20 | std::wstring_view wstrView = L"wstring view"; 21 | PLOG_INFO << wstrView; // on Linux is printed as a container, will be fixed in future 22 | #endif 23 | 24 | PLOG_INFO << "Current path: " << std::filesystem::current_path(); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /samples/Chained/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # some systems have no shared libraries support, so check it 2 | if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) 3 | add_executable(ChainedApp ChainedApp/Main.cpp) 4 | target_link_libraries(ChainedApp ChainedLib plog) 5 | set_target_properties(ChainedApp PROPERTIES FOLDER Samples/Chained) 6 | # set PLOG to local, so instances will not be shared across modules 7 | target_compile_definitions(ChainedApp PRIVATE PLOG_LOCAL) 8 | 9 | add_library(ChainedLib SHARED ChainedLib/Main.cpp) 10 | target_link_libraries(ChainedLib plog) 11 | set_target_properties(ChainedLib PROPERTIES FOLDER Samples/Chained) 12 | # set PLOG to local, so instances will not be shared across modules 13 | target_compile_definitions(ChainedLib PRIVATE PLOG_LOCAL) 14 | endif() 15 | -------------------------------------------------------------------------------- /samples/Chained/ChainedApp/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Chained - shows how to chain a logger (route messages) in a shared library with the main logger. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | // Functions imported form the shared library. 9 | extern "C" void initialize(plog::Severity severity, plog::IAppender* appender); 10 | extern "C" void foo(); 11 | 12 | int main() 13 | { 14 | plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger. 15 | 16 | PLOGD << "Hello from app!"; // Write a log message. 17 | 18 | initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity. 19 | foo(); // Call a function from the shared library that produces a log message. 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /samples/Chained/ChainedLib/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Chained - shows how to chain a logger (route messages) in a shared library with the main logger. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | // Helper macro to mark functions exported from the library. 9 | #ifdef _WIN32 10 | # define EXPORT __declspec(dllexport) 11 | #else 12 | # define EXPORT __attribute__ ((visibility ("default"))) 13 | #endif 14 | 15 | // Function that initializes the logger in the shared library. 16 | extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender) 17 | { 18 | plog::init(severity, appender); // Initialize the shared library logger. 19 | } 20 | 21 | // Function that produces a log message. 22 | extern "C" void EXPORT foo() 23 | { 24 | PLOGI << "Hello from shared lib!"; 25 | } 26 | -------------------------------------------------------------------------------- /samples/ColorConsole/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(ColorConsole Main.cpp) 2 | target_link_libraries(ColorConsole plog) 3 | set_target_properties(ColorConsole PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/ColorConsole/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ColorConsole - shows how to use a color console appender. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | static plog::ColorConsoleAppender consoleAppender; 13 | plog::init(plog::verbose, &consoleAppender); 14 | 15 | // Log severity levels are printed in different colors. 16 | PLOG_VERBOSE << "This is a VERBOSE message"; 17 | PLOG_DEBUG << "This is a DEBUG message"; 18 | PLOG_INFO << "This is an INFO message"; 19 | PLOG_WARNING << "This is a WARNING message"; 20 | PLOG_ERROR << "This is an ERROR message"; 21 | PLOG_FATAL << "This is a FATAL message"; 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /samples/CustomAppender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(CustomAppender Main.cpp) 2 | target_link_libraries(CustomAppender plog) 3 | set_target_properties(CustomAppender PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/CustomAppender/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CustomAppender - shows how to implement a custom appender that stores log messages in memory. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace plog 11 | { 12 | template // Typically a formatter is passed as a template parameter. 13 | class MyAppender : public IAppender // All appenders MUST inherit IAppender interface. 14 | { 15 | public: 16 | virtual void write(const Record& record) PLOG_OVERRIDE // This is a method from IAppender that MUST be implemented. 17 | { 18 | util::nstring str = Formatter::format(record); // Use the formatter to get a string from a record. 19 | 20 | m_messageList.push_back(str); // Store a log message in a list. 21 | } 22 | 23 | std::list& getMessageList() 24 | { 25 | return m_messageList; 26 | } 27 | 28 | private: 29 | std::list m_messageList; 30 | }; 31 | } 32 | 33 | int main() 34 | { 35 | static plog::MyAppender myAppender; // Create our custom appender. 36 | plog::init(plog::debug, &myAppender); // Initialize the logger with our appender. 37 | 38 | PLOGD << "A debug message!"; 39 | 40 | myAppender.getMessageList(); // This returns a list of stored log messages. 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /samples/CustomConverter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(CustomConverter Main.cpp) 2 | target_link_libraries(CustomConverter plog) 3 | set_target_properties(CustomConverter PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/CustomConverter/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CustomConverter - shows how to implement a custom converter that encrypts log messages. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace plog 11 | { 12 | class MyConverter 13 | { 14 | public: 15 | static std::string header(const util::nstring& str) 16 | { 17 | return convert(str); // We have no special header for a file, so just call convert. 18 | } 19 | 20 | static std::string convert(const util::nstring& str) 21 | { 22 | const std::string& in = UTF8Converter::convert(str); // Convert to UTF8 first as it is more compact. 23 | 24 | std::string out; 25 | out.resize(in.size()); 26 | 27 | // This is an encryption key. 28 | const char kKey[] = "\x56\x5a\x43\x4d\x5f\x81\x4c\x4e\x19\x29\x2e\x13\x7c\x31\x14\x17\x5d\x63\x32\x39"; 29 | 30 | // Simple XOR encryption. 31 | for (size_t i = 0; i < out.size(); ++i) 32 | { 33 | out[i] = in[i] ^ kKey[i % (sizeof(kKey) - 1)]; 34 | } 35 | 36 | return out; 37 | } 38 | }; 39 | } 40 | 41 | int main() 42 | { 43 | static plog::RollingFileAppender appender("CustomConverter.txt"); // Create an appender and pass our converter as a template parameter. 44 | plog::init(plog::debug, &appender); // Initialize the logger with the appender. 45 | 46 | PLOGD << "A debug message!"; 47 | PLOGD << "Another one debug message!"; 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /samples/CustomFormatter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(CustomFormatter Main.cpp) 2 | target_link_libraries(CustomFormatter plog) 3 | set_target_properties(CustomFormatter PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/CustomFormatter/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CustomFormatter - shows how to implement a custom formatter. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | namespace plog 9 | { 10 | class MyFormatter 11 | { 12 | public: 13 | static util::nstring header() // This method returns a header for a new file. In our case it is empty. 14 | { 15 | return util::nstring(); 16 | } 17 | 18 | static util::nstring format(const Record& record) // This method returns a string from a record. 19 | { 20 | util::nostringstream ss; 21 | ss << record.getMessage() << "\n"; // Produce a simple string with a log message. 22 | 23 | return ss.str(); 24 | } 25 | }; 26 | } 27 | 28 | int main() 29 | { 30 | plog::init(plog::debug, "CustomFormatter.txt"); // Initialize the logger and pass our formatter as a template parameter to init function. 31 | 32 | PLOGD << "A debug message!"; 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /samples/CustomType/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(CustomType Main.cpp) 2 | target_link_libraries(CustomType plog) 3 | set_target_properties(CustomType PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/CustomType/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // CustomType - shows how to print a custom type to the log stream. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | struct Point // This is our custom type that we want to print to the log stream. 9 | { 10 | int x; 11 | int y; 12 | }; 13 | 14 | namespace plog 15 | { 16 | Record& operator<<(Record& record, const Point& pt) // Implement a stream operator for our type. 17 | { 18 | return record << "(" << pt.x << ";" << pt.y << ")"; 19 | } 20 | } 21 | 22 | int main() 23 | { 24 | plog::init(plog::debug, "CustomType.txt"); // Initialize the logger. 25 | 26 | Point pt1 = { 0, 0 }; 27 | Point pt2 = { 10, -5 }; 28 | 29 | PLOGI << "We've got a line with coords: " << pt1 << pt2; // Print our type to the log stream. 30 | PLOGI << pt1 << pt2 << " - test for a custom type at begin of the stream"; // Print our type to the log stream. 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /samples/DebugOutput/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(WIN32) 2 | add_executable(DebugOutput Main.cpp) 3 | target_link_libraries(DebugOutput plog) 4 | set_target_properties(DebugOutput PROPERTIES FOLDER Samples) 5 | endif() -------------------------------------------------------------------------------- /samples/DebugOutput/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DebugOutput - shows how to use DebugOutputAppender to write to the windows debug output. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | static plog::DebugOutputAppender debugOutputAppender; 13 | plog::init(plog::verbose, &debugOutputAppender); 14 | 15 | PLOGD << "Hello log!"; // short macro 16 | PLOG_DEBUG << "Hello log!"; // long macro 17 | PLOG(plog::debug) << "Hello log!"; // function-style macro 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /samples/Demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(Demo Main.cpp MyClass.h MyClass.cpp Customer.h) 2 | target_link_libraries(Demo plog) 3 | set_target_properties(Demo PROPERTIES FOLDER Samples) 4 | 5 | if(MSVC AND NOT (MSVC_VERSION LESS 1700)) # Visual Studio 2012 and higher 6 | add_executable(DemoManaged Main.cpp MyClass.h MyClass.cpp Customer.h) 7 | target_link_libraries(DemoManaged plog) 8 | set_property(TARGET DemoManaged PROPERTY COMPILE_FLAGS "/EHa") 9 | set_property(TARGET DemoManaged PROPERTY COMMON_LANGUAGE_RUNTIME "") 10 | set_target_properties(DemoManaged PROPERTIES FOLDER Samples CXX_STANDARD 17 CXX_STANDARD_REQUIRED OFF) # the latest standard supported by CLR is C++17 11 | endif() 12 | 13 | if(NOT WIN32) 14 | add_executable(DemoWchar Main.cpp MyClass.h MyClass.cpp Customer.h) 15 | target_link_libraries(DemoWchar plog) 16 | set_target_properties(DemoWchar PROPERTIES COMPILE_FLAGS "-DPLOG_ENABLE_WCHAR_INPUT=1") 17 | set_target_properties(DemoWchar PROPERTIES FOLDER Samples) 18 | 19 | if(APPLE) 20 | target_link_libraries(DemoWchar -liconv) 21 | endif() 22 | endif() 23 | -------------------------------------------------------------------------------- /samples/Demo/Customer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | struct Customer 6 | { 7 | int id; 8 | std::string name; 9 | }; 10 | 11 | inline std::ostream& operator<<(std::ostream& os, const Customer& obj) 12 | { 13 | os << "Customer (id: " << obj.id << ", name: " << obj.name << ")"; 14 | return os; 15 | } 16 | -------------------------------------------------------------------------------- /samples/Demo/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Demo - demonstrates log stream abilities, prints various types of messages. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "MyClass.h" 17 | #include "Customer.h" 18 | 19 | #ifdef __cplusplus_cli 20 | #pragma managed(push, off) 21 | void unmanagedFunc() 22 | { 23 | PLOGI << "Inside unmanaged function (char)"; 24 | PLOGI << L"Inside unmanaged function (wchar_t)"; 25 | } 26 | #pragma managed(pop) 27 | #endif 28 | 29 | int main() 30 | { 31 | plog::init(plog::debug, "Demo.csv", 5000, 3); // Initialize logging to the file. 32 | 33 | plog::ColorConsoleAppender consoleAppender; 34 | plog::get()->addAppender(&consoleAppender); // Also add logging to the console. 35 | 36 | // Log macro types. 37 | PLOGD << "Hello log!"; // short macro 38 | PLOG_DEBUG << "Hello log!"; // long macro 39 | PLOG(plog::debug) << "Hello log!"; // function-style macro 40 | 41 | // Log severity levels. 42 | PLOG_VERBOSE << "This is a VERBOSE message"; 43 | PLOG_DEBUG << "This is a DEBUG message"; 44 | PLOG_INFO << "This is an INFO message"; 45 | PLOG_WARNING << "This is a WARNING message"; 46 | PLOG_ERROR << "This is an ERROR message"; 47 | PLOG_FATAL << "This is a FATAL message"; 48 | PLOG_NONE << "This is a NONE message"; 49 | 50 | // Integers demo. 51 | PLOG_INFO << "This is a bool: " << std::boolalpha << true; 52 | PLOG_INFO << "This is a char: " << 'x'; 53 | PLOG_INFO << "This is an unsigned char: " << (unsigned char)40; 54 | PLOG_INFO << "This is a short: " << (short)-1000; 55 | PLOG_INFO << "This is an unsigned short: " << (unsigned short)1000; 56 | PLOG_INFO << "This is an int: " << (int)-1000000; 57 | PLOG_INFO << "This is an unsigned int: " << (unsigned int)1000000; 58 | PLOG_INFO << "This is a long(hex): " << std::hex << (long)100000000; 59 | PLOG_INFO << "This is an unsigned long: " << (unsigned long)100000000; 60 | PLOG_INFO << "This is a float: " << 1.2345f; 61 | PLOG_INFO << "This is a double: " << std::setprecision(15) << 1.234512345; 62 | 63 | #ifndef __cplusplus_cli 64 | PLOG_INFO.printf("This is a format %s %d", "message", 42); 65 | 66 | #ifdef _WIN32 67 | PLOG_INFO.printf(L"This is a wide format %s %d", L"message", 42); 68 | #endif 69 | #endif //__cplusplus_cli 70 | 71 | // Managed string. 72 | #ifdef __cplusplus_cli 73 | System::String^ managedStr = "This is a managed string"; 74 | PLOG_INFO << managedStr; 75 | 76 | unmanagedFunc(); 77 | #endif 78 | 79 | // Null strings are safe. 80 | PLOG_DEBUG << static_cast(NULL); 81 | PLOG_DEBUG << static_cast(NULL); 82 | 83 | #if PLOG_ENABLE_WCHAR_INPUT 84 | PLOG_DEBUG << static_cast(NULL); 85 | PLOG_DEBUG << static_cast(NULL); 86 | #endif 87 | 88 | // Plog handles unicode and std::string/wstring. 89 | #ifndef _WIN32 // On Windows the following code produces a warning C4566 if the codepage is not Cyrillic. 90 | PLOG_DEBUG << "test - тест"; 91 | PLOG_DEBUG << std::string("test - тест"); 92 | #endif 93 | 94 | #if PLOG_ENABLE_WCHAR_INPUT 95 | PLOG_DEBUG << L"test - тест"; 96 | PLOG_DEBUG << std::wstring(L"test - тест"); 97 | PLOG_DEBUG << L'ж'; 98 | #endif 99 | 100 | #ifdef __cpp_char8_t 101 | PLOG_DEBUG << u8"Chinese: 中文"; 102 | PLOG_DEBUG << const_cast(u8"Cyrillic: тест"); 103 | #endif 104 | 105 | // Multiline. 106 | PLOG_INFO << "This\nis\na" << std::endl << "multiline\nmessage!"; 107 | 108 | // Quotes. 109 | PLOG_INFO << "This is a message with \"quotes\"!"; 110 | 111 | // Conditional logging. 112 | int var = 0; 113 | PLOG_DEBUG_IF(var != 0) << "You shouldn't see this message"; 114 | PLOG_DEBUG_IF(var == 0) << "This is a conditional log message"; 115 | 116 | // Executed only on log level >= debug. 117 | IF_PLOG(plog::debug) var = 5; // one line 118 | IF_PLOG(plog::debug) // block 119 | { 120 | var++; 121 | } 122 | 123 | // Log macros don't break then-else clause without braces. 124 | if (var == 0) PLOGI << "then clause (condition is false, so it is skipped)"; else PLOGI << "else clase (should be visible)"; 125 | 126 | // Log in a class (capture this pointer, c++ function names). 127 | MyClass obj; 128 | obj.method(); 129 | obj.inlineMethod(); 130 | 131 | MyClass::staticMethod(); 132 | 133 | // Log in a template class. 134 | MyTemplateClass(1, 2).inlineMethod(); 135 | 136 | // Implicit cast to string. 137 | PLOG_INFO << obj; 138 | 139 | // ostream operator<< (on Windows wostream operator<< has priority but not required) 140 | Customer customer = { 10, "John" }; 141 | PLOG_INFO << customer; 142 | 143 | // Std containers can be printed 144 | std::vector vectorOfInts; 145 | vectorOfInts.push_back(1); 146 | vectorOfInts.push_back(2); 147 | vectorOfInts.push_back(3); 148 | PLOG_INFO << "std::vector: " << vectorOfInts; 149 | 150 | std::deque dequeOfStrings; 151 | dequeOfStrings.push_back("one"); 152 | dequeOfStrings.push_back("two"); 153 | dequeOfStrings.push_back("three"); 154 | PLOG_INFO << "std::deque: " << dequeOfStrings; 155 | 156 | std::list listOfCharPointers; 157 | listOfCharPointers.push_back("one"); 158 | listOfCharPointers.push_back("two"); 159 | listOfCharPointers.push_back(NULL); 160 | PLOG_INFO << "std::list: " << listOfCharPointers; 161 | 162 | std::set setOfInts; 163 | setOfInts.insert(10); 164 | setOfInts.insert(20); 165 | setOfInts.insert(30); 166 | PLOG_INFO << "std::set: " << setOfInts; 167 | 168 | std::map mapStringToInt; 169 | mapStringToInt["red"] = 1; 170 | mapStringToInt["green"] = 2; 171 | mapStringToInt["blue"] = 4; 172 | PLOG_INFO << "std::map: " << mapStringToInt; 173 | 174 | std::multimap multimapIntToString; 175 | multimapIntToString.insert(std::make_pair(1, "one")); 176 | multimapIntToString.insert(std::make_pair(1, "uno")); 177 | multimapIntToString.insert(std::make_pair(2, "two")); 178 | multimapIntToString.insert(std::make_pair(2, "due")); 179 | PLOG_INFO << "std::multimap: " << multimapIntToString; 180 | 181 | std::vector > vectorOfVectorsOfInts(3); 182 | vectorOfVectorsOfInts[0].push_back(1); 183 | vectorOfVectorsOfInts[0].push_back(2); 184 | vectorOfVectorsOfInts[1].push_back(-1); 185 | vectorOfVectorsOfInts[1].push_back(-2); 186 | PLOG_INFO << "std::vector >: " << vectorOfVectorsOfInts; 187 | 188 | return 0; 189 | } 190 | -------------------------------------------------------------------------------- /samples/Demo/MyClass.cpp: -------------------------------------------------------------------------------- 1 | #include "MyClass.h" 2 | 3 | MyClass::MyClass() 4 | { 5 | PLOGD; 6 | } 7 | 8 | MyClass::~MyClass() 9 | { 10 | PLOGD; 11 | } 12 | 13 | void MyClass::method() 14 | { 15 | PLOGD; 16 | } 17 | 18 | void MyClass::staticMethod() 19 | { 20 | PLOGD; 21 | } 22 | 23 | MyClass::operator std::string() const 24 | { 25 | return std::string("This is an implicit cast to string."); 26 | } 27 | -------------------------------------------------------------------------------- /samples/Demo/MyClass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class MyClass 5 | { 6 | public: 7 | MyClass(); 8 | ~MyClass(); 9 | 10 | void method(); 11 | 12 | void inlineMethod() 13 | { 14 | PLOGD; 15 | } 16 | 17 | static void staticMethod(); 18 | 19 | operator std::string() const; 20 | }; 21 | 22 | template 23 | class MyTemplateClass 24 | { 25 | public: 26 | MyTemplateClass(T t1, U t2) 27 | : m_t1(t1) 28 | , m_t2(t2) 29 | { 30 | } 31 | 32 | inline void inlineMethod() 33 | { 34 | PLOGD; 35 | } 36 | private: 37 | T m_t1; 38 | U m_t2; 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /samples/DisableLogging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(DisableLogging Main.cpp) 2 | target_link_libraries(DisableLogging plog) 3 | set_target_properties(DisableLogging PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/DisableLogging/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DisableLogging - shows how to disable logging (so it will be stripped from the binary). 3 | // 4 | 5 | #define PLOG_DISABLE_LOGGING 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | #ifndef PLOG_DISABLE_LOGGING 15 | static plog::ColorConsoleAppender consoleAppender; 16 | plog::init(plog::verbose, &consoleAppender); 17 | #endif 18 | 19 | // The following code will be stripped from the binary by optimizer. 20 | PLOG_VERBOSE << "This is a VERBOSE message"; 21 | PLOG_DEBUG << "This is a DEBUG message"; 22 | PLOG_INFO << "This is an INFO message"; 23 | PLOG_WARNING << "This is a WARNING message"; 24 | PLOG_ERROR << "This is an ERROR message"; 25 | PLOG_FATAL << "This is a FATAL message"; 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /samples/DynamicAppender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(DynamicAppender Main.cpp) 2 | target_link_libraries(DynamicAppender plog) 3 | set_target_properties(DynamicAppender PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/DynamicAppender/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicAppender - shows how to add/remove appenders dynamically. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | static plog::DynamicAppender dynamicAppender; 14 | plog::init(plog::verbose, &dynamicAppender); 15 | 16 | PLOGW << "This message goes nowhere as no real appenders exist"; 17 | 18 | { 19 | plog::ColorConsoleAppender consoleAppender; 20 | dynamicAppender.addAppender(&consoleAppender); 21 | 22 | PLOGI << "Message from a dynamically added appender"; 23 | 24 | dynamicAppender.removeAppender(&consoleAppender); 25 | } 26 | 27 | PLOGW << "This message goes nowhere as no real appenders exist"; 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /samples/EventLog/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(WIN32) 2 | add_executable(EventLog Main.cpp) 3 | target_link_libraries(EventLog plog) 4 | set_target_properties(EventLog PROPERTIES FOLDER Samples) 5 | endif() -------------------------------------------------------------------------------- /samples/EventLog/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // EventLog - shows how to use EventLogAppender to write to the windows event log. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | int main(int argc, char* argv[]) 14 | { 15 | const wchar_t kEventSourceName[] = L"EventLogSample"; 16 | 17 | if (argc == 2) 18 | { 19 | // 20 | // Note: register/unregister operations require admin rights. 21 | // 22 | 23 | if (0 == strcmp(argv[1], "--register")) 24 | { 25 | if (!plog::EventLogAppenderRegistry::add(kEventSourceName)) 26 | { 27 | cerr << "Failed to register eventlog source." << endl; 28 | return -1; 29 | } 30 | 31 | cout << "Successfully registered eventlog source." << endl; 32 | } 33 | else if (0 == strcmp(argv[1], "--unregister")) 34 | { 35 | plog::EventLogAppenderRegistry::remove(kEventSourceName); 36 | cout << "Successfully unregistered eventlog source." << endl; 37 | } 38 | else if (0 == strcmp(argv[1], "--query")) 39 | { 40 | cout << "Eventlog source exists: " << plog::EventLogAppenderRegistry::exists(kEventSourceName) << endl; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | // 47 | // Note: eventlog source must be registered prior to creating its appender. 48 | // 49 | 50 | static plog::EventLogAppender eventLogAppender(kEventSourceName); 51 | plog::init(plog::verbose, &eventLogAppender); 52 | 53 | PLOG_VERBOSE << "This is a VERBOSE message"; 54 | PLOG_DEBUG << "This is a DEBUG message"; 55 | PLOG_INFO << "This is an INFO message"; 56 | PLOG_WARNING << "This is a WARNING message"; 57 | PLOG_ERROR << "This is an ERROR message"; 58 | PLOG_FATAL << "This is a FATAL message"; 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /samples/Facilities/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(Facilities Main.cpp) 2 | target_link_libraries(Facilities plog) 3 | set_target_properties(Facilities PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/Facilities/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Facilities - shows how to use logging per facilities via multiple logger instances (useful for big projects). 3 | // 4 | 5 | #include 6 | #include 7 | 8 | enum Facility // Define log facilities. 9 | { 10 | Default, // The default is 0. 11 | Auth, 12 | FileIO, 13 | Sink = -1 // This is a log sink. Messages from other facilities go there. 14 | }; 15 | 16 | int main() 17 | { 18 | plog::init(plog::debug, "Facility.csv"); // Initialize the sink logger. 19 | 20 | // Initialize all other loggers and set the sink logger as an appender. Each of the loggers can have their own severity level. 21 | plog::init(plog::debug, plog::get()); 22 | plog::init(plog::warning, plog::get()); 23 | plog::init(plog::info, plog::get()); 24 | 25 | PLOGD_(Default) << "This is a message from the Default facility"; 26 | PLOGD << "This is a message from the Default facility too because Default = 0"; 27 | 28 | PLOGW_(Auth) << "This is a message from the Auth facility"; 29 | PLOGI_(FileIO) << "This is a message from the FileIO facility"; 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /samples/FreeRTOS/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 2 | include(FetchContent) 3 | 4 | # Select FreeRTOS port 5 | set(FREERTOS_PORT "GCC_POSIX") 6 | 7 | # Select FreeRTOS heap 8 | set(FREERTOS_HEAP "4") 9 | 10 | # Add the freertos_config for FreeRTOS-Kernel 11 | add_subdirectory(freertos_config) 12 | 13 | # Download FreeRTOS-Kernel sources 14 | FetchContent_Declare( 15 | freertos_kernel 16 | URL https://github.com/FreeRTOS/FreeRTOS-Kernel/archive/refs/tags/V11.1.0.tar.gz 17 | URL_HASH SHA256=0e21928b3bcc4f9bcaf7333fb1c8c0299d97e2ec9e13e3faa2c5a7ac8a3bc573 18 | DOWNLOAD_EXTRACT_TIMESTAMP TRUE 19 | ) 20 | 21 | FetchContent_MakeAvailable(freertos_kernel) 22 | 23 | # Describe our executable 24 | add_executable(FreeRTOS main.cpp) 25 | target_link_libraries(FreeRTOS freertos_kernel freertos_config plog::plog) 26 | 27 | # Important!!! There is no standard way to know if the code is compiled for FreeRTOS. So we define __FREERTOS__ macro. 28 | target_compile_definitions(FreeRTOS PUBLIC __FREERTOS__) 29 | endif() 30 | -------------------------------------------------------------------------------- /samples/FreeRTOS/freertos_config/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(freertos_config INTERFACE) 2 | target_include_directories(freertos_config INTERFACE .) 3 | -------------------------------------------------------------------------------- /samples/FreeRTOS/main.cpp: -------------------------------------------------------------------------------- 1 | // FreeRTOS includes 2 | #include 3 | #include 4 | 5 | // PLOG includes 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void exampleTask(void* /*parameter*/) 12 | { 13 | for (;;) 14 | { 15 | PLOGI << "tick"; 16 | vTaskDelay(1000 / portTICK_PERIOD_MS); 17 | } 18 | } 19 | 20 | int main() 21 | { 22 | static plog::ColorConsoleAppender appender; 23 | plog::init(plog::verbose, &appender); 24 | 25 | PLOG_VERBOSE << "verbose"; 26 | PLOG_DEBUG << "debug"; 27 | PLOG_INFO << "info"; 28 | PLOG_WARNING << "warning"; 29 | PLOG_ERROR << "error"; 30 | PLOG_FATAL << "fatal"; 31 | 32 | xTaskCreate(exampleTask, "example1", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1U, NULL); 33 | xTaskCreate(exampleTask, "example2", configMINIMAL_STACK_SIZE, NULL, configMAX_PRIORITIES - 1U, NULL); 34 | 35 | // Start the scheduler 36 | vTaskStartScheduler(); 37 | 38 | // Should not reach here 39 | for (;;) 40 | { 41 | } 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /samples/Hello/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(Hello Main.cpp) 2 | target_link_libraries(Hello plog) 3 | set_target_properties(Hello PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/Hello/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Hello - a minimal introduction sample, shows the basic 3 steps to start using plog. 3 | // 4 | 5 | #include // Step1: include the header. 6 | #include 7 | 8 | int main() 9 | { 10 | plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger. 11 | 12 | // Step3: write log messages using a special macro. There are several log macros, use the macro you liked the most. 13 | 14 | PLOGD << "Hello log!"; // short macro 15 | PLOG_DEBUG << "Hello log!"; // long macro 16 | PLOG(plog::debug) << "Hello log!"; // function-style macro 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /samples/HexDump/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(HexDump Main.cpp) 2 | target_link_libraries(HexDump plog) 3 | set_target_properties(HexDump PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/HexDump/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HexDump - shows how to use plog::hexdump to dump binary buffers into hex. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | int main() 15 | { 16 | static plog::ColorConsoleAppender consoleAppender; 17 | plog::init(plog::verbose, &consoleAppender); 18 | 19 | std::vector v; 20 | v.push_back(10); 21 | v.push_back(20); 22 | v.push_back(30); 23 | v.push_back(40); 24 | v.push_back(1000000); 25 | v.push_back(2000000); 26 | v.push_back(3000000); 27 | 28 | PLOGI << "v: " << plog::hexdump(v); 29 | PLOGI << "v: " << plog::hexdump(v).group(0); 30 | PLOGI << "v: " << plog::hexdump(v).separator("", ""); 31 | PLOGI << "v: " << plog::hexdump(v).group(4).separator(" ", "|"); 32 | PLOGI << "v: " << plog::hexdump(v).separator("", " "); 33 | 34 | std::string s("Hello!"); 35 | PLOGI << "s: " << plog::hexdump(s); 36 | 37 | int arr[] = {255, 511, 65535}; 38 | PLOGI << "arr: " << plog::hexdump(arr); 39 | 40 | void* p = malloc(100); 41 | PLOGI << "p: " << plog::hexdump(p, 100); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /samples/Library/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(LibraryApp LibraryApp/Main.cpp) 2 | target_link_libraries(LibraryApp LibraryLib plog) 3 | set_target_properties(LibraryApp PROPERTIES FOLDER Samples/Library) 4 | 5 | add_library(LibraryLib STATIC LibraryLib/Lib.cpp) 6 | target_link_libraries(LibraryLib plog) 7 | set_target_properties(LibraryLib PROPERTIES FOLDER Samples/Library) -------------------------------------------------------------------------------- /samples/Library/LibraryApp/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Library - shows plog usage in static libraries. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | void foo(); // Function from the static library. 9 | 10 | int main() 11 | { 12 | plog::init(plog::debug, "LibraryApp.txt"); // Initialize the logger. The static library will use it. 13 | // Note that the main app is not required to use plog, the static library will be linked fine in any case. 14 | 15 | foo(); 16 | 17 | PLOGD << "A message from the main application!"; 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /samples/Library/LibraryLib/Lib.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Library - shows plog usage in static libraries. 3 | // 4 | 5 | #include 6 | 7 | void foo() 8 | { 9 | // The logger is initialized in the main app. It is safe not to do that and even not to use plog at all. The library will be linked fine. 10 | PLOGD << "A message from the static library!"; 11 | } 12 | -------------------------------------------------------------------------------- /samples/MultiAppender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(MultiAppender Main.cpp) 2 | target_link_libraries(MultiAppender plog) 3 | set_target_properties(MultiAppender PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/MultiAppender/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MultiAppender - shows how to use multiple appenders with the same logger. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | static plog::RollingFileAppender fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender. 15 | static plog::ConsoleAppender consoleAppender; // Create the 2nd appender. 16 | plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders. 17 | 18 | // A bunch of log lines that goes to the both appenders: to the file and to the console. 19 | for (int i = 0; i < 100; ++i) 20 | { 21 | PLOG_INFO << "i: " << i; 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /samples/MultiInstance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(MultiInstance Main.cpp) 2 | target_link_libraries(MultiInstance plog) 3 | set_target_properties(MultiInstance PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/MultiInstance/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MultiInstance - shows how to use multiple logger instances, each instance has its own independent configuration. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | enum // Define log instances. Default is 0 and is omitted from this enum. 9 | { 10 | SecondLog = 1 11 | }; 12 | 13 | int main() 14 | { 15 | plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance. 16 | plog::init(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance. 17 | 18 | // Write some messages to the default log. 19 | PLOGD << "Hello default log!"; 20 | PLOG_DEBUG << "Hello default log!"; 21 | PLOG(plog::debug) << "Hello default log!"; 22 | 23 | // Write some messages to the 2nd log. 24 | PLOGD_(SecondLog) << "Hello second log!"; 25 | PLOG_DEBUG_(SecondLog) << "Hello second log!"; 26 | PLOG_(SecondLog, plog::debug) << "Hello second log!"; 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /samples/NotShared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # some systems have no shared libraries support, so check it 2 | if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) 3 | # reset visibility to default to test more harsh conditions 4 | # in real code it's recommended to use hidden visibility 5 | set(CMAKE_CXX_VISIBILITY_PRESET default) 6 | set(CMAKE_VISIBILITY_INLINES_HIDDEN 0) 7 | 8 | add_executable(NotSharedApp NotSharedApp/Main.cpp) 9 | target_link_libraries(NotSharedApp PRIVATE plog::plog NotSharedLib1 NotSharedLib2) 10 | set_target_properties(NotSharedApp PROPERTIES FOLDER Samples/NotShared) 11 | # define PLOG_LOCAL to make plog instances local (not shared between shared objects) 12 | target_compile_definitions(NotSharedApp PRIVATE PLOG_LOCAL) 13 | 14 | add_library(NotSharedLib1 SHARED NotSharedLib1/Main.cpp) 15 | target_link_libraries(NotSharedLib1 PRIVATE plog::plog) 16 | set_target_properties(NotSharedLib1 PROPERTIES FOLDER Samples/NotShared) 17 | # define PLOG_LOCAL to make plog instances local (not shared between shared objects) 18 | target_compile_definitions(NotSharedLib1 PRIVATE PLOG_LOCAL) 19 | 20 | add_library(NotSharedLib2 SHARED NotSharedLib2/Main.cpp) 21 | target_link_libraries(NotSharedLib2 PRIVATE plog::plog) 22 | set_target_properties(NotSharedLib2 PROPERTIES FOLDER Samples/NotShared) 23 | # define PLOG_LOCAL to make plog instances local (not shared between shared objects) 24 | target_compile_definitions(NotSharedLib2 PRIVATE PLOG_LOCAL) 25 | endif() 26 | -------------------------------------------------------------------------------- /samples/NotShared/NotSharedApp/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). 3 | // 4 | 5 | #include 6 | #include 7 | 8 | // Functions imported form the shared libraries. 9 | extern "C" void foo1(); 10 | extern "C" void foo2(); 11 | 12 | int main() 13 | { 14 | plog::init(plog::debug, "NotSharedApp.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. 15 | 16 | PLOGD << "Hello from app!"; // The message will go to the logger in this module. 17 | 18 | foo1(); // Call a function from the shared library that produces a log message. 19 | foo2(); // Call a function from the shared library that produces a log message. 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /samples/NotShared/NotSharedLib1/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). 3 | // 4 | 5 | #include 6 | #include 7 | 8 | // Helper macro to mark functions exported from the library. 9 | #ifdef _WIN32 10 | # define EXPORT __declspec(dllexport) 11 | #else 12 | # define EXPORT __attribute__ ((visibility ("default"))) 13 | #endif 14 | 15 | // Function that produces a log message. 16 | extern "C" void EXPORT foo1() 17 | { 18 | plog::init(plog::debug, "NotSharedLib1.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. 19 | PLOGI << "Hello from shared lib #1!"; // The message will go to the logger in this module. 20 | } 21 | -------------------------------------------------------------------------------- /samples/NotShared/NotSharedLib2/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). 3 | // 4 | 5 | #include 6 | #include 7 | 8 | // Helper macro to mark functions exported from the library. 9 | #ifdef _WIN32 10 | # define EXPORT __declspec(dllexport) 11 | #else 12 | # define EXPORT __attribute__ ((visibility ("default"))) 13 | #endif 14 | 15 | // Function that produces a log message. 16 | extern "C" void EXPORT foo2() 17 | { 18 | plog::init(plog::debug, "NotSharedLib2.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. 19 | PLOGI << "Hello from shared lib #2!"; // The message will go to the logger in this module. 20 | } 21 | -------------------------------------------------------------------------------- /samples/ObjectiveC/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(CMAKE_OBJCXX_AVAILABLE AND NOT CMAKE_COMPILER_IS_CLANGXX) 2 | add_executable(ObjectiveC Main.mm) 3 | target_link_libraries(ObjectiveC objc plog) 4 | else() 5 | add_custom_target(ObjectiveC SOURCES Main.mm) 6 | endif() 7 | set_target_properties(ObjectiveC PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/ObjectiveC/Main.mm: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectiveC - shows that plog can be used in ObjectiveC++. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | @interface Greeter : Object 10 | { 11 | int dummy; 12 | } 13 | +(void) greet; 14 | @end 15 | 16 | @implementation Greeter 17 | +(void) greet 18 | { 19 | PLOGD << "Hello ObjC++!"; 20 | } 21 | @end 22 | 23 | int main() 24 | { 25 | plog::init(plog::debug, "ObjectiveC.csv"); // Initialize the logger. 26 | PLOGD << "Hello ObjC++!"; 27 | 28 | [Greeter greet]; 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /samples/Path/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (CMAKE_VERSION VERSION_GREATER 3.1.0) 2 | if ((CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) OR (MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.10)) 3 | add_executable(Path Main.cpp) 4 | target_link_libraries(Path plog) 5 | set_target_properties(Path PROPERTIES FOLDER Samples) 6 | set_target_properties(Path PROPERTIES CXX_STANDARD 17) 7 | if (MSVC) 8 | target_compile_options(Path PRIVATE "/permissive-") # enable conformance mode to be more strict 9 | endif() 10 | if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) 11 | target_link_libraries(Path stdc++fs) 12 | endif() 13 | endif() 14 | endif() 15 | -------------------------------------------------------------------------------- /samples/Path/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Path - a test sample to check that std::filesystem::path can be logged. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #if __has_include() 9 | #include 10 | namespace fs = std::filesystem; 11 | #else 12 | #include 13 | namespace fs = std::experimental::filesystem; 14 | #endif 15 | 16 | int main() 17 | { 18 | plog::init(plog::debug, "Path.txt"); 19 | 20 | PLOGI << "Current path: " << fs::current_path(); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /samples/Performance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(Performance Main.cpp) 2 | target_link_libraries(Performance plog) 3 | set_target_properties(Performance PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/Performance/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Performance - measures time per a log call. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | enum 12 | { 13 | Console = 1 14 | }; 15 | 16 | int main() 17 | { 18 | // Initialize the logger that will be measured. 19 | plog::init(plog::debug, "Performance.txt"); 20 | 21 | // Initialize the logger for printing info messages. 22 | static plog::ConsoleAppender consoleAppender; 23 | plog::init(plog::debug, &consoleAppender); 24 | 25 | PLOGI_(Console) << "Test started"; 26 | 27 | plog::util::Time startTime; 28 | plog::util::ftime(&startTime); 29 | 30 | const int kCount = 50000; 31 | 32 | // Performance measure loop. 33 | for (int i = 0; i < kCount; ++i) 34 | { 35 | PLOGD << "Hello log!"; 36 | } 37 | 38 | plog::util::Time finishTime; 39 | plog::util::ftime(&finishTime); 40 | 41 | time_t timeDiff = (finishTime.millitm - startTime.millitm) + (finishTime.time - startTime.time) * 1000; 42 | 43 | PLOGI_(Console) << "Test finished: " << static_cast(timeDiff) * 1000 / kCount << " microsec per call"; 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /samples/PrintVar/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(PrintVar Main.cpp) 2 | target_link_libraries(PrintVar plog) 3 | set_target_properties(PrintVar PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/PrintVar/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // PrintVar - shows how to use PLOG_PRINT_VAR to print variables. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class C 11 | { 12 | public: 13 | C(int value) : m_value(value) 14 | { 15 | PLOGD << PLOG_PRINT_VAR(this, m_value) << " - can print 'this' (useful for distinguishing class instances)"; 16 | } 17 | 18 | ~C() 19 | { 20 | PLOGD << PLOG_PRINT_VAR(this); 21 | } 22 | 23 | private: 24 | int m_value; 25 | }; 26 | 27 | int main() 28 | { 29 | plog::init(plog::verbose, plog::streamStdOut); 30 | 31 | C c1(42); 32 | C c2(42); 33 | 34 | int x = 10; 35 | int y = 20; 36 | int z = 30; 37 | 38 | PLOGI << PLOG_PRINT_VAR(x); 39 | PLOGI << PLOG_PRINT_VAR(x, y); 40 | PLOGI << PLOG_PRINT_VAR(x, y, z); 41 | PLOGI << PLOG_PRINT_VAR(x, y, z, x); 42 | PLOGI << PLOG_PRINT_VAR(x, y, z, x, y); 43 | PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z); 44 | PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x); 45 | PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x, y); 46 | PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x, y, z) << " - can print up to 9 variables"; 47 | 48 | PLOGI << PLOG_PRINT_VAR(x + y, z * 2, &c1) << " - can execute expressions"; 49 | PLOGI << "can print some data before, " << PLOG_PRINT_VAR(x, y) << " - in the middle, " PLOG_PRINT_VAR(z) << " - and after"; 50 | 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /samples/SetFileName/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(SetFileName Main.cpp) 2 | target_link_libraries(SetFileName plog) 3 | set_target_properties(SetFileName PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/SetFileName/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SetFileName - shows how to change a log file name at arbitrary moment. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main() 11 | { 12 | static plog::RollingFileAppender fileAppender("SetFileNameAAA.log"); 13 | plog::init(plog::debug, &fileAppender); 14 | 15 | for (int i = 0; i < 100; ++i) 16 | { 17 | PLOG_INFO << "i: " << i; 18 | } 19 | 20 | fileAppender.setFileName("SetFileNameBBB.log"); 21 | 22 | for (int i = 0; i < 100; ++i) 23 | { 24 | PLOG_INFO << "i: " << i; 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /samples/Shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # some systems have no shared libraries support, so check it 2 | if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) 3 | add_executable(SharedApp SharedApp/Main.cpp) 4 | target_link_libraries(SharedApp PRIVATE plog::plog) 5 | set_target_properties(SharedApp PROPERTIES FOLDER Samples/Shared) 6 | # define PLOG_GLOBAL to share plog instances across modules (PLOG_EXPORT to export on Windows) 7 | if(WIN32) 8 | target_compile_definitions(SharedApp PRIVATE PLOG_EXPORT) 9 | set_target_properties(SharedApp PROPERTIES ENABLE_EXPORTS 1) 10 | else() 11 | target_compile_definitions(SharedApp PRIVATE PLOG_GLOBAL) 12 | target_link_libraries(SharedApp PRIVATE SharedLib1 SharedLib2) 13 | endif() 14 | 15 | add_library(SharedLib1 SHARED SharedLib1/Main.cpp) 16 | target_link_libraries(SharedLib1 PRIVATE plog::plog) 17 | set_target_properties(SharedLib1 PROPERTIES FOLDER Samples/Shared) 18 | # define PLOG_GLOBAL to share plog instances across modules (PLOG_IMPORT to import on Windows) 19 | if(WIN32) 20 | target_compile_definitions(SharedLib1 PRIVATE PLOG_IMPORT) 21 | target_link_libraries(SharedLib1 PRIVATE SharedApp) 22 | else() 23 | target_compile_definitions(SharedLib1 PRIVATE PLOG_GLOBAL) 24 | endif() 25 | 26 | add_library(SharedLib2 SHARED SharedLib2/Main.cpp) 27 | target_link_libraries(SharedLib2 PRIVATE plog::plog) 28 | set_target_properties(SharedLib2 PROPERTIES FOLDER Samples/Shared) 29 | # define PLOG_GLOBAL to share plog instances across modules (PLOG_IMPORT to import on Windows) 30 | if(WIN32) 31 | target_compile_definitions(SharedLib2 PRIVATE PLOG_IMPORT) 32 | target_link_libraries(SharedLib2 PRIVATE SharedApp) 33 | else() 34 | target_compile_definitions(SharedLib2 PRIVATE PLOG_GLOBAL) 35 | endif() 36 | endif() 37 | -------------------------------------------------------------------------------- /samples/Shared/SharedApp/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). 3 | // 4 | 5 | #include 6 | #include 7 | 8 | // Functions imported form the shared libraries. 9 | // 10 | // NOTE: 11 | // We use dynamic linking on Windows as DLL imports logger instances from EXE and static linking will make a dependency loop: 12 | // DLL --(import logger)--> EXE, EXE --(import foo)--> DLL. 13 | // 14 | // On everything except Winows logger instances are shared across all modules, so there is no dependency loop. 15 | #ifdef _WIN32 16 | # include 17 | typedef void (*Foo1Fn)(); 18 | typedef void (*Foo2Fn)(); 19 | #else 20 | extern "C" void foo1(); 21 | extern "C" void foo2(); 22 | #endif 23 | 24 | int main() 25 | { 26 | plog::init(plog::debug, "Shared.txt"); // Initialize the logger. 27 | // It will be shared across modules because PLOG_GLOBAL is defined, so no need to call `plog::init` in them. 28 | // On Windows the logger will be exported because PLOG_EXPORT is defined. 29 | 30 | PLOGD << "Hello from app!"; // The message will go to the logger in this module. 31 | 32 | #ifdef _WIN32 33 | HMODULE lib1 = LoadLibraryW(L"SharedLib1.dll"); 34 | PLOGE_IF(!lib1) << "Couldn't load SharedLib1.dll"; 35 | 36 | Foo1Fn foo1 = reinterpret_cast(GetProcAddress(lib1, "foo1")); 37 | PLOGE_IF(!foo1) << "Couldn't get foo1 from SharedLib1.dll"; 38 | 39 | HMODULE lib2 = LoadLibraryW(L"SharedLib2.dll"); 40 | PLOGE_IF(!lib2) << "Couldn't load SharedLib2.dll"; 41 | 42 | Foo1Fn foo2 = reinterpret_cast(GetProcAddress(lib2, "foo2")); 43 | PLOGE_IF(!foo2) << "Couldn't get foo2 from SharedLib2.dll"; 44 | #endif 45 | 46 | foo1(); // Call a function from the shared library that produces a log message. 47 | foo2(); // Call a function from the shared library that produces a log message. 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /samples/Shared/SharedLib1/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). 3 | // 4 | 5 | #include 6 | 7 | // Helper macro to mark functions exported from the library. 8 | #ifdef _WIN32 9 | # define EXPORT __declspec(dllexport) 10 | #else 11 | # define EXPORT __attribute__ ((visibility ("default"))) 12 | #endif 13 | 14 | // Function that produces a log message. 15 | extern "C" void EXPORT foo1() 16 | { 17 | PLOGI << "Hello from shared lib #1!"; // The message will go to the logger initialized in the main module because PLOG_GLOBAL is defined. 18 | // On Windows the logger instance is imported from the main module because PLOG_IMPORT is defined. 19 | } 20 | -------------------------------------------------------------------------------- /samples/Shared/SharedLib2/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). 3 | // 4 | 5 | #include 6 | 7 | // Helper macro to mark functions exported from the library. 8 | #ifdef _WIN32 9 | # define EXPORT __declspec(dllexport) 10 | #else 11 | # define EXPORT __attribute__ ((visibility ("default"))) 12 | #endif 13 | 14 | // Function that produces a log message. 15 | extern "C" void EXPORT foo2() 16 | { 17 | PLOGI << "Hello from shared lib #2!"; // The message will go to the logger initialized in the main module because PLOG_GLOBAL is defined. 18 | // On Windows the logger instance is imported from the main module because PLOG_IMPORT is defined. 19 | } 20 | -------------------------------------------------------------------------------- /samples/SkipNativeEOL/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(SkipNativeEOL Main.cpp) 2 | target_link_libraries(SkipNativeEOL plog) 3 | set_target_properties(SkipNativeEOL PROPERTIES FOLDER Samples) 4 | -------------------------------------------------------------------------------- /samples/SkipNativeEOL/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SkipNativeEOL - shows how to skip NativeEOLConverter. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() 12 | { 13 | // NativeEOLConverter will use on Windows and on everything else as line endings. 14 | // It's used by default. 15 | // If you want to always use you can skip NativeEOLConverter and specify UTF8Converter directly. 16 | static plog::RollingFileAppender fileAppender("SkipNativeEOL.log", 8000, 3); // Create an appender without NativeEOLConverter. 17 | plog::init(plog::debug, &fileAppender); // Initialize the logger. 18 | 19 | // Write some data. 20 | for (int i = 0; i < 100; ++i) 21 | { 22 | PLOGI << "i: " << i; 23 | } 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /samples/UtcTime/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(UtcTime Main.cpp) 2 | target_link_libraries(UtcTime plog) 3 | set_target_properties(UtcTime PROPERTIES FOLDER Samples) -------------------------------------------------------------------------------- /samples/UtcTime/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // UtcTime - shows how to use UTC time in logs. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | static plog::ColorConsoleAppender consoleAppender; // TxtFormatter in UTC 15 | static plog::RollingFileAppender fileAppender("UtcTime.csv", 10000, 2); // CsvFormatter in UTC 16 | plog::init(plog::verbose, &consoleAppender).addAppender(&fileAppender); 17 | 18 | PLOG_VERBOSE << "This is a VERBOSE message"; 19 | PLOG_DEBUG << "This is a DEBUG message"; 20 | PLOG_INFO << "This is an INFO message"; 21 | PLOG_WARNING << "This is a WARNING message"; 22 | PLOG_ERROR << "This is an ERROR message"; 23 | PLOG_FATAL << "This is a FATAL message"; 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /samples/Utf8Everywhere/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher 2 | add_executable(Utf8Everywhere Main.cpp) 3 | target_link_libraries(Utf8Everywhere plog) 4 | set_target_properties(Utf8Everywhere PROPERTIES FOLDER Samples COMPILE_FLAGS "/utf-8") 5 | endif() 6 | -------------------------------------------------------------------------------- /samples/Utf8Everywhere/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Utf8Everywhere - demonstrates using http://utf8everywhere.org on Windows. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void 中文() 11 | { 12 | PLOGD << "Chinese: 中文"; 13 | PLOGD << L"Chinese: 中文"; 14 | } 15 | 16 | void тест() 17 | { 18 | PLOGD << "Cyrillic: тест"; 19 | PLOGD << L"Cyrillic: тест"; 20 | } 21 | 22 | int main() 23 | { 24 | plog::init(plog::debug, "Utf8Everywhere.log"); // Initialize logging to the file. 25 | 26 | plog::ColorConsoleAppender consoleAppender; 27 | plog::get()->addAppender(&consoleAppender); // Also add logging to the console. 28 | 29 | 中文(); 30 | тест(); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(${CMAKE_VERSION} VERSION_LESS 3.27.0) 2 | cmake_minimum_required(VERSION 3.1) 3 | elseif(${CMAKE_VERSION} VERSION_LESS 3.31.0) 4 | cmake_minimum_required(VERSION 3.6) 5 | else() 6 | cmake_minimum_required(VERSION 3.10) 7 | endif() 8 | 9 | project(PlogTest CXX) 10 | 11 | # 12 | # Specify test sources 13 | # 14 | 15 | set(SOURCES 16 | CastToString.cpp 17 | Common.h 18 | Conditional.cpp 19 | TestAppender.h 20 | Main.cpp 21 | NullCharPointer.cpp 22 | Path.cpp 23 | Printf.cpp 24 | SimpleTypes.cpp 25 | StringTypes.cpp 26 | StdContainers.cpp 27 | StdManipulators.cpp 28 | StdStreamable.cpp 29 | ) 30 | 31 | # 32 | # Check wchar_t platform support 33 | # 34 | 35 | include(CheckTypeSize) 36 | CHECK_TYPE_SIZE(wchar_t PLOG_SIZEOF_WCHAR) 37 | 38 | # 39 | # Test creation functions to avoid code duplication 40 | # 41 | 42 | function(plog_add_test _target) 43 | add_executable(${_target} ${SOURCES}) 44 | 45 | target_link_libraries(${_target} plog::plog) 46 | target_compile_definitions(${_target} PRIVATE DOCTEST_CONFIG_NO_POSIX_SIGNALS DOCTEST_CONFIG_NO_MULTITHREADING) 47 | set_target_properties(${_target} PROPERTIES FOLDER Test) 48 | 49 | # Add std::filesystem support library for GCC 50 | if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) 51 | target_link_libraries(${_target} stdc++fs) 52 | endif() 53 | 54 | # Enable conformance mode to be more strict 55 | if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher 56 | target_compile_options(${_target} PRIVATE "/permissive-") 57 | endif() 58 | 59 | add_test(${_target} ${_target}) 60 | endfunction() 61 | 62 | function(plog_add_test_utf8 _target) 63 | if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher 64 | plog_add_test(${_target}) 65 | target_compile_options(${_target} PRIVATE "/utf-8") 66 | endif() 67 | endfunction() 68 | 69 | function(plog_add_test_wchar _target) 70 | if(NOT WIN32 AND NOT BSD AND PLOG_SIZEOF_WCHAR) # FIXME: for some reason wchar_t is not working on BSD 71 | plog_add_test(${_target}) 72 | target_compile_definitions(${_target} PRIVATE PLOG_ENABLE_WCHAR_INPUT=1) 73 | 74 | if(APPLE) 75 | target_link_libraries(${_target} -liconv) 76 | endif() 77 | endif() 78 | endfunction() 79 | 80 | # 81 | # Create a basic test with default compiler features 82 | # 83 | 84 | plog_add_test(${PROJECT_NAME}) 85 | 86 | # 87 | # Create a test for Utf8Everywhere mode with default compiler features 88 | # 89 | 90 | plog_add_test_utf8(${PROJECT_NAME}_utf8) 91 | 92 | # 93 | # Create a test for wchar input with default compiler features 94 | # 95 | 96 | plog_add_test_wchar(${PROJECT_NAME}_wchar) 97 | 98 | 99 | # 100 | # Check if running a descent version of CMake and enable it 101 | # 102 | 103 | if(CMAKE_VERSION VERSION_LESS 3.8.0) 104 | return() 105 | endif() 106 | 107 | if(${CMAKE_VERSION} VERSION_LESS 3.31.0) 108 | cmake_policy(VERSION 3.8) 109 | else() 110 | cmake_policy(VERSION 3.10) 111 | endif() 112 | 113 | set(PLOG_CXX_STANDARDS_LIST LIST cxx_std_11 cxx_std_14 cxx_std_17 cxx_std_20 cxx_std_23) 114 | 115 | function(plog_get_latest_cxx_std _output) 116 | foreach(cxx_std IN ITEMS ${PLOG_CXX_STANDARDS_LIST}) 117 | if(${cxx_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES) 118 | set(${_output} ${cxx_std} PARENT_SCOPE) 119 | endif() 120 | endforeach() 121 | endfunction() 122 | 123 | plog_get_latest_cxx_std(PLOG_LATEST_CXX_STD) 124 | 125 | function(plog_set_target_cxx_standard _target _cxx_std) 126 | if(TARGET ${_target}) 127 | target_compile_features(${_target} PRIVATE ${_cxx_std}) 128 | endif() 129 | endfunction() 130 | 131 | # 132 | # Create basic tests for different C++ Standard versions 133 | # 134 | 135 | foreach(cxx_std IN ITEMS ${PLOG_CXX_STANDARDS_LIST}) 136 | if(${cxx_std} IN_LIST CMAKE_CXX_COMPILE_FEATURES) 137 | plog_add_test(${PROJECT_NAME}_${cxx_std}) 138 | plog_set_target_cxx_standard(${PROJECT_NAME}_${cxx_std} ${cxx_std}) 139 | endif() 140 | endforeach() 141 | 142 | # 143 | # Create a test for Utf8Everywhere mode with the latest C++ Standard 144 | # 145 | 146 | if(PLOG_LATEST_CXX_STD) 147 | plog_add_test_utf8(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_utf8) 148 | plog_set_target_cxx_standard(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_utf8 ${PLOG_LATEST_CXX_STD}) 149 | endif() 150 | 151 | # 152 | # Create a test for wchar input with the latest C++ Standard 153 | # 154 | 155 | if(PLOG_LATEST_CXX_STD) 156 | plog_add_test_wchar(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_wchar) 157 | plog_set_target_cxx_standard(${PROJECT_NAME}_${PLOG_LATEST_CXX_STD}_wchar ${PLOG_LATEST_CXX_STD}) 158 | endif() 159 | -------------------------------------------------------------------------------- /test/CastToString.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | class CastableToString 4 | { 5 | public: 6 | operator std::string() const 7 | { 8 | return "object"; 9 | } 10 | }; 11 | 12 | #if PLOG_ENABLE_WCHAR_INPUT 13 | class CastableToWString 14 | { 15 | public: 16 | operator std::wstring() const 17 | { 18 | return L"object"; 19 | } 20 | }; 21 | #endif 22 | 23 | SCENARIO("cast to string") 24 | { 25 | GIVEN("logger is initialised") 26 | { 27 | plog::TestAppender testAppender; 28 | plog::Logger logger(plog::verbose); 29 | logger.addAppender(&testAppender); 30 | 31 | WHEN("type is castable to std::string") 32 | { 33 | CastableToString var; 34 | PLOGI << var; 35 | 36 | THEN("the result is as expected") 37 | { 38 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("object")); 39 | } 40 | } 41 | 42 | #if PLOG_ENABLE_WCHAR_INPUT 43 | WHEN("type is castable to std::wstring") 44 | { 45 | CastableToWString var; 46 | PLOGI << var; 47 | 48 | THEN("the result is as expected") 49 | { 50 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("object")); 51 | } 52 | } 53 | #endif 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__cpp_constexpr) && (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)) 4 | # include "doctest/2.4.11/doctest.h" // C++11 and higher 5 | #else 6 | # include "doctest/1.2.9/doctest.h" // pre C++11 7 | #endif 8 | 9 | #include 10 | #include "TestAppender.h" 11 | 12 | #ifdef __has_include 13 | # if __has_include() 14 | # include 15 | # endif 16 | #endif 17 | -------------------------------------------------------------------------------- /test/Conditional.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | SCENARIO("conditional logging") 4 | { 5 | GIVEN("logger is initialised") 6 | { 7 | plog::TestAppender testAppender; 8 | plog::Logger logger(plog::info); 9 | logger.addAppender(&testAppender); 10 | 11 | WHEN("condition is true") 12 | { 13 | int var = 0; 14 | PLOG_INFO_IF(var == 0) << "message"; 15 | 16 | THEN("the message is printed") 17 | { 18 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("message")); 19 | } 20 | } 21 | 22 | WHEN("condition is false") 23 | { 24 | int var = 0; 25 | PLOG_INFO_IF(var != 0) << "message"; 26 | 27 | THEN("the message is not printed") 28 | { 29 | CHECK(testAppender.getMessage().empty()); 30 | } 31 | } 32 | 33 | WHEN("log level check is true") 34 | { 35 | int var = 0; 36 | IF_PLOG(plog::info) var = 5; // one line 37 | 38 | IF_PLOG(plog::info) // block 39 | { 40 | var++; 41 | } 42 | 43 | THEN("statements were executed") 44 | { 45 | CHECK_EQ(var, 5 + 1); 46 | } 47 | } 48 | 49 | WHEN("log level check is false") 50 | { 51 | int var = 0; 52 | IF_PLOG(plog::debug) var = 5; // one line 53 | 54 | IF_PLOG(plog::debug) // block 55 | { 56 | var++; 57 | } 58 | 59 | THEN("statements were not executed") 60 | { 61 | CHECK_EQ(var, 0); 62 | } 63 | } 64 | 65 | WHEN("log macros are used in 'then-else' clauses without braces and condition is true") 66 | { 67 | int var = 0; 68 | if (var == 0) PLOGI << "then clause"; else PLOGI << "else clause"; 69 | 70 | THEN("nothing is broken, 'then' clause is executed") 71 | { 72 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("then clause")); 73 | } 74 | } 75 | 76 | WHEN("log macros are used in 'then-else' clauses without braces and condition is false") 77 | { 78 | int var = 0; 79 | if (var != 0) PLOGI << "then clause"; else PLOGI << "else clause"; 80 | 81 | THEN("nothing is broken, 'else' clause is executed") 82 | { 83 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("else clause")); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /test/Main.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include "Common.h" 3 | -------------------------------------------------------------------------------- /test/NullCharPointer.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include 3 | #include 4 | 5 | SCENARIO("handling null char pointers") 6 | { 7 | GIVEN("logger is initialised") 8 | { 9 | plog::TestAppender testAppender; 10 | plog::Logger logger(plog::verbose); 11 | logger.addAppender(&testAppender); 12 | 13 | WHEN("type is char* and its value is NULL") 14 | { 15 | char* var = NULL; 16 | PLOGI << var; 17 | 18 | THEN("the result is as expected") 19 | { 20 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null)")); 21 | } 22 | } 23 | 24 | WHEN("type is std::vector and value of elements is NULL") 25 | { 26 | std::vector var; 27 | var.push_back(NULL); 28 | var.push_back(NULL); 29 | PLOGI << var; 30 | 31 | THEN("the result is as expected") 32 | { 33 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[(null), (null)]")); 34 | } 35 | } 36 | 37 | WHEN("type is std::pair and value of elements is NULL") 38 | { 39 | std::pair var; 40 | PLOGI << var; 41 | 42 | THEN("the result is as expected") 43 | { 44 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null):(null)")); 45 | } 46 | } 47 | 48 | #if PLOG_ENABLE_WCHAR_INPUT 49 | WHEN("type is wchar_t* and its value is NULL") 50 | { 51 | wchar_t* var = NULL; 52 | PLOGI << var; 53 | 54 | THEN("the result is as expected") 55 | { 56 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null)")); 57 | } 58 | } 59 | 60 | WHEN("type is std::vector and value of elements is NULL") 61 | { 62 | std::vector var; 63 | var.push_back(NULL); 64 | var.push_back(NULL); 65 | 66 | PLOGI << var; 67 | 68 | THEN("the result is as expected") 69 | { 70 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[(null), (null)]")); 71 | } 72 | } 73 | 74 | WHEN("type is std::pair and value of elements is NULL") 75 | { 76 | std::pair var; 77 | PLOGI << var; 78 | 79 | THEN("the result is as expected") 80 | { 81 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("(null):(null)")); 82 | } 83 | } 84 | #endif 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/Path.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | #ifdef __cpp_lib_filesystem 4 | # include 5 | 6 | SCENARIO("std::filesystem::path") 7 | { 8 | GIVEN("logger is initialised") 9 | { 10 | plog::TestAppender testAppender; 11 | plog::Logger logger(plog::verbose); 12 | logger.addAppender(&testAppender); 13 | 14 | WHEN("type is std::filesystem::path") 15 | { 16 | std::filesystem::path path("/usr/lib"); 17 | PLOGI << path; 18 | 19 | THEN("the result is as expected") 20 | { 21 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("/usr/lib")); 22 | } 23 | } 24 | } 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /test/Printf.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | #ifndef __cplusplus_cli 4 | SCENARIO("printf-style messages") 5 | { 6 | GIVEN("logger is initialised") 7 | { 8 | plog::TestAppender testAppender; 9 | plog::Logger logger(plog::verbose); 10 | logger.addAppender(&testAppender); 11 | 12 | WHEN("use empty format") 13 | { 14 | PLOGI.printf(""); 15 | 16 | THEN("the result is as expected") 17 | { 18 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("")); 19 | } 20 | } 21 | 22 | WHEN("use text only format") 23 | { 24 | PLOGI.printf("test"); 25 | 26 | THEN("the result is as expected") 27 | { 28 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 29 | } 30 | } 31 | 32 | WHEN("use format %d") 33 | { 34 | PLOGI.printf("test %d", 42); 35 | 36 | THEN("the result is as expected") 37 | { 38 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test 42")); 39 | } 40 | } 41 | 42 | WHEN("use format %s") 43 | { 44 | PLOGI.printf("hello %s", "world"); 45 | 46 | THEN("the result is as expected") 47 | { 48 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("hello world")); 49 | } 50 | } 51 | 52 | #ifdef _WIN32 53 | WHEN("use empty format (wide)") 54 | { 55 | PLOGI.printf(L""); 56 | 57 | THEN("the result is as expected") 58 | { 59 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("")); 60 | } 61 | } 62 | 63 | WHEN("use text only format (wide)") 64 | { 65 | PLOGI.printf(L"test"); 66 | 67 | THEN("the result is as expected") 68 | { 69 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 70 | } 71 | } 72 | 73 | WHEN("use format %d (wide)") 74 | { 75 | PLOGI.printf(L"test %d", 42); 76 | 77 | THEN("the result is as expected") 78 | { 79 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test 42")); 80 | } 81 | } 82 | 83 | WHEN("use format %s (wide)") 84 | { 85 | PLOGI.printf(L"hello %s", L"world"); 86 | 87 | THEN("the result is as expected") 88 | { 89 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("hello world")); 90 | } 91 | } 92 | #endif 93 | } 94 | } 95 | #endif 96 | -------------------------------------------------------------------------------- /test/SimpleTypes.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | SCENARIO("simple types") 4 | { 5 | GIVEN("logger is initialised") 6 | { 7 | plog::TestAppender testAppender; 8 | plog::Logger logger(plog::verbose); 9 | logger.addAppender(&testAppender); 10 | 11 | WHEN("type is bool") 12 | { 13 | bool var = true; 14 | PLOGI << var; 15 | 16 | THEN("the result is as expected") 17 | { 18 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1")); 19 | } 20 | } 21 | 22 | WHEN("type is char") 23 | { 24 | char var = 'a'; 25 | PLOGI << var; 26 | 27 | THEN("the result is as expected") 28 | { 29 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); 30 | } 31 | } 32 | 33 | WHEN("type is signed char") 34 | { 35 | signed char var = 'a'; 36 | PLOGI << var; 37 | 38 | THEN("the result is as expected") 39 | { 40 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); 41 | } 42 | } 43 | 44 | WHEN("type is unsigned char") 45 | { 46 | unsigned char var = 'a'; 47 | PLOGI << var; 48 | 49 | THEN("the result is as expected") 50 | { 51 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); 52 | } 53 | } 54 | 55 | WHEN("type is short") 56 | { 57 | short var = -1000; 58 | PLOGI << var; 59 | 60 | THEN("the result is as expected") 61 | { 62 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); 63 | } 64 | } 65 | 66 | WHEN("type is unsigned short") 67 | { 68 | unsigned short var = 1000; 69 | PLOGI << var; 70 | 71 | THEN("the result is as expected") 72 | { 73 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); 74 | } 75 | } 76 | 77 | WHEN("type is int") 78 | { 79 | int var = -1000; 80 | PLOGI << var; 81 | 82 | THEN("the result is as expected") 83 | { 84 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); 85 | } 86 | } 87 | 88 | WHEN("type is unsigned int") 89 | { 90 | unsigned int var = 1000; 91 | PLOGI << var; 92 | 93 | THEN("the result is as expected") 94 | { 95 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); 96 | } 97 | } 98 | 99 | WHEN("type is long") 100 | { 101 | long var = -1000; 102 | PLOGI << var; 103 | 104 | THEN("the result is as expected") 105 | { 106 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); 107 | } 108 | } 109 | 110 | WHEN("type is unsigned long") 111 | { 112 | unsigned long var = 1000; 113 | PLOGI << var; 114 | 115 | THEN("the result is as expected") 116 | { 117 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); 118 | } 119 | } 120 | 121 | WHEN("type is long long") 122 | { 123 | long long var = -1000; 124 | PLOGI << var; 125 | 126 | THEN("the result is as expected") 127 | { 128 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); 129 | } 130 | } 131 | 132 | WHEN("type is unsigned long long") 133 | { 134 | unsigned long long var = 1000; 135 | PLOGI << var; 136 | 137 | THEN("the result is as expected") 138 | { 139 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); 140 | } 141 | } 142 | 143 | WHEN("type is float") 144 | { 145 | float var = 1.2345f; 146 | PLOGI << var; 147 | 148 | THEN("the result is as expected") 149 | { 150 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1.2345")); 151 | } 152 | } 153 | 154 | WHEN("type is double") 155 | { 156 | double var = 1.2345; 157 | PLOGI << var; 158 | 159 | THEN("the result is as expected") 160 | { 161 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1.2345")); 162 | } 163 | } 164 | 165 | WHEN("type is void pointer") 166 | { 167 | void* var = &var; 168 | PLOGI << var; 169 | 170 | THEN("the result is as expected") 171 | { 172 | CHECK(testAppender.getMessage().size() >= 4); 173 | 174 | // could be 2 hex formats for pointers: with 0x prefix and without it 175 | if (testAppender.getMessage().at(1) == PLOG_NSTR('x')) 176 | { 177 | CHECK_EQ(testAppender.getMessage().at(0), PLOG_NSTR('0')); 178 | 179 | for (size_t i = 2; i < testAppender.getMessage().size(); ++i) 180 | { 181 | CHECK(std::isxdigit(testAppender.getMessage().at(i))); 182 | } 183 | } 184 | else 185 | { 186 | for (size_t i = 0; i < testAppender.getMessage().size(); ++i) 187 | { 188 | CHECK(std::isxdigit(testAppender.getMessage().at(i))); 189 | } 190 | } 191 | } 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /test/StdContainers.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cpp_lib_span 10 | # include 11 | #endif 12 | 13 | SCENARIO("std containers") 14 | { 15 | GIVEN("logger is initialised") 16 | { 17 | plog::TestAppender testAppender; 18 | plog::Logger logger(plog::verbose); 19 | logger.addAppender(&testAppender); 20 | 21 | WHEN("empty collection") 22 | { 23 | std::vector vectorOfInts; 24 | PLOGI << vectorOfInts; 25 | 26 | THEN("the result is as expected") 27 | { 28 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[]")); 29 | } 30 | } 31 | 32 | WHEN("std::vector") 33 | { 34 | std::vector vectorOfInts; 35 | vectorOfInts.push_back(1); 36 | vectorOfInts.push_back(2); 37 | vectorOfInts.push_back(3); 38 | PLOGI << vectorOfInts; 39 | 40 | THEN("the result is as expected") 41 | { 42 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1, 2, 3]")); 43 | } 44 | } 45 | 46 | WHEN("std::deque") 47 | { 48 | std::deque dequeOfStrings; 49 | dequeOfStrings.push_back("one"); 50 | dequeOfStrings.push_back("two"); 51 | dequeOfStrings.push_back("three"); 52 | PLOGI << dequeOfStrings; 53 | 54 | THEN("the result is as expected") 55 | { 56 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[one, two, three]")); 57 | } 58 | } 59 | 60 | WHEN("std::list") 61 | { 62 | std::list listOfCharPointers; 63 | listOfCharPointers.push_back("one"); 64 | listOfCharPointers.push_back("two"); 65 | listOfCharPointers.push_back(NULL); 66 | PLOGI << listOfCharPointers; 67 | 68 | THEN("the result is as expected") 69 | { 70 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[one, two, (null)]")); 71 | } 72 | } 73 | 74 | WHEN("std::set") 75 | { 76 | std::set setOfInts; 77 | setOfInts.insert(10); 78 | setOfInts.insert(20); 79 | setOfInts.insert(30); 80 | PLOGI << setOfInts; 81 | 82 | THEN("the result is as expected") 83 | { 84 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[10, 20, 30]")); 85 | } 86 | } 87 | 88 | WHEN("std::map") 89 | { 90 | std::map mapStringToInt; 91 | mapStringToInt["red"] = 1; 92 | mapStringToInt["green"] = 2; 93 | mapStringToInt["blue"] = 4; 94 | PLOGI << mapStringToInt; 95 | 96 | THEN("the result is as expected") 97 | { 98 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[blue:4, green:2, red:1]")); 99 | } 100 | } 101 | 102 | WHEN("std::multimap") 103 | { 104 | std::multimap multimapIntToString; 105 | multimapIntToString.insert(std::make_pair(1, "one")); 106 | multimapIntToString.insert(std::make_pair(1, "uno")); 107 | multimapIntToString.insert(std::make_pair(2, "two")); 108 | multimapIntToString.insert(std::make_pair(2, "due")); 109 | PLOGI << multimapIntToString; 110 | 111 | THEN("the result is as expected") 112 | { 113 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1:one, 1:uno, 2:two, 2:due]")); 114 | } 115 | } 116 | 117 | WHEN("std::vector of std::vector") 118 | { 119 | std::vector > vectorOfVectorsOfInts(3); 120 | vectorOfVectorsOfInts[0].push_back(1); 121 | vectorOfVectorsOfInts[0].push_back(2); 122 | vectorOfVectorsOfInts[1].push_back(-1); 123 | vectorOfVectorsOfInts[1].push_back(-2); 124 | PLOGI << vectorOfVectorsOfInts; 125 | 126 | THEN("the result is as expected") 127 | { 128 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[[1, 2], [-1, -2], []]")); 129 | } 130 | } 131 | 132 | WHEN("std::pair") 133 | { 134 | std::pair pairOfInts(5, 10); 135 | PLOGI << pairOfInts; 136 | 137 | THEN("the result is as expected") 138 | { 139 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("5:10")); 140 | } 141 | } 142 | 143 | #if 0 // std::span is not supported yet as it has no const_iterator till c++23 144 | #ifdef __cpp_lib_span 145 | WHEN("std::span") 146 | { 147 | int arr[] = {1, 2, 3}; 148 | std::span spanOfInts(std::begin(arr), std::end(arr)); 149 | PLOGI << spanOfInts; 150 | 151 | THEN("the result is as expected") 152 | { 153 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1, 2, 3]")); 154 | } 155 | } 156 | #endif 157 | #endif 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /test/StdManipulators.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | SCENARIO("std manipulators") 4 | { 5 | GIVEN("logger is initialised") 6 | { 7 | plog::TestAppender testAppender; 8 | plog::Logger logger(plog::verbose); 9 | logger.addAppender(&testAppender); 10 | 11 | WHEN("std::boolalpha") 12 | { 13 | PLOGI << std::boolalpha << true; 14 | 15 | THEN("the result is as expected") 16 | { 17 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("true")); 18 | } 19 | } 20 | 21 | WHEN("std::hex") 22 | { 23 | PLOGI << std::hex << 65534; 24 | 25 | THEN("the result is as expected") 26 | { 27 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("fffe")); 28 | } 29 | } 30 | 31 | WHEN("use manipulator for the 1st message") 32 | { 33 | bool var = true; 34 | PLOGI << std::boolalpha << var; 35 | 36 | AND_WHEN("log the 2nd message") 37 | { 38 | PLOGI << var; 39 | 40 | THEN("manipulators are cleared for the 2nd message") 41 | { 42 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1")); 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/StdStreamable.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include 3 | #include 4 | 5 | struct StdStreamable 6 | { 7 | int id; 8 | std::string name; 9 | }; 10 | 11 | inline std::ostream& operator<<(std::ostream& os, const StdStreamable& obj) 12 | { 13 | os << "StdStreamable (id: " << obj.id << ", name: " << obj.name << ")"; 14 | return os; 15 | } 16 | 17 | #if PLOG_ENABLE_WCHAR_INPUT 18 | struct StdWStreamable 19 | { 20 | int id; 21 | std::wstring name; 22 | }; 23 | 24 | inline std::wostream& operator<<(std::wostream& os, const StdWStreamable& obj) 25 | { 26 | os << L"StdWStreamable (id: " << obj.id << L", name: " << obj.name << L")"; 27 | return os; 28 | } 29 | #endif 30 | 31 | SCENARIO("cast to string") 32 | { 33 | GIVEN("logger is initialised") 34 | { 35 | plog::TestAppender testAppender; 36 | plog::Logger logger(plog::verbose); 37 | logger.addAppender(&testAppender); 38 | 39 | WHEN("type is streamable to std::ostream") 40 | { 41 | StdStreamable var; 42 | var.id = 1; 43 | var.name = "water"; 44 | 45 | PLOGI << var; 46 | 47 | THEN("the result is as expected") 48 | { 49 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("StdStreamable (id: 1, name: water)")); 50 | } 51 | } 52 | 53 | #if PLOG_ENABLE_WCHAR_INPUT 54 | WHEN("type is streamable to std::wostream") 55 | { 56 | StdWStreamable var; 57 | var.id = 1; 58 | var.name = L"water"; 59 | 60 | PLOGI << var; 61 | 62 | THEN("the result is as expected") 63 | { 64 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("StdWStreamable (id: 1, name: water)")); 65 | } 66 | } 67 | #endif 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/StringTypes.cpp: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | 3 | #ifdef __cpp_lib_string_view 4 | # include 5 | #endif 6 | 7 | SCENARIO("string types") 8 | { 9 | GIVEN("logger is initialised") 10 | { 11 | plog::TestAppender testAppender; 12 | plog::Logger logger(plog::verbose); 13 | logger.addAppender(&testAppender); 14 | 15 | WHEN("type is char*") 16 | { 17 | char* var = const_cast("test"); 18 | PLOGI << var; 19 | 20 | THEN("the result is as expected") 21 | { 22 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 23 | } 24 | } 25 | 26 | WHEN("type is const char*") 27 | { 28 | const char* var = "test"; 29 | PLOGI << var; 30 | 31 | THEN("the result is as expected") 32 | { 33 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 34 | } 35 | } 36 | 37 | WHEN("type is char[]") 38 | { 39 | char var[] = "test"; 40 | PLOGI << var; 41 | 42 | THEN("the result is as expected") 43 | { 44 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 45 | } 46 | } 47 | 48 | WHEN("type is const char[]") 49 | { 50 | const char var[] = "test"; 51 | PLOGI << var; 52 | 53 | THEN("the result is as expected") 54 | { 55 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 56 | } 57 | } 58 | 59 | WHEN("type is std::string") 60 | { 61 | std::string var = "test"; 62 | PLOGI << var; 63 | 64 | THEN("the result is as expected") 65 | { 66 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 67 | } 68 | } 69 | 70 | WHEN("type is const std::string") 71 | { 72 | const std::string var = "test"; 73 | PLOGI << var; 74 | 75 | THEN("the result is as expected") 76 | { 77 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 78 | } 79 | } 80 | 81 | #ifdef __cpp_lib_string_view 82 | WHEN("type is std::string_view") 83 | { 84 | std::string_view var = "test"; 85 | PLOGI << var; 86 | 87 | THEN("the result is as expected") 88 | { 89 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 90 | } 91 | } 92 | 93 | WHEN("type is const std::string_view") 94 | { 95 | const std::string_view var = "test"; 96 | PLOGI << var; 97 | 98 | THEN("the result is as expected") 99 | { 100 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 101 | } 102 | } 103 | #endif 104 | 105 | #if PLOG_ENABLE_WCHAR_INPUT 106 | WHEN("type is wchar_t*") 107 | { 108 | wchar_t* var = const_cast(L"test"); 109 | PLOGI << var; 110 | 111 | THEN("the result is as expected") 112 | { 113 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 114 | } 115 | } 116 | 117 | WHEN("type is const wchar_t*") 118 | { 119 | const wchar_t* var = L"test"; 120 | PLOGI << var; 121 | 122 | THEN("the result is as expected") 123 | { 124 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 125 | } 126 | } 127 | 128 | WHEN("type is wchar_t[]") 129 | { 130 | wchar_t var[] = L"test"; 131 | PLOGI << var; 132 | 133 | THEN("the result is as expected") 134 | { 135 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 136 | } 137 | } 138 | 139 | WHEN("type is const wchar_t[]") 140 | { 141 | const wchar_t var[] = L"test"; 142 | PLOGI << var; 143 | 144 | THEN("the result is as expected") 145 | { 146 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 147 | } 148 | } 149 | 150 | WHEN("type is std::wstring") 151 | { 152 | std::wstring var = L"test"; 153 | PLOGI << var; 154 | 155 | THEN("the result is as expected") 156 | { 157 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 158 | } 159 | } 160 | 161 | WHEN("type is const std::wstring") 162 | { 163 | const std::wstring var = L"test"; 164 | PLOGI << var; 165 | 166 | THEN("the result is as expected") 167 | { 168 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 169 | } 170 | } 171 | 172 | # ifdef __cpp_lib_string_view 173 | WHEN("type is std::wstring_view") 174 | { 175 | std::wstring_view var = L"test"; 176 | PLOGI << var; 177 | 178 | THEN("the result is as expected") 179 | { 180 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 181 | } 182 | } 183 | 184 | WHEN("type is const std::wstring_view") 185 | { 186 | const std::wstring_view var = L"test"; 187 | PLOGI << var; 188 | 189 | THEN("the result is as expected") 190 | { 191 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 192 | } 193 | } 194 | # endif 195 | #endif 196 | 197 | #ifdef __cplusplus_cli 198 | WHEN("type is const System::String^") 199 | { 200 | System::String^ var = "test"; 201 | PLOGI << var; 202 | 203 | THEN("the result is as expected") 204 | { 205 | CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); 206 | } 207 | } 208 | #endif 209 | 210 | //TODO: add more tests 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /test/TestAppender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace plog 5 | { 6 | class TestAppender : public IAppender 7 | { 8 | public: 9 | virtual void write(const Record& record) PLOG_OVERRIDE 10 | { 11 | m_message = record.getMessage(); 12 | } 13 | 14 | const util::nstring& getMessage() const 15 | { 16 | return m_message; 17 | } 18 | 19 | private: 20 | util::nstring m_message; 21 | }; 22 | } --------------------------------------------------------------------------------