diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..80d192d --- /dev/null +++ b/.clang-format @@ -0,0 +1,56 @@ +--- +BasedOnStyle: Google +AlignAfterOpenBracket: 'AlwaysBreak' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AlignConsecutiveMacros: 'true' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: 'None' +AllowShortIfStatementsOnASingleLine: 'Never' +AllowShortLoopsOnASingleLine: 'false' +BreakBeforeBraces: Allman +BinPackArguments: 'false' +BinPackParameters: 'false' +Cpp11BracedListStyle: 'false' +ColumnLimit: 125 +NamespaceIndentation: All +SpaceAfterTemplateKeyword: 'false' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyBlock: true +Standard: 'Latest' +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 1 + SortPriority: 3 + CaseSensitive: true + - Regex: '^(<|")(windows.h)' + Priority: 1 + SortPriority: 1 + - Regex: '^(<|")(processthreadsapi.h)' + Priority: 2 + SortPriority: 2 + - Regex: '<[[:alnum:].]+>' + Priority: 5 + SortPriority: 1 + - Regex: '<[[:alnum:].]+_+[[:alnum:].]+>' + Priority: 5 + SortPriority: 4 + - Regex: '^((<|")(gtest|gmock|isl|json)/)' + Priority: 8 + SortPriority: 5 + - Regex: '^((<|")(fmt/|fmtlog/))' + Priority: 11 + SortPriority: 6 + - Regex: '^(<|")(arby)' + Priority: 8 + SortPriority: 100 + - Regex: '^(<|")./' + Priority: 10 + SortPriority: 101 + - Regex: '^(<|")(.*)' + Priority: 15 + SortPriority: 7 +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..e5fa9ea --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,6 @@ +--- +Checks: '*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-modernize-use-trailing-return-type,-llvm-*,-llvmlibc-*' +CheckOptions: [{ key: misc-non-private-member-variables-in-classes, value: IgnoreClassesWithAllMemberVariablesBeingPublic }] +WarningsAsErrors: '' +HeaderFilterRegex: '' +FormatStyle: none diff --git a/.cmake-format b/.cmake-format new file mode 100644 index 0000000..8c355cf --- /dev/null +++ b/.cmake-format @@ -0,0 +1,57 @@ +format: + tab_size: 2 + line_width: 100 + dangle_parens: true + +parse: + additional_commands: + cpmaddpackage: + pargs: + nargs: '*' + flags: [] + spelling: CPMAddPackage + kwargs: &cpmaddpackagekwargs + NAME: 1 + FORCE: 1 + VERSION: 1 + GIT_TAG: 1 + DOWNLOAD_ONLY: 1 + GITHUB_REPOSITORY: 1 + GITLAB_REPOSITORY: 1 + GIT_REPOSITORY: 1 + SVN_REPOSITORY: 1 + SVN_REVISION: 1 + SOURCE_DIR: 1 + DOWNLOAD_COMMAND: 1 + FIND_PACKAGE_ARGUMENTS: 1 + NO_CACHE: 1 + GIT_SHALLOW: 1 + URL: 1 + URL_HASH: 1 + URL_MD5: 1 + DOWNLOAD_NAME: 1 + DOWNLOAD_NO_EXTRACT: 1 + HTTP_USERNAME: 1 + HTTP_PASSWORD: 1 + OPTIONS: + + cpmfindpackage: + pargs: + nargs: '*' + flags: [] + spelling: CPMFindPackage + kwargs: *cpmaddpackagekwargs + packageproject: + pargs: + nargs: '*' + flags: [] + spelling: packageProject + kwargs: + NAME: 1 + VERSION: 1 + NAMESPACE: 1 + INCLUDE_DIR: 1 + INCLUDE_DESTINATION: 1 + BINARY_DIR: 1 + COMPATIBILITY: 1 + VERSION_HEADER: 1 + DEPENDENCIES: + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..7532789 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help me improve +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +* OS: [e.g. Windows] +* Version [e.g. 10] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..48d5f81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e75a04b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated +when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Provide usage examples** +A few examples of how the feature should be used. Please make sure they are clear +and concise. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml new file mode 100644 index 0000000..9e90bf5 --- /dev/null +++ b/.github/workflows/documentation.yaml @@ -0,0 +1,37 @@ +name: Documentation + +on: + push: + tags: + - "*" + +env: + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + name: Build and publish documentation + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: Install dependencies + run: | + brew install doxygen + pip3 install jinja2 Pygments + + - name: Build + run: | + cmake -Sdocumentation -Bbuild + cmake --build build --target GenerateDocs + + - name: Publish + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./build/doxygen/html diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml new file mode 100644 index 0000000..8dfbece --- /dev/null +++ b/.github/workflows/install.yml @@ -0,0 +1,44 @@ +name: Install + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: build and install library + run: | + cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release --log-level=DEBUG + sudo cmake --build build --target install --verbose -j4 + rm -rf build + + - name: configure + run: cmake -Stest -Bbuild -DTEST_INSTALLED_VERSION=1 -DCPM_DOWNLOAD_ALL=1 -DCPM_SOURCE_CACHE=~/.cache/cpm --log-level=DEBUG + + - name: build + run: cmake --build build --config Debug -j4 --verbose + + - name: test + run: | + cd build + ctest --build-config Debug -j4 diff --git a/.github/workflows/install_download_all.yml b/.github/workflows/install_download_all.yml new file mode 100644 index 0000000..b3007a8 --- /dev/null +++ b/.github/workflows/install_download_all.yml @@ -0,0 +1,45 @@ +name: Install_CPM_DOWNLOAD_ALL + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: build and install library + run: | + cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCPM_DOWNLOAD_ALL=1 --log-level=DEBUG + cmake --build build --target all -j4 --verbose + cmake --install ./build --prefix ./install_dir + rm -rf build + + - name: configure + run: CMAKE_PREFIX_PATH="./install_dir" cmake -Stest -Bbuild -DTEST_INSTALLED_VERSION=1 -DCPM_DOWNLOAD_ALL=1 -DCPM_SOURCE_CACHE=~/.cache/cpm --log-level=DEBUG + + - name: build + run: cmake --build build --config Debug -j4 --verbose + + - name: test + run: | + cd build + ctest --build-config Debug -j4 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..d4b4207 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,38 @@ +name: MacOS + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: configure + run: cmake -Stest -Bbuild -DCMAKE_BUILD_TYPE=Debug --log-level=DEBUG + + - name: build + run: cmake --build build -j4 --verbose + + - name: test + run: | + cd build + ctest --build-config Debug -j4 diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..788d1a7 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,35 @@ +name: Style + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +env: + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: Install format dependencies + run: pip3 install clang-format==14.0.6 cmake_format==0.6.11 pyyaml + + - name: configure + run: cmake -Stest -Bbuild --log-level=DEBUG + + - name: check style + run: cmake --build build --target check-format -j4 --verbose diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000..9719498 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,42 @@ +name: Ubuntu + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: configure + run: cmake -Stest -Bbuild -DENABLE_TEST_COVERAGE=1 -DCMAKE_BUILD_TYPE=Debug --log-level=DEBUG + + - name: build + run: cmake --build build -j4 --verbose + + - name: test + run: | + cd build + ctest --build-config Debug -j4 + + - name: collect code coverage + run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..7d84be3 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,41 @@ +name: Windows + +on: + push: + branches: + - master + - main + pull_request: + branches: + - master + - main + +env: + CTEST_OUTPUT_ON_FAILURE: 1 + CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: "**/cpm_modules" + key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} + + - name: Support longpaths + run: git config --system core.longpaths true # https://github.com/orgs/community/discussions/26952 + + - name: configure + run: cmake -Stest -Bbuild --log-level=DEBUG + + - name: build + run: cmake --build build --config Debug -j4 --verbose + + - name: test + run: | + cd build + ctest --build-config Debug -j4 diff --git a/.gitignore b/.gitignore index d8cdaa2..e394f43 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ -.build -out -CMakeSettings.json +/build* +/.vscode +/cpm_modules +.DS_Store +/.cache +build/ +cmake_build*/ +.cache/ +install_dir/ +compile_commands.json + diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index c8edf6a..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "fmt"] - path = fmt - url = https://github.com/fmtlib/fmt diff --git a/CMakeLists.txt b/CMakeLists.txt index aa64a8a..8997b7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,31 +1,108 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.14...3.22) -project(fmtlog CXX) +# ---- Project ---- -if(MSVC) - add_compile_options(/std:c++latest) +# Note: update this to your new project's name and version +set(fmtlog_VERSION 2.3.0) +project( + fmtlog + VERSION ${fmtlog_VERSION} + LANGUAGES CXX +) + +if(CMAKE_BUILD_TYPE STREQUAL Debug) + set(default_value_options ON) else() - add_compile_options(-Wall -O3 -std=c++17) - #add_compile_options(-Wall -Ofast -std=c++2a -DNDEBUG -march=skylake -flto -fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables -DFMTLOG_NO_CHECK_LEVEL=1) - #SET(CMAKE_AR "gcc-ar") - #SET(CMAKE_RANLIB "gcc-ranlib") - link_libraries(pthread) + set(default_value_options OFF) endif() -link_directories(.) -include_directories(fmt/include) +option(${PROJECT_NAME}_BUILD_WITH_ASAN "add -fsanitize=address aka turn on ASAN?" + ${default_value_options} +) +option(BUILD_SHARED_LIBS "yes/no" NO) -add_library(fmtlog-shared SHARED fmtlog.cc) -if(MSVC) - target_link_libraries(fmtlog-shared fmt) +if(${BUILD_SHARED_LIBS}) + # https://stackoverflow.com/questions/33062728/cmake-link-shared-library-on-windows + if(WIN32) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + endif() endif() -install(TARGETS fmtlog-shared) -add_library(fmtlog-static fmtlog.cc) -if(MSVC) - target_link_libraries(fmtlog-static fmt) +set(FETCHCONTENT_QUIET + OFF + CACHE BOOL "Make downloading of packages quiet" +) + +# ---- Include guards ---- + +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." + ) endif() -install(TARGETS fmtlog-static) -add_subdirectory(fmt) -add_subdirectory(test) +# ---- Add source files ---- + +# Note: globbing sources is considered bad practice as CMake's generators may not detect new files +# automatically. Keep that in mind when changing files, or explicitly mention them here. +file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp") +# file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") + +# ---- Create library ---- + +# Note: for header-only libraries change all PUBLIC flags to INTERFACE and create an interface +# target: +add_library(${PROJECT_NAME} INTERFACE) +# add_library(${PROJECT_NAME} ${headers} ${sources}) +set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17) + +target_include_directories( + ${PROJECT_NAME} INTERFACE $ + $ +) + +# being a cross-platform target, we enforce standards conformance on MSVC +target_compile_options(${PROJECT_NAME} INTERFACE "$<$:/permissive->") + +if(CMAKE_BUILD_TYPE STREQUAL Debug) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${PROJECT_NAME} INTERFACE -fconcepts-diagnostics-depth=10) + endif() + if(${${PROJECT_NAME}_BUILD_WITH_ASAN}) + target_compile_options(${PROJECT_NAME} INTERFACE -fsanitize=address) + target_link_options(${PROJECT_NAME} INTERFACE -fsanitize=address) + endif() +endif() + +# ---- Add dependencies via CPM ---- +# see https://github.com/TheLartians/CPM.cmake for more info + +include(cmake/getCPM.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/cmake/packages/add_fmt.cmake) +target_link_libraries(${PROJECT_NAME} INTERFACE fmt::fmt-header-only) +# ---- Create an installable target ---- +# this allows users to install and find the library via `find_package()`. + +include(${CMAKE_CURRENT_LIST_DIR}/cmake/packages/add_packageproject.cmake) + +# the location where the project's version header will be placed should match the project's regular +# header paths +string(TOLOWER ${PROJECT_NAME}/version.h VERSION_HEADER_LOCATION) +string(TOLOWER ${PROJECT_NAME}/export.h EXPORT_HEADER_LOCATION) + +set_property(TARGET ${PROJECT_NAME} PROPERTY VERSION ${PROJECT_VERSION}) +set_property(TARGET ${PROJECT_NAME} PROPERTY SOVERSION ${PROJECT_VERSION}) + +packageProject( + NAME ${PROJECT_NAME} + VERSION ${PROJECT_VERSION} + NAMESPACE ${PROJECT_NAME} + BINARY_DIR ${PROJECT_BINARY_DIR} + INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include + INCLUDE_DESTINATION include/${PROJECT_NAME} + VERSION_HEADER "${VERSION_HEADER_LOCATION}" EXPORT_HEADER "${EXPORT_HEADER_LOCATION}" + COMPATIBILITY "AnyNewerVersion" DISABLE_VERSION_SUFFIX ON + DEPENDENCIES "fmt ${fmt_VERSION}" +) diff --git a/README.md b/README.md index d40da2c..996495b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,37 @@ +Ubuntu latest: [![Actions Status](https://github.com/Arniiiii/fmtlog_cmake_fix/workflows/Ubuntu/badge.svg)](https://github.com/Arniiiii/fmtlog_cmake_fix/actions) + +MacOS: [![Actions Status](https://github.com/Arniiiii/fmtlog_cmake_fix/workflows/MacOS/badge.svg)](https://github.com/Arniiiii/fmtlog_cmake_fix/actions) + +MSVC Windows:[![Actions Status](https://github.com/Arniiiii/fmtlog_cmake_fix/workflows/Windows/badge.svg)](https://github.com/Arniiiii/fmtlog_cmake_fix/actions) + +Are clang-format and cmake-format used everywhere: [![Actions Status](https://github.com/Arniiiii/fmtlog_cmake_fix/workflows/Style/badge.svg)](https://github.com/Arniiiii/fmtlog_cmake_fix/actions) + +Is installable if dependencies are installed on system: [![Actions Status](https://github.com/Arniiiii/fmtlog_cmake_fix/workflows/Install/badge.svg)](https://github.com/Arniiiii/fmtlog_cmake_fix/actions) + +Is installable if dependencies are downloaded at configure time via CPM_DOWNLOAD_ALL=1 : [![Actions Status](https://github.com/Arniiiii/fmtlog_cmake_fix/workflows/Install_CPM_DOWNLOAD_ALL/badge.svg)](https://github.com/Arniiiii/fmtlog_cmake_fix/actions) + + +# Important + +I ( Arniiiii ) don't like the code from the project. +I won't use the log library again. + +Why: + - It has 324 warnings just from tests. + - The code uses `reinterpret_cast` like cast via C to get access to private members of some fmt classes + - It had a memory leak which I tried to fix. Now it requires `fmtlog::poll()` every time to get it write to at an output. + - A test at Windows with MSVC just runs forever and I don't know why. + +# What you can use instead? + +On Quill's README there are some benchmark results for some log libraries you may want, so I will choose an another o ne. + # fmtlog -fmtlog is a performant asynchronous logging library using [fmt](https://github.com/fmtlib/fmt) library format. +fmtlog is a performant asynchronous header-only logging library using [fmt](https://github.com/fmtlib/fmt) library format. ## Features * Faster - lower runtime latency than [NanoLog](https://github.com/PlatformLab/NanoLog) and higher throughput than [spdlog](https://github.com/gabime/spdlog) (see [Performance](https://github.com/MengRao/fmtlog#Performance) below). -* Headers only or compiled +* Headers only * Feature rich formatting on top of excellent fmt library. * Asynchronous multi-threaded logging **in time order** and can also be used synchronously in single thread. * Custom formatting @@ -12,25 +40,45 @@ fmtlog is a performant asynchronous logging library using [fmt](https://github.c * Log frequency limitation - specific logs can be set a minimum logging interval. ## Platforms -* Linux (GCC 10.2 tested) +* Linux (GCC 10.2,14.0 ; Clang 16.0.6 tested) * Windows (MSVC 2019 tested) ## Install C++17 is required, and fmtlog is dependent on [fmtlib](https://github.com/fmtlib/fmt), you need to install fmtlib first if you haven't. -#### Header only version -Just copy `fmtlog.h` and `fmtlog-inl.h` to your project, and: -* Either define macro `FMTLOG_HEADER_ONLY` before including fmtlog.h. -* Or include `fmtlog-inl.h` in one of your source files. - -#### Static/Shared lib version built by CMake -```console -$ git clone https://github.com/MengRao/fmtlog.git -$ cd fmtlog -$ git submodule init -$ git submodule update -$ ./build.sh +#### Add headers manually +Just copy `include/fmtlog/fmtlog.h` and `include/fmtlog/internal/fmtlog-inl.h` to your project and somehow link `fmtlib`. +### CMake +If you are using CMake, there's some options to add library, and some options to link fmtlib +#### How to add library? +##### Use add_subdirectory somehow and link fmtlib somehow +###### CPM aka CMake's missing package manager +Put somewhere next code (obviously after adding [CPM](https://github.com/cpm-cmake/CPM.cmake) itself ): +``` +CPMAddPackage( + NAME fmtlog + GIT_REPOSITORY "https://github.com/Arniiiii/fmtlog_cmake_fix.git" + TAG master +) + +target_link_libraries( [PRIVATE/PUBLIC/INTERFACE] fmtlog::fmtlog) ``` -Then copy `fmtlog.h` and `libfmtlog-static.a`/`libfmtlog-shared.so` generated in `.build` dir. +###### Through a submodule +We do **not** recommend do that. Use CPM instead. +###### Have it somewhere at your repo +We do **not** recommend do that. Use CPM instead. + +#### How to link fmtlib? + +- Through the fmtlog library +- Just link fmtlib with your target additionally ( do it yourself ) +##### How to link fmtlib through fmtlog library? +There's next options: + ++ if you want to use system's fmt: add `CPM_USE_LOCAL_PACKAGES ON` or add `CPM_LOCAL_PACKAGES_ONLY ON` ++ if you want to download the fmtlib and everything that you can have from CPM and compile all of it yourself everytime (consider installing `ccache`), add nothing or add `CPM_DOWNLOAD_ALL ON` . + +## CMake options +Check them at `cmake/StandardSettings.cmake` . ## Usage ```c++ diff --git a/all/CMakeLists.txt b/all/CMakeLists.txt new file mode 100644 index 0000000..9f518d2 --- /dev/null +++ b/all/CMakeLists.txt @@ -0,0 +1,15 @@ +# this script adds all subprojects to a single build to allow IDEs understand the full project +# structure. + +cmake_minimum_required(VERSION 3.14...3.22) + +project(fmtlogBuildAll LANGUAGES CXX) + +include(../cmake/tools.cmake) + +# needed to generate test target +enable_testing() + +# add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../standalone ${CMAKE_BINARY_DIR}/standalone) +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../test ${CMAKE_BINARY_DIR}/test) +# add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../documentation ${CMAKE_BINARY_DIR}/documentation) diff --git a/build.sh b/build.sh deleted file mode 100755 index 2618423..0000000 --- a/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -BUILD_DIR=${BUILD_DIR:-.build} - -mkdir -p "$BUILD_DIR" \ - && cd "$BUILD_DIR" \ - && cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..\ - && cmake --build . -j \ - && cmake --install . --prefix . diff --git a/clear.sh b/clear.sh deleted file mode 100755 index f267d7e..0000000 --- a/clear.sh +++ /dev/null @@ -1 +0,0 @@ -rm fmtlog.txt spdlog.txt nanalog.bin compressedLog diff --git a/cmake/getCPM.cmake b/cmake/getCPM.cmake new file mode 100644 index 0000000..c1fbaf5 --- /dev/null +++ b/cmake/getCPM.cmake @@ -0,0 +1,53 @@ +# =============================================================================== + +# This file should be like getCPM.cmake from CPM project. But it's too long to wait until a PR is +# merged. So we use here a my version. + +# ============================================================================= +# from release +# ============================================================================= + +# # SPDX-License-Identifier: MIT # # SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior +# and contributors +# +# set(CPM_DOWNLOAD_VERSION 0.40.2) set(CPM_HASH_SUM +# "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d") +# +# if(CPM_SOURCE_CACHE) set(CPM_DOWNLOAD_LOCATION +# "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") elseif(DEFINED ENV{CPM_SOURCE_CACHE}) +# set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") else() +# set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") endif() +# +# # Expand relative path. This is important if the provided path contains a tilde (~) +# get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) +# +# file(DOWNLOAD +# https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake +# ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} ) +# +# include(${CPM_DOWNLOAD_LOCATION}) +# + +# ============================================================================= +# my version: +# ============================================================================= + +# set(CPM_DOWNLOAD_VERSION 0.40.2) set(CPM_HASH_SUM +# "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD https://raw.githubusercontent.com/Arniiiii/CPM.cmake/main/cmake/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} # EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake/optimization_flags/lto.cmake b/cmake/optimization_flags/lto.cmake new file mode 100644 index 0000000..356ccf8 --- /dev/null +++ b/cmake/optimization_flags/lto.cmake @@ -0,0 +1,43 @@ +function(set_project_lto_opts project_name) + # if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") message(AUTHOR_WARNING "The project ${PROJECT_NAME} is + # building in debug mode, thus no compiler flags for link time optimization were added.") return() + # endif() + + set(MSVC_LTO # sorry idk + ) + + if(${PROJECT_NAME}_CLANG_LTO_THIN) + set(CLANG_LTO -flto=thin -Werror=odr -Werror=strict-aliasing) + else() + set(CLANG_LTO -flto -Werror=odr -Werror=strict-aliasing) + endif() + + set(GCC_LTO -flto -Werror=odr -Werror=lto-type-mismatch -Werror=strict-aliasing) + + # if(MSVC) set(PROJECT_LTO ${MSVC_LTO}) elseif + if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_LTO ${CLANG_LTO}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_LTO ${GCC_LTO}) + else() + message( + AUTHOR_WARNING + "No compiler flags for link time optimization were set for '${CMAKE_CXX_COMPILER_ID}' compiler." + ) + endif() + + if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + target_compile_options(${project_name} INTERFACE ${PROJECT_LTO}) + target_link_options(${project_name} INTERFACE ${PROJECT_LTO}) + else() + target_compile_options(${project_name} PUBLIC ${PROJECT_LTO}) + target_link_options(${project_name} PUBLIC ${PROJECT_LTO}) + endif() + + if(NOT TARGET ${project_name}) + message( + AUTHOR_WARNING + "${project_name} is not a target, thus no compiler flags for link time optimization were added." + ) + endif() +endfunction() diff --git a/cmake/optimization_flags/polyhedral.cmake b/cmake/optimization_flags/polyhedral.cmake new file mode 100644 index 0000000..9d29d30 --- /dev/null +++ b/cmake/optimization_flags/polyhedral.cmake @@ -0,0 +1,43 @@ +function(set_project_polyhedral_opts project_name) + if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + message( + AUTHOR_WARNING + "The project ${PROJECT_NAME} is building in debug mode, thus no compiler flags for polyhedral optimization were added." + ) + return() + endif() + + set(MSVC_POLYHEDRAL # sorry idk + ) + + set(CLANG_POLLY -mllvm -polly) + + set(GCC_GRAPHITE -fgraphite-identity -floop-interchange -floop-strip-mine -floop-nest-optimize) + + # if(MSVC) set(PROJECT_POLYHEDRAL ${MSVC_POLYHEDRAL}) elseif + if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_POLYHEDRAL ${CLANG_POLLY}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_POLYHEDRAL ${GCC_GRAPHITE}) + else() + message( + AUTHOR_WARNING + "No compiler flags for polyhedral optimization were set for '${CMAKE_CXX_COMPILER_ID}' compiler." + ) + endif() + + if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + target_compile_options(${project_name} INTERFACE ${PROJECT_POLYHEDRAL}) + target_link_options(${project_name} INTERFACE ${PROJECT_POLYHEDRAL}) + else() + target_compile_options(${project_name} PUBLIC ${PROJECT_POLYHEDRAL}) + target_link_options(${project_name} PUBLIC ${PROJECT_POLYHEDRAL}) + endif() + + if(NOT TARGET ${project_name}) + message( + AUTHOR_WARNING + "${project_name} is not a target, thus no compiler flags for polyhedral optimization were added." + ) + endif() +endfunction() diff --git a/cmake/packages/add_fmt.cmake b/cmake/packages/add_fmt.cmake new file mode 100644 index 0000000..bed0a82 --- /dev/null +++ b/cmake/packages/add_fmt.cmake @@ -0,0 +1,10 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../getCPM.cmake) + +set(fmt_VERSION 11.0.2) +CPMAddPackage( + NAME fmt + # GIT_TAG ${fmt_VERSION} GITHUB_REPOSITORY fmtlib/fmt + VERSION ${fmt_VERSION} + URL "https://github.com/fmtlib/fmt/releases/download/${fmt_VERSION}/fmt-${fmt_VERSION}.zip" + OPTIONS "FMT_OS OFF" "fmtlog_ENABLE_CPM=1" +) diff --git a/cmake/packages/add_packageproject.cmake b/cmake/packages/add_packageproject.cmake new file mode 100644 index 0000000..283ad2e --- /dev/null +++ b/cmake/packages/add_packageproject.cmake @@ -0,0 +1,16 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../getCPM.cmake) + +# PackageProject.cmake will be used to make our target installable +file(GLOB_RECURSE patches_for_boost CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/patches/packageProject.cmake/*.patch" +) + +set(packageProject_VERSION 1.12.0) +CPMAddPackage( + NAME PackageProject.cmake + VERSION ${packageProject_VERSION} + # GIT_REPOSITORY "https://github.com/TheLartians/PackageProject.cmake.git" GIT_TAG + # "v${packageProject_VERSION}" + URL "https://github.com/TheLartians/PackageProject.cmake/archive/refs/tags/v${packageProject_VERSION}.zip" + PATCHES ${patches_for_boost} +) diff --git a/cmake/tools.cmake b/cmake/tools.cmake new file mode 100644 index 0000000..9938dbd --- /dev/null +++ b/cmake/tools.cmake @@ -0,0 +1,66 @@ +# this file contains a list of tools that can be activated and downloaded on-demand each tool is +# enabled during configuration by passing an additional `-DUSE_=` argument to CMake + +# only activate tools for top level project +if(NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + return() +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/getCPM.cmake) + +# enables sanitizers support using the the `USE_SANITIZER` flag available values are: Address, +# Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined' +if(USE_SANITIZER OR USE_STATIC_ANALYZER) + CPMAddPackage("gh:StableCoder/cmake-scripts@23.04") + + if(USE_SANITIZER) + include(${cmake-scripts_SOURCE_DIR}/sanitizers.cmake) + endif() + + if(USE_STATIC_ANALYZER) + if("clang-tidy" IN_LIST USE_STATIC_ANALYZER) + set(CLANG_TIDY + ON + CACHE INTERNAL "" + ) + else() + set(CLANG_TIDY + OFF + CACHE INTERNAL "" + ) + endif() + if("iwyu" IN_LIST USE_STATIC_ANALYZER) + set(IWYU + ON + CACHE INTERNAL "" + ) + else() + set(IWYU + OFF + CACHE INTERNAL "" + ) + endif() + if("cppcheck" IN_LIST USE_STATIC_ANALYZER) + set(CPPCHECK + ON + CACHE INTERNAL "" + ) + else() + set(CPPCHECK + OFF + CACHE INTERNAL "" + ) + endif() + + include(${cmake-scripts_SOURCE_DIR}/tools.cmake) + + clang_tidy(${CLANG_TIDY_ARGS}) + include_what_you_use(${IWYU_ARGS}) + cppcheck(${CPPCHECK_ARGS}) + endif() +endif() + +# enables CCACHE support through the USE_CCACHE flag possible values are: YES, NO or equivalent +if(USE_CCACHE) + CPMAddPackage("gh:TheLartians/Ccache.cmake@1.2.4") +endif() diff --git a/fmt b/fmt deleted file mode 160000 index e69e5f9..0000000 --- a/fmt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e69e5f977d458f2650bb346dadf2ad30c5320281 diff --git a/fmtlog.cc b/fmtlog.cc deleted file mode 100644 index da6c2ce..0000000 --- a/fmtlog.cc +++ /dev/null @@ -1 +0,0 @@ -#include "fmtlog-inl.h" diff --git a/fmtlog.h b/include/fmtlog/fmtlog.h similarity index 66% rename from fmtlog.h rename to include/fmtlog/fmtlog.h index 566b34f..9f1bfc5 100644 --- a/fmtlog.h +++ b/include/fmtlog/fmtlog.h @@ -22,14 +22,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once -//#define FMT_HEADER_ONLY -#include "fmt/format.h" -#include -#include -#include +// #define FMT_HEADER_ONLY +#include #include -#include +#include #include +#include +#include +#include + +#include "fmt/format.h" #ifdef _MSC_VER #include @@ -61,37 +63,50 @@ SOFTWARE. #define FMTLOG_QUEUE_SIZE (1 << 20) #endif -namespace fmtlogdetail { -template -struct UnrefPtr : std::false_type -{ using type = Arg; }; +namespace fmtlogdetail +{ + template + struct UnrefPtr : std::false_type + { + using type = Arg; + }; -template<> -struct UnrefPtr : std::false_type -{ using type = char*; }; + template<> + struct UnrefPtr : std::false_type + { + using type = char*; + }; -template<> -struct UnrefPtr : std::false_type -{ using type = void*; }; + template<> + struct UnrefPtr : std::false_type + { + using type = void*; + }; -template -struct UnrefPtr> : std::true_type -{ using type = Arg; }; + template + struct UnrefPtr> : std::true_type + { + using type = Arg; + }; -template -struct UnrefPtr> : std::true_type -{ using type = Arg; }; + template + struct UnrefPtr> : std::true_type + { + using type = Arg; + }; -template -struct UnrefPtr : std::true_type -{ using type = Arg; }; + template + struct UnrefPtr : std::true_type + { + using type = Arg; + }; -}; // namespace fmtlogdetail +}; // namespace fmtlogdetail template class fmtlogT { -public: + public: enum LogLevel : uint8_t { DBG = 0, @@ -135,9 +150,15 @@ class fmtlogT // msg: full log msg with header // bodyPos: log body index in the msg // logFilePos: log file position of this msg - typedef void (*LogCBFn)(int64_t ns, LogLevel level, fmt::string_view location, size_t basePos, - fmt::string_view threadName, fmt::string_view msg, size_t bodyPos, - size_t logFilePos); + typedef void (*LogCBFn)( + int64_t ns, + LogLevel level, + fmt::string_view location, + size_t basePos, + fmt::string_view threadName, + fmt::string_view msg, + size_t bodyPos, + size_t logFilePos); // Set a callback function for all log msgs with a mininum log level static void setLogCB(LogCBFn cb, LogLevel minCBLogLevel) noexcept; @@ -174,10 +195,13 @@ class fmtlogT // https://github.com/MengRao/SPSC_Queue class SPSCVarQueueOPT { - public: + public: struct MsgHeader { - inline void push(uint32_t sz) { *(volatile uint32_t*)&size = sz + sizeof(MsgHeader); } + inline void push(uint32_t sz) + { + *(volatile uint32_t*)&size = sz + sizeof(MsgHeader); + } uint32_t size; uint32_t logId; @@ -186,24 +210,30 @@ class fmtlogT MsgHeader* allocMsg(uint32_t size) noexcept; - MsgHeader* alloc(uint32_t size) { + MsgHeader* alloc(uint32_t size) + { size += sizeof(MsgHeader); uint32_t blk_sz = (size + sizeof(MsgHeader) - 1) / sizeof(MsgHeader); - if (blk_sz >= free_write_cnt) { + if (blk_sz >= free_write_cnt) + { uint32_t read_idx_cache = *(volatile uint32_t*)&read_idx; - if (read_idx_cache <= write_idx) { + if (read_idx_cache <= write_idx) + { free_write_cnt = BLK_CNT - write_idx; - if (blk_sz >= free_write_cnt && read_idx_cache != 0) { // wrap around + if (blk_sz >= free_write_cnt && read_idx_cache != 0) + { // wrap around blk[0].size = 0; blk[write_idx].size = 1; write_idx = 0; free_write_cnt = read_idx_cache; } } - else { + else + { free_write_cnt = read_idx_cache - write_idx; } - if (free_write_cnt <= blk_sz) { + if (free_write_cnt <= blk_sz) + { return nullptr; } } @@ -214,22 +244,26 @@ class fmtlogT return ret; } - inline const MsgHeader* front() { + inline const MsgHeader* front() + { uint32_t size = blk[read_idx].size; - if (size == 1) { // wrap around + if (size == 1) + { // wrap around read_idx = 0; size = blk[0].size; } - if (size == 0) return nullptr; + if (size == 0) + return nullptr; return &blk[read_idx]; } - inline void pop() { + inline void pop() + { uint32_t blk_sz = (blk[read_idx].size + sizeof(MsgHeader) - 1) / sizeof(MsgHeader); *(volatile uint32_t*)&read_idx = read_idx + blk_sz; } - private: + private: alignas(64) MsgHeader blk[BLK_CNT] = {}; uint32_t write_idx = 0; uint32_t free_write_cnt = BLK_CNT; @@ -248,35 +282,39 @@ class fmtlogT // https://github.com/MengRao/tscns class TSCNS { - public: + public: static const int64_t NsPerSec = 1000000000; - void init(int64_t init_calibrate_ns = 20000000, int64_t calibrate_interval_ns = 3 * NsPerSec) { + void init(int64_t init_calibrate_ns = 20000000, int64_t calibrate_interval_ns = 3 * NsPerSec) + { calibate_interval_ns_ = calibrate_interval_ns; int64_t base_tsc, base_ns; syncTime(base_tsc, base_ns); int64_t expire_ns = base_ns + init_calibrate_ns; - while (rdsysns() < expire_ns) std::this_thread::yield(); + while (rdsysns() < expire_ns) + std::this_thread::yield(); int64_t delayed_tsc, delayed_ns; syncTime(delayed_tsc, delayed_ns); double init_ns_per_tsc = (double)(delayed_ns - base_ns) / (delayed_tsc - base_tsc); saveParam(base_tsc, base_ns, base_ns, init_ns_per_tsc); } - void calibrate() { - if (rdtsc() < next_calibrate_tsc_) return; + void calibrate() + { + if (rdtsc() < next_calibrate_tsc_) + return; int64_t tsc, ns; syncTime(tsc, ns); int64_t calulated_ns = tsc2ns(tsc); int64_t ns_err = calulated_ns - ns; int64_t expected_err_at_next_calibration = - ns_err + (ns_err - base_ns_err_) * calibate_interval_ns_ / (ns - base_ns_ + base_ns_err_); - double new_ns_per_tsc = - ns_per_tsc_ * (1.0 - (double)expected_err_at_next_calibration / calibate_interval_ns_); + ns_err + (ns_err - base_ns_err_) * calibate_interval_ns_ / (ns - base_ns_ + base_ns_err_); + double new_ns_per_tsc = ns_per_tsc_ * (1.0 - (double)expected_err_at_next_calibration / calibate_interval_ns_); saveParam(tsc, calulated_ns, ns, new_ns_per_tsc); } - static inline int64_t rdtsc() { + static inline int64_t rdtsc() + { #ifdef _MSC_VER return __rdtsc(); #elif defined(__i386__) || defined(__x86_64__) || defined(__amd64__) @@ -286,31 +324,42 @@ class fmtlogT #endif } - inline int64_t tsc2ns(int64_t tsc) const { - while (true) { + inline int64_t tsc2ns(int64_t tsc) const + { + while (true) + { uint32_t before_seq = param_seq_.load(std::memory_order_acquire) & ~1; std::atomic_signal_fence(std::memory_order_acq_rel); int64_t ns = base_ns_ + (int64_t)((tsc - base_tsc_) * ns_per_tsc_); std::atomic_signal_fence(std::memory_order_acq_rel); uint32_t after_seq = param_seq_.load(std::memory_order_acquire); - if (before_seq == after_seq) return ns; + if (before_seq == after_seq) + return ns; } } - inline int64_t rdns() const { return tsc2ns(rdtsc()); } + inline int64_t rdns() const + { + return tsc2ns(rdtsc()); + } - static inline int64_t rdsysns() { + static inline int64_t rdsysns() + { using namespace std::chrono; return duration_cast(system_clock::now().time_since_epoch()).count(); } - double getTscGhz() const { return 1.0 / ns_per_tsc_; } + double getTscGhz() const + { + return 1.0 / ns_per_tsc_; + } // Linux kernel sync time by finding the first trial with tsc diff < 50000 // We try several times and return the one with the mininum tsc diff. // Note that MSVC has a 100ns resolution clock, so we need to combine those ns with the same // value, and drop the first and the last value as they may not scan a full 100ns range - static void syncTime(int64_t& tsc_out, int64_t& ns_out) { + static void syncTime(int64_t& tsc_out, int64_t& ns_out) + { #ifdef _MSC_VER const int N = 15; #else @@ -320,15 +369,18 @@ class fmtlogT int64_t ns[N + 1]; tsc[0] = rdtsc(); - for (int i = 1; i <= N; i++) { + for (int i = 1; i <= N; i++) + { ns[i] = rdsysns(); tsc[i] = rdtsc(); } #ifdef _MSC_VER int j = 1; - for (int i = 2; i <= N; i++) { - if (ns[i] == ns[i - 1]) continue; + for (int i = 2; i <= N; i++) + { + if (ns[i] == ns[i - 1]) + continue; tsc[j - 1] = tsc[i - 1]; ns[j++] = ns[i]; } @@ -338,14 +390,17 @@ class fmtlogT #endif int best = 1; - for (int i = 2; i < j; i++) { - if (tsc[i] - tsc[i - 1] < tsc[best] - tsc[best - 1]) best = i; + for (int i = 2; i < j; i++) + { + if (tsc[i] - tsc[i - 1] < tsc[best] - tsc[best - 1]) + best = i; } tsc_out = (tsc[best] + tsc[best - 1]) >> 1; ns_out = ns[best]; } - void saveParam(int64_t base_tsc, int64_t base_ns, int64_t sys_ns, double new_ns_per_tsc) { + void saveParam(int64_t base_tsc, int64_t base_ns, int64_t sys_ns, double new_ns_per_tsc) + { base_ns_err_ = base_ns - sys_ns; next_calibrate_tsc_ = base_tsc + (int64_t)((calibate_interval_ns_ - 1000) / new_ns_per_tsc); uint32_t seq = param_seq_.load(std::memory_order_relaxed); @@ -367,18 +422,23 @@ class fmtlogT int64_t next_calibrate_tsc_; }; - void init() { + void init() + { tscns.init(); currentLogLevel = INF; } using Context = fmt::format_context; using MemoryBuffer = fmt::basic_memory_buffer; - typedef const char* (*FormatToFn)(fmt::string_view format, const char* data, MemoryBuffer& out, - int& argIdx, std::vector>& args); + typedef const char* (*FormatToFn)( + fmt::string_view format, + const char* data, + MemoryBuffer& out, + int& argIdx, + std::vector>& args); - static void registerLogInfo(uint32_t& logId, FormatToFn fn, const char* location, LogLevel level, - fmt::string_view fmtString) noexcept; + static void + registerLogInfo(uint32_t& logId, FormatToFn fn, const char* location, LogLevel level, std::string fmtString) noexcept; static void vformat_to(MemoryBuffer& out, fmt::string_view fmt, fmt::format_args args); @@ -394,100 +454,122 @@ class fmtlogT static FAST_THREAD_LOCAL ThreadBuffer* threadBuffer; template - static inline constexpr bool isNamedArg() { + static inline constexpr bool isNamedArg() + { return fmt::detail::is_named_arg>::value; } template struct unNamedType - { using type = Arg; }; + { + using type = Arg; + }; template struct unNamedType> - { using type = Arg; }; + { + using type = Arg; + }; #if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> struct unNamedType> - { using type = Arg; }; + { + using type = Arg; + }; #endif template - static inline constexpr bool isCstring() { - return fmt::detail::mapped_type_constant::value == - fmt::detail::type::cstring_type; + static inline constexpr bool isCstring() + { + return fmt::detail::mapped_type_constant::value == fmt::detail::type::cstring_type; } template - static inline constexpr bool isString() { + static inline constexpr bool isString() + { return fmt::detail::mapped_type_constant::value == fmt::detail::type::string_type; } template - static inline constexpr bool needCallDtor() { + static inline constexpr bool needCallDtor() + { using ArgType = fmt::remove_cvref_t; - if constexpr (isNamedArg()) { + if constexpr (isNamedArg()) + { return needCallDtor::type>(); } - if constexpr (isString()) return false; + if constexpr (isString()) + return false; return !std::is_trivially_destructible::value; } template - static inline constexpr size_t getArgSizes(size_t* cstringSize) { + static inline constexpr size_t getArgSizes(size_t* cstringSize) + { return 0; } template - static inline constexpr size_t getArgSizes(size_t* cstringSize, const Arg& arg, - const Args&... args) { - if constexpr (isNamedArg()) { + static inline constexpr size_t getArgSizes(size_t* cstringSize, const Arg& arg, const Args&... args) + { + if constexpr (isNamedArg()) + { return getArgSizes(cstringSize, arg.value, args...); } - else if constexpr (isCstring()) { + else if constexpr (isCstring()) + { size_t len = strlen(arg) + 1; cstringSize[CstringIdx] = len; return len + getArgSizes(cstringSize, args...); } - else if constexpr (isString()) { + else if constexpr (isString()) + { size_t len = arg.size() + 1; return len + getArgSizes(cstringSize, args...); } - else { + else + { return sizeof(Arg) + getArgSizes(cstringSize, args...); } } template - static inline constexpr char* encodeArgs(size_t* cstringSize, char* out) { + static inline constexpr char* encodeArgs(size_t* cstringSize, char* out) + { return out; } template - static inline constexpr char* encodeArgs(size_t* cstringSize, char* out, Arg&& arg, - Args&&... args) { - if constexpr (isNamedArg()) { + static inline constexpr char* encodeArgs(size_t* cstringSize, char* out, Arg&& arg, Args&&... args) + { + if constexpr (isNamedArg()) + { return encodeArgs(cstringSize, out, arg.value, std::forward(args)...); } - else if constexpr (isCstring()) { + else if constexpr (isCstring()) + { memcpy(out, arg, cstringSize[CstringIdx]); - return encodeArgs(cstringSize, out + cstringSize[CstringIdx], - std::forward(args)...); + return encodeArgs(cstringSize, out + cstringSize[CstringIdx], std::forward(args)...); } - else if constexpr (isString()) { + else if constexpr (isString()) + { size_t len = arg.size(); memcpy(out, arg.data(), len); out[len] = 0; return encodeArgs(cstringSize, out + len + 1, std::forward(args)...); } - else { + else + { // If Arg has alignment >= 16, gcc could emit aligned move instructions(e.g. movdqa) for // placement new even if the *out* is misaligned, which would cause segfault. So we use memcpy // when possible - if constexpr (std::is_trivially_copyable_v>) { + if constexpr (std::is_trivially_copyable_v>) + { memcpy(out, &arg, sizeof(Arg)); } - else { + else + { new (out) fmt::remove_cvref_t(std::forward(arg)); } return encodeArgs(cstringSize, out + sizeof(Arg), std::forward(args)...); @@ -495,111 +577,137 @@ class fmtlogT } template - static inline constexpr void storeNamedArgs(fmt::detail::named_arg_info* named_args_store) { + static inline constexpr void storeNamedArgs(fmt::detail::named_arg_info* named_args_store) + { } template - static inline constexpr void storeNamedArgs(fmt::detail::named_arg_info* named_args_store, - const Arg& arg, const Args&... args) { - if constexpr (isNamedArg()) { - named_args_store[NamedIdx] = {arg.name, Idx}; + static inline constexpr void + storeNamedArgs(fmt::detail::named_arg_info* named_args_store, const Arg& arg, const Args&... args) + { + if constexpr (isNamedArg()) + { + named_args_store[NamedIdx] = { arg.name, Idx }; storeNamedArgs(named_args_store, args...); } - else { + else + { storeNamedArgs(named_args_store, args...); } } template - static inline const char* decodeArgs(const char* in, fmt::basic_format_arg* args, - const char** destruct_args) { + static inline const char* decodeArgs(const char* in, fmt::basic_format_arg* args, const char** destruct_args) + { return in; } template - static inline const char* decodeArgs(const char* in, fmt::basic_format_arg* args, - const char** destruct_args) { + static inline const char* decodeArgs(const char* in, fmt::basic_format_arg* args, const char** destruct_args) + { using namespace fmtlogdetail; using ArgType = fmt::remove_cvref_t; - if constexpr (isNamedArg()) { - return decodeArgs::type, Args...>( - in, args, destruct_args); + if constexpr (isNamedArg()) + { + return decodeArgs::type, Args...>(in, args, destruct_args); } - else if constexpr (isCstring() || isString()) { + else if constexpr (isCstring() || isString()) + { size_t size = strlen(in); fmt::string_view v(in, size); - if constexpr (ValueOnly) { + if constexpr (ValueOnly) + { fmt::detail::value& value_ = *(fmt::detail::value*)(args + Idx); value_ = fmt::detail::arg_mapper().map(v); } - else { + else + { args[Idx] = fmt::detail::make_arg(v); } - return decodeArgs(in + size + 1, args, - destruct_args); + return decodeArgs(in + size + 1, args, destruct_args); } - else { - if constexpr (ValueOnly) { + else + { + if constexpr (ValueOnly) + { fmt::detail::value& value_ = *(fmt::detail::value*)(args + Idx); - if constexpr (UnrefPtr::value) { + if constexpr (UnrefPtr::value) + { value_ = fmt::detail::arg_mapper().map(**(ArgType*)in); } - else { + else + { value_ = fmt::detail::arg_mapper().map(*(ArgType*)in); } } - else { - if constexpr (UnrefPtr::value) { + else + { + if constexpr (UnrefPtr::value) + { args[Idx] = fmt::detail::make_arg(**(ArgType*)in); } - else { + else + { args[Idx] = fmt::detail::make_arg(*(ArgType*)in); } } - if constexpr (needCallDtor()) { + if constexpr (needCallDtor()) + { destruct_args[DestructIdx] = in; - return decodeArgs(in + sizeof(ArgType), args, - destruct_args); + return decodeArgs(in + sizeof(ArgType), args, destruct_args); } - else { - return decodeArgs(in + sizeof(ArgType), args, - destruct_args); + else + { + return decodeArgs(in + sizeof(ArgType), args, destruct_args); } } } template - static inline void destructArgs(const char** destruct_args) {} + static inline void destructArgs(const char** destruct_args) + { + } template - static inline void destructArgs(const char** destruct_args) { + static inline void destructArgs(const char** destruct_args) + { using ArgType = fmt::remove_cvref_t; - if constexpr (isNamedArg()) { + if constexpr (isNamedArg()) + { destructArgs::type, Args...>(destruct_args); } - else if constexpr (needCallDtor()) { + else if constexpr (needCallDtor()) + { ((ArgType*)destruct_args[DestructIdx])->~ArgType(); destructArgs(destruct_args); } - else { + else + { destructArgs(destruct_args); } } template - static const char* formatTo(fmt::string_view format, const char* data, MemoryBuffer& out, - int& argIdx, std::vector>& args) { + static const char* formatTo( + fmt::string_view format, + const char* data, + MemoryBuffer& out, + int& argIdx, + std::vector>& args) + { constexpr size_t num_args = sizeof...(Args); constexpr size_t num_dtors = fmt::detail::count()...>(); const char* dtor_args[std::max(num_dtors, (size_t)1)]; const char* ret; - if (argIdx < 0) { + if (argIdx < 0) + { argIdx = (int)args.size(); args.resize(argIdx + num_args); ret = decodeArgs(data, args.data() + argIdx, dtor_args); } - else { + else + { ret = decodeArgs(data, args.data() + argIdx, dtor_args); } vformat_to(out, format, fmt::basic_format_args(args.data() + argIdx, num_args)); @@ -609,80 +717,105 @@ class fmtlogT } template - static fmt::string_view unNameFormat(fmt::string_view in, uint32_t* reorderIdx, - const Args&... args) { + static std::string unNameFormat(std::string in, uint32_t* reorderIdx, const Args&... args) + { constexpr size_t num_named_args = fmt::detail::count()...>(); - if constexpr (num_named_args == 0) { + if constexpr (num_named_args == 0) + { return in; } const char* begin = in.data(); const char* p = begin; - std::unique_ptr unnamed_str(new char[in.size() + 1 + num_named_args * 5]); + std::size_t size_unnamed_str = in.size() + 1 + num_named_args * 5; + auto unnamed_str = std::make_unique(size_unnamed_str); fmt::detail::named_arg_info named_args[std::max(num_named_args, (size_t)1)]; storeNamedArgs<0, 0>(named_args, args...); char* out = (char*)unnamed_str.get(); uint8_t arg_idx = 0; - while (true) { + while (true) + { auto c = *p++; - if (!c) { + if (!c) + { size_t copy_size = p - begin - 1; memcpy(out, begin, copy_size); out += copy_size; break; } - if (c != '{') continue; + if (c != '{') + continue; size_t copy_size = p - begin; memcpy(out, begin, copy_size); out += copy_size; begin = p; c = *p++; - if (!c) fmt::detail::throw_format_error("invalid format string"); - if (fmt::detail::is_name_start(c)) { - while ((fmt::detail::is_name_start(c = *p) || ('0' <= c && c <= '9'))) { + if (!c) + fmt::throw_format_error("invalid format string"); + if (fmt::detail::is_name_start(c)) + { + while ((fmt::detail::is_name_start(c = *p) || ('0' <= c && c <= '9'))) + { ++p; } fmt::string_view name(begin, p - begin); int id = -1; - for (size_t i = 0; i < num_named_args; ++i) { - if (named_args[i].name == name) { + for (size_t i = 0; i < num_named_args; ++i) + { + if (named_args[i].name == name) + { id = named_args[i].id; break; } } - if (id < 0) fmt::detail::throw_format_error("invalid format string"); - if constexpr (Reorder) { + if (id < 0) + fmt::throw_format_error("invalid format string"); + if constexpr (Reorder) + { reorderIdx[id] = arg_idx++; } - else { + else + { out = fmt::format_to(out, "{}", id); } } - else { + else + { *out++ = c; } begin = p; } - const char* ptr = unnamed_str.release(); - return fmt::string_view(ptr, out - ptr); + std::string result; + result.reserve(size_unnamed_str); + std::string_view ptr_span(unnamed_str.get(), size_unnamed_str); + std::copy_n(ptr_span.begin(), out - unnamed_str.get(), std::back_inserter(result)); + return result; } -public: + public: template inline void log( - uint32_t& logId, int64_t tsc, const char* location, LogLevel level, - fmt::format_string>::type...> format, - Args&&... args) noexcept { - if (!logId) { - auto unnamed_format = unNameFormat(fmt::string_view(format), nullptr, args...); + uint32_t& logId, + int64_t tsc, + const char* location, + LogLevel level, + fmt::format_string>::type...> format, + Args&&... args) noexcept + { + if (!logId) + { + fmt::string_view format_str_v = format.get(); + auto unnamed_format = unNameFormat(std::string{ format_str_v.begin(), format_str_v.end() }, nullptr, args...); registerLogInfo(logId, formatTo, location, level, unnamed_format); } constexpr size_t num_cstring = fmt::detail::count()...>(); size_t cstringSizes[std::max(num_cstring, (size_t)1)]; uint32_t alloc_size = 8 + (uint32_t)getArgSizes<0>(cstringSizes, args...); bool q_full_cb = true; - do { - if (auto header = allocMsg(alloc_size, q_full_cb)) { + do + { + if (auto header = allocMsg(alloc_size, q_full_cb)) + { header->logId = logId; char* out = (char*)(header + 1); *(int64_t*)out = tsc; @@ -696,15 +829,17 @@ class fmtlogT } template - inline void logOnce(const char* location, LogLevel level, fmt::format_string format, - Args&&... args) { + inline void logOnce(const char* location, LogLevel level, fmt::format_string format, Args&&... args) + { fmt::string_view sv(format); auto&& fmt_args = fmt::make_format_args(args...); uint32_t fmt_size = formatted_size(sv, fmt_args); uint32_t alloc_size = 8 + 8 + fmt_size; bool q_full_cb = true; - do { - if (auto header = allocMsg(alloc_size, q_full_cb)) { + do + { + if (auto header = allocMsg(alloc_size, q_full_cb)) + { header->logId = (uint32_t)level; char* out = (char*)(header + 1); *(int64_t*)out = tscns.rdtsc(); @@ -727,23 +862,28 @@ FAST_THREAD_LOCAL typename fmtlogT<_>::ThreadBuffer* fmtlogT<_>::threadBuffer; template struct fmtlogWrapper -{ static fmtlog impl; }; +{ + static fmtlog impl; +}; template fmtlog fmtlogWrapper<_>::impl; template -inline void fmtlogT<_>::setLogLevel(LogLevel logLevel) noexcept { +inline void fmtlogT<_>::setLogLevel(LogLevel logLevel) noexcept +{ fmtlogWrapper<>::impl.currentLogLevel = logLevel; } template -inline typename fmtlogT<_>::LogLevel fmtlogT<_>::getLogLevel() noexcept { +inline typename fmtlogT<_>::LogLevel fmtlogT<_>::getLogLevel() noexcept +{ return fmtlogWrapper<>::impl.currentLogLevel; } template -inline bool fmtlogT<_>::checkLogLevel(LogLevel logLevel) noexcept { +inline bool fmtlogT<_>::checkLogLevel(LogLevel logLevel) noexcept +{ #ifdef FMTLOG_NO_CHECK_LEVEL return true; #else @@ -751,76 +891,80 @@ inline bool fmtlogT<_>::checkLogLevel(LogLevel logLevel) noexcept { #endif } -#define __FMTLOG_S1(x) #x -#define __FMTLOG_S2(x) __FMTLOG_S1(x) +#define __FMTLOG_S1(x) #x +#define __FMTLOG_S2(x) __FMTLOG_S1(x) #define __FMTLOG_LOCATION __FILE__ ":" __FMTLOG_S2(__LINE__) -#define FMTLOG(level, format, ...) \ - do { \ - static uint32_t logId = 0; \ - if (!fmtlog::checkLogLevel(level)) break; \ - fmtlogWrapper<>::impl.log(logId, fmtlogWrapper<>::impl.tscns.rdtsc(), __FMTLOG_LOCATION, \ - level, format, ##__VA_ARGS__); \ +#define FMTLOG(level, format, ...) \ + do \ + { \ + static uint32_t logId = 0; \ + if (!fmtlog::checkLogLevel(level)) \ + break; \ + fmtlogWrapper<>::impl.log(logId, fmtlogWrapper<>::impl.tscns.rdtsc(), __FMTLOG_LOCATION, level, format, ##__VA_ARGS__); \ } while (0) -#define FMTLOG_LIMIT(min_interval, level, format, ...) \ - do { \ - static uint32_t logId = 0; \ - static int64_t limitNs = 0; \ - if (!fmtlog::checkLogLevel(level)) break; \ - int64_t tsc = fmtlogWrapper<>::impl.tscns.rdtsc(); \ - int64_t ns = fmtlogWrapper<>::impl.tscns.tsc2ns(tsc); \ - if (ns < limitNs) break; \ - limitNs = ns + min_interval; \ - fmtlogWrapper<>::impl.log(logId, tsc, __FMTLOG_LOCATION, level, format, ##__VA_ARGS__); \ +#define FMTLOG_LIMIT(min_interval, level, format, ...) \ + do \ + { \ + static uint32_t logId = 0; \ + static int64_t limitNs = 0; \ + if (!fmtlog::checkLogLevel(level)) \ + break; \ + int64_t tsc = fmtlogWrapper<>::impl.tscns.rdtsc(); \ + int64_t ns = fmtlogWrapper<>::impl.tscns.tsc2ns(tsc); \ + if (ns < limitNs) \ + break; \ + limitNs = ns + min_interval; \ + fmtlogWrapper<>::impl.log(logId, tsc, __FMTLOG_LOCATION, level, format, ##__VA_ARGS__); \ } while (0) -#define FMTLOG_ONCE(level, format, ...) \ - do { \ - if (!fmtlog::checkLogLevel(level)) break; \ - fmtlogWrapper<>::impl.logOnce(__FMTLOG_LOCATION, level, format, ##__VA_ARGS__); \ +#define FMTLOG_ONCE(level, format, ...) \ + do \ + { \ + if (!fmtlog::checkLogLevel(level)) \ + break; \ + fmtlogWrapper<>::impl.logOnce(__FMTLOG_LOCATION, level, format, ##__VA_ARGS__); \ } while (0) #if FMTLOG_ACTIVE_LEVEL <= FMTLOG_LEVEL_DBG -#define logd(format, ...) FMTLOG(fmtlog::DBG, format, ##__VA_ARGS__) -#define logdo(format, ...) FMTLOG_ONCE(fmtlog::DBG, format, ##__VA_ARGS__) +#define logd(format, ...) FMTLOG(fmtlog::DBG, format, ##__VA_ARGS__) +#define logdo(format, ...) FMTLOG_ONCE(fmtlog::DBG, format, ##__VA_ARGS__) #define logdl(min_interval, format, ...) FMTLOG_LIMIT(min_interval, fmtlog::DBG, format, ##__VA_ARGS__) #else -#define logd(format, ...) (void)0 -#define logdo(format, ...) (void)0 +#define logd(format, ...) (void)0 +#define logdo(format, ...) (void)0 #define logdl(min_interval, format, ...) (void)0 #endif #if FMTLOG_ACTIVE_LEVEL <= FMTLOG_LEVEL_INF -#define logi(format, ...) FMTLOG(fmtlog::INF, format, ##__VA_ARGS__) -#define logio(format, ...) FMTLOG_ONCE(fmtlog::INF, format, ##__VA_ARGS__) +#define logi(format, ...) FMTLOG(fmtlog::INF, format, ##__VA_ARGS__) +#define logio(format, ...) FMTLOG_ONCE(fmtlog::INF, format, ##__VA_ARGS__) #define logil(min_interval, format, ...) FMTLOG_LIMIT(min_interval, fmtlog::INF, format, ##__VA_ARGS__) #else -#define logi(format, ...) (void)0 -#define logio(format, ...) (void)0 +#define logi(format, ...) (void)0 +#define logio(format, ...) (void)0 #define logil(min_interval, format, ...) (void)0 #endif #if FMTLOG_ACTIVE_LEVEL <= FMTLOG_LEVEL_WRN -#define logw(format, ...) FMTLOG(fmtlog::WRN, format, ##__VA_ARGS__) -#define logwo(format, ...) FMTLOG_ONCE(fmtlog::WRN, format, ##__VA_ARGS__) +#define logw(format, ...) FMTLOG(fmtlog::WRN, format, ##__VA_ARGS__) +#define logwo(format, ...) FMTLOG_ONCE(fmtlog::WRN, format, ##__VA_ARGS__) #define logwl(min_interval, format, ...) FMTLOG_LIMIT(min_interval, fmtlog::WRN, format, ##__VA_ARGS__) #else -#define logw(format, ...) (void)0 -#define logwo(format, ...) (void)0 +#define logw(format, ...) (void)0 +#define logwo(format, ...) (void)0 #define logwl(min_interval, format, ...) (void)0 #endif #if FMTLOG_ACTIVE_LEVEL <= FMTLOG_LEVEL_ERR -#define loge(format, ...) FMTLOG(fmtlog::ERR, format, ##__VA_ARGS__) -#define logeo(format, ...) FMTLOG_ONCE(fmtlog::ERR, format, ##__VA_ARGS__) +#define loge(format, ...) FMTLOG(fmtlog::ERR, format, ##__VA_ARGS__) +#define logeo(format, ...) FMTLOG_ONCE(fmtlog::ERR, format, ##__VA_ARGS__) #define logel(min_interval, format, ...) FMTLOG_LIMIT(min_interval, fmtlog::ERR, format, ##__VA_ARGS__) #else -#define loge(format, ...) (void)0 -#define logeo(format, ...) (void)0 +#define loge(format, ...) (void)0 +#define logeo(format, ...) (void)0 #define logel(min_interval, format, ...) (void)0 #endif -#ifdef FMTLOG_HEADER_ONLY -#include "fmtlog-inl.h" -#endif +#include "fmtlog/internal/fmtlog-inl.h" diff --git a/fmtlog-inl.h b/include/fmtlog/internal/fmtlog-inl.h similarity index 64% rename from fmtlog-inl.h rename to include/fmtlog/internal/fmtlog-inl.h index f02cab6..95cdb8e 100644 --- a/fmtlog-inl.h +++ b/include/fmtlog/internal/fmtlog-inl.h @@ -21,53 +21,75 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "fmtlog.h" + +#pragma once + +#include +#include #include #include -#include -#include + +#include "fmtlog/fmtlog.h" #ifdef _WIN32 #ifndef NOMINMAX #define NOMINMAX #endif #include + #include #else -#include #include + +#include #endif -namespace { -void fmtlogEmptyFun(void*) { -} -} // namespace +namespace +{ + void fmtlogEmptyFun(void*) + { + } +} // namespace template class fmtlogDetailT { -public: + public: // https://github.com/MengRao/str template class Str { - public: + public: static const int Size = SIZE; char s[SIZE]; - Str() {} - Str(const char* p) { *this = *(const Str*)p; } + Str() + { + } + Str(const char* p) + { + *this = *(const Str*)p; + } - char& operator[](int i) { return s[i]; } - char operator[](int i) const { return s[i]; } + char& operator[](int i) + { + return s[i]; + } + char operator[](int i) const + { + return s[i]; + } template - void fromi(T num) { - if constexpr (Size & 1) { + void fromi(T num) + { + if constexpr (Size & 1) + { s[Size - 1] = '0' + (num % 10); num /= 10; } - switch (Size & -2) { + switch (Size & -2) + { case 18: *(uint16_t*)(s + 16) = *(uint16_t*)(digit_pairs + ((num % 100) << 1)); num /= 100; case 16: *(uint16_t*)(s + 14) = *(uint16_t*)(digit_pairs + ((num % 100) << 1)); num /= 100; case 14: *(uint16_t*)(s + 12) = *(uint16_t*)(digit_pairs + ((num % 100) << 1)); num /= 100; @@ -80,20 +102,21 @@ class fmtlogDetailT } } - static constexpr const char* digit_pairs = "00010203040506070809" - "10111213141516171819" - "20212223242526272829" - "30313233343536373839" - "40414243444546474849" - "50515253545556575859" - "60616263646566676869" - "70717273747576777879" - "80818283848586878889" - "90919293949596979899"; + static constexpr const char* digit_pairs = + "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899"; }; - fmtlogDetailT() - : flushDelay(3000000000) { + fmtlogDetailT() : flushDelay(3000000000) + { args.reserve(4096); args.resize(parttenArgSize); @@ -103,34 +126,59 @@ class fmtlogDetailT setHeaderPattern("{HMSf} {s:<16} {l}[{t:<6}] "); logInfos.reserve(32); bgLogInfos.reserve(128); - bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::DBG, fmt::string_view()); - bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::INF, fmt::string_view()); - bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::WRN, fmt::string_view()); - bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::ERR, fmt::string_view()); + bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::DBG, std::string{}); + bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::INF, std::string{}); + bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::WRN, std::string{}); + bgLogInfos.emplace_back(nullptr, nullptr, fmtlog::ERR, std::string{}); threadBuffers.reserve(8); bgThreadBuffers.reserve(8); memset(membuf.data(), 0, membuf.capacity()); } - ~fmtlogDetailT() { + ~fmtlogDetailT() + { stopPollingThread(); poll(true); closeLogFile(); } - void setHeaderPattern(const char* pattern) { - if (shouldDeallocateHeader) delete[] headerPattern.data(); + void setHeaderPattern(std::string pattern) + { + // if (shouldDeallocateHeader) delete[] headerPattern.data(); using namespace fmt::literals; - for (int i = 0; i < parttenArgSize; i++) { + for (int i = 0; i < parttenArgSize; i++) + { reorderIdx[i] = parttenArgSize - 1; } headerPattern = fmtlog::unNameFormat( - pattern, reorderIdx, "a"_a = "", "b"_a = "", "C"_a = "", "Y"_a = "", "m"_a = "", "d"_a = "", - "t"_a = "thread name", "F"_a = "", "f"_a = "", "e"_a = "", "S"_a = "", "M"_a = "", "H"_a = "", - "l"_a = fmtlog::LogLevel(), "s"_a = "fmtlog.cc:123", "g"_a = "/home/raomeng/fmtlog/fmtlog.cc:123", "Ymd"_a = "", - "HMS"_a = "", "HMSe"_a = "", "HMSf"_a = "", "HMSF"_a = "", "YmdHMS"_a = "", "YmdHMSe"_a = "", "YmdHMSf"_a = "", - "YmdHMSF"_a = ""); - shouldDeallocateHeader = headerPattern.data() != pattern; + pattern, + reorderIdx, + "a"_a = "", + "b"_a = "", + "C"_a = "", + "Y"_a = "", + "m"_a = "", + "d"_a = "", + "t"_a = "thread name", + "F"_a = "", + "f"_a = "", + "e"_a = "", + "S"_a = "", + "M"_a = "", + "H"_a = "", + "l"_a = fmtlog::LogLevel(), + "s"_a = "fmtlog.cc:123", + "g"_a = "/home/raomeng/fmtlog/fmtlog.cc:123", + "Ymd"_a = "", + "HMS"_a = "", + "HMSe"_a = "", + "HMSf"_a = "", + "HMSF"_a = "", + "YmdHMS"_a = "", + "YmdHMSe"_a = "", + "YmdHMSf"_a = "", + "YmdHMSF"_a = ""); + // shouldDeallocateHeader = headerPattern.data() != pattern; setArg<0>(fmt::string_view(weekdayName.s, 3)); setArg<1>(fmt::string_view(monthName.s, 3)); @@ -148,26 +196,32 @@ class fmtlogDetailT setArg<13>(fmt::string_view(logLevel.s, 3)); setArg<14>(fmt::string_view()); setArg<15>(fmt::string_view()); - setArg<16>(fmt::string_view(year.s, 10)); // Ymd - setArg<17>(fmt::string_view(hour.s, 8)); // HMS - setArg<18>(fmt::string_view(hour.s, 12)); // HMSe - setArg<19>(fmt::string_view(hour.s, 15)); // HMSf - setArg<20>(fmt::string_view(hour.s, 18)); // HMSF - setArg<21>(fmt::string_view(year.s, 19)); // YmdHMS - setArg<22>(fmt::string_view(year.s, 23)); // YmdHMSe - setArg<23>(fmt::string_view(year.s, 26)); // YmdHMSf - setArg<24>(fmt::string_view(year.s, 29)); // YmdHMSF + setArg<16>(fmt::string_view(year.s, 10)); // Ymd + setArg<17>(fmt::string_view(hour.s, 8)); // HMS + setArg<18>(fmt::string_view(hour.s, 12)); // HMSe + setArg<19>(fmt::string_view(hour.s, 15)); // HMSf + setArg<20>(fmt::string_view(hour.s, 18)); // HMSF + setArg<21>(fmt::string_view(year.s, 19)); // YmdHMS + setArg<22>(fmt::string_view(year.s, 23)); // YmdHMSe + setArg<23>(fmt::string_view(year.s, 26)); // YmdHMSf + setArg<24>(fmt::string_view(year.s, 29)); // YmdHMSF } class ThreadBufferDestroyer { - public: - explicit ThreadBufferDestroyer() {} + public: + explicit ThreadBufferDestroyer() + { + } - void threadBufferCreated() {} + void threadBufferCreated() + { + } - ~ThreadBufferDestroyer() { - if (fmtlog::threadBuffer != nullptr) { + ~ThreadBufferDestroyer() + { + if (fmtlog::threadBuffer != nullptr) + { fmtlog::threadBuffer->shouldDeallocate = true; fmtlog::threadBuffer = nullptr; } @@ -177,24 +231,30 @@ class fmtlogDetailT struct StaticLogInfo { // Constructor - constexpr StaticLogInfo(fmtlog::FormatToFn fn, const char* loc, fmtlog::LogLevel level, fmt::string_view fmtString) - : formatToFn(fn) - , formatString(fmtString) - , location(loc) - , logLevel(level) - , argIdx(-1) {} - - void processLocation() { + StaticLogInfo(fmtlog::FormatToFn fn, const char* loc, fmtlog::LogLevel level, std::string fmtString) + : formatToFn(fn), + formatString(fmtString), + location(loc), + logLevel(level), + argIdx(-1) + { + } + + void processLocation() + { size_t size = strlen(location); const char* p = location + size; - if (size > 255) { + if (size > 255) + { location = p - 255; } endPos = p - location; const char* base = location; - while (p > location) { + while (p > location) + { char c = *--p; - if (c == '/' || c == '\\') { + if (c == '/' || c == '\\') + { base = p + 1; break; } @@ -202,12 +262,18 @@ class fmtlogDetailT basePos = base - location; } - inline fmt::string_view getBase() { return fmt::string_view(location + basePos, endPos - basePos); } + inline fmt::string_view getBase() + { + return fmt::string_view(location + basePos, endPos - basePos); + } - inline fmt::string_view getLocation() { return fmt::string_view(location, endPos); } + inline fmt::string_view getLocation() + { + return fmt::string_view(location, endPos); + } fmtlog::FormatToFn formatToFn; - fmt::string_view formatString; + std::string formatString; const char* location; uint8_t basePos; uint8_t endPos; @@ -217,11 +283,11 @@ class fmtlogDetailT static thread_local ThreadBufferDestroyer sbc; int64_t midnightNs; - fmt::string_view headerPattern; + std::string headerPattern; bool shouldDeallocateHeader = false; FILE* outputFp = nullptr; bool manageFp = false; - size_t fpos = 0; // file position of membuf, used only when manageFp == true + size_t fpos = 0; // file position of membuf, used only when manageFp == true int64_t flushDelay; int64_t nextFlushTime = (std::numeric_limits::max)(); uint32_t flushBufSize = 8 * 1024; @@ -230,8 +296,9 @@ class fmtlogDetailT std::vector threadBuffers; struct HeapNode { - HeapNode(fmtlog::ThreadBuffer* buffer) - : tb(buffer) {} + HeapNode(fmtlog::ThreadBuffer* buffer) : tb(buffer) + { + } fmtlog::ThreadBuffer* tb; const fmtlog::SPSCVarQueueOPT::MsgHeader* header = nullptr; @@ -271,7 +338,8 @@ class fmtlogDetailT volatile bool threadRunning = false; std::thread thr; - void resetDate() { + void resetDate() + { time_t rawtime = fmtlogWrapper<>::impl.tscns.rdns() / 1000000000; struct tm* timeinfo = localtime(&rawtime); timeinfo->tm_sec = timeinfo->tm_min = timeinfo->tm_hour = 0; @@ -279,14 +347,16 @@ class fmtlogDetailT year.fromi(1900 + timeinfo->tm_year); month.fromi(1 + timeinfo->tm_mon); day.fromi(timeinfo->tm_mday); - const char* weekdays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + const char* weekdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; weekdayName = weekdays[timeinfo->tm_wday]; - const char* monthNames[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + const char* monthNames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; monthName = monthNames[timeinfo->tm_mon]; } - void preallocate() { - if (fmtlog::threadBuffer) return; + void preallocate() + { + if (fmtlog::threadBuffer) + return; fmtlog::threadBuffer = new fmtlog::ThreadBuffer(); #ifdef _WIN32 uint32_t tid = static_cast(::GetCurrentThreadId()); @@ -294,7 +364,7 @@ class fmtlogDetailT uint32_t tid = static_cast(::syscall(SYS_gettid)); #endif fmtlog::threadBuffer->nameSize = - fmt::format_to_n(fmtlog::threadBuffer->name, sizeof(fmtlog::threadBuffer->name), "{}", tid).size; + fmt::format_to_n(fmtlog::threadBuffer->name, sizeof(fmtlog::threadBuffer->name), "{}", tid).size; sbc.threadBufferCreated(); std::unique_lock guard(bufferMutex); @@ -302,20 +372,25 @@ class fmtlogDetailT } template - inline void setArg(const T& arg) { + inline void setArg(const T& arg) + { args[reorderIdx[I]] = fmt::detail::make_arg(arg); } template - inline void setArgVal(const T& arg) { + inline void setArgVal(const T& arg) + { fmt::detail::value& value_ = *(fmt::detail::value*)&args[reorderIdx[I]]; value_ = fmt::detail::arg_mapper().map(arg); } - void flushLogFile() { - if (outputFp) { + void flushLogFile() + { + if (outputFp) + { fwrite(membuf.data(), 1, membuf.size(), outputFp); - if (!manageFp) fflush(outputFp); + if (!manageFp) + fflush(outputFp); else fpos += membuf.size(); } @@ -323,43 +398,56 @@ class fmtlogDetailT nextFlushTime = (std::numeric_limits::max)(); } - void closeLogFile() { - if (membuf.size()) flushLogFile(); - if (manageFp) fclose(outputFp); + void closeLogFile() + { + if (membuf.size()) + flushLogFile(); + if (manageFp) + fclose(outputFp); outputFp = nullptr; manageFp = false; } - void startPollingThread(int64_t pollInterval) { + void startPollingThread(int64_t pollInterval) + { stopPollingThread(); threadRunning = true; - thr = std::thread([pollInterval, this]() { - while (threadRunning) { - int64_t before = fmtlogWrapper<>::impl.tscns.rdns(); - poll(false); - int64_t delay = fmtlogWrapper<>::impl.tscns.rdns() - before; - if (delay < pollInterval) { - std::this_thread::sleep_for(std::chrono::nanoseconds(pollInterval - delay)); - } - } - poll(true); - }); + thr = std::thread( + [pollInterval, this]() + { + while (threadRunning) + { + int64_t before = fmtlogWrapper<>::impl.tscns.rdns(); + poll(false); + int64_t delay = fmtlogWrapper<>::impl.tscns.rdns() - before; + if (delay < pollInterval) + { + std::this_thread::sleep_for(std::chrono::nanoseconds(pollInterval - delay)); + } + } + poll(true); + }); } - void stopPollingThread() { - if (!threadRunning) return; + void stopPollingThread() + { + if (!threadRunning) + return; threadRunning = false; - if (thr.joinable()) thr.join(); + if (thr.joinable()) + thr.join(); } - void handleLog(fmt::string_view threadName, const fmtlog::SPSCVarQueueOPT::MsgHeader* header) { + void handleLog(fmt::string_view threadName, const fmtlog::SPSCVarQueueOPT::MsgHeader* header) + { setArgVal<6>(threadName); StaticLogInfo& info = bgLogInfos[header->logId]; const char* data = (const char*)(header + 1); const char* end = (const char*)header + header->size; int64_t tsc = *(int64_t*)data; data += 8; - if (!info.formatToFn) { // log once + if (!info.formatToFn) + { // log once info.location = *(const char**)data; data += 8; info.processLocation(); @@ -373,8 +461,9 @@ class fmtlogDetailT t /= 60; minute.fromi(t % 60); t /= 60; - uint32_t h = t; // hour - if (h > 23) { + uint32_t h = t; // hour + if (h > 23) + { h %= 24; resetDate(); } @@ -387,62 +476,85 @@ class fmtlogDetailT fmtlog::vformat_to(membuf, headerPattern, fmt::basic_format_args(args.data(), parttenArgSize)); size_t bodyPos = membuf.size(); - if (info.formatToFn) { + if (info.formatToFn) + { info.formatToFn(info.formatString, data, membuf, info.argIdx, args); } - else { // log once + else + { // log once membuf.append(fmt::string_view(data, end - data)); } - if (logCB && info.logLevel >= minCBLogLevel) { - logCB(ts, info.logLevel, info.getLocation(), info.basePos, threadName, - fmt::string_view(membuf.data() + headerPos, membuf.size() - headerPos), bodyPos - headerPos, - fpos + headerPos); + if (logCB && info.logLevel >= minCBLogLevel) + { + logCB( + ts, + info.logLevel, + info.getLocation(), + info.basePos, + threadName, + fmt::string_view(membuf.data() + headerPos, membuf.size() - headerPos), + bodyPos - headerPos, + fpos + headerPos); } membuf.push_back('\n'); - if (membuf.size() >= flushBufSize || info.logLevel >= flushLogLevel) { + if (membuf.size() >= flushBufSize || info.logLevel >= flushLogLevel) + { flushLogFile(); } } - void adjustHeap(size_t i) { - while (true) { + void adjustHeap(size_t i) + { + while (true) + { size_t min_i = i; - for (size_t ch = i * 2 + 1, end = std::min(ch + 2, bgThreadBuffers.size()); ch < end; ch++) { + for (size_t ch = i * 2 + 1, end = std::min(ch + 2, bgThreadBuffers.size()); ch < end; ch++) + { auto h_ch = bgThreadBuffers[ch].header; auto h_min = bgThreadBuffers[min_i].header; - if (h_ch && (!h_min || *(int64_t*)(h_ch + 1) < *(int64_t*)(h_min + 1))) min_i = ch; + if (h_ch && (!h_min || *(int64_t*)(h_ch + 1) < *(int64_t*)(h_min + 1))) + min_i = ch; } - if (min_i == i) break; + if (min_i == i) + break; std::swap(bgThreadBuffers[i], bgThreadBuffers[min_i]); i = min_i; } } - void poll(bool forceFlush) { + void poll(bool forceFlush) + { fmtlogWrapper<>::impl.tscns.calibrate(); int64_t tsc = fmtlogWrapper<>::impl.tscns.rdtsc(); - if (logInfos.size()) { + if (logInfos.size()) + { std::unique_lock lock(logInfoMutex); - for (auto& info : logInfos) { + for (auto& info : logInfos) + { info.processLocation(); } bgLogInfos.insert(bgLogInfos.end(), logInfos.begin(), logInfos.end()); logInfos.clear(); } - if (threadBuffers.size()) { + if (threadBuffers.size()) + { std::unique_lock lock(bufferMutex); - for (auto tb : threadBuffers) { + for (auto tb : threadBuffers) + { bgThreadBuffers.emplace_back(tb); } threadBuffers.clear(); } - for (size_t i = 0; i < bgThreadBuffers.size(); i++) { + for (size_t i = 0; i < bgThreadBuffers.size(); i++) + { auto& node = bgThreadBuffers[i]; - if (node.header) continue; + if (node.header) + continue; node.header = node.tb->varq.front(); - if (!node.header && node.tb->shouldDeallocate) { + if (node.tb->shouldDeallocate) + { delete node.tb; node = bgThreadBuffers.back(); bgThreadBuffers.pop_back(); @@ -450,16 +562,20 @@ class fmtlogDetailT } } - if (bgThreadBuffers.empty()) return; + if (bgThreadBuffers.empty()) + return; // build heap - for (int i = bgThreadBuffers.size() / 2; i >= 0; i--) { + for (int i = bgThreadBuffers.size() / 2; i >= 0; i--) + { adjustHeap(i); } - while (true) { + while (true) + { auto h = bgThreadBuffers[0].header; - if (!h || h->logId >= bgLogInfos.size() || *(int64_t*)(h + 1) >= tsc) break; + if (!h || h->logId >= bgLogInfos.size() || *(int64_t*)(h + 1) >= tsc) + break; auto tb = bgThreadBuffers[0].tb; handleLog(fmt::string_view(tb->name, tb->nameSize), h); tb->varq.pop(); @@ -467,16 +583,20 @@ class fmtlogDetailT adjustHeap(0); } - if (membuf.size() == 0) return; - if (!manageFp || forceFlush) { + if (membuf.size() == 0) + return; + if (!manageFp || forceFlush) + { flushLogFile(); return; } int64_t now = fmtlogWrapper<>::impl.tscns.tsc2ns(tsc); - if (now > nextFlushTime) { + if (now > nextFlushTime) + { flushLogFile(); } - else if (nextFlushTime == (std::numeric_limits::max)()) { + else if (nextFlushTime == (std::numeric_limits::max)()) + { nextFlushTime = now + flushDelay; } } @@ -487,67 +607,82 @@ thread_local typename fmtlogDetailT<_>::ThreadBufferDestroyer fmtlogDetailT<_>:: template struct fmtlogDetailWrapper -{ static fmtlogDetailT<> impl; }; +{ + static fmtlogDetailT<> impl; +}; template fmtlogDetailT<> fmtlogDetailWrapper<_>::impl; template -void fmtlogT<_>::registerLogInfo(uint32_t& logId, FormatToFn fn, const char* location, - LogLevel level, fmt::string_view fmtString) noexcept { +void fmtlogT<_>::registerLogInfo( + uint32_t& logId, + FormatToFn fn, + const char* location, + LogLevel level, + std::string fmtString) noexcept +{ auto& d = fmtlogDetailWrapper<>::impl; std::lock_guard lock(d.logInfoMutex); - if (logId) return; + if (logId) + return; logId = d.logInfos.size() + d.bgLogInfos.size(); d.logInfos.emplace_back(fn, location, level, fmtString); } template -void fmtlogT<_>::vformat_to(fmtlog::MemoryBuffer& out, fmt::string_view fmt, - fmt::format_args args) { +void fmtlogT<_>::vformat_to(fmtlog::MemoryBuffer& out, fmt::string_view fmt, fmt::format_args args) +{ fmt::detail::vformat_to(out, fmt, args); } template -size_t fmtlogT<_>::formatted_size(fmt::string_view fmt, fmt::format_args args) { +size_t fmtlogT<_>::formatted_size(fmt::string_view fmt, fmt::format_args args) +{ auto buf = fmt::detail::counting_buffer<>(); fmt::detail::vformat_to(buf, fmt, args); return buf.count(); } template -void fmtlogT<_>::vformat_to(char* out, fmt::string_view fmt, fmt::format_args args) { +void fmtlogT<_>::vformat_to(char* out, fmt::string_view fmt, fmt::format_args args) +{ fmt::vformat_to(out, fmt, args); } template -typename fmtlogT<_>::SPSCVarQueueOPT::MsgHeader* fmtlogT<_>::allocMsg(uint32_t size, - bool q_full_cb) noexcept { +typename fmtlogT<_>::SPSCVarQueueOPT::MsgHeader* fmtlogT<_>::allocMsg(uint32_t size, bool q_full_cb) noexcept +{ auto& d = fmtlogDetailWrapper<>::impl; - if (threadBuffer == nullptr) preallocate(); + if (threadBuffer == nullptr) + preallocate(); auto ret = threadBuffer->varq.alloc(size); - if ((ret == nullptr) & q_full_cb) d.logQFullCB(d.logQFullCBArg); + if ((ret == nullptr) & q_full_cb) + d.logQFullCB(d.logQFullCBArg); return ret; } template -typename fmtlogT<_>::SPSCVarQueueOPT::MsgHeader* -fmtlogT<_>::SPSCVarQueueOPT::allocMsg(uint32_t size) noexcept { +typename fmtlogT<_>::SPSCVarQueueOPT::MsgHeader* fmtlogT<_>::SPSCVarQueueOPT::allocMsg(uint32_t size) noexcept +{ return alloc(size); } template -void fmtlogT<_>::preallocate() noexcept { +void fmtlogT<_>::preallocate() noexcept +{ fmtlogDetailWrapper<>::impl.preallocate(); } template -void fmtlogT<_>::setLogFile(const char* filename, bool truncate) { +void fmtlogT<_>::setLogFile(const char* filename, bool truncate) +{ auto& d = fmtlogDetailWrapper<>::impl; FILE* newFp = fopen(filename, truncate ? "w" : "a"); - if (!newFp) { + if (!newFp) + { std::string err = fmt::format("Unable to open file: {}: {}", filename, strerror(errno)); - fmt::detail::throw_format_error(err.c_str()); + fmt::throw_format_error(err.c_str()); } setbuf(newFp, nullptr); d.fpos = ftell(newFp); @@ -558,10 +693,12 @@ void fmtlogT<_>::setLogFile(const char* filename, bool truncate) { } template -void fmtlogT<_>::setLogFile(FILE* fp, bool manageFp) { +void fmtlogT<_>::setLogFile(FILE* fp, bool manageFp) +{ auto& d = fmtlogDetailWrapper<>::impl; closeLogFile(); - if (manageFp) { + if (manageFp) + { setbuf(fp, nullptr); d.fpos = ftell(fp); } @@ -572,64 +709,74 @@ void fmtlogT<_>::setLogFile(FILE* fp, bool manageFp) { } template -void fmtlogT<_>::setFlushDelay(int64_t ns) noexcept { +void fmtlogT<_>::setFlushDelay(int64_t ns) noexcept +{ fmtlogDetailWrapper<>::impl.flushDelay = ns; } template -void fmtlogT<_>::flushOn(LogLevel flushLogLevel) noexcept { +void fmtlogT<_>::flushOn(LogLevel flushLogLevel) noexcept +{ fmtlogDetailWrapper<>::impl.flushLogLevel = flushLogLevel; } template -void fmtlogT<_>::setFlushBufSize(uint32_t bytes) noexcept { +void fmtlogT<_>::setFlushBufSize(uint32_t bytes) noexcept +{ fmtlogDetailWrapper<>::impl.flushBufSize = bytes; } template -void fmtlogT<_>::closeLogFile() noexcept { +void fmtlogT<_>::closeLogFile() noexcept +{ fmtlogDetailWrapper<>::impl.closeLogFile(); } template -void fmtlogT<_>::poll(bool forceFlush) { +void fmtlogT<_>::poll(bool forceFlush) +{ fmtlogDetailWrapper<>::impl.poll(forceFlush); } template -void fmtlogT<_>::setThreadName(const char* name) noexcept { +void fmtlogT<_>::setThreadName(const char* name) noexcept +{ preallocate(); threadBuffer->nameSize = fmt::format_to_n(threadBuffer->name, sizeof(fmtlog::threadBuffer->name), "{}", name).size; } template -void fmtlogT<_>::setLogCB(LogCBFn cb, LogLevel minCBLogLevel_) noexcept { +void fmtlogT<_>::setLogCB(LogCBFn cb, LogLevel minCBLogLevel_) noexcept +{ auto& d = fmtlogDetailWrapper<>::impl; d.logCB = cb; d.minCBLogLevel = minCBLogLevel_; } template -void fmtlogT<_>::setLogQFullCB(LogQFullCBFn cb, void* userData) noexcept { +void fmtlogT<_>::setLogQFullCB(LogQFullCBFn cb, void* userData) noexcept +{ auto& d = fmtlogDetailWrapper<>::impl; d.logQFullCB = cb; d.logQFullCBArg = userData; } template -void fmtlogT<_>::setHeaderPattern(const char* pattern) { +void fmtlogT<_>::setHeaderPattern(const char* pattern) +{ fmtlogDetailWrapper<>::impl.setHeaderPattern(pattern); } template -void fmtlogT<_>::startPollingThread(int64_t pollInterval) noexcept { +void fmtlogT<_>::startPollingThread(int64_t pollInterval) noexcept +{ fmtlogDetailWrapper<>::impl.startPollingThread(pollInterval); } template -void fmtlogT<_>::stopPollingThread() noexcept { +void fmtlogT<_>::stopPollingThread() noexcept +{ fmtlogDetailWrapper<>::impl.stopPollingThread(); } template class fmtlogT<0>; - diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a4e6d38..d290c1d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,54 +1,70 @@ -if(NOT MSVC) - add_library(static_lib lib.cc) +cmake_minimum_required(VERSION 3.14...3.22) - add_library(static_header_lib lib.cc) - target_compile_definitions(static_header_lib PUBLIC FMTLOG_HEADER_ONLY) +project(fmtlogTests LANGUAGES CXX) - add_library(shared_lib SHARED lib.cc) - install(TARGETS shared_lib) +# ---- Options ---- - add_library(shared_header_lib SHARED lib.cc) - target_compile_definitions(shared_header_lib PUBLIC FMTLOG_HEADER_ONLY) - install(TARGETS shared_header_lib) +option(ENABLE_TEST_COVERAGE "Enable test coverage" OFF) +option(TEST_INSTALLED_VERSION "Test the version found by find_package" OFF) - add_executable(link_static_static link_test.cc) - target_link_libraries(link_static_static fmtlog-static static_lib fmt) - install(TARGETS link_static_static) +# --- Import tools ---- - add_executable(link_static_shared link_test.cc) - target_link_libraries(link_static_shared fmtlog-static shared_lib fmt) - install(TARGETS link_static_shared) +include(../cmake/tools.cmake) - add_executable(link_shared_static link_test.cc) - target_link_libraries(link_shared_static fmtlog-shared static_lib fmt) - install(TARGETS link_shared_static) +# ---- Dependencies ---- - add_executable(link_shared_shared link_test.cc) - target_link_libraries(link_shared_shared fmtlog-shared shared_lib fmt) - install(TARGETS link_shared_shared) +include(../cmake/getCPM.cmake) +CPMAddPackage("gh:TheLartians/Format.cmake@1.7.3") - add_executable(link_header_static link_test.cc) - target_link_libraries(link_header_static static_header_lib fmt) - install(TARGETS link_header_static) +if(TEST_INSTALLED_VERSION) + find_package(fmtlog REQUIRED) +else() + CPMAddPackage(NAME fmtlog SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) +endif() + +# ---- Create binary ---- - add_executable(link_header_shared link_test.cc) - target_link_libraries(link_header_shared shared_header_lib fmt) - install(TARGETS link_header_shared) +file( + GLOB_RECURSE sources CONFIGURE_DEPENDS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/src/ + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc +) +foreach(source ${sources}) + string(REPLACE "." "_" name ${source}) + string(REPLACE "/" "_" name ${source}) + message(DEBUG "Adding test with name ${PROJECT_NAME}_test_${name}") + add_executable(${PROJECT_NAME}_test_${name} ${CMAKE_CURRENT_SOURCE_DIR}/src/${source}) + target_link_libraries(${PROJECT_NAME}_test_${name} PRIVATE fmtlog::fmtlog) + set_target_properties(${PROJECT_NAME}_test_${name} PROPERTIES CXX_STANDARD 17) + add_test(NAME ${PROJECT_NAME}_test_${name} COMMAND ${PROJECT_NAME}_test_${name}) +endforeach() +# enable compiler warnings +if(NOT TEST_INSTALLED_VERSION) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + target_compile_options(fmtlog INTERFACE -Wall -Wpedantic -Wextra) + elseif(MSVC) + target_compile_options(fmtlog INTERFACE /W4) + # target_compile_definitions(${PROJECT_NAME} PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS) + endif() endif() -add_executable(log_test log_test.cc) -target_link_libraries(log_test fmtlog-static fmt) -#target_compile_definitions(log_test PUBLIC FMTLOG_HEADER_ONLY) -install(TARGETS log_test) +# ---- Add fmtlogTests ---- + +enable_testing() -add_executable(enc_dec_test enc_dec_test.cc) -target_link_libraries(enc_dec_test fmtlog-static fmt) -install(TARGETS enc_dec_test) +# Note: doctest and similar testing frameworks can automatically configure CMake tests. For other +# testing frameworks add the tests target instead: add_test(NAME ${PROJECT_NAME} COMMAND +# ${PROJECT_NAME}) -add_executable(multithread_test multithread_test.cc) -target_compile_definitions(multithread_test PUBLIC FMTLOG_HEADER_ONLY) -#target_link_libraries(multithread_test fmtlog-static fmt) -target_link_libraries(multithread_test fmt) -install(TARGETS multithread_test) +# if(NOT DEFINED doctest_ADDED OR "${doctest_ADDED}" STREQUAL "") +# include("/usr/lib64/cmake/doctest/doctest.cmake") else() +# include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) endif() +# doctest_discover_tests(${PROJECT_NAME}) +# ---- code coverage ---- + +if(ENABLE_TEST_COVERAGE) + target_compile_options(fmtlog INTERFACE -O0 -g -fprofile-arcs -ftest-coverage) + target_link_options(fmtlog INTERFACE -fprofile-arcs -ftest-coverage) +endif() diff --git a/test/lib.cc b/test/lib.cc deleted file mode 100644 index 9b763ca..0000000 --- a/test/lib.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include "lib.h" -#include "../fmtlog.h" - -void libFun(int i) { - logi("libFun: {}", i); -} - diff --git a/test/lib.h b/test/lib.h deleted file mode 100644 index a4bc61d..0000000 --- a/test/lib.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void libFun(int i); diff --git a/test/link_test.cc b/test/link_test.cc deleted file mode 100644 index 5d83c9e..0000000 --- a/test/link_test.cc +++ /dev/null @@ -1,8 +0,0 @@ -#include "lib.h" -#include "../fmtlog.h" - -int main() { - logi("link test: {}", 123); - libFun(321); - return 0; -} diff --git a/test/enc_dec_test.cc b/test/src/enc_dec_test.cc similarity index 74% rename from test/enc_dec_test.cc rename to test/src/enc_dec_test.cc index cfa46f3..d7ea71f 100644 --- a/test/enc_dec_test.cc +++ b/test/src/enc_dec_test.cc @@ -1,15 +1,18 @@ -#include "../fmtlog.h" -#include "fmt/ranges.h" #include #include + +#include "fmt/ranges.h" +#include "fmtlog/fmtlog.h" using namespace std; using namespace fmt::literals; struct MyType { - MyType(int val) - : v(val) {} - ~MyType() { + MyType(int val) : v(val) + { + } + ~MyType() + { dtor_cnt++; // fmt::print("dtor_cnt: {}\n", dtor_cnt); } @@ -24,16 +27,18 @@ struct fmt::formatter : formatter { // parse is inherited from formatter. template - auto format(const MyType& val, FormatContext& ctx) const { + auto format(const MyType& val, FormatContext& ctx) const + { return formatter::format(val.v, ctx); } }; struct MovableType { -public: - MovableType(int v = 0) - : val{MyType(v)} {} + public: + MovableType(int v = 0) : val{ MyType(v) } + { + } std::vector val; }; @@ -43,20 +48,22 @@ struct fmt::formatter : formatter { // parse is inherited from formatter. template - auto format(const MovableType& val, FormatContext& ctx) { + auto format(const MovableType& val, FormatContext& ctx) const + { return formatter::format(val.val[0].v, ctx); } }; template -void test(const S& format, Args&&... args) { +void test(const S& format, Args&&... args) +{ fmt::detail::check_format_string(format); auto sv = fmt::string_view(format); size_t formatted_size = fmt::formatted_size(fmt::runtime(sv), std::forward(args)...); string ans = fmt::format(fmt::runtime(sv), std::forward(args)...); assert(ans.size() == formatted_size); - auto unnamed_format = fmtlog::unNameFormat(sv, nullptr, args...); + auto unnamed_format = fmtlog::unNameFormat(format, nullptr, args...); fmt::print("unnamed_format: {}\n", unnamed_format); size_t cstringSizes[1000]; char buf[1024]; @@ -77,7 +84,8 @@ void test(const S& format, Args&&... args) { assert(res == ans); } -int main() { +int main() +{ char cstring[100] = "cstring cstring"; const char* p = "haha"; const char* pcstring = cstring; @@ -90,17 +98,38 @@ int main() { float f = 55.2; uint16_t short_int = 2222; - test("test basic types: {}, {}, {}, {}, {}, {}, {}, {}, {:.1f}, {}, {}, {}, {}, {}, {}, {}", cstring, p, pcstring, - "wow", 'a', 5, str, string_view(str), 1.34, ch, ch2, i, ri, d, f, short_int); - - test("test positional, {one}, {two:>5}, {three}, {four}, {0:.1f}", 5.012, "three"_a = 3, "two"_a = "two", - "one"_a = string("one"), "four"_a = string("4")); + test( + "test basic types: {}, {}, {}, {}, {}, {}, {}, {}, {:.1f}, {}, {}, {}, {}, {}, {}, {}", + cstring, + p, + pcstring, + "wow", + 'a', + 5, + str, + string_view(str), + 1.34, + ch, + ch2, + i, + ri, + d, + f, + short_int); + + test( + "test positional, {one}, {two:>5}, {three}, {four}, {0:.1f}", + 5.012, + "three"_a = 3, + "two"_a = "two", + "one"_a = string("one"), + "four"_a = string("4")); test("test dynamic spec: {:.{}f}, {:*^30}", 3.14, 1, "centered"); test("test positional spec: int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42); test("test custom types: {}, {}, {}", MyType(1), MyType(2), MovableType(3)); - test("test ranges: {}, {}", vector{1, 2, 3}, vector{4, 5, 6}); + test("test ranges: {}, {}", vector{ 1, 2, 3 }, vector{ 4, 5, 6 }); int dtor_cnt; { @@ -126,4 +155,3 @@ int main() { return 0; } - diff --git a/test/log_test.cc b/test/src/log_test.cc similarity index 78% rename from test/log_test.cc rename to test/src/log_test.cc index 6729c6e..7d15ed8 100644 --- a/test/log_test.cc +++ b/test/src/log_test.cc @@ -1,25 +1,34 @@ #include #include -#include "../fmtlog.h" +#include "fmtlog/fmtlog.h" void runBenchmark(); -void logcb(int64_t ns, fmtlog::LogLevel level, fmt::string_view location, size_t basePos, fmt::string_view threadName, - fmt::string_view msg, size_t bodyPos, size_t logFilePos) { +void logcb( + int64_t ns, + fmtlog::LogLevel level, + fmt::string_view location, + size_t basePos, + fmt::string_view threadName, + fmt::string_view msg, + size_t bodyPos, + size_t logFilePos) +{ fmt::print("callback full msg: {}, logFilePos: {}\n", msg, logFilePos); msg.remove_prefix(bodyPos); fmt::print("callback msg body: {}\n", msg); } -void logQFullCB(void* userData) { +void logQFullCB(void* userData) +{ fmt::print("log q full\n"); } -int main() { +int main() +{ char randomString[] = "Hello World"; - logi("A string, pointer, number, and float: '{}', {}, {}, {}", randomString, (void*)&randomString, - 512, 3.14159); + logi("A string, pointer, number, and float: '{}', {}, {}, {}", randomString, (void*)&randomString, 512, 3.14159); int a = 4; auto sptr = std::make_shared(5); @@ -38,14 +47,16 @@ int main() { // logi(FMT_STRING("This msg will trigger compile error: {:d}"), "I am not a number"); // FMT_STRING() above is not needed for c++20 - logd("This message wont be logged since it is lower " - "than the current log level."); + logd( + "This message wont be logged since it is lower " + "than the current log level."); fmtlog::setLogLevel(fmtlog::DBG); logd("Now debug msg is shown"); fmtlog::poll(); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) + { logio("log once: {}", i); } @@ -59,7 +70,8 @@ int main() { fmtlog::poll(); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) + { logil(10, "This msg will be logged at an interval of at least 10 ns: {}.", i); } @@ -69,12 +81,14 @@ int main() { logw("This msg will be called back"); fmtlog::setLogFile("/tmp/wow", false); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) + { logw("test logfilepos: {}.", i); } fmtlog::setLogQFullCB(logQFullCB, nullptr); - for (int i = 0; i < 1024; i++) { + for (int i = 0; i < 1024; i++) + { std::string str(1000, ' '); logi("log q full cb test: {}", str); } @@ -85,7 +99,8 @@ int main() { return 0; } -void runBenchmark() { +void runBenchmark() +{ const int RECORDS = 10000; // fmtlog::setLogFile("/dev/null", false); fmtlog::closeLogFile(); @@ -94,7 +109,8 @@ void runBenchmark() { std::chrono::high_resolution_clock::time_point t0, t1; t0 = std::chrono::high_resolution_clock::now(); - for (int i = 0; i < RECORDS; ++i) { + for (int i = 0; i < RECORDS; ++i) + { logi("Simple log message with one parameters, {}", i); } t1 = std::chrono::high_resolution_clock::now(); diff --git a/test/multithread_test.cc b/test/src/multithread_test.cc similarity index 59% rename from test/multithread_test.cc rename to test/src/multithread_test.cc index 33d1700..60e9f0a 100644 --- a/test/multithread_test.cc +++ b/test/src/multithread_test.cc @@ -1,8 +1,8 @@ +#include #include #include -#include -#include "../fmtlog.h" +#include "fmtlog/fmtlog.h" using namespace std; const int thr_cnt = 4; @@ -11,31 +11,44 @@ atomic start_cnt; size_t msg_cnt = 0; size_t cb_size = 0; -void logcb(int64_t ns, fmtlog::LogLevel level, fmt::string_view location, size_t basePos, fmt::string_view threadName, - fmt::string_view msg, size_t bodyPos, size_t logFilePos) { +void logcb( + int64_t ns, + fmtlog::LogLevel level, + fmt::string_view location, + size_t basePos, + fmt::string_view threadName, + fmt::string_view msg, + size_t bodyPos, + size_t logFilePos) +{ msg_cnt++; cb_size += msg.size(); } -void threadRun(int id) { +void threadRun(int id) +{ fmtlog::setThreadName(fmt::format("thread {}", id).c_str()); start_cnt++; while (start_cnt < thr_cnt) ; - for (int i = 0; i < 100000; i++) { + for (int i = 0; i < 100000; i++) + { logi("msg : {}, i: {}", id, i); - if (i % 1000 == 0) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (i % 1000 == 0) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } -int main() { +int main() +{ fmtlog::setLogCB(logcb, fmtlog::INF); // fmtlog::closeLogFile(); fmtlog::setLogFile("multithread.txt"); // fmtlog::setHeaderPattern(""); vector thrs; fmtlog::startPollingThread(1); - for (int i = 0; i < thr_cnt; i++) { + for (int i = 0; i < thr_cnt; i++) + { thrs.emplace_back([i]() { threadRun(i); }); } @@ -44,7 +57,8 @@ int main() { std::chrono::high_resolution_clock::time_point t0, t1; t0 = std::chrono::high_resolution_clock::now(); - for (auto& t : thrs) { + for (auto& t : thrs) + { t.join(); } @@ -53,8 +67,12 @@ int main() { t1 = std::chrono::high_resolution_clock::now(); double span = std::chrono::duration_cast>(t1 - t0).count(); - fmt::print("total msg_cnt: {}, cb_size: {}, throughput: {:.1f} MB/second, {} msg/sec\n", msg_cnt, cb_size, - (cb_size / 1000.0 / 1000 / span), (msg_cnt) / span); + fmt::print( + "total msg_cnt: {}, cb_size: {}, throughput: {:.1f} MB/second, {} msg/sec\n", + msg_cnt, + cb_size, + (cb_size / 1000.0 / 1000 / span), + (msg_cnt) / span); return 0; } diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..d741630 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,8 @@ +{ + "name": "fmtlog", + "version-string": "2.3.0", + "dependencies": [ + "fmt" + ] +} +