├── .clang-format ├── .devcontainer ├── Dockerfile ├── devcontainer.json └── reinstall_cmake.sh ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md └── workflows │ ├── buildAndRunTests.yml │ ├── codeql-analysis.yml │ ├── ctest.yml │ └── documentation.yml ├── .gitignore ├── .hgrc_copy ├── .hgtags ├── .vscode ├── easycode.ignore ├── launch.json └── settings.json ├── Build.cmake ├── CMakeLists.txt ├── CMakeLists.txt.in ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CPackLists.txt ├── CleanAll.cmake ├── GenerateMacroDefinitionsFile.cmake ├── LICENSE ├── Options.cmake ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── cmake └── g3logConfig.cmake ├── docs ├── .DS_Store ├── .ciignore ├── API.md ├── API_custom_formatting.md ├── building.md ├── codespaces.md ├── contributing.md ├── event_sequence.png ├── fatal_log_sequence.png ├── fatal_signal_sequence.png ├── g3log_usage.md ├── index.md └── log_sequence.png ├── example ├── Example.cmake ├── main_contract.cpp ├── main_fatal_choice.cpp └── main_sigsegv.cpp ├── iOS.cmake ├── iOSBuild.cmake ├── mkdocs.yml ├── scripts ├── .travis-bootstrap-ubuntu.sh └── buildAndRunTests.sh ├── src ├── crashhandler_unix.cpp ├── crashhandler_windows.cpp ├── filesink.cpp ├── filesinkhelper.ipp ├── g2log.hpp ├── g3log.cpp ├── g3log │ ├── active.hpp │ ├── atomicbool.hpp │ ├── crashhandler.hpp │ ├── filesink.hpp │ ├── future.hpp │ ├── g3log.hpp │ ├── logcapture.hpp │ ├── loglevels.hpp │ ├── logmessage.hpp │ ├── logworker.hpp │ ├── moveoncopy.hpp │ ├── shared_queue.hpp │ ├── sink.hpp │ ├── sinkhandle.hpp │ ├── sinkwrapper.hpp │ ├── stacktrace_windows.hpp │ ├── stlpatch_future.hpp │ └── time.hpp ├── logcapture.cpp ├── loglevels.cpp ├── logmessage.cpp ├── logworker.cpp ├── stacktrace_windows.cpp └── time.cpp ├── test_main └── test_main.cpp ├── test_performance ├── Performance.cmake ├── main_threaded_mean.cpp ├── main_threaded_worst.cpp └── performance.h └── test_unit ├── Test.cmake ├── test_concept_sink.cpp ├── test_cpp_future_concepts.cpp ├── test_crashhandler_windows.cpp ├── test_fatal.cpp ├── test_filechange.cpp ├── test_io.cpp ├── test_linux_dynamic_loaded_sharedlib.cpp ├── test_message.cpp ├── test_signal.cpp ├── test_sink.cpp ├── tester_sharedlib.cpp ├── tester_sharedlib.h ├── testing_helpers.cpp └── testing_helpers.h /.clang-format: -------------------------------------------------------------------------------- 1 | # Google C/C++ Code Style settings 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | # Author: Kehan Xue, kehan.xue (at) gmail.com 4 | Language: Cpp 5 | BasedOnStyle: Google 6 | AccessModifierOffset: -1 7 | AlignAfterOpenBracket: Align 8 | AlignConsecutiveAssignments: None 9 | AlignOperands: Align 10 | AllowAllArgumentsOnNextLine: true 11 | AllowAllConstructorInitializersOnNextLine: true 12 | AllowAllParametersOfDeclarationOnNextLine: false 13 | AllowShortBlocksOnASingleLine: Empty 14 | AllowShortCaseLabelsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: Inline 16 | AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding 17 | AllowShortLambdasOnASingleLine: Inline 18 | AllowShortLoopsOnASingleLine: false 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakTemplateDeclarations: Yes 21 | BinPackArguments: true 22 | PackConstructorInitializers: Never 23 | BreakBeforeBraces: Attach 24 | BraceWrapping: 25 | AfterCaseLabel: false 26 | AfterClass: false 27 | AfterStruct: false 28 | AfterControlStatement: Never 29 | AfterEnum: false 30 | AfterFunction: false 31 | AfterNamespace: false 32 | AfterUnion: false 33 | AfterExternBlock: false 34 | BeforeCatch: false 35 | BeforeElse: false 36 | BeforeLambdaBody: false 37 | IndentBraces: false 38 | SplitEmptyFunction: false 39 | SplitEmptyRecord: false 40 | SplitEmptyNamespace: false 41 | BreakBeforeBinaryOperators: None 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializers: AfterColon 44 | BreakInheritanceList: BeforeColon 45 | ColumnLimit: 0 46 | CompactNamespaces: false 47 | ContinuationIndentWidth: 3 48 | Cpp11BracedListStyle: true 49 | DerivePointerAlignment: false # Make sure the * or & align on the left 50 | EmptyLineBeforeAccessModifier: LogicalBlock 51 | FixNamespaceComments: true 52 | IncludeBlocks: Preserve 53 | IndentCaseLabels: false 54 | IndentPPDirectives: None 55 | IndentWidth: 3 56 | KeepEmptyLinesAtTheStartOfBlocks: true 57 | MaxEmptyLinesToKeep: 1 58 | NamespaceIndentation: All 59 | ObjCSpaceAfterProperty: false 60 | ObjCSpaceBeforeProtocolList: true 61 | PointerAlignment: Left 62 | ReflowComments: false 63 | # SeparateDefinitionBlocks: Always # Only support since clang-format 14 64 | SpaceAfterCStyleCast: false 65 | SpaceAfterLogicalNot: false 66 | SpaceAfterTemplateKeyword: true 67 | SpaceBeforeAssignmentOperators: true 68 | SpaceBeforeCpp11BracedList: false 69 | SpaceBeforeCtorInitializerColon: true 70 | SpaceBeforeInheritanceColon: true 71 | SpaceBeforeParens: ControlStatements 72 | SpaceBeforeRangeBasedForLoopColon: true 73 | SpaceBeforeSquareBrackets: false 74 | SpaceInEmptyParentheses: false 75 | SpacesBeforeTrailingComments: 2 76 | SpacesInAngles: false 77 | SpacesInCStyleCastParentheses: false 78 | SpacesInContainerLiterals: false 79 | SpacesInParentheses: false 80 | SpacesInSquareBrackets: false 81 | Standard: c++11 82 | TabWidth: 3 83 | UseTab: Never -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Latest Debian 2 | FROM mcr.microsoft.com/devcontainers/cpp:debian 3 | 4 | ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none" 5 | 6 | # Optionally install the cmake for vcpkg 7 | COPY ./reinstall_cmake.sh /tmp/ 8 | 9 | RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ 10 | chmod +x /tmp/reinstall_cmake.sh && /tmp/reinstall_cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \ 11 | fi \ 12 | && rm -f /tmp/reinstall_cmake.sh 13 | 14 | # [Optional] Uncomment this section to install additional vcpkg ports. 15 | # RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " 16 | 17 | # [Optional] Uncomment this section to install additional packages. 18 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 19 | # && apt-get -y install --no-install-recommends 20 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/cpp 3 | { 4 | "name": "C++", 5 | "build": { 6 | "dockerfile": "Dockerfile" 7 | }, 8 | 9 | // Features to add to the dev container. More info: https://containers.dev/features. 10 | // "features": {}, 11 | 12 | // Configure tool-specific properties. 13 | "customizations": { 14 | // Configure properties specific to VS Code. 15 | "vscode": { 16 | "settings": {}, 17 | "extensions": [ 18 | "streetsidesoftware.code-spell-checker", 19 | "genieai.chatgpt-vscode", 20 | "ms-vscode.cpptools-extension-pack", 21 | "ms-vscode.cpptools" 22 | ] 23 | } 24 | } 25 | 26 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 27 | // "forwardPorts": [], 28 | 29 | // Use 'postCreateCommand' to run commands after the container is created. 30 | // "postCreateCommand": "gcc -v", 31 | 32 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 33 | // "remoteUser": "root" 34 | } 35 | -------------------------------------------------------------------------------- /.devcontainer/reinstall_cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #------------------------------------------------------------------------------------------------------------- 3 | # Copyright (c) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 5 | #------------------------------------------------------------------------------------------------------------- 6 | # 7 | # testing, unsure if it's needed. following steps from example: https://github.com/microsoft/vscode-remote-try-cpp/tree/main/.devcontainer 8 | 9 | set -e 10 | 11 | CMAKE_VERSION=${1:-"none"} 12 | 13 | if [ "${CMAKE_VERSION}" = "none" ]; then 14 | echo "No CMake version specified, skipping CMake reinstallation" 15 | exit 0 16 | fi 17 | 18 | # Cleanup temporary directory and associated files when exiting the script. 19 | cleanup() { 20 | EXIT_CODE=$? 21 | set +e 22 | if [[ -n "${TMP_DIR}" ]]; then 23 | echo "Executing cleanup of tmp files" 24 | rm -Rf "${TMP_DIR}" 25 | fi 26 | exit $EXIT_CODE 27 | } 28 | trap cleanup EXIT 29 | 30 | 31 | echo "Installing CMake..." 32 | apt-get -y purge --auto-remove cmake 33 | mkdir -p /opt/cmake 34 | 35 | architecture=$(dpkg --print-architecture) 36 | case "${architecture}" in 37 | arm64) 38 | ARCH=aarch64 ;; 39 | amd64) 40 | ARCH=x86_64 ;; 41 | *) 42 | echo "Unsupported architecture ${architecture}." 43 | exit 1 44 | ;; 45 | esac 46 | 47 | CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" 48 | CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" 49 | TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) 50 | 51 | echo "${TMP_DIR}" 52 | cd "${TMP_DIR}" 53 | 54 | curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O 55 | curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O 56 | 57 | sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" 58 | sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license 59 | 60 | ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: under investigation 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | - What system, compiler etc was used to run this? 15 | - Is something done differently on your setup than what the documentation specifies? 16 | - Have you provided a code snippet that can be tested? 17 | 18 | **Additional context** 19 | Add any other context about the problem here. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | Keep in mind that g3log is community driven. Do you want to take on the feature work yourself, or do you want someone else to take it on? The latter will require a whole lot more "selling". If you want to drive it yourself you can likely get community feedback on your idea + definitely great code reviews. 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Additional context** 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Question to the community, request for help to troubleshoot or undersand 4 | title: "[Question]" 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Uncertain what's going on? Do you need advice or help troubleshooting? 11 | A clear and concise description of what the problem is. 12 | 13 | ### Describe the solution you'd like 14 | A clear and concise description of what you want to happen. 15 | 16 | ### Describe alternatives you've considered 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | ### Additional context 20 | Add any other context or screenshots that might help. 21 | -------------------------------------------------------------------------------- /.github/workflows/buildAndRunTests.yml: -------------------------------------------------------------------------------- 1 | name: ci/action ctest Ubuntu v3 2 | on: 3 | push: 4 | paths-ignore: 5 | - docs/** 6 | - '**.md' 7 | - '**.markdown' 8 | - '**.yml' 9 | branches: [ master ] 10 | pull_request: 11 | paths-ignore: 12 | - docs/** 13 | - '**.md' 14 | - '**.markdown' 15 | branches: [ master ] 16 | 17 | jobs: 18 | build: 19 | 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Print env 26 | run: | 27 | echo github.event.action: ${{ github.event.action }} 28 | echo github.event_name: ${{ github.event_name }} 29 | gcc --version 30 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 31 | sudo apt-get update 32 | sudo apt-get install ninja-build cmake gcc-9 g++-9 33 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9 34 | echo gcc version after 35 | gcc --version 36 | cmake --version 37 | # example: https://gist.github.com/NickNaso/0d478f1481686d5bcc868cac06620a60 38 | - name: configure 39 | shell: bash 40 | # run: ./configure 41 | run: ./scripts/buildAndRunTests.sh 42 | 43 | 44 | #- name: make 45 | # run: make 46 | #- name: make check 47 | # run: make check 48 | #- name: make distcheck 49 | # run: make distcheck 50 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "ci/action CodeQL" 13 | 14 | on: 15 | push: 16 | paths-ignore: 17 | - docs/** 18 | - '**.md' 19 | - '**.markdown' 20 | - '**.yml' 21 | branches: [ master ] 22 | pull_request: 23 | paths-ignore: 24 | - docs/** 25 | - '**.md' 26 | - '**.markdown' 27 | # The branches below must be a subset of the branches above 28 | branches: [ master ] 29 | schedule: 30 | - cron: '17 8 * * 2' 31 | 32 | jobs: 33 | analyze: 34 | name: Analyze 35 | runs-on: ubuntu-latest 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | language: [ 'cpp' ] 41 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'cmake' ] 42 | # Learn more: 43 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 44 | 45 | steps: 46 | - name: Checkout repository 47 | uses: actions/checkout@v3 48 | 49 | # Initializes the CodeQL tools for scanning. 50 | - name: Initialize CodeQL 51 | uses: github/codeql-action/init@v3 52 | with: 53 | languages: ${{ matrix.language }} 54 | # If you wish to specify custom queries, you can do so here or in a config file. 55 | # By default, queries listed here will override any specified in a config file. 56 | # Prefix the list here with "+" to use these queries and those in the config file. 57 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 58 | 59 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 60 | # If this step fails, then you should remove it and run the build manually (see below) 61 | - name: Autobuild 62 | uses: github/codeql-action/autobuild@v3 63 | 64 | # ℹ️ Command-line programs to run using the OS shell. 65 | # 📚 https://git.io/JvXDl 66 | 67 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 68 | # and modify them (or add more) to build your code if your project 69 | # uses a compiled language 70 | 71 | #- run: | 72 | # make bootstrap 73 | # make release 74 | 75 | - name: Perform CodeQL Analysis 76 | uses: github/codeql-action/analyze@v3 77 | -------------------------------------------------------------------------------- /.github/workflows/ctest.yml: -------------------------------------------------------------------------------- 1 | name: matrix (ubuntu, macos, windows) ctes 2 | on: 3 | push: 4 | paths-ignore: 5 | - docs/** 6 | - "**.md" 7 | - "**.markdown" 8 | branches: 9 | - master 10 | pull_request: 11 | paths-ignore: 12 | - docs/** 13 | - "**.md" 14 | - "**.markdown" 15 | - '**.yml' 16 | branches: 17 | - master 18 | env: 19 | BUILD_TYPE: Release 20 | concurrency: 21 | group: ${{ github.workflow }}-${{ github.ref }} 22 | cancel-in-progress: true 23 | jobs: 24 | build: 25 | strategy: 26 | matrix: 27 | os: 28 | - ubuntu-latest 29 | - macos-latest 30 | - windows-latest 31 | runs-on: ${{ matrix.os }} 32 | steps: 33 | # checkout full depth of history. 34 | - uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | 38 | - name: Run Linux Build 39 | if: matrix.os == 'ubuntu-latest' 40 | run: echo "Ubuntu Latest" > release_ubuntu 41 | 42 | - name: Run Mac Build 43 | if: matrix.os == 'macos-latest' 44 | run: echo "MacOS Latest" > release_mac 45 | 46 | - name: Run Windows Build 47 | if: matrix.os == 'windows-latest' 48 | run: echo "Windows Latest" > release_windows 49 | 50 | - name: Create Build Environment 51 | run: cmake -E make_directory ${{github.workspace}}/build 52 | 53 | - name: Configure Linux/OSX CMake 54 | if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' 55 | shell: bash 56 | working-directory: ${{github.workspace}}/build 57 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DADD_G3LOG_UNIT_TEST=ON 58 | 59 | - name: Configure Windows CMake 60 | if: matrix.os == 'windows-latest' 61 | shell: cmd 62 | working-directory: ${{github.workspace}}/build 63 | run: ls && cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DADD_G3LOG_UNIT_TEST=ON .. 64 | 65 | - name: Build Linux/OSx 66 | working-directory: ${{github.workspace}}/build 67 | shell: bash 68 | run: cmake --build . --config $BUILD_TYPE 69 | 70 | - name: Test 71 | working-directory: ${{github.workspace}}/build 72 | shell: bash 73 | run: ctest -V 74 | 75 | - name: Fatal Exit Example Linux/OSX 76 | working-directory: ${{github.workspace}}/build 77 | if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' 78 | shell: bash 79 | run: ./g3log-FATAL-sigsegv || true && echo -e 80 | "\n\nverifying SIGSEGV existed in stackdump\n\n\n\n" && cat /tmp/*3log*FATAL*.log && cat /tmp/g3log*FATAL*.log | grep "SIGSEGV" 81 | 82 | - name: Fatal Exit Example Windows 83 | working-directory: ${{github.workspace}}/build 84 | if: matrix.os == 'windows-latest' 85 | shell: bash 86 | run: ./Release/g3log-FATAL-sigsegv.exe || true && echo -e "\n\nverifying SIGSEGV - EXCEPTION_ACCESS_VIOLATION existed in 87 | stackdump\n\n\n\n" && cat *3log*FATAL*.log && cat *3log*FATAL*.log | grep "EXCEPTION_ACCESS_VIOLATION" 88 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | # Locally you can try this out also with `mkdocs serve` 2 | # Remember if doing changes that github pages, need to deploy branch gh_pages 3 | # which points to root. 4 | name: Dockumentation Publish v2 5 | on: 6 | push: 7 | branches: 8 | - master 9 | permissions: 10 | contents: write 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Configure Git Credentials 17 | run: | 18 | git config user.name github-actions[bot] 19 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 20 | - uses: actions/setup-python@v5 21 | with: 22 | python-version: 3.x 23 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 24 | - uses: actions/cache@v4 25 | with: 26 | key: mkdocs-material-${{ env.cache_id }} 27 | path: .cache 28 | restore-keys: | 29 | mkdocs-material- 30 | - run: pip install mkdocs-material 31 | - run: mkdocs gh-deploy --force 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | sinks/* 3 | build/* 4 | nbproject/* 5 | build_clang 6 | build_travis 7 | gtest-1.7.0 8 | *~ 9 | -------------------------------------------------------------------------------- /.hgrc_copy: -------------------------------------------------------------------------------- 1 | [paths] 2 | default = https://bitbucket.org/KjellKod/g3log 3 | 4 | [extensions] 5 | hgext.bookmarks = 6 | hggit = 7 | 8 | [paths] 9 | github = git+ssh://git@github.com/KjellKod/g3log.git 10 | 11 | [hooks] 12 | outgoing = hg bookmark -r default master || true && hg push github || true 13 | -------------------------------------------------------------------------------- /.hgtags: -------------------------------------------------------------------------------- 1 | d9a55a4a615449b8eb50ed6208e3dcaf1c678d7e version-1.0 2 | -------------------------------------------------------------------------------- /.vscode/easycode.ignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | vendor/ 4 | cache/ 5 | .*/ 6 | *.min.* 7 | *.test.* 8 | *.spec.* 9 | *.bundle.* 10 | *.bundle-min.* 11 | *.*.js 12 | *.*.ts 13 | *.log -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | // Remember to build the specific part of cmake with 6 | // mkdir build; cd build 7 | // "cmake -DCMAKE_BUILD_TYPE=Debug .. " if you want to be able to debug it. 8 | // don't forget to inspect the cmake output for more configuration options 9 | "configurations": [ 10 | { 11 | "name": "(lldb) Launch", 12 | "type": "cppdbg", 13 | "request": "launch", 14 | "program": "${workspaceFolder}/build/test_signal", 15 | "args": [], 16 | "stopAtEntry": false, 17 | "cwd": "${fileDirname}", 18 | "environment": [], 19 | "externalConsole": false, 20 | "MIMode": "lldb" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.configureOnOpen": false, 3 | "editor.formatOnSave": true, 4 | "files.associations": { 5 | "ostream": "cpp" 6 | } 7 | } -------------------------------------------------------------------------------- /Build.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations 9 | # =================================================================== 10 | 11 | # GENERIC STEPS 12 | SET(LOG_SRC ${g3log_SOURCE_DIR}/src) 13 | 14 | file(GLOB SRC_FILES ${LOG_SRC}/*.cpp ${LOG_SRC}/*.ipp) 15 | file(GLOB HEADER_FILES ${LOG_SRC}/g3log/*.hpp) 16 | 17 | list( APPEND HEADER_FILES ${GENERATED_G3_DEFINITIONS} ) 18 | list( APPEND SRC_FILES ${GENERATED_G3_DEFINITIONS} ) 19 | 20 | IF (MSVC OR MINGW) 21 | list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_unix.cpp) 22 | ELSE() 23 | list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_windows.cpp ${LOG_SRC}/g3log/stacktrace_windows.hpp ${LOG_SRC}/stacktrace_windows.cpp) 24 | ENDIF (MSVC OR MINGW) 25 | 26 | set(SRC_FILES ${SRC_FILES} ${SRC_PLATFORM_SPECIFIC}) 27 | 28 | # Create the g3log library 29 | SET(G3LOG_LIBRARY g3log) 30 | 31 | 32 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 33 | message("CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") 34 | IF( NOT CMAKE_INSTALL_PREFIX) 35 | SET(CMAKE_INSTALL_PREFIX /usr/local) 36 | ENDIF() 37 | 38 | set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}) 39 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 40 | message("Install rpath location: ${CMAKE_INSTALL_RPATH}") 41 | ENDIF() 42 | 43 | IF( G3_SHARED_LIB ) 44 | IF( WIN32 ) 45 | IF(NOT(${CMAKE_VERSION} VERSION_LESS "3.4")) 46 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 47 | ELSE() 48 | message( FATAL_ERROR "Need CMake version >=3.4 to build shared windows library!" ) 49 | ENDIF() 50 | ENDIF() 51 | ADD_LIBRARY(${G3LOG_LIBRARY} SHARED ${SRC_FILES}) 52 | ELSE() 53 | IF(MSVC) 54 | IF(NOT G3_SHARED_RUNTIME) 55 | SET(CompilerFlags 56 | CMAKE_CXX_FLAGS 57 | CMAKE_CXX_FLAGS_DEBUG 58 | CMAKE_CXX_FLAGS_RELEASE 59 | CMAKE_C_FLAGS 60 | CMAKE_C_FLAGS_DEBUG 61 | CMAKE_C_FLAGS_RELEASE 62 | ) 63 | foreach(CompilerFlag ${CompilerFlags}) 64 | string(REPLACE "/MDd" "/MTd" ${CompilerFlag} "${${CompilerFlag}}") 65 | string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") 66 | endforeach() 67 | ENDIF() 68 | ENDIF() 69 | ADD_LIBRARY(${G3LOG_LIBRARY} STATIC ${SRC_FILES}) 70 | ENDIF() 71 | 72 | SET(${G3LOG_LIBRARY}_VERSION_STRING ${VERSION}) 73 | MESSAGE( STATUS "Creating ${G3LOG_LIBRARY} VERSION: ${VERSION}" ) 74 | MESSAGE( STATUS "Creating ${G3LOG_LIBRARY} SOVERSION: ${MAJOR_VERSION}" ) 75 | 76 | SET_TARGET_PROPERTIES(${G3LOG_LIBRARY} PROPERTIES 77 | LINKER_LANGUAGE CXX 78 | OUTPUT_NAME g3log 79 | CLEAN_DIRECT_OUTPUT 1 80 | SOVERSION ${MAJOR_VERSION} 81 | VERSION ${VERSION} 82 | ) 83 | 84 | 85 | IF(APPLE) 86 | SET_TARGET_PROPERTIES(${G3LOG_LIBRARY} PROPERTIES MACOSX_RPATH TRUE) 87 | ENDIF() 88 | 89 | # require here some proxy for c++14/c++17? standard to avoid problems TARGET_PROPERTY CXX_STANDARD 90 | TARGET_COMPILE_FEATURES(${G3LOG_LIBRARY} PUBLIC cxx_variable_templates) 91 | 92 | TARGET_INCLUDE_DIRECTORIES(${G3LOG_LIBRARY} 93 | PUBLIC 94 | $ 95 | $ 96 | ) 97 | 98 | SET(ACTIVE_CPP0xx_DIR "Release") 99 | 100 | # find corresponding thread lib (e.g. whether -lpthread is needed or not) 101 | FIND_PACKAGE(Threads REQUIRED) 102 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} Threads::Threads ) 103 | 104 | # check for backtrace and cxa_demangle only in non-Windows dev environments 105 | IF(NOT(MSVC OR MINGW)) 106 | # the backtrace module does not provide a modern cmake target 107 | FIND_PACKAGE(Backtrace REQUIRED) 108 | if(Backtrace_FOUND) 109 | TARGET_INCLUDE_DIRECTORIES(${G3LOG_LIBRARY} PRIVATE ${Backtrace_INCLUDE_DIRS}) 110 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} ${Backtrace_LIBRARIES}) 111 | else() 112 | message( FATAL_ERROR "Could not find Library to create backtraces") 113 | endif() 114 | 115 | 116 | INCLUDE(CheckLibraryExists) 117 | INCLUDE(CheckCXXSymbolExists) 118 | 119 | #if demangle is in c++ runtime lib 120 | CHECK_CXX_SYMBOL_EXISTS(abi::__cxa_demangle "cxxabi.h" DEMANGLE_EXISTS) 121 | IF( NOT (DEMANGLE_EXISTS)) 122 | #try to link against c++abi to get demangle 123 | CHECK_LIBRARY_EXISTS(c++abi abi::__cxa_demangle "cxxabi.h" NEED_C++ABI) 124 | IF( NEED_C++ABI) 125 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} c++abi) 126 | ELSE() 127 | message( FATAL_ERROR "Could not find function abi::__cxa_demangle") 128 | ENDIF() 129 | endif() 130 | ENDIF() 131 | # add Warnings 132 | target_compile_options(${G3LOG_LIBRARY} PRIVATE 133 | # clang/GCC warnings 134 | $<$,$>:-Wall -Wunused> 135 | # MSVC warnings 136 | $<$:/W4>) 137 | # add GCC specific stuff 138 | target_compile_options(${G3LOG_LIBRARY} PRIVATE 139 | # clang/GCC warnings 140 | $<$,$>>:-rdynamic> 141 | ) 142 | 143 | #cmake -DCMAKE_CXX_COMPILER=clang++ .. 144 | # WARNING: If Clang for Linux does not work with full c++14 support it might be your 145 | # installation that is faulty. When I tested Clang on Ubuntu I followed the following 146 | # description 147 | # 1) http://kjellkod.wordpress.com/2013/09/23/experimental-g3log-with-clang/ 148 | # 2) https://github.com/maidsafe/MaidSafe/wiki/Hacking-with-Clang-llvm-abi-and-llvm-libc 149 | 150 | # Windows Stuff 151 | IF(MSVC OR MINGW) 152 | TARGET_COMPILE_DEFINITIONS(${G3LOG_LIBRARY} PRIVATE NOGDI) 153 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} dbghelp) 154 | # VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408 155 | # add_definition(-D_VARIADIC_MAX=10) 156 | # https://github.com/anhstudios/swganh/pull/186/files 157 | TARGET_COMPILE_DEFINITIONS(${G3LOG_LIBRARY} PRIVATE _VARIADIC_MAX=10) 158 | MESSAGE(STATUS "- MSVC: Set variadic max to 10 for MSVC compatibility") 159 | # Remember to set set target properties if using GTEST similar to done below on target "unit_test" 160 | # "set_target_properties(unit_test PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0") 161 | message( STATUS "" ) 162 | message( STATUS "Windows: Run cmake with the appropriate Visual Studio generator" ) 163 | message( STATUS "The generator is one number below the official version number. I.e. VS2013 -> Generator 'Visual Studio 12'" ) 164 | MESSAGE( STATUS "I.e. if VS2013: Please run the command [cmake -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 12\" ..]") 165 | message( STATUS "if cmake finishes OK, do 'msbuild g3log.sln /p:Configuration=Release'" ) 166 | message( STATUS "then run 'Release\\g3log-FATAL-*' examples" ) 167 | message( STATUS "" ) 168 | ENDIF() 169 | 170 | TARGET_COMPILE_OPTIONS(${G3LOG_LIBRARY} PRIVATE 171 | $<$:/utf-8> # source code already in utf-8, force it for compilers in non-utf8_windows_locale 172 | $<$:$<$:/arch:IA32>> 173 | ) 174 | -------------------------------------------------------------------------------- /CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | URL https://github.com/google/googletest/archive/refs/heads/main.zip 8 | https://github.com/KjellKod/g3sinks/issues/117 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | 17 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone. 7 | 8 | We pledge to act and interact in ways that contribute to an open, welcoming, 9 | diverse, inclusive, and healthy community. 10 | 11 | ## Our Standards 12 | 13 | Examples of behavior that contributes to a positive environment for our 14 | community include: 15 | 16 | * Demonstrating empathy and kindness toward other people 17 | * Being respectful of differing opinions, viewpoints, and experiences 18 | * Giving and gracefully accepting constructive feedback 19 | * Accepting responsibility and apologizing to those affected by our mistakes, 20 | and learning from the experience 21 | * Focusing on what is best not just for us as individuals, but for the 22 | community 23 | 24 | Examples of unacceptable behavior include: 25 | 26 | * The use of sexualized language or imagery, and sexual attention or 27 | advances of any kind 28 | * Trolling, insulting or derogatory comments, and personal or political attacks 29 | * Public or private harassment 30 | * Publishing others' private information, such as a physical or email 31 | address, without their explicit permission 32 | * Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Enforcement Responsibilities 36 | 37 | Community leaders are responsible for clarifying and enforcing our standards of 38 | acceptable behavior and will take appropriate and fair corrective action in 39 | response to any behavior that they deem inappropriate, threatening, offensive, 40 | or harmful. 41 | 42 | Community leaders have the right and responsibility to remove, edit, or reject 43 | comments, commits, code, wiki edits, issues, and other contributions that are 44 | not aligned to this Code of Conduct, and will communicate reasons for moderation 45 | decisions when appropriate. 46 | 47 | 48 | ## Enforcement 49 | 50 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 51 | reported to the community leaders responsible for enforcement at 52 | kjell.hedstrom+g3log_code_of_conduct@gmail.com. 53 | All complaints will be reviewed and investigated promptly and fairly. 54 | 55 | All community leaders are obligated to respect the privacy and security of the 56 | reporter of any incident. 57 | 58 | ## Enforcement Guidelines 59 | 60 | Community leaders will follow these Community Impact Guidelines in determining 61 | the consequences for any action they deem in violation of this Code of Conduct: 62 | 63 | ### 1. Warning 64 | 65 | **Community Impact**: A violation through a single incident or series 66 | of actions. Use of inappropriate language or other behavior deemed 67 | unprofessional or unwelcome in the community. 68 | 69 | **Consequence**: A written warning from community leaders, providing 70 | clarity around the nature of the violation and an explanation of why the 71 | behavior was inappropriate. A public apology may be requested. Violating these terms may lead to a permanent ban. 72 | 73 | ### 2. Permanent Ban 74 | 75 | **Community Impact**: Demonstrating a pattern of violation of community 76 | standards, including sustained inappropriate behavior, harassment of an 77 | individual, or aggression toward or disparagement of classes of individuals. 78 | 79 | **Consequence**: A permanent ban from any sort of public interaction within 80 | the community. 81 | 82 | ## Attribution 83 | 84 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 85 | version 2.0, available at 86 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 87 | 88 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 89 | enforcement ladder](https://github.com/mozilla/diversity). 90 | 91 | [homepage]: https://www.contributor-covenant.org 92 | 93 | For answers to common questions about this code of conduct, see the FAQ at 94 | https://www.contributor-covenant.org/faq. Translations are available at 95 | https://www.contributor-covenant.org/translations. 96 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # New Code 2 | 1. Please support all changed functionality with unit testing. A TDD approach usually leaves a cleaner end result than writing test afterwards 3 | 4 | 5 | # Issues 6 | 1. Please explain your environment in the ticket. Frequently initialization issues due to not following best practices or the documentation are the causes. 7 | Check the documentation and search previous issues before opening up a new one. 8 | 1. Don't be afraid of adding additional contexst of an old issue in case you think 9 | this will improve things for the community going forward. 10 | 11 | 12 | # Community Driven 13 | G3log is community driven. Be respectful. G3log is developed and maintained without financial support, being helpful and polite will move your request and input along faster. 14 | -------------------------------------------------------------------------------- /CPackLists.txt: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================*/ 8 | 9 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 10 | IF(NOT CPACK_PACKAGING_INSTALL_PREFIX) 11 | IF(NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 12 | SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) 13 | # set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}) 14 | ELSE() 15 | SET(CPACK_PACKAGING_INSTALL_PREFIX /usr/local) 16 | ENDIF() 17 | ENDIF() 18 | # message("Install rpath location: ${CMAKE_INSTALL_RPATH}") 19 | ENDIF() 20 | 21 | INCLUDE(CMakePackageConfigHelpers) 22 | INCLUDE(GNUInstallDirs) 23 | 24 | SET(CPACK_PACKAGE_NAME g3log) 25 | SET(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION}) 26 | SET(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION}) 27 | SET(CPACK_PACKAGE_VERSION_PATCH ${GIT_RELEASE_COMMITS}) 28 | SET(CPACK_PACKAGE_DESCRIPTION "Asynchronous 'crash safe' logger 29 | License: http://unlicense.org 30 | Repository: https://github.com/KjellKod/g3log") 31 | SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CPACK_PACKAGE_DESCRIPTION}) 32 | SET(CPACK_PACKAGE_CONTACT "Kjell Hedstrom hedstrom@kjellkoc.cc") 33 | SET(CPACK_RESOURCE_FILE_LICENSE ${g3log_SOURCE_DIR}/LICENSE) 34 | SET(CPACK_PACKAGE_VENDOR "KjellKod") 35 | 36 | IF(INSTALL_G3LOG) 37 | INSTALL( TARGETS g3log 38 | EXPORT g3log-targets 39 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries 40 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries 41 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries 42 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 43 | ) 44 | 45 | INSTALL( FILES ${HEADER_FILES} 46 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/g3log 47 | COMPONENT headers) 48 | 49 | INSTALL( 50 | EXPORT g3log-targets 51 | FILE g3logTargets.cmake 52 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log 53 | ) 54 | 55 | CONFIGURE_PACKAGE_CONFIG_FILE( 56 | ${PROJECT_SOURCE_DIR}/cmake/g3logConfig.cmake 57 | ${CMAKE_CURRENT_BINARY_DIR}/g3logConfig.cmake 58 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log 59 | ) 60 | 61 | install( 62 | FILES ${CMAKE_CURRENT_BINARY_DIR}/g3logConfig.cmake 63 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log 64 | ) 65 | ENDIF() 66 | 67 | 68 | SET(CPACK_COMPONENTS_ALL libraries headers) 69 | SET(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "g3log libraries") 70 | SET(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "g3log C++ headers") 71 | 72 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 73 | SET(CPACK_GENERATOR "DEB") 74 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "KjellKod - Kjell Hedstrom") 75 | ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 76 | SET(CPACK_GENERATOR "ZIP") # Otherwise, NSIS is needed. 77 | ENDIF() 78 | 79 | message( STATUS "\nTo create installation package: " ) 80 | message( STATUS "make package" ) 81 | 82 | message( STATUS "\nOption to install using 'make install'" ) 83 | message( STATUS "Installation locations: " ) 84 | message( STATUS "====================" ) 85 | message( STATUS "Headers: ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/g3log" ) 86 | message( STATUS "Library installation directory: ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" ) 87 | 88 | message( STATUS "For more information please see g3log/CPackLists.txt\n\n" ) 89 | IF(NOT MINGW) 90 | message( STATUS "To install: sudo dpkg -i g3log-***Linux.deb" ) 91 | message( STATUS "To list package contents: sudo dpkg --contents g3log-***Linux.deb" ) 92 | 93 | message( STATUS "List content of the installed package: sudo dpkg -L g3log" ) 94 | message( STATUS "To remove: sudo dpkg -r g3log" ) 95 | ENDIF() 96 | # NOTE: to change installation locations you can use the settings below 97 | # examples: 98 | # CPACK_PACKAGING_INSTALL_PREFIX 99 | # CPACK_OUTPUT_FILE_PREFIX 100 | # CMAKE_INSTALL_PREFIX 101 | 102 | INCLUDE(CPack) 103 | 104 | 105 | -------------------------------------------------------------------------------- /CleanAll.cmake: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================*/ 8 | 9 | 10 | set(cmake_generated ${CMAKE_BINARY_DIR}/CMakeCache.txt 11 | ${CMAKE_BINARY_DIR}/cmake_install.cmake 12 | ${CMAKE_BINARY_DIR}/Makefile 13 | ${CMAKE_BINARY_DIR}/CMakeFiles 14 | ) 15 | 16 | foreach(file ${cmake_generated}) 17 | if (EXISTS ${file}) 18 | message( STATUS "Removing: ${file}" ) 19 | file(REMOVE_RECURSE ${file}) 20 | endif() 21 | endforeach(file) -------------------------------------------------------------------------------- /GenerateMacroDefinitionsFile.cmake: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================*/ 8 | 9 | # Prerequisite : Options.cmake should run first 10 | 11 | SET(HEADER "/** ========================================================================== 12 | * 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 13 | * with no warranties. This code is yours to share, use and modify with no 14 | * strings attached and no restrictions or obligations. 15 | * 16 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 17 | * ============================================================================*/") 18 | 19 | 20 | 21 | message( STATUS "" ) 22 | message( STATUS "COMPILE_DEFINITIONS:\n\t[${G3_DEFINITIONS}]" ) 23 | message( STATUS "" ) 24 | 25 | SET(GENERATED_G3_DEFINITIONS "${CMAKE_CURRENT_BINARY_DIR}/include/g3log/generated_definitions.hpp") 26 | 27 | # If it exists, read existing file 28 | set(current_content "") 29 | if(EXISTS ${GENERATED_G3_DEFINITIONS}) 30 | file(READ ${GENERATED_G3_DEFINITIONS} current_content) 31 | endif() 32 | 33 | set(generated_content "// AUTO GENERATED MACRO DEFINITIONS FOR G3LOG\n\n") 34 | set(generated_content "${generated_content}\n${HEADER}\n") 35 | set(generated_content "${generated_content}\n#pragma once\n\n") 36 | set(generated_content "${generated_content}\n// CMake induced definitions below. See g3log/Options.cmake for details.\n\n") 37 | 38 | FOREACH(definition ${G3_DEFINITIONS} ) 39 | set(generated_content "${generated_content}\n#define ${definition}\n") 40 | ENDFOREACH(definition) 41 | 42 | if(NOT "${current_content}" STREQUAL "${generated_content}") 43 | 44 | message( STATUS "Generated ${GENERATED_G3_DEFINITIONS}" ) 45 | 46 | message( STATUS "******************** START *************************" ) 47 | message(${generated_content}) 48 | message( STATUS "******************** END *************************" ) 49 | 50 | file(WRITE ${GENERATED_G3_DEFINITIONS} ${generated_content}) 51 | 52 | endif() 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | # PULL REQUEST DESCRIPTION 3 | 4 | `ADD CONTENT HERE TO DESCRIBE THE PURPOSE OF THE PULL REQUEST` 5 | 6 | 7 | # Formatting 8 | - [ ] I am following the formatting style of the existing codebase. 9 | 10 | _a clang-format configuration file is available in the root of g3log._ 11 | - _Use VSCode with clang-formatter or commandline:_ 12 | `clang-format -i path_to_file` 13 | - _or recursive throughout the whole repo:_ `find . -iname "*.hpp" -o -iname "*.cpp" | xargs clang-format -i` 14 | 15 | 16 | # Testing 17 | 18 | - [ ] This new/modified code was covered by unit tests. 19 | - [ ] (insight) Was all tests written using TDD (Test Driven Development) style? 20 | - [ ] The CI (Windows, Linux, OSX) are working without issues. 21 | - [ ] Was new functionality documented? 22 | - [ ] The testing steps 1 - 2 below were followed 23 | 24 | _step 1_ 25 | 26 | ```bash 27 | mkdir build; cd build; cmake -DADD_G3LOG_UNIT_TEST=ON .. 28 | 29 | // linux/osx alternative, simply run: ./scripts/buildAndRunTests.sh 30 | ``` 31 | 32 | _step 2: use one of these alternatives to run tests:_ 33 | 34 | - Cross-Platform: `ctest` 35 | - or `ctest -V` for verbose output 36 | - Linux: `make test` 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Scanning Update**: [![ci/action CodeQL](https://github.com/KjellKod/g3log/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/KjellKod/g3log/actions/workflows/codeql-analysis.yml) 2 | 3 | 4 | # Contents 5 | [**introduction**](docs/index.md) | [detailed information](docs/g3log_usage.md) | [Configure & Build](docs/building.md) | [API description](docs/API.md) | [Custom log formatting](docs/API_custom_formatting.md) 6 | 7 | 8 | # Welcome to g3log 9 | ### Use [kjellkod.github.io/g3log/](https://kjellkod.github.io/g3log/) for best reading / searching / navigating of g3log's documentation 10 | 11 | G3log is an asynchronous logger with three main features: 12 | 1. Intuitive `LOG(...)` API 13 | 2. `Design-by-contract` `CHECK(...)` functionality 14 | 3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash. 15 | 16 | The super quick introduction to g3log can be seen in the steps 1 - 9 below. 17 | 18 | For more in-depth information please see the full usage description in [g3log_usage.md](docs/g3log_usage.md). If you want to understand better the internals of g3log, then plase look at the [API.md](docs/API.md) for both high-level and deep-dive insights. 19 | 20 | 21 | ## Experiment and try-out g3log in Github Codespaces 22 | ref: [codespaces.md](docs/codespaces.md) 23 | 24 | 25 | ## 1. Easy usage in files 26 | Avoid deep dependency injection complexity and instead get access to the logger as easy as: 27 | ``` 28 | #include 29 | ``` 30 | 31 | 32 | ## 2. Access to streaming and print_f log call syntax 33 | Both streaming syntax `LOG` and print_f `LOGF` syntax are available: 34 | 35 | ``` 36 | LOGF(INFO, "Hi log %d", 123); 37 | LOG(INF) << "Hi log " << 123; 38 | 39 | ``` 40 | 41 | ## 3. Conditional logging 42 | 43 | ``` 44 | LOG_IF(INFO, (1 < 2)) << "If true this message will be logged"; 45 | LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message"); 46 | ``` 47 | 48 | ## 4. Design-by-contract framework 49 | ``` 50 | CHECK(less != more); // not fatal 51 | CHECK_F(less > more, "CHECK(false) will trigger a fatal message") 52 | ``` 53 | 54 | ## 5. Handling of fatal 55 | By default g3log will capture fatal events such as `LOG(FATAL)`, `CHECK(false)` and otherwise fatal signals such as: 56 | ``` 57 | SIGABRT 58 | SIGFPE 59 | SIGILL 60 | SIGSEGV 61 | SIGTERM 62 | ``` 63 | 64 | When a fatal event happens the not-yet written log activity will be flushed to the logging sinks. Only when all logging activity up to the point of the fatal event has happend, will g3log allow the fatal event to proceed and exit the process. 65 | 66 | If `object` symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink. 67 | 68 | #### 5b. Overriding and customization of fatal event handling 69 | For overriding fatal error handling to use your own, or to add code `hooks` that you want to execute please see the [API.md](docs/API.md) doc. 70 | 71 | ## 6. Default and Custom logging levels 72 | The default logging levels are `DEBUG`, `INFO`, `WARNING` and `FATAL`. You can define your own logging levels or completely replace the logging levels. Ref: [API.md](docs/API.md) 73 | 74 | 75 | ### 7. Log filtering 76 | Log filtering is handled in g3log if dynamic logging levels are enabled 77 | in the configuration. See the [API.md](docs/API.md) for information. Log filtering can also be handled through the sink as can be seen in [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks) 78 | 79 | 80 | ## 8. 3rd party and custom logging sinks 81 | The default logging sink has no external 3rd party dependencies. For more logging sinks please see [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks) 82 | 83 | - log rotate 84 | - log to syslog 85 | - log to colored terminal output 86 | - log rotate with filter 87 | 88 | See the [API.md](docs/API.md) for more information about the simple steps to creating your own logging sink. 89 | 90 | 91 | ## 9. Log instantiation 92 | With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this: 93 | 94 | ```cpp 95 | const std::string directory = "./"; 96 | const std::string name = "TestLogFile"; 97 | auto worker = g3::LogWorker::createLogWorker(); 98 | auto handle = worker->addDefaultLogger(name, directory); 99 | ``` 100 | The resulting filename would be something like: 101 | ``` 102 | ./TestLogFile.g3log.20160217-001406.log 103 | ``` 104 | 105 | ## Performance 106 | G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small "worst case latency" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my 2010 laptop the average call, when doing extreme performance testing, will be about ~2 us. 107 | 108 | The worst case latency is kept stable with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest. 109 | You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/ 110 | 111 | 112 | ## Continuous Integration 113 | The g3log repository is evaluating github actions for executing test coverage, installation and document generation. In case you want to look into change any of these setups the following files are the ones of interest. 114 | See `Actions` for matrix (ubuntu, macos, windows) testing as well as other actions for doc publishing. 115 | 116 | 117 | ## Feedback 118 | If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and it is always nice to hear when and how someone is using it. 119 | 120 | If you have ANY questions or problems please do not hesitate in contacting me at 121 | `Hedstrom @ Kjellod. cc` 122 | 123 | # Say Thanks 124 | This logger is available for free and all of its source code is public domain. A great way of saying thanks is to send a donation. It would go a long way not only to show your support but also to boost continued development. 125 | 126 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/g3log/25) 127 | 128 | * $5 for a cup of coffee 129 | * $25 for a late evening coding with takeout 130 | 131 | 132 | Cheers 133 | 134 | Kjell *(a.k.a. KjellKod)* 135 | 136 | [**introduction**](docs/index.md) | [detailed information](docs/g3log_usage.md) | [Configure & Build](docs/building.md) | [API description](docs/API.md) | [Custom log formatting](docs/API_custom_formatting.md) 137 | -------------------------------------------------------------------------------- /cmake/g3logConfig.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindG3log 3 | # ------- 4 | # 5 | # Find libg3log, G3log is an asynchronous, "crash safe", logger that is easy to use with default logging sinks or you can add your own. 6 | # 7 | # This defines the cmake import target "g3log" you can use like this 8 | #``` 9 | # target_link_libraries(YourTarget PUBLIC g3log) 10 | #``` 11 | # Variables and features 12 | # ---------------------- 13 | # * ``G3LOG`` -- if this environment variable is set, it'll be used as a hint as to where the g3log files are. 14 | # * ``G3LOG_INCLUDE_DIRS`` -- raw cmake variable with include path 15 | # * ``G3LOG_LIBRARIES`` -- raw cmake variable with library link line 16 | # * ``G3LOG_FOUND`` -- check if the lib was found without using the newer ``if(TARGET g3log)...`` 17 | 18 | include(FindPackageHandleStandardArgs) 19 | include(SelectLibraryConfigurations) 20 | 21 | @PACKAGE_INIT@ 22 | 23 | find_package(Threads REQUIRED) 24 | 25 | if (NOT TARGET g3log) 26 | include("${CMAKE_CURRENT_LIST_DIR}/g3logTargets.cmake") 27 | 28 | get_target_property(G3LOG_INCLUDE_DIR g3log INTERFACE_INCLUDE_DIRECTORIES) 29 | get_target_property(G3LOG_LIBRARY_DEBUG g3log IMPORTED_IMPLIB_DEBUG) 30 | 31 | if (G3LOG_LIBRARY_DEBUG MATCHES ".*-NOTFOUND") 32 | get_target_property(G3LOG_LIBRARY_DEBUG g3log IMPORTED_LOCATION_DEBUG) 33 | endif () 34 | 35 | get_target_property(G3LOG_LIBRARY_RELEASE g3log IMPORTED_IMPLIB_RELEASE) 36 | if (G3LOG_LIBRARY_RELEASE MATCHES ".*-NOTFOUND") 37 | get_target_property(G3LOG_LIBRARY_RELEASE g3log IMPORTED_LOCATION_RELEASE) 38 | endif () 39 | 40 | select_library_configurations(G3LOG) 41 | 42 | if (G3LOG_LIBRARY) 43 | list(APPEND G3LOG_LIBRARY Threads::Threads) 44 | if (WIN32) 45 | list(APPEND G3LOG_LIBRARY DbgHelp.lib) 46 | endif () 47 | endif () 48 | endif () 49 | 50 | find_package_handle_standard_args(g3log REQUIRED_VARS G3LOG_INCLUDE_DIR G3LOG_LIBRARY) 51 | mark_as_advanced(G3LOG_INCLUDE_DIR G3LOG_LIBRARY) 52 | set(G3LOG_INCLUDE_DIRS ${G3LOG_INCLUDE_DIR}) 53 | set(G3LOG_LIBRARIES ${G3LOG_LIBRARY}) 54 | -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KjellKod/g3log/cec84ba5926e29eaf10b649e02a82639104eb66c/docs/.DS_Store -------------------------------------------------------------------------------- /docs/.ciignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/API_custom_formatting.md: -------------------------------------------------------------------------------- 1 | [introduction](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [**Custom log formatting**](API_custom_formatting.md) 2 | 3 | 4 | # Custom LOG formatting 5 | ### Overriding the Default File Sink's file header 6 | The default file header can be customized in the default file sink in calling 7 | ```cpp 8 | FileSink::overrideLogHeader(std::string); 9 | ``` 10 | 11 | 12 | ### Overriding the Default FileSink's log formatting 13 | The default log formatting is defined in `LogMessage.hpp` 14 | ```cpp 15 | static std::string DefaultLogDetailsToString(const LogMessage& msg); 16 | ``` 17 | 18 | ### Adding thread ID to the log formatting 19 | An "all details" log formatting function is also defined - this one also adds the "calling thread's ID" 20 | ```cpp 21 | static std::string FullLogDetailsToString(const LogMessage& msg); 22 | ``` 23 | 24 | ### Override default sink log formatting 25 | For convenience the *Default* sink has a function 26 | for doing exactly this 27 | ```cpp 28 | void overrideLogDetails(LogMessage::LogDetailsFunc func); 29 | ``` 30 | 31 | 32 | Example code for replacing the default log formatting for "full details" formatting (it adds thread ID) 33 | 34 | ```cpp 35 | auto worker = g3::LogWorker::createLogWorker(); 36 | auto handle= worker->addDefaultLogger(argv[0], path_to_log_file); 37 | g3::initializeLogging(worker.get()); 38 | handle->call(&g3::FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString); 39 | ``` 40 | 41 | See [test_message.cpp](https://github.com/KjellKod/g3log/tree/master/test_unit/test_message.cpp) for details and testing 42 | 43 | 44 | Example code for overloading the formatting of a custom sink. The log formatting function will be passed into the 45 | `LogMessage::toString(...)` this will override the default log formatting 46 | 47 | Example 48 | ```cpp 49 | namespace { 50 | std::string MyCustomFormatting(const LogMessage& msg) { 51 | ... how you want it ... 52 | } 53 | } 54 | 55 | void MyCustomSink::ReceiveLogEntry(LogMessageMover message) { 56 | std::string formatted = message.get().toString(&MyCustomFormatting) << std::flush; 57 | } 58 | ... 59 | ... 60 | auto worker = g3::LogWorker::createLogWorker(); 61 | auto sinkHandle = worker->addSink(std::make_unique(), 62 | &MyCustomSink::ReceiveLogMessage); 63 | // ReceiveLogMessage(...) will used the custom formatting function "MyCustomFormatting(...) 64 | 65 | ``` 66 | 67 | 68 | [introduction](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [**Custom log formatting**](API_custom_formatting.md) 69 | 70 | -------------------------------------------------------------------------------- /docs/codespaces.md: -------------------------------------------------------------------------------- 1 | # Codespaces 2 | 3 | You can experiment with codespaces and g3log. 4 | 5 | ## Learn about Github Codespaces 6 | For an introduction to codespaces you can check out [example c++ codespace](https://github.com/microsoft/vscode-remote-try-cpp/tree/main) and [using-github-codespaces-with-github-cli](https://docs.github.com/en/codespaces/developing-in-a-codespace/using-github-codespaces-with-github-cli) 7 | 8 | 9 | # Commandline codespaces Quick Reference 10 | 11 | 1. List all your codespaces `gh codespace list` 12 | 2. Create a new codespace `gh codespace create -r OWNER/REPO_NAME [-b BRANCH]`. Ref [docs/github: Creating a codespace for a repository](https://docs.github.com/en/codespaces/developing-in-a-codespace/creating-a-codespace-for-a-repository) 13 | 3. View codebase details `gh codespace view` 14 | 4. Stop `gh codespace stop -c CODESPACE-NAME` 15 | 5. Delete `gh codespace delete -c CODESPACE-NAME` 16 | 6. Rebuild `gh codespace rebuild` 17 | 7. Rename `gh codespace edit -c CODESPACE-NAME -d DISPLAY-NAME` 18 | 8. SSH into REMOTE codespace `gh codespace ssh -c CODESPACE-NAME` 19 | 9. Open a remote codespace in CVisual Studio `gh codespace code -c CODESPACE-NAME` (ref: [github:doc cs studio](https://docs.github.com/en/codespaces/developing-in-a-codespace/using-github-codespaces-in-visual-studio-code)) 20 | 10. Copy local file to/from codespace `gh codespace cp [-r] SOURCE(S) DESTINATION`. Example: Copy a file from the local machine to the $HOME directory of a codespace: `gh codespace cp myfile.txt remote:`. Example Copy a file from a codespace to the current directory on the local machine: `gh codespace cp remote:myfile.txt .` (more information available [here](https://cli.github.com/manual/gh_codespace_cp)) 21 | 22 | 23 | # Try g3log in a local dev container. 24 | 25 | Please note that this will build g3log as if it's on a Debian Linux platform. 26 | 27 | 1. Clone this repository to your local filesystem. 28 | 2. Start Visual Studio Code. Press F1 and select the `Dev Containers: Open Folder in Container...` command. 29 | 3. Select the cloned copy of this g3log folder, wait for the container to start, and try things out! You should have debian C++ environment at hand. 30 | 31 | ### Example cmake configuration and build 32 | ``` 33 | Open a terminal in Visual Studio Code 34 | mkdir debianbuild 35 | cd debianbuild 36 | cmake -DADD_G3LOG_UNIT_TEST=ON -DADD_G3LOG_BENCH_PERFORMANCE=ON .. 37 | make -j 38 | ``` 39 | 40 | ### Example runs 41 | 1. performance test in the container `./g3log-performance-threaded_mean 4` 42 | 2. unit tests `ctest -v` 43 | 3. Try a fatal example with dumped stack trace `./g3log-FATAL-contract` 44 | 45 | 46 | ### Example with Debugging. 47 | Without any need to set up environment on your local machine you can also use Codespaces to debug examples, unit tests etc of g3log. 48 | The pesky thing with VSCode, especially with cmake is to set up the launh.json. 49 | It's a little bit easier if you open a VSCode terminal and do the cmake configuration and build there. Then the `launch.json` only needs to 50 | contain information about the pecific executable. 51 | 52 | Here we try out the `g3log-FATAL-contract` after cmake configure with `-DCMAKE_BUILD_TYPE=Debug` 53 | ``` 54 | { 55 | // Use IntelliSense to learn about possible attributes. 56 | // Hover to view descriptions of existing attributes. 57 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 58 | // Remember to build the specific part of cmake with 59 | // "cmake -DCMAKE_BUILD_TYPE=Debug" if you want to be able to debug it. 60 | "version": "0.2.0", 61 | "configurations": [ 62 | { 63 | "name": "(gdb) Start", 64 | "type": "cppdbg", 65 | "request": "launch", 66 | "program": "${workspaceFolder}/build/g3log-FATAL-contract", 67 | "MIMode": "gdb", 68 | "cwd": "${workspaceFolder}/build" 69 | "setupCommands": [ 70 | { 71 | "description": "Enable pretty-printing for gdb", 72 | "text": "-enable-pretty-printing", 73 | "ignoreFailures": true 74 | }, 75 | { 76 | "description": "Set Disassembly Flavor to Intel", 77 | "text": "-gdb-set disassembly-flavor intel", 78 | "ignoreFailures": true 79 | } 80 | ] 81 | } 82 | 83 | ] 84 | } 85 | ``` -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | 2 | # Information for contributing to g3log 3 | 4 | ## License 5 | [LICENSE](https://github.com/KjellKod/g3log/blob/master/LICENSE) 6 | 7 | 8 | ## Contributing 9 | [CONTRIBUTING.md](https://github.com/KjellKod/g3log/blob/master/CONTRIBUTING.md) 10 | 11 | ### Code of conduct 12 | [CODE_OF_CONDUCT.md](https://github.com/KjellKod/g3log/blob/master/CODE_OF_CONDUCT.md) 13 | 14 | ### Pull request template 15 | [PULL_REQUEST_TEMPLATE.md](https://github.com/KjellKod/g3log/blob/master/PULL_REQUEST_TEMPLATE.md) -------------------------------------------------------------------------------- /docs/event_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KjellKod/g3log/cec84ba5926e29eaf10b649e02a82639104eb66c/docs/event_sequence.png -------------------------------------------------------------------------------- /docs/fatal_log_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KjellKod/g3log/cec84ba5926e29eaf10b649e02a82639104eb66c/docs/fatal_log_sequence.png -------------------------------------------------------------------------------- /docs/fatal_signal_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KjellKod/g3log/cec84ba5926e29eaf10b649e02a82639104eb66c/docs/fatal_signal_sequence.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | [**introduction**](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md) 2 | 3 | 4 | # Welcome to g3log 5 | 6 | G3log is an asynchronous logger with three main features: 7 | 1. Intuitive `LOG(...)` API 8 | 2. `Design-by-contract` `CHECK(...)` functionality 9 | 3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash 10 | 11 | The super quick introduction to g3log can be seen in the steps 1 - 9 below. 12 | 13 | For more in-depth information please see the full usage description in [g3log_usage.md](g3log_usage.md). The internal API for more advanced integration with g3log can be accessed in [API.md](API.md) 14 | 15 | ## 1. Easy usage in files 16 | Avoid deep dependency injection complexity and instead get access to the logger as easy as 17 | ``` 18 | #include 19 | ``` 20 | 21 | 22 | ## 2. Access to streaming and print_f log call syntax 23 | Both streaming syntax `LOG` and print_f `LOGF` syntax are available. 24 | 25 | ``` 26 | LOGF(INFO, "Hi log %d", 123); 27 | LOG(INF) << "Hi log " << 123; 28 | 29 | ``` 30 | 31 | ## 3. Conditional logging 32 | 33 | ``` 34 | LOG_IF(INFO, (1 < 2)) << "If true this message will be logged"; 35 | LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message"); 36 | ``` 37 | 38 | ## 4. Design-by-contract framework 39 | ``` 40 | CHECK(less != more); // not fatal 41 | CHECK_F(less > more, "CHECK(false) will trigger a fatal message") 42 | ``` 43 | 44 | ## 5. Handling of fatal 45 | By default g3log will capture fatal events such as `LOG(FATAL)`, `CHECK(false)` and otherwise fatal signals such as: 46 | ``` 47 | SIGABRT 48 | SIGFPE 49 | SIGILL 50 | SIGSEGV 51 | SIGTERM 52 | ``` 53 | 54 | When a fatal event happens the not-yet written log activity will be flushed to the logging sinks. Only when all logging activity up to the point of the fatal event has happend, will g3log allow the fatal event to proceed and exit the process. 55 | 56 | If `object` symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink. 57 | 58 | #### 5b. Overriding and customization of fatal event handling 59 | For overriding fatal error handling to use your own, or to add code `hooks` that you want to execute please see the [API.md](API.md) doc. 60 | 61 | ## 6. Default and Custom logging levels 62 | The default logging levels are `DEBUG`, `INFO`, `WARNING` and `FATAL`. You can define your own logging levels or completely replace the logging levels. Ref: [API.md](API.md) 63 | 64 | 65 | ### 7. Log filtering 66 | Log filtering is handled in g3log if dynamic logging levels are enabled 67 | in the configuration. See the [API.md](API.md) for information. Log filtering can also be handled through the sink as can be seen in [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks) 68 | 69 | 70 | ## 8. 3rd party and custom logging sinks 71 | The default logging sink has no external 3rd party dependencies. For more logging sinks please see [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks) 72 | 73 | - log rotate 74 | - log to syslog 75 | - log to colored terminal output 76 | - log rotate with filter 77 | 78 | See the [API.md](API.md) for more information about the simple steps to creating your own logging sink. 79 | 80 | 81 | ## 9. Log instantiation 82 | With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this: 83 | 84 | ```cpp 85 | const std::string directory = "./"; 86 | const std::string name = "TestLogFile"; 87 | auto worker = g3::LogWorker::createLogWorker(); 88 | auto handle = worker->addDefaultLogger(name, directory); 89 | ``` 90 | The resulting filename would be something like: 91 | ``` 92 | ./TestLogFile.g3log.20160217-001406.log 93 | ``` 94 | 95 | ## Performance 96 | G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small "worst case latency" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my 2010 laptop the average call, when doing extreme performance testing, will be about ~2 us. 97 | 98 | The worst case latency is kept stable with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest. 99 | You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/ 100 | 101 | 102 | ## Continuos Integration 103 | The g3log repository is evaluating both github actions and CircleCI for executing test coverage, installation and document generation. For windows the repo is still relying on appveyor. In case you want to look into change any of these setups the following files are the ones of interest. 104 | ``` 105 | 1. appveyor --> g3log/appveyor.yml 106 | 2. circleCI --> g3log/.circleci/config.yml 107 | 3. github actions --> g3log/.github/workflows/*.yml 108 | 109 | 110 | ``` 111 | 112 | 113 | ## Feedback 114 | If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and it is always nice to hear when and how someone is using it. 115 | 116 | If you have ANY questions or problems please do not hesitate in contacting me at 117 | `Hedstrom @ Kjellod. cc` 118 | 119 | # Say Thanks 120 | This logger is available for free and all of its source code is public domain. A great way of saying thanks is to send a donation. It would go a long way not only to show your support but also to boost continued development. 121 | 122 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/g3log/25) 123 | 124 | * $5 for a cup of coffee 125 | * $25 for a late evening coding with takeout 126 | 127 | 128 | Cheers 129 | 130 | Kjell *(a.k.a. KjellKod)* 131 | 132 | [**introduction**](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md) 133 | -------------------------------------------------------------------------------- /docs/log_sequence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KjellKod/g3log/cec84ba5926e29eaf10b649e02a82639104eb66c/docs/log_sequence.png -------------------------------------------------------------------------------- /example/Example.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | 13 | 14 | 15 | # ============================================================== 16 | # -DUSE_SIMPLE_EXAMPLE=OFF : to turn off the fatal examples 17 | # 18 | # 19 | # Leaving it to ON will create 20 | # g3log-FATAL-sigsegv 21 | # g3log-FATAL-contract 22 | # 23 | # ============================================================== 24 | 25 | IF (MSVC OR MINGW) 26 | set(EXAMPLE_PLATFORM_LINK_LIBRIES dbghelp) 27 | ENDIF() 28 | 29 | set(DIR_EXAMPLE ${g3log_SOURCE_DIR}/example) 30 | option (ADD_FATAL_EXAMPLE "Fatal (fatal-crashes/contract) examples " ON) 31 | 32 | 33 | IF (ADD_FATAL_EXAMPLE) 34 | message( STATUS "-DADD_FATAL_EXAMPLE=ON" ) 35 | message( STATUS "\t\t[contract][sigsegv][fatal choice] are examples of when g3log comes in handy\n" ) 36 | include_directories (${DIR_EXAMPLE}) 37 | add_executable(g3log-FATAL-contract ${DIR_EXAMPLE}/main_contract.cpp) 38 | add_executable(g3log-FATAL-sigsegv ${DIR_EXAMPLE}/main_sigsegv.cpp) 39 | add_executable(g3log-FATAL-choice ${DIR_EXAMPLE}/main_fatal_choice.cpp) 40 | 41 | target_link_libraries(g3log-FATAL-contract ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES}) 42 | target_link_libraries(g3log-FATAL-sigsegv ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES}) 43 | target_link_libraries(g3log-FATAL-choice ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES}) 44 | ELSE() 45 | message( STATUS "-DADD_SIMPLE_EXAMPLE=OFF" ) 46 | ENDIF (ADD_FATAL_EXAMPLE) 47 | -------------------------------------------------------------------------------- /example/main_contract.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace { 16 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 17 | const std::string path_to_log_file = "./"; 18 | #else 19 | const std::string path_to_log_file = "/tmp/"; 20 | #endif 21 | } // namespace 22 | 23 | namespace example_fatal { 24 | void killWithContractIfNonEqual(int first, int second) { 25 | CHECK(first == second) << "Test to see if contract works: onetwothree: " << 123 << ". This should be at the end of the log, and will exit this example"; 26 | } 27 | } // namespace example_fatal 28 | 29 | int main(int argc, char** argv) { 30 | double pi_d = 3.1415926535897932384626433832795; 31 | float pi_f = 3.1415926535897932384626433832795f; 32 | 33 | auto worker = g3::LogWorker::createLogWorker(); 34 | auto handle = worker->addDefaultLogger(argv[0], path_to_log_file); 35 | g3::initializeLogging(worker.get()); 36 | std::future log_file_name = handle->call(&g3::FileSink::fileName); 37 | 38 | // Exmple of overriding the default formatting of log entry 39 | auto changeFormatting = handle->call(&g3::FileSink::overrideLogDetails, g3::LogMessage::FullLogDetailsToString); 40 | const std::string newHeader = "\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL THREAD_ID FILE->FUNCTION:LINE] message\n\t\t(uuu*: microseconds fractions of the seconds value)\n\n"; 41 | // example of ovrriding the default formatting of header 42 | auto changeHeader = handle->call(&g3::FileSink::overrideLogHeader, newHeader); 43 | 44 | changeFormatting.wait(); 45 | changeHeader.wait(); 46 | 47 | std::cout << "* This is an example of g3log. It WILL exit by a failed CHECK(...)" << std::endl; 48 | std::cout << "* that acts as a FATAL trigger. Please see the generated log and " << std::endl; 49 | std::cout << "* compare to the code at:\n* \t g3log/test_example/main_contract.cpp" << std::endl; 50 | std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" 51 | << std::endl; 52 | 53 | LOGF(INFO, "Hi log %d", 123); 54 | LOG(INFO) << "Test SLOG INFO"; 55 | LOG(G3LOG_DEBUG) << "Test SLOG DEBUG"; 56 | LOG(INFO) << "one: " << 1; 57 | LOG(INFO) << "two: " << 2; 58 | LOG(INFO) << "one and two: " << 1 << " and " << 2; 59 | LOG(G3LOG_DEBUG) << "float 2.14: " << 1000 / 2.14f; 60 | LOG(G3LOG_DEBUG) << "pi double: " << pi_d; 61 | LOG(G3LOG_DEBUG) << "pi float: " << pi_f; 62 | LOG(G3LOG_DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f; 63 | LOGF(INFO, "pi float printf:%f", pi_f); 64 | 65 | // FATAL SECTION 66 | int smaller = 1; 67 | int larger = 2; 68 | example_fatal::killWithContractIfNonEqual(smaller, larger); 69 | } 70 | -------------------------------------------------------------------------------- /example/main_sigsegv.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | namespace { 17 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 18 | const std::string path_to_log_file = "./"; 19 | #else 20 | const std::string path_to_log_file = "/tmp/"; 21 | #endif 22 | } // namespace 23 | 24 | namespace example_fatal { 25 | // on Ubunti this caused get a compiler warning with gcc4.6 26 | // from gcc 4.7.2 (at least) it causes a crash (as expected) 27 | // On windows it'll probably crash too. 28 | void tryToKillWithIllegalPrintout() { 29 | std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl 30 | << std::flush; 31 | std::cout << "************************************************************\n\n" 32 | << std::endl 33 | << std::flush; 34 | std::this_thread::sleep_for(std::chrono::seconds(1)); 35 | const std::string logging = "logging"; 36 | LOGF(G3LOG_DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str()); 37 | } 38 | 39 | // The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a segmentation 40 | // fault as expected by the illegal printf-format usage. just in case we exit by zero division" 41 | void killByZeroDivision(int value) { 42 | int zero = 0; // trying to fool the compiler to automatically warn 43 | LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero; 44 | } 45 | 46 | void tryToKillWithAccessingIllegalPointer(std::unique_ptr badStringPtr) { 47 | auto badPtr = std::move(badStringPtr); 48 | LOG(INFO) << "Function calls through a nullptr object will trigger segmentation fault"; 49 | badStringPtr->append("crashing"); 50 | } 51 | 52 | } // namespace example_fatal 53 | 54 | int main(int argc, char** argv) { 55 | double pi_d = 3.1415926535897932384626433832795; 56 | float pi_f = 3.1415926535897932384626433832795f; 57 | 58 | using namespace g3; 59 | 60 | std::unique_ptr logworker{LogWorker::createLogWorker()}; 61 | auto sinkHandle = logworker->addSink(std::make_unique(argv[0], path_to_log_file), 62 | &FileSink::fileWrite); 63 | 64 | initializeLogging(logworker.get()); 65 | std::future log_file_name = sinkHandle->call(&FileSink::fileName); 66 | std::cout << "* This is an example of g3log. It WILL exit by a FATAL trigger" << std::endl; 67 | std::cout << "* Please see the generated log and compare to the code at" << std::endl; 68 | std::cout << "* g3log/test_example/main.cpp" << std::endl; 69 | std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" 70 | << std::endl; 71 | 72 | LOGF(INFO, "Hi log %d", 123); 73 | LOG(INFO) << "Test SLOG INFO"; 74 | LOG(G3LOG_DEBUG) << "Test SLOG DEBUG"; 75 | LOG(INFO) << "one: " << 1; 76 | LOG(INFO) << "two: " << 2; 77 | LOG(INFO) << "one and two: " << 1 << " and " << 2; 78 | LOG(G3LOG_DEBUG) << "float 2.14: " << 1000 / 2.14f; 79 | LOG(G3LOG_DEBUG) << "pi double: " << pi_d; 80 | LOG(G3LOG_DEBUG) << "pi float: " << pi_f; 81 | LOG(G3LOG_DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f; 82 | LOGF(INFO, "pi float printf:%f", pi_f); 83 | 84 | // 85 | // START: LOG Entris that were in the CodeProject article 86 | // 87 | //LOG(UNKNOWN_LEVEL) << "This log attempt will cause a compiler error"; 88 | 89 | LOG(INFO) << "Simple to use with streaming syntax, easy as abc or " << 123; 90 | LOGF(WARNING, "Printf-style syntax is also %s", "available"); 91 | LOG_IF(INFO, (1 < 2)) << "If true this text will be logged"; 92 | LOGF_IF(INFO, (1 < 2), "if %d<%d : then this text will be logged", 1, 2); 93 | LOG_IF(FATAL, (2 > 3)) << "This message should NOT throw"; 94 | LOGF(G3LOG_DEBUG, "This API is popular with some %s", "programmers"); 95 | LOGF_IF(G3LOG_DEBUG, (1 < 2), "If true, then this %s will be logged", "message"); 96 | 97 | // OK --- on Ubunti this caused get a compiler warning with gcc4.6 98 | // from gcc 4.7.2 (at least) it causes a crash (as expected) 99 | // On windows itll probably crash 100 | example_fatal::tryToKillWithIllegalPrintout(); 101 | 102 | // try 2 103 | std::unique_ptr badStringPtr; 104 | example_fatal::tryToKillWithAccessingIllegalPointer(std::move(badStringPtr)); 105 | 106 | // what happened? OK. let us just exit with SIGFPE 107 | int value = 1; // system dependent but it SHOULD never reach this line 108 | example_fatal::killByZeroDivision(value); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /iOSBuild.cmake: -------------------------------------------------------------------------------- 1 | if(IOS_PLATFORM) 2 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) 3 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) 4 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) 5 | endif(IOS_PLATFORM) 6 | 7 | 8 | if(G3_IOS_LIB) 9 | if (CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET) 10 | set (ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET} ${CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET}) 11 | endif(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET) 12 | 13 | set(TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/iOS.cmake") 14 | 15 | set(SIM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.i386" CACHE INTERNAL "") 16 | set(SIM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") 17 | 18 | set(SIM64_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.x86_64" CACHE INTERNAL "") 19 | set(SIM64_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") 20 | 21 | set(ARM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.arm" CACHE INTERNAL "") 22 | set(ARM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") 23 | 24 | file(MAKE_DIRECTORY ${SIM_BINARY_DIR}) 25 | execute_process(WORKING_DIRECTORY ${SIM_BINARY_DIR} 26 | COMMAND ${CMAKE_COMMAND} 27 | -GXcode 28 | -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} 29 | -DIOS_PLATFORM=SIMULATOR 30 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 31 | -DADD_FATAL_EXAMPLE=OFF 32 | -DADD_G3LOG_BENCH_PERFORMANCE=OFF 33 | -DADD_G3LOG_UNIT_TEST=OFF 34 | -DG3_SHARED_LIB=OFF 35 | -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON 36 | -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE} 37 | -DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING} 38 | "${SIM_SOURCE_DIR}" 39 | ) 40 | 41 | file(MAKE_DIRECTORY ${SIM64_BINARY_DIR}) 42 | execute_process(WORKING_DIRECTORY ${SIM64_BINARY_DIR} 43 | COMMAND ${CMAKE_COMMAND} 44 | -GXcode 45 | -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} 46 | -DIOS_PLATFORM=SIMULATOR64 47 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 48 | -DADD_FATAL_EXAMPLE=OFF 49 | -DG3_SHARED_LIB=OFF 50 | -DADD_G3LOG_BENCH_PERFORMANCE=OFF 51 | -DADD_G3LOG_UNIT_TEST=OFF 52 | -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON 53 | -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE} 54 | -DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING} 55 | "${SIM64_SOURCE_DIR}" 56 | ) 57 | 58 | file(MAKE_DIRECTORY ${ARM_BINARY_DIR}) 59 | execute_process(WORKING_DIRECTORY ${ARM_BINARY_DIR} 60 | COMMAND ${CMAKE_COMMAND} 61 | -GXcode 62 | -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} 63 | -DIOS_PLATFORM=OS 64 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 65 | -DADD_FATAL_EXAMPLE=OFF 66 | -DG3_SHARED_LIB=OFF 67 | -DADD_G3LOG_BENCH_PERFORMANCE=OFF 68 | -DADD_G3LOG_UNIT_TEST=OFF 69 | -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON 70 | -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE} 71 | -DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING} 72 | "${ARM_SOURCE_DIR}" 73 | ) 74 | 75 | ## Simulator i386 version 76 | add_custom_target(sim 77 | COMMAND ${CMAKE_COMMAND} 78 | --build ${SIM_BINARY_DIR} 79 | --config ${CMAKE_BUILD_TYPE} 80 | COMMENT "Building for i386 (simulator)" 81 | VERBATIM 82 | ) 83 | 84 | ## Simulator x86_64 version 85 | add_custom_target(sim64 86 | COMMAND ${CMAKE_COMMAND} 87 | --build ${SIM64_BINARY_DIR} 88 | --config ${CMAKE_BUILD_TYPE} 89 | COMMENT "Building for x86_64 (simulator)" 90 | VERBATIM 91 | ) 92 | 93 | ## ARM version 94 | add_custom_target(arm 95 | COMMAND ${CMAKE_COMMAND} 96 | --build ${ARM_BINARY_DIR} 97 | --config ${CMAKE_BUILD_TYPE} 98 | COMMENT "Building for armv7, armv7s, arm64, arm64e" 99 | VERBATIM 100 | ) 101 | 102 | set(LIB_G3 libg3log.a) 103 | add_custom_command( 104 | OUTPUT ${LIB_G3} 105 | COMMAND lipo -create 106 | -output "${CMAKE_CURRENT_BINARY_DIR}/${LIB_G3}" 107 | ${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3} 108 | ${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3} 109 | ${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3} 110 | DEPENDS 111 | sim 112 | sim64 113 | arm 114 | "${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}" 115 | "${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}" 116 | "${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}" 117 | VERBATIM 118 | ) 119 | add_custom_target(g3log ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${LIB_G3}) 120 | endif(G3_IOS_LIB) 121 | 122 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: G3log, an asynchronous "crash-safe" logger 2 | site_author: 'Kjell Hedstrom' 3 | site_url: https://kjellkod.github.io/g3log/ 4 | theme: 5 | name: material 6 | 7 | docs_dir: docs/ 8 | 9 | nav: 10 | - Introduction to G3log: index.md 11 | - G3log usage: g3log.md 12 | - API description: API.md 13 | - API for custom log formatting: API_custom_formatting.md 14 | - Configure, Build, Package, Install and Test: building.md 15 | - License and contribution: contributing.md 16 | 17 | 18 | -------------------------------------------------------------------------------- /scripts/.travis-bootstrap-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ev 4 | set -x 5 | 6 | apt-get update -y 7 | apt-get install -y apt-utils | true 8 | apt-get install -y software-properties-common | true 9 | apt-get install -y python-software-properties 10 | apt-get update -y 11 | add-apt-repository -y ppa:jonathonf/gcc 12 | apt-get update -y 13 | apt-get install -y cmake software-properties-common git make 14 | apt-get install -y gcc-7 g++-7 15 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 90 16 | update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90 17 | apt-get install -y unzip zlib1g-dev 18 | apt-get install -y libboost-all-dev -------------------------------------------------------------------------------- /scripts/buildAndRunTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ev 4 | set -x 5 | 6 | 7 | mkdir -p build_travis 8 | cd build_travis 9 | cmake -DADD_G3LOG_BENCH_PERFORMANCE=ON -DPRETTY_FUNCTION=ON -DADD_G3LOG_UNIT_TEST=ON -DCMAKE_INSTALL_PREFIX=./install -DCPACK_PACKAGING_INSTALL_PREFIX=/opt/g3log .. 10 | cmake --build . --target install 11 | 12 | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 13 | cpack -G "ZIP" 14 | unzip g3log-*-Darwin.zip 15 | fi 16 | 17 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 18 | cpack -G "DEB;TGZ" 19 | tar zxvf g3log-*-Linux.tar.gz 20 | fi 21 | 22 | # LINUX OR OSX 23 | makeArg=`grep -c ^processor /proc/cpuinfo || sysctl -n hw.ncpu` 24 | 25 | make -j$makeArg 26 | ctest -V 27 | 28 | -------------------------------------------------------------------------------- /src/filesink.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/filesink.hpp" 10 | #include 11 | #include 12 | #include "filesinkhelper.ipp" 13 | 14 | namespace g3 { 15 | using namespace internal; 16 | 17 | FileSink::FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id, size_t write_to_log_every_x_message) : 18 | _log_details_func(&LogMessage::DefaultLogDetailsToString), 19 | _log_file_with_path(log_directory), 20 | _log_prefix_backup(log_prefix), 21 | _outptr(new std::ofstream), 22 | _header("\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message\n\n\t\t(uuu*: microseconds fractions of the seconds value)\n\n"), 23 | _firstEntry(true), 24 | _write_counter(0), 25 | _write_to_log_every_x_message(write_to_log_every_x_message) { 26 | _log_prefix_backup = prefixSanityFix(log_prefix); 27 | if (!isValidFilename(_log_prefix_backup)) { 28 | std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; 29 | abort(); 30 | } 31 | 32 | std::string file_name = createLogFileName(_log_prefix_backup, logger_id); 33 | _log_file_with_path = pathSanityFix(_log_file_with_path, file_name); 34 | _outptr = createLogFile(_log_file_with_path); 35 | 36 | if (!_outptr) { 37 | std::cerr << "Cannot write log file to location, attempting current directory" << std::endl; 38 | _log_file_with_path = "./" + file_name; 39 | _outptr = createLogFile(_log_file_with_path); 40 | } 41 | assert(_outptr && "cannot open log file at startup"); 42 | } 43 | 44 | FileSink::~FileSink() { 45 | std::string exit_msg = {"g3log g3FileSink shutdown at: "}; 46 | auto now = std::chrono::system_clock::now(); 47 | exit_msg.append(localtime_formatted(now, internal::time_formatted)).append("\n"); 48 | 49 | // write anything buffered up and then end with the exit msg 50 | filestream() << _write_buffer << exit_msg << std::flush; 51 | 52 | exit_msg.append("Log file at: [").append(_log_file_with_path).append("]\n"); 53 | std::cerr << exit_msg << std::flush; 54 | } 55 | 56 | // The actual log receiving function 57 | void FileSink::fileWrite(LogMessageMover message) { 58 | if (_firstEntry) { 59 | addLogFileHeader(); 60 | _firstEntry = false; 61 | } 62 | 63 | auto data = message.get().toString(_log_details_func); 64 | 65 | _write_buffer.append(data); 66 | if (++_write_counter % _write_to_log_every_x_message == 0) { 67 | filestream() << _write_buffer << std::flush; 68 | _write_buffer.clear(); 69 | } 70 | } 71 | 72 | std::string FileSink::changeLogFile(const std::string& directory, const std::string& logger_id) { 73 | 74 | auto now = std::chrono::system_clock::now(); 75 | auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted}); 76 | 77 | std::string file_name = createLogFileName(_log_prefix_backup, logger_id); 78 | std::string prospect_log = directory + file_name; 79 | std::unique_ptr log_stream = createLogFile(prospect_log); 80 | if (nullptr == log_stream) { 81 | filestream() << "\n" 82 | << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; 83 | return {}; // no success 84 | } 85 | 86 | addLogFileHeader(); 87 | std::ostringstream ss_change; 88 | ss_change << "\n\tChanging log file from : " << _log_file_with_path; 89 | ss_change << "\n\tto new location: " << prospect_log << "\n"; 90 | filestream() << now_formatted << ss_change.str(); 91 | ss_change.str(""); 92 | 93 | std::string old_log = _log_file_with_path; 94 | _log_file_with_path = std::move(prospect_log); 95 | _outptr = std::move(log_stream); 96 | ss_change << "\n\tNew log file. The previous log file was at: "; 97 | ss_change << old_log << "\n"; 98 | filestream() << now_formatted << ss_change.str(); 99 | return _log_file_with_path; 100 | } 101 | 102 | std::string FileSink::fileName() { 103 | return _log_file_with_path; 104 | } 105 | 106 | void FileSink::overrideLogDetails(LogMessage::LogDetailsFunc func) { 107 | _log_details_func = func; 108 | } 109 | 110 | void FileSink::overrideLogHeader(const std::string& change) { 111 | _header = change; 112 | } 113 | 114 | void FileSink::addLogFileHeader() { 115 | filestream() << header(_header); 116 | } 117 | } // namespace g3 118 | -------------------------------------------------------------------------------- /src/filesinkhelper.ipp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace g3 { 22 | namespace internal { 23 | static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S"; 24 | 25 | // check for filename validity - filename should not be part of PATH 26 | bool isValidFilename(const std::string &prefix_filename) { 27 | std::string illegal_characters("/,|<>:#$%{}[]\'\"^!?+* "); 28 | size_t pos = prefix_filename.find_first_of(illegal_characters, 0); 29 | if (pos != std::string::npos) { 30 | std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl; 31 | return false; 32 | } else if (prefix_filename.empty()) { 33 | std::cerr << "Empty filename prefix is not allowed" << std::endl; 34 | return false; 35 | } 36 | 37 | return true; 38 | } 39 | 40 | std::string prefixSanityFix(std::string prefix) { 41 | prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end()); 42 | prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); 43 | prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); 44 | prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); 45 | prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end()); 46 | if (!isValidFilename(prefix)) { 47 | return 48 | { 49 | }; 50 | } 51 | return prefix; 52 | } 53 | 54 | std::string pathSanityFix(std::string path, const std::string &file_name) { 55 | // Unify the delimeters,. maybe sketchy solution but it seems to work 56 | // on at least win7 + ubuntu. All bets are off for older windows 57 | std::replace(path.begin(), path.end(), '\\', '/'); 58 | 59 | // clean up in case of multiples 60 | auto contains_end = [&](std::string & in) -> bool { 61 | size_t size = in.size(); 62 | if (!size) return false; 63 | char end = in[size - 1]; 64 | return (end == '/' || end == ' '); 65 | }; 66 | 67 | while (contains_end(path)) { 68 | path.erase(path.size() - 1); 69 | } 70 | 71 | if (!path.empty()) { 72 | path.insert(path.end(), '/'); 73 | } 74 | 75 | path.insert(path.size(), file_name); 76 | return path; 77 | } 78 | 79 | std::string header(const std::string& headerFormat) { 80 | std::ostringstream ss_entry; 81 | // Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012 82 | auto now = std::chrono::system_clock::now(); 83 | ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n"; 84 | ss_entry << headerFormat; 85 | return ss_entry.str(); 86 | } 87 | 88 | std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) { 89 | std::stringstream oss_name; 90 | oss_name << verified_prefix << "."; 91 | if( !logger_id.empty() ) { 92 | oss_name << logger_id << "."; 93 | } 94 | auto now = std::chrono::system_clock::now(); 95 | oss_name << g3::localtime_formatted(now, file_name_time_formatted); 96 | oss_name << ".log"; 97 | return oss_name.str(); 98 | } 99 | 100 | bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) { 101 | std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream 102 | mode |= std::ios_base::trunc; 103 | outstream.open(complete_file_with_path, mode); 104 | if (!outstream.is_open()) { 105 | std::ostringstream ss_error; 106 | ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]"; 107 | ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate(); 108 | std::cerr << ss_error.str().c_str() << std::endl; 109 | outstream.close(); 110 | return false; 111 | } 112 | return true; 113 | } 114 | 115 | std::unique_ptr createLogFile(const std::string &file_with_full_path) { 116 | std::unique_ptr out(new std::ofstream); 117 | std::ofstream &stream(*(out.get())); 118 | bool success_with_open_file = openLogFile(file_with_full_path, stream); 119 | if (false == success_with_open_file) { 120 | out.reset(); 121 | } 122 | return out; 123 | } 124 | 125 | 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/g2log.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | // For convenience: If you don't want to do a recursive search and replace in your source code 12 | // for replacing g2log.hpp for g3log/g3log.hpp then you can choose to add this header file to your 13 | // code. It will get the necessary includes 14 | // 15 | // 16 | // Btw: replacing g2log for g3log include is easy on Linux 17 | // find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g' 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | -------------------------------------------------------------------------------- /src/g3log/active.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================ 8 | * 9 | * Example of a Active Object, using C++11 std::thread mechanisms to make it 10 | * safe for thread communication. 11 | * 12 | * This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x 13 | * and inspired from Herb Sutter's C++11 Active Object 14 | * http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads 15 | * 16 | * Last update 2013-12-19 by Kjell Hedstrom, 17 | * e-mail: hedstrom at kjellkod dot cc 18 | * linkedin: http://linkedin.com/se/kjellkod */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include "g3log/shared_queue.hpp" 26 | 27 | namespace kjellkod { 28 | typedef std::function Callback; 29 | 30 | class Active { 31 | private: 32 | Active() : 33 | done_(false) {} // Construction ONLY through factory createActive(); 34 | Active(const Active&) = delete; 35 | Active& operator=(const Active&) = delete; 36 | 37 | void run() { 38 | while (!done_) { 39 | Callback func; 40 | mq_.wait_and_pop(func); 41 | func(); 42 | } 43 | } 44 | 45 | shared_queue mq_; 46 | std::thread thd_; 47 | bool done_; 48 | 49 | public: 50 | virtual ~Active() { 51 | send([this]() noexcept { done_ = true; }); 52 | thd_.join(); 53 | } 54 | 55 | void send(Callback msg_) { 56 | mq_.push(msg_); 57 | } 58 | 59 | /// Factory: safe construction of object before thread start 60 | static std::unique_ptr createActive() { 61 | std::unique_ptr aPtr(new Active()); 62 | aPtr->thd_ = std::thread(&Active::run, aPtr.get()); 63 | return aPtr; 64 | } 65 | }; 66 | 67 | } // namespace kjellkod 68 | -------------------------------------------------------------------------------- /src/g3log/atomicbool.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | namespace g3 { 14 | /// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c 15 | struct atomicbool { 16 | private: 17 | std::atomic value_; 18 | 19 | public: 20 | atomicbool() : 21 | value_{false} {} 22 | atomicbool(bool value) : 23 | value_{value} {} 24 | atomicbool(const std::atomic& value) : 25 | value_{value.load(std::memory_order_acquire)} {} 26 | atomicbool(const atomicbool& other) : 27 | value_{other.value_.load(std::memory_order_acquire)} {} 28 | 29 | atomicbool& operator=(const atomicbool& other) { 30 | value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release); 31 | return *this; 32 | } 33 | 34 | atomicbool& operator=(const bool other) { 35 | value_.store(other, std::memory_order_release); 36 | return *this; 37 | } 38 | 39 | bool operator==(const atomicbool& rhs) const { 40 | return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire)); 41 | } 42 | 43 | bool value() { return value_.load(std::memory_order_acquire); } 44 | std::atomic& get() { return value_; } 45 | }; 46 | } // namespace g3 47 | // explicit whitespace/EOF for VS15 -------------------------------------------------------------------------------- /src/g3log/crashhandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** ========================================================================== 4 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 5 | * with no warranties. This code is yours to share, use and modify with no 6 | * strings attached and no restrictions or obligations. 7 | * 8 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 9 | * ============================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include "g3log/generated_definitions.hpp" 14 | #include "g3log/loglevels.hpp" 15 | 16 | // kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp 17 | // implementationsfilen kan vara den samma 18 | namespace g3 { 19 | 20 | // PUBLIC API: 21 | /** Install signal handler that catches FATAL C-runtime or OS signals 22 | See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE 23 | See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling 24 | SIGABRT ABORT (ANSI), abnormal termination 25 | SIGFPE Floating point exception (ANSI) 26 | SIGILL ILlegal instruction (ANSI) 27 | SIGSEGV Segmentation violation i.e. illegal memory reference 28 | SIGTERM TERMINATION (ANSI) */ 29 | void installCrashHandler(); 30 | 31 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 32 | typedef unsigned long SignalType; 33 | /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread 34 | /// on Windows. This is automatically done if you do at least one LOG(...) call 35 | /// you can also use this function call, per thread so make sure these three 36 | /// fatal signals are covered in your thread (even if you don't do a LOG(...) call 37 | void installSignalHandlerForThread(); 38 | #else 39 | typedef int SignalType; 40 | std::string signalToStr(int signal_number); 41 | 42 | // restore to whatever signal handler was used before signal handler installation 43 | void restoreSignalHandler(int signal_number); 44 | 45 | /// Overrides the existing signal handling for custom signals 46 | /// For example: usage of zcmq relies on its own signal handler for SIGTERM 47 | /// so users of g3log with zcmq should then use the @ref overrideSetupSignals 48 | /// , likely with the original set of signals but with SIGTERM removed 49 | /// 50 | /// call example: 51 | /// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"}, 52 | // {SIGSEGV, "SIGSEGV"},}); 53 | void overrideSetupSignals(const std::map overrideSignals); 54 | #endif 55 | 56 | namespace internal { 57 | /// Resets the fatal signal/exception handling back to default 58 | /// which might be needed in case it was previously overridden 59 | /// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM 60 | void restoreFatalHandlingToDefault(); 61 | 62 | /** return whether or any fatal handling is still ongoing 63 | * this is used by g3log::fatalCallToLogger 64 | * only in the case of Windows exceptions (not fatal signals) 65 | * are we interested in changing this from false to true to 66 | * help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/ 67 | bool shouldBlockForFatalHandling(); 68 | 69 | /** \return signal_name Ref: signum.hpp and \ref installSignalHandler 70 | * or for Windows exception name */ 71 | std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number); 72 | 73 | /** return calling thread's stackdump*/ 74 | std::string stackdump(const char* dump = nullptr); 75 | 76 | /** Re-"throw" a fatal signal, previously caught. This will exit the application 77 | * This is an internal only function. Do not use it elsewhere. It is triggered 78 | * from g3log, g3LogWorker after flushing messages to file */ 79 | void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number); 80 | size_t writeErrorMessage(const char* message); 81 | } // namespace internal 82 | } // namespace g3 83 | -------------------------------------------------------------------------------- /src/g3log/filesink.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | #include "g3log/logmessage.hpp" 14 | namespace g3 { 15 | 16 | class FileSink { 17 | public: 18 | FileSink(const std::string& log_prefix, const std::string& log_directory, const std::string& logger_id = "g3log", size_t write_to_log_every_x_message = 100); 19 | virtual ~FileSink(); 20 | 21 | void fileWrite(LogMessageMover message); 22 | std::string changeLogFile(const std::string& directory, const std::string& logger_id); 23 | std::string fileName(); 24 | void overrideLogDetails(LogMessage::LogDetailsFunc func); 25 | void overrideLogHeader(const std::string& change); 26 | 27 | private: 28 | LogMessage::LogDetailsFunc _log_details_func; 29 | std::string _log_file_with_path; 30 | std::string _log_prefix_backup; // needed in case of future log file changes of directory 31 | std::unique_ptr _outptr; 32 | std::string _header; 33 | bool _firstEntry; 34 | std::string _write_buffer; 35 | size_t _write_counter; 36 | size_t _write_to_log_every_x_message; 37 | 38 | void addLogFileHeader(); 39 | std::ofstream& filestream() { 40 | return *(_outptr.get()); 41 | } 42 | 43 | FileSink& operator=(const FileSink&) = delete; 44 | FileSink(const FileSink& other) = delete; 45 | }; 46 | } // namespace g3 47 | -------------------------------------------------------------------------------- /src/g3log/future.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** ========================================================================== 3 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 4 | * with no warranties. This code is yours to share, use and modify with no 5 | * strings attached and no restrictions or obligations. 6 | * 7 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 8 | * ============================================================================ 9 | * Filename:g3future.hpp 10 | * Helper functionality to put packaged_tasks in standard container. This 11 | * is especially helpful for background thread processing a la async but through 12 | * an actor pattern (active object), thread pool or similar. 13 | * Created: 2012 by Kjell Hedström 14 | * 15 | * COMMUNITY THANKS: 16 | * The code below is in large thanks to exemplifying code snippets from StackOverflow 17 | * question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm 18 | * and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html 19 | * 20 | * Both are highly recommended reads if you are interested in c++ concurrency library 21 | * - Kjell, 2012 22 | * 23 | * PUBLIC DOMAIN and NOT under copywrite protection. 24 | * ********************************************* */ 25 | 26 | #include 27 | #include "g3log/active.hpp" 28 | #include "g3log/moveoncopy.hpp" 29 | #include "g3log/stlpatch_future.hpp" 30 | 31 | namespace g3 { 32 | // Generic helper function to avoid repeating the steps for managing 33 | // asynchronous task job (by active object) that returns a future results 34 | // could of course be made even more generic if done more in the way of 35 | // std::async, ref: http://en.cppreference.com/w/cpp/thread/async 36 | // 37 | // Example usage: 38 | // std::unique_ptr bgWorker{Active::createActive()}; 39 | // ... 40 | // auto msg_call=[=](){return ("Hello from the Background");}; 41 | // auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get()); 42 | template 43 | std::future> spawn_task(Func func, BgWorker* worker) { 44 | typedef std::invoke_result_t result_type; 45 | typedef std::packaged_task task_type; 46 | 47 | if (nullptr == worker) { 48 | auto p = std::make_shared>(); 49 | std::future future_result = p->get_future(); 50 | p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker"))); 51 | return future_result; 52 | } 53 | 54 | task_type task(std::move(func)); 55 | 56 | std::future result = task.get_future(); 57 | worker->send(MoveOnCopy(std::move(task))); 58 | return result; 59 | } 60 | } // end namespace g3 61 | -------------------------------------------------------------------------------- /src/g3log/logcapture.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/crashhandler.hpp" 12 | #include "g3log/loglevels.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef _MSC_VER 19 | #include 20 | #endif 21 | 22 | /** 23 | * Simple struct for capturing log/fatal entries. At destruction the captured message is 24 | * forwarded to background worker. 25 | * As a safety precaution: No memory allocated here will be moved into the background 26 | * worker in case of dynamic loaded library reasons 27 | */ 28 | struct LogCapture { 29 | /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) 30 | LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump = nullptr); 31 | 32 | /** 33 | * @file, line, function are given in g3log.hpp from macros 34 | * @level INFO/DEBUG/WARNING/FATAL 35 | * @expression for CHECK calls 36 | * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler 37 | */ 38 | LogCapture(const char* file, const int line, const char* function, const LEVELS& level, const char* expression = "", g3::SignalType fatal_signal = SIGABRT, const char* dump = nullptr); 39 | 40 | // At destruction the message will be forwarded to the g3log worker. 41 | // In the case of dynamically (at runtime) loaded libraries, the important thing to know is that 42 | // all strings are copied, so the original are not destroyed at the receiving end, only the copy 43 | virtual ~LogCapture() noexcept(false); 44 | 45 | #ifdef _MSC_VER 46 | #if _MSC_VER >= 1400 47 | #define G3LOG_FORMAT_STRING _Printf_format_string_ 48 | #else 49 | #define G3LOG_FORMAT_STRING __format_string 50 | #endif 51 | 52 | void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); 53 | #else 54 | #define G3LOG_FORMAT_STRING 55 | 56 | // Use "-Wall" to generate warnings in case of illegal printf format. 57 | // Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html 58 | [[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18 59 | #endif 60 | 61 | /// prettifying API for this completely open struct 62 | std::ostringstream& stream() { 63 | return _stream; 64 | } 65 | 66 | std::ostringstream _stream; 67 | std::string _stack_trace; 68 | const char* _file; 69 | const int _line; 70 | const char* _function; 71 | const LEVELS& _level; 72 | const char* _expression; 73 | const g3::SignalType _fatal_signal; 74 | }; 75 | //} // g3 76 | -------------------------------------------------------------------------------- /src/g3log/loglevels.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | #include "g3log/generated_definitions.hpp" 11 | 12 | // Users of Juce or other libraries might have a define DEBUG which clashes with 13 | // the DEBUG logging level for G3log. In that case they can instead use the define 14 | // "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG 15 | #if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG)) 16 | #if (defined(DBUG)) 17 | #error "DBUG is already defined elsewhere which clashes with G3Log's log level DBUG" 18 | #endif 19 | #else 20 | #if (defined(DEBUG)) 21 | #error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG" 22 | #endif 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod 32 | struct LEVELS { 33 | // force internal copy of the const char*. This is a simple safeguard for when g3log is used in a 34 | // "dynamic, runtime loading of shared libraries" 35 | 36 | LEVELS(const LEVELS& other) : 37 | value(other.value), 38 | text(other.text.c_str()) {} 39 | 40 | LEVELS(int id, const std::string& idtext) : 41 | value(id), 42 | text(idtext) {} 43 | 44 | bool operator==(const LEVELS& rhs) const { 45 | return (value == rhs.value && text == rhs.text); 46 | } 47 | 48 | bool operator!=(const LEVELS& rhs) const { 49 | return (value != rhs.value || text != rhs.text); 50 | } 51 | 52 | friend void swap(LEVELS& first, LEVELS& second) { 53 | using std::swap; 54 | swap(first.value, second.value); 55 | swap(first.text, second.text); 56 | } 57 | 58 | LEVELS& operator=(LEVELS other) { 59 | swap(*this, other); 60 | return *this; 61 | } 62 | 63 | int value; 64 | std::string text; 65 | }; 66 | 67 | // If you want to add any extra logging level then please add to your own source file the logging level you need 68 | // 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...). 69 | // to give g3log a record of your logging level and if it is an enabled or disbled logging level. 70 | // 71 | // 2. If the cmake dynamic logging option is turned OFF 72 | // then giving g3log a record of your logging level with 'addLogLevel(...) is NOT needed since no "disbled/enabled" 73 | // check will happen - all logging levels will be considered enabled. 74 | // 3. See also the [g3log/API.markdown](https://github.com/KjellKod/g3log/blob/master/API.markdown) for for information. 75 | // 76 | // example: MyLoggingLevel.h 77 | // #pragma once 78 | // const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"}; 79 | // const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"}; 80 | // 81 | // ... somewhere else when G3_DYNAMIC_LOGGING is enabled 82 | // addLogLevel(MYINFO, true); 83 | // LOG(MYINFO) << "some text"; 84 | // 85 | // ... another example, when G3_DYNAMIC_LOGGING is enabled 86 | // 'addLogLevel' is NOT required 87 | // LOG(MYFATAL) << "this will just work, and it will be counted as a FATAL event"; 88 | namespace g3 { 89 | static const int kDebugValue = 100; 90 | static const int kInfoValue = 300; 91 | static const int kWarningValue = 500; 92 | static const int kFatalValue = 1000; 93 | static const int kInternalFatalValue = 2000; 94 | } // namespace g3 95 | 96 | const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"}, 97 | INFO{g3::kInfoValue, "INFO"}, 98 | WARNING{g3::kWarningValue, "WARNING"}, 99 | FATAL{g3::kFatalValue, "FATAL"}; 100 | 101 | namespace g3 { 102 | // Logging level and atomic status collection struct 103 | struct LoggingLevel { 104 | atomicbool status; 105 | LEVELS level; 106 | 107 | // default operator needed for std::map compliance 108 | LoggingLevel() : 109 | status(false), 110 | level(INFO){}; 111 | LoggingLevel(const LoggingLevel& lvl) : 112 | status(lvl.status), 113 | level(lvl.level) {} 114 | LoggingLevel(const LEVELS& lvl) : 115 | status(true), 116 | level(lvl){}; 117 | LoggingLevel(const LEVELS& lvl, bool enabled) : 118 | status(enabled), 119 | level(lvl){}; 120 | ~LoggingLevel() = default; 121 | 122 | LoggingLevel& operator=(const LoggingLevel& other) { 123 | status = other.status; 124 | level = other.level; 125 | return *this; 126 | } 127 | 128 | bool operator==(const LoggingLevel& rhs) const { 129 | return (status == rhs.status && level == rhs.level); 130 | } 131 | }; 132 | } // namespace g3 133 | 134 | namespace g3 { 135 | namespace internal { 136 | const LEVELS CONTRACT{g3::kInternalFatalValue, {"CONTRACT"}}, 137 | FATAL_SIGNAL{g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}}, 138 | FATAL_EXCEPTION{kInternalFatalValue + 2, {"FATAL_EXCEPTION"}}; 139 | 140 | /// helper function to tell the logger if a log message was fatal. If it is it will force 141 | /// a shutdown after all log entries are saved to the sinks 142 | bool wasFatal(const LEVELS& level); 143 | } // namespace internal 144 | 145 | #ifdef G3_DYNAMIC_LOGGING 146 | // Only safe if done at initialization in a single-thread context 147 | namespace only_change_at_initialization { 148 | 149 | /// add a custom level - enabled or disabled 150 | void addLogLevel(LEVELS level, bool enabled); 151 | 152 | /// add a custom level - enabled 153 | void addLogLevel(LEVELS level); 154 | 155 | /// reset all default logging levels to enabled 156 | /// remove any added logging levels so that the only ones left are 157 | /// {DEBUG,INFO,WARNING,FATAL} 158 | void reset(); 159 | } // namespace only_change_at_initialization 160 | 161 | namespace log_levels { 162 | /// Enable log level >= log_level. 163 | /// log levels below will be disabled 164 | /// log levels equal or higher will be enabled. 165 | void setHighest(LEVELS level); 166 | 167 | void set(LEVELS level, bool enabled); 168 | void disable(LEVELS level); 169 | void enable(LEVELS level); 170 | 171 | /// WARNING: This will also disable FATAL events from being logged 172 | void disableAll(); 173 | void enableAll(); 174 | 175 | /// print all levels with their disabled or enabled status 176 | std::string to_string(std::map levelsToPrint); 177 | 178 | /// print snapshot of system levels with their 179 | /// disabled or enabled status 180 | std::string to_string(); 181 | 182 | /// Snapshot view of the current logging levels' status 183 | std::map getAll(); 184 | 185 | enum class status { Absent, 186 | Enabled, 187 | Disabled }; 188 | status getStatus(LEVELS level); 189 | } // namespace log_levels 190 | 191 | #endif 192 | /// Enabled status for the given logging level 193 | bool logLevel(const LEVELS& level); 194 | 195 | } // namespace g3 196 | -------------------------------------------------------------------------------- /src/g3log/logmessage.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/crashhandler.hpp" 12 | #include "g3log/loglevels.hpp" 13 | #include "g3log/moveoncopy.hpp" 14 | #include "g3log/time.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace g3 { 22 | 23 | /** LogMessage contains all the data collected from the LOG(...) call. 24 | * If the sink receives a std::string it will be the std::string toString()... function 25 | * that will format the data into a string 26 | * 27 | * For sinks that receive a LogMessage they can either use the toString() function, or use 28 | * the helper functions or even the public raw data to format the saved log message any 29 | * desired way. 30 | */ 31 | struct LogMessage { 32 | std::string file_path() const { 33 | return _file_path; 34 | } 35 | std::string file() const { 36 | return _file; 37 | } 38 | std::string line() const { 39 | return std::to_string(_line); 40 | } 41 | std::string function() const { 42 | return _function; 43 | } 44 | std::string level() const { 45 | return _level.text; 46 | } 47 | 48 | /// use a different format string to get a different look on the time. 49 | // default look is Y/M/D H:M:S 50 | std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const; 51 | 52 | std::string message() const { 53 | return _message; 54 | } 55 | std::string& write() const { 56 | return _message; 57 | } 58 | 59 | std::string expression() const { 60 | return _expression; 61 | } 62 | bool wasFatal() const { 63 | return internal::wasFatal(_level); 64 | } 65 | 66 | std::string threadID() const; 67 | 68 | void setExpression(std::string expression) { 69 | _expression = std::move(expression); 70 | } 71 | 72 | LogMessage& operator=(LogMessage other); 73 | 74 | LogMessage(std::string file, const int line, std::string function, const LEVELS level); 75 | 76 | explicit LogMessage(const std::string& fatalOsSignalCrashMessage); 77 | LogMessage(const LogMessage& other); 78 | LogMessage(LogMessage&& other); 79 | virtual ~LogMessage() {} 80 | 81 | // helper log printing functions used by "toString()" 82 | static std::string splitFileName(const std::string& str); 83 | static std::string fatalSignalToString(const LogMessage& msg); 84 | // windows only: fatalExceptionToString 85 | static std::string fatalExceptionToString(const LogMessage& msg); 86 | static std::string fatalLogToString(const LogMessage& msg); 87 | static std::string fatalCheckToString(const LogMessage& msg); 88 | static std::string normalToString(const LogMessage& msg); 89 | 90 | // the default formatting option 91 | static std::string DefaultLogDetailsToString(const LogMessage& msg); 92 | 93 | // this function can be used by the logging sink to add thread ID 94 | // see this concept and it is easy to make your own custom formatting 95 | static std::string FullLogDetailsToString(const LogMessage& msg); 96 | 97 | using LogDetailsFunc = std::string (*)(const LogMessage&); 98 | std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const; 99 | 100 | void overrideLogDetailsFunc(LogDetailsFunc func) const; 101 | 102 | // 103 | // Complete access to the raw data in case the helper functions above 104 | // are not enough. 105 | // 106 | mutable LogDetailsFunc _logDetailsToStringFunc; 107 | g3::high_resolution_time_point _timestamp; 108 | std::thread::id _call_thread_id; 109 | std::string _file; 110 | std::string _file_path; 111 | int _line; 112 | std::string _function; 113 | LEVELS _level; 114 | std::string _expression; // only with content for CHECK(...) calls 115 | mutable std::string _message; 116 | 117 | friend void swap(LogMessage& first, LogMessage& second) { 118 | using std::swap; 119 | swap(first._timestamp, second._timestamp); 120 | swap(first._call_thread_id, second._call_thread_id); 121 | swap(first._file, second._file); 122 | swap(first._line, second._line); 123 | swap(first._function, second._function); 124 | swap(first._level, second._level); 125 | swap(first._expression, second._expression); 126 | swap(first._message, second._message); 127 | } 128 | }; 129 | 130 | /** Trigger for flushing the message queue and exiting the application 131 | * A thread that causes a FatalMessage will sleep forever until the 132 | * application has exited (after message flush) */ 133 | struct FatalMessage : public LogMessage { 134 | FatalMessage(const LogMessage& details, g3::SignalType signal_id); 135 | FatalMessage(const FatalMessage&); 136 | FatalMessage& operator=(const FatalMessage&) = delete; 137 | virtual ~FatalMessage() {} 138 | 139 | LogMessage copyToLogMessage() const; 140 | std::string reason() const; 141 | 142 | const SignalType _signal_id; 143 | }; 144 | 145 | typedef MoveOnCopy> FatalMessagePtr; 146 | typedef MoveOnCopy> LogMessagePtr; 147 | typedef MoveOnCopy LogMessageMover; 148 | } // namespace g3 149 | -------------------------------------------------------------------------------- /src/g3log/logworker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** ========================================================================== 3 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 4 | * with no warranties. This code is yours to share, use and modify with no 5 | * strings attached and no restrictions or obligations. 6 | * 7 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 8 | * ============================================================================ 9 | * Filename:g3logworker.h Framework for Logging and Design By Contract 10 | * Created: 2011 by Kjell Hedström 11 | * 12 | * PUBLIC DOMAIN and Not copyrighted. First published at KjellKod.cc 13 | * ********************************************* */ 14 | #include 15 | #include "g3log/filesink.hpp" 16 | #include "g3log/g3log.hpp" 17 | #include "g3log/logmessage.hpp" 18 | #include "g3log/sinkhandle.hpp" 19 | #include "g3log/sinkwrapper.hpp" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace g3 { 26 | class LogWorker; 27 | struct LogWorkerImpl; 28 | using FileSinkHandle = g3::SinkHandle; 29 | 30 | /// Background side of the LogWorker. Internal use only 31 | struct LogWorkerImpl final { 32 | typedef std::shared_ptr SinkWrapperPtr; 33 | std::vector _sinks; 34 | std::unique_ptr _bg; // do not change declaration order. _bg must be destroyed before sinks 35 | 36 | LogWorkerImpl(); 37 | ~LogWorkerImpl() = default; 38 | 39 | void bgSave(g3::LogMessagePtr msgPtr); 40 | void bgFatal(FatalMessagePtr msgPtr); 41 | 42 | LogWorkerImpl(const LogWorkerImpl&) = delete; 43 | LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; 44 | }; 45 | 46 | /// Front end of the LogWorker. API that is useful is 47 | /// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example 48 | /// save( msg ) : internal use 49 | /// fatal ( fatal_msg ) : internal use 50 | class LogWorker final { 51 | LogWorker() = default; 52 | void addWrappedSink(std::shared_ptr wrapper); 53 | 54 | LogWorkerImpl _impl; 55 | LogWorker(const LogWorker&) = delete; 56 | LogWorker& operator=(const LogWorker&) = delete; 57 | 58 | public: 59 | ~LogWorker(); 60 | 61 | /// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it 62 | /// if you want to use the default file logger then see below for @ref addDefaultLogger 63 | static std::unique_ptr createLogWorker(); 64 | 65 | /** 66 | A convenience function to add the default g3::FileSink to the log worker 67 | @param log_prefix that you want 68 | @param log_directory where the log is to be stored. 69 | @return a handle for API access to the sink. See the README for example usage 70 | 71 | @verbatim 72 | Example: 73 | using namespace g3; 74 | std::unique_ptr logworker {LogWorker::createLogWorker()}; 75 | auto handle = addDefaultLogger("my_test_log", "/tmp"); 76 | initializeLogging(logworker.get()); // ref. g3log.hpp 77 | 78 | std::future log_file_name = sinkHandle->call(&FileSink::fileName); 79 | std::cout << "The filename is: " << log_file_name.get() << std::endl; 80 | // something like: /tmp/my_test_log.g3log.20150819-100300.log 81 | */ 82 | std::unique_ptr addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log"); 83 | 84 | /// Adds a sink and returns the handle for access to the sink 85 | /// @param real_sink unique_ptr ownership is passed to the log worker 86 | /// @param call the default call that should receive either a std::string or a LogMessageMover message 87 | /// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger 88 | template 89 | std::unique_ptr> addSink(std::unique_ptr real_sink, DefaultLogCall call) { 90 | using namespace g3; 91 | using namespace g3::internal; 92 | auto sink = std::make_shared>(std::move(real_sink), call); 93 | addWrappedSink(sink); 94 | return std::make_unique>(sink); 95 | } 96 | 97 | /// Removes a sink. This is a synchronous call. 98 | /// You are guaranteed that the sink is removed by the time the call returns 99 | /// @param sink_handle the ownership of the sink handle is given 100 | template 101 | void removeSink(std::unique_ptr> sink_handle) { 102 | if (sink_handle) { 103 | // sink_handle->sink().use_count() is 1 at this point 104 | // i.e. this would be safe as long as no other weak_ptr to shared_ptr conversion 105 | // was made by the client: assert(sink_handle->sink().use_count() == 0); 106 | auto weak_ptr_sink = sink_handle->sink(); 107 | { 108 | auto bg_removesink_call = [this, weak_ptr_sink] { 109 | auto shared_sink = weak_ptr_sink.lock(); 110 | if (shared_sink) { 111 | _impl._sinks.erase(std::remove(_impl._sinks.begin(), _impl._sinks.end(), shared_sink), _impl._sinks.end()); 112 | } 113 | }; 114 | auto token_done = g3::spawn_task(bg_removesink_call, _impl._bg.get()); 115 | token_done.wait(); 116 | } 117 | // sink_handle->sink().use_count() is 1 at this point. 118 | // i.e. this would be safe: assert(sink_handle->sink().use_count() == 0); 119 | // as long as the client has not converted more instances from the weak_ptr 120 | } 121 | } 122 | 123 | /// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink 124 | /// handle then the sink will be removed internally but will live on in the client's instance 125 | void removeAllSinks() { 126 | auto bg_clear_sink_call = [this]() noexcept { 127 | _impl._sinks.clear(); 128 | }; 129 | auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get()); 130 | token_cleared.wait(); 131 | } 132 | 133 | /// internal: 134 | /// pushes in background thread (asynchronously) input messages to log file 135 | void save(LogMessagePtr entry); 136 | 137 | /// internal: 138 | // pushes a fatal message on the queue, this is the last message to be processed 139 | /// this way it's ensured that all existing entries were flushed before 'fatal' 140 | /// Will abort the application! 141 | void fatal(FatalMessagePtr fatal_message); 142 | }; 143 | } // namespace g3 144 | -------------------------------------------------------------------------------- /src/g3log/moveoncopy.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | namespace g3 { 11 | 12 | // A straightforward technique to move around packaged_tasks. 13 | // Instances of std::packaged_task are MoveConstructible and MoveAssignable, but 14 | // not CopyConstructible or CopyAssignable. To put them in a std container they need 15 | // to be wrapped and their internals "moved" when tried to be copied. 16 | 17 | template 18 | struct MoveOnCopy { 19 | mutable Moveable _move_only; 20 | 21 | explicit MoveOnCopy(Moveable&& m) : 22 | _move_only(std::move(m)) {} 23 | MoveOnCopy(MoveOnCopy const& t) : 24 | _move_only(std::move(t._move_only)) {} 25 | MoveOnCopy(MoveOnCopy&& t) : 26 | _move_only(std::move(t._move_only)) {} 27 | 28 | MoveOnCopy& operator=(MoveOnCopy const& other) { 29 | _move_only = std::move(other._move_only); 30 | return *this; 31 | } 32 | 33 | MoveOnCopy& operator=(MoveOnCopy&& other) { 34 | _move_only = std::move(other._move_only); 35 | return *this; 36 | } 37 | 38 | void operator()() { 39 | _move_only(); 40 | } 41 | 42 | Moveable& get() { 43 | return _move_only; 44 | } 45 | 46 | Moveable release() { 47 | return std::move(_move_only); 48 | } 49 | }; 50 | 51 | } // namespace g3 52 | -------------------------------------------------------------------------------- /src/g3log/shared_queue.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================ 8 | * 9 | * Example of a normal std::queue protected by a mutex for operations, 10 | * making it safe for thread communication, using std::mutex from C++0x with 11 | * the help from the std::thread library from JustSoftwareSolutions 12 | * ref: http://www.stdthread.co.uk/doc/headers/mutex.html 13 | * 14 | * This example was totally inspired by Anthony Williams lock-based data structures in 15 | * Ref: "C++ Concurrency In Action" http://www.manning.com/williams */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /** Multiple producer, multiple consumer thread safe queue 25 | * Since 'return by reference' is used this queue won't throw */ 26 | template 27 | class shared_queue { 28 | std::queue queue_; 29 | mutable std::mutex m_; 30 | std::condition_variable data_cond_; 31 | 32 | shared_queue& operator=(const shared_queue&) = delete; 33 | shared_queue(const shared_queue& other) = delete; 34 | 35 | public: 36 | shared_queue() = default; 37 | 38 | void push(T item) { 39 | { 40 | std::lock_guard lock(m_); 41 | queue_.push(std::move(item)); 42 | } 43 | data_cond_.notify_one(); 44 | } 45 | 46 | /// \return immediately, with true if successful retrieval 47 | bool try_and_pop(T& popped_item) { 48 | std::lock_guard lock(m_); 49 | if (queue_.empty()) { 50 | return false; 51 | } 52 | popped_item = std::move(queue_.front()); 53 | queue_.pop(); 54 | return true; 55 | } 56 | 57 | /// Try to retrieve, if no items, wait till an item is available and try again 58 | void wait_and_pop(T& popped_item) { 59 | std::unique_lock lock(m_); 60 | while (queue_.empty()) { 61 | data_cond_.wait(lock); 62 | // This 'while' loop is equal to 63 | // data_cond_.wait(lock, [](bool result){return !queue_.empty();}); 64 | } 65 | popped_item = std::move(queue_.front()); 66 | queue_.pop(); 67 | } 68 | 69 | bool empty() const { 70 | std::lock_guard lock(m_); 71 | return queue_.empty(); 72 | } 73 | 74 | unsigned size() const { 75 | std::lock_guard lock(m_); 76 | return queue_.size(); 77 | } 78 | }; 79 | -------------------------------------------------------------------------------- /src/g3log/sink.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/active.hpp" 12 | #include "g3log/future.hpp" 13 | #include "g3log/logmessage.hpp" 14 | #include "g3log/sinkwrapper.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | namespace g3 { 21 | namespace internal { 22 | typedef std::function AsyncMessageCall; 23 | 24 | /// The asynchronous Sink has an active object, incoming requests for actions 25 | // will be processed in the background by the specific object the Sink represents. 26 | // 27 | // The Sink will wrap either 28 | // a Sink with Message object receiving call 29 | // or a Sink with a LogEntry (string) receiving call 30 | // 31 | // The Sink can also be used through the SinkHandler to call Sink specific function calls 32 | // Ref: send(Message) deals with incoming log entries (converted if necessary to string) 33 | // Ref: send(Call call, Args... args) deals with calls 34 | // to the real sink's API 35 | 36 | template 37 | struct Sink : public SinkWrapper { 38 | std::unique_ptr _real_sink; 39 | std::unique_ptr _bg; 40 | AsyncMessageCall _default_log_call; 41 | 42 | template 43 | Sink(std::unique_ptr sink, DefaultLogCall call) : 44 | SinkWrapper(), 45 | _real_sink{std::move(sink)}, 46 | _bg(kjellkod::Active::createActive()), 47 | _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { 48 | } 49 | 50 | Sink(std::unique_ptr sink, void (T::*Call)(std::string)) : 51 | SinkWrapper(), 52 | _real_sink{std::move(sink)}, 53 | _bg(kjellkod::Active::createActive()) { 54 | std::function adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); 55 | _default_log_call = [=](LogMessageMover m) { 56 | adapter(m.get().toString()); 57 | }; 58 | } 59 | 60 | virtual ~Sink() { 61 | _bg.reset(); // TODO: to remove 62 | } 63 | 64 | void send(LogMessageMover msg) override { 65 | _bg->send([this, msg] { 66 | _default_log_call(msg); 67 | }); 68 | } 69 | 70 | template 71 | auto async(Call call, Args&&... args) -> std::future> { 72 | return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward(args)...), _bg.get()); 73 | } 74 | }; 75 | } // namespace internal 76 | } // namespace g3 77 | -------------------------------------------------------------------------------- /src/g3log/sinkhandle.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/sink.hpp" 12 | 13 | #include 14 | #include 15 | 16 | namespace g3 { 17 | 18 | // The Sinkhandle is the client's access point to the specific sink instance. 19 | // Only through the Sinkhandle can, and should, the real sink's specific API 20 | // be called. 21 | // 22 | // The real sink will be owned by g3log. If the real sink is deleted 23 | // calls to sink's API through the SinkHandle will return an exception embedded 24 | // in the resulting future. Ref: SinkHandle::call 25 | template 26 | class SinkHandle { 27 | std::weak_ptr> _sink; 28 | 29 | public: 30 | SinkHandle(std::shared_ptr> sink) : 31 | _sink(sink) {} 32 | 33 | ~SinkHandle() = default; 34 | 35 | // Asynchronous call to the real sink. If the real sink is already deleted 36 | // the returned future will contain a bad_weak_ptr exception instead of the 37 | // call result. 38 | template 39 | auto call(AsyncCall func, Args&&... args) -> std::future> { 40 | try { 41 | std::shared_ptr> sink(_sink); 42 | return sink->async(func, std::forward(args)...); 43 | } catch (const std::bad_weak_ptr& e) { 44 | typedef std::invoke_result_t PromiseType; 45 | std::promise promise; 46 | promise.set_exception(std::make_exception_ptr(e)); 47 | return std::move(promise.get_future()); 48 | } 49 | } 50 | 51 | /// Get weak_ptr access to the sink(). Make sure to check that the returned pointer is valid, 52 | /// auto p = sink(); auto ptr = p.lock(); if (ptr) { .... } 53 | /// ref: https://en.cppreference.com/w/cpp/memory/weak_ptr/lock 54 | std::weak_ptr> sink() { 55 | return _sink.lock(); 56 | } 57 | }; 58 | 59 | } // namespace g3 60 | -------------------------------------------------------------------------------- /src/g3log/sinkwrapper.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/logmessage.hpp" 12 | 13 | namespace g3 { 14 | namespace internal { 15 | 16 | struct SinkWrapper { 17 | virtual ~SinkWrapper() {} 18 | virtual void send(LogMessageMover msg) = 0; 19 | }; 20 | } // namespace internal 21 | } // namespace g3 22 | -------------------------------------------------------------------------------- /src/g3log/stacktrace_windows.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc AND Robert Engeln. 3 | * The stacktrace code was given as a public domain dedication by Robert Engeln 4 | * It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html 5 | * It was (here) modified for g3log purposes. 6 | * 7 | * This is PUBLIC DOMAIN to use at your own risk and comes 8 | * with no warranties. This code is yours to share, use and modify with no 9 | * strings attached and no restrictions or obligations. 10 | * 11 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 12 | * ============================================================================*/ 13 | 14 | #pragma once 15 | #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 16 | #error "stacktrace_win.cpp used but not on a windows system" 17 | #endif 18 | 19 | #include "g3log/crashhandler.hpp" 20 | 21 | #include 22 | #include 23 | 24 | namespace stacktrace { 25 | /// return the text description of a Windows exception code 26 | std::string exceptionIdToText(g3::SignalType id); 27 | 28 | /// return whether or not the exception is a known exception, i.e. 29 | /// an exception that we should treat as a fatal event 30 | bool isKnownException(g3::SignalType id); 31 | 32 | /// helper function: retrieve stackdump from no excisting exception pointer 33 | std::string stackdump(); 34 | 35 | /// helper function: retrieve stackdump, starting from an exception pointer 36 | std::string stackdump(EXCEPTION_POINTERS* info); 37 | 38 | /// main stackdump function. retrieve stackdump, from the given context 39 | std::string stackdump(CONTEXT* context); 40 | 41 | } // namespace stacktrace 42 | -------------------------------------------------------------------------------- /src/g3log/stlpatch_future.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * 8 | * 9 | * 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well 10 | * std::packaged_task. Thanks to Michael Rasmussen (lap777) 11 | * Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable 12 | * ============================================================================*/ 13 | 14 | #pragma once 15 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800) 16 | namespace std { 17 | 18 | template 19 | class packaged_task { 20 | promise _my_promise; 21 | function _my_func; 22 | 23 | public: 24 | packaged_task() { 25 | } 26 | 27 | template 28 | explicit packaged_task(_Fty2&& _Fnarg) : 29 | _my_func(_Fnarg) { 30 | } 31 | 32 | packaged_task(packaged_task&& _Other) : 33 | _my_promise(move(_Other._my_promise)), 34 | _my_func(move(_Other._my_func)) { 35 | } 36 | 37 | packaged_task& operator=(packaged_task&& _Other) { 38 | _my_promise = move(_Other._my_promise); 39 | _my_func = move(_Other._my_func); 40 | return (*this); 41 | } 42 | 43 | packaged_task(const packaged_task&) = delete; 44 | packaged_task& operator=(const packaged_task&) = delete; 45 | 46 | ~packaged_task() { 47 | } 48 | 49 | void swap(packaged_task& _Other) { 50 | swap(_my_promise, _Other._my_promise); 51 | swap(_my_func, _Other._my_func); 52 | } 53 | 54 | explicit operator bool() const { 55 | return _my_func != false; 56 | } 57 | 58 | bool valid() const { 59 | return _my_func != false; 60 | } 61 | 62 | future get_future() { 63 | return _my_promise.get_future(); 64 | } 65 | 66 | void operator()(_ArgTypes... _Args) { 67 | _my_func(forward<_ArgTypes>(_Args)...); 68 | _my_promise.set_value(); 69 | } 70 | 71 | void reset() { 72 | _my_promise.swap(promise()); 73 | _my_func.swap(function()); 74 | } 75 | }; 76 | 77 | }; // namespace std 78 | #endif // defined(WIN32) ... 79 | -------------------------------------------------------------------------------- /src/g3log/time.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** ========================================================================== 3 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 4 | * with no warranties. This code is yours to share, use and modify with no 5 | * strings attached and no restrictions or obligations. 6 | * 7 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 8 | * ============================================================================ 9 | * Filename:g3time.h cross-platform, thread-safe replacement for C++11 non-thread-safe 10 | * localtime (and similar) 11 | * Created: 2012 by Kjell Hedström 12 | * 13 | * PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc 14 | * ********************************************* */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | // FYI: 21 | // namespace g3::internal ONLY in g3time.cpp 22 | // std::string put_time(const struct tm* tmb, const char* c_time_format) 23 | 24 | namespace g3 { 25 | typedef std::chrono::time_point system_time_point; 26 | typedef std::chrono::time_point high_resolution_time_point; 27 | typedef std::chrono::milliseconds milliseconds; 28 | typedef std::chrono::microseconds microseconds; 29 | 30 | namespace internal { 31 | enum class Fractional { Millisecond, 32 | Microsecond, 33 | Nanosecond, 34 | NanosecondDefault }; 35 | Fractional getFractional(const std::string& format_buffer, size_t pos); 36 | std::string to_string(const g3::system_time_point& ts, Fractional fractional); 37 | std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer); 38 | static const std::string date_formatted = "%Y/%m/%d"; 39 | // %f: fractions of seconds (%f is nanoseconds) 40 | // %f3: milliseconds, 3 digits: 001 41 | // %6: microseconds: 6 digits: 000001 --- default for the time_format 42 | // %f9, %f: nanoseconds, 9 digits: 000000001 43 | static const std::string time_formatted = "%H:%M:%S %f6"; 44 | } // namespace internal 45 | 46 | // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" 47 | // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. 48 | // return value is SIMPLIFIED to only return a std::string 49 | std::string put_time(const struct tm* tmb, const char* c_time_format); 50 | 51 | /** return time representing POD struct (ref ctime + wchar) that is normally 52 | * retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not. 53 | * g3::localtime is probably used together with @ref g3::systemtime_now */ 54 | tm localtime(std::time_t time); 55 | 56 | /** format string must conform to std::put_time's demands. 57 | * WARNING: At time of writing there is only so-so compiler support for 58 | * std::put_time. A possible fix if your c++11 library is not updated is to 59 | * modify this to use std::strftime instead */ 60 | std::string localtime_formatted(const system_time_point& ts, const std::string& time_format); 61 | 62 | inline system_time_point to_system_time(const high_resolution_time_point& ts) { 63 | // On some (windows) systems, the system_clock does not provide the highest possible time 64 | // resolution. Thus g3log uses high_resolution_clock for message time stamps. However, 65 | // unlike system_clock, high_resolution_clock cannot be converted to a time and date as 66 | // it usually measures reflects the time since power-up. 67 | // Thus, hrs_now and sys_now are recorded once when the program starts to be able to convert 68 | // timestamps to dime and date using to_system_time(). The precision of the absolute time is 69 | // of course that of system_clock() with some error added due to the non-simultaneous initialization 70 | // of the two static variables but relative times within one log will be as precise as 71 | // high_resolution_clock. 72 | using namespace std::chrono; 73 | static const auto hrs_now = high_resolution_clock::now(); 74 | static const auto sys_now = system_clock::now(); 75 | 76 | return time_point_cast(sys_now + (ts - hrs_now)); 77 | } 78 | } // namespace g3 79 | -------------------------------------------------------------------------------- /src/logcapture.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/logcapture.hpp" 10 | #include "g3log/crashhandler.hpp" 11 | #include "g3log/g3log.hpp" 12 | 13 | #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE 14 | #include 15 | #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ 16 | 17 | // For Windows we need force a thread_local install per thread of three 18 | // signals that must have a signal handler installed per thread-basis 19 | // It is really a royal pain. Seriously Microsoft? Seriously? 20 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 21 | #define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread() 22 | #else 23 | // Does nothing --- enforces that semicolon must be written 24 | #define SIGNAL_HANDLER_VERIFY() \ 25 | do { \ 26 | } while (0) 27 | #endif 28 | 29 | #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE 30 | // MaxMessageSize is message limit used with vsnprintf/vsnprintf_s 31 | static int MaxMessageSize = 2048; 32 | 33 | void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) { 34 | MaxMessageSize = max_size; 35 | } 36 | #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ 37 | 38 | /** logCapture is a simple struct for capturing log/fatal entries. At destruction the 39 | * captured message is forwarded to background worker. 40 | * As a safety precaution: No memory allocated here will be moved into the background 41 | * worker in case of dynamic loaded library reasons instead the arguments are copied 42 | * inside of g3log.cpp::saveMessage*/ 43 | LogCapture::~LogCapture() noexcept(false) { 44 | using namespace g3::internal; 45 | SIGNAL_HANDLER_VERIFY(); 46 | saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str()); 47 | } 48 | 49 | /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) 50 | LogCapture::LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump) : 51 | LogCapture("", 0, "", level, "", fatal_signal, dump) { 52 | } 53 | 54 | /** 55 | * @file, line, function are given in g3log.hpp from macros 56 | * @level INFO/DEBUG/WARNING/FATAL 57 | * @expression for CHECK calls 58 | * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler 59 | */ 60 | LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS& level, 61 | const char* expression, g3::SignalType fatal_signal, const char* dump) : 62 | _file(file), 63 | _line(line), 64 | _function(function), 65 | _level(level), 66 | _expression(expression), 67 | _fatal_signal(fatal_signal) { 68 | 69 | if (g3::internal::wasFatal(level)) { 70 | _stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"}; 71 | _stack_trace.append(g3::internal::stackdump(dump)); 72 | } 73 | } 74 | 75 | /** 76 | * capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF 77 | * See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18 78 | */ 79 | void LogCapture::capturef(const char* printf_like_message, ...) { 80 | static const std::string kTruncatedWarningText = "[...truncated...]"; 81 | #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE 82 | std::vector finished_message_backing(MaxMessageSize); 83 | char* finished_message = finished_message_backing.data(); 84 | auto finished_message_len = MaxMessageSize; 85 | #else 86 | static const int kMaxMessageSize = 2048; 87 | char finished_message[kMaxMessageSize]; 88 | #if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__)) 89 | auto finished_message_len = _countof(finished_message); 90 | #else 91 | int finished_message_len = sizeof(finished_message); 92 | #endif 93 | #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE*/ 94 | 95 | va_list arglist; 96 | va_start(arglist, printf_like_message); 97 | 98 | #if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__)) 99 | const int nbrcharacters = vsnprintf_s(finished_message, finished_message_len, _TRUNCATE, printf_like_message, arglist); 100 | #else 101 | const int nbrcharacters = vsnprintf(finished_message, finished_message_len, printf_like_message, arglist); 102 | #endif 103 | va_end(arglist); 104 | 105 | if (nbrcharacters < 0) { 106 | stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to successfully parse the message"; 107 | stream() << '"' << printf_like_message << '"' << std::endl; 108 | } else if (nbrcharacters > finished_message_len) { 109 | stream() << finished_message << kTruncatedWarningText; 110 | } else { 111 | stream() << finished_message; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/loglevels.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/loglevels.hpp" 10 | #include 11 | 12 | #include 13 | 14 | namespace g3 { 15 | namespace internal { 16 | bool wasFatal(const LEVELS& level) { 17 | return level.value >= FATAL.value; 18 | } 19 | 20 | #ifdef G3_DYNAMIC_LOGGING 21 | const std::map g_log_level_defaults = { 22 | {G3LOG_DEBUG.value, {G3LOG_DEBUG}}, 23 | {INFO.value, {INFO}}, 24 | {WARNING.value, {WARNING}}, 25 | {FATAL.value, {FATAL}}}; 26 | 27 | std::map g_log_levels = g_log_level_defaults; 28 | #endif 29 | } // namespace internal 30 | 31 | #ifdef G3_DYNAMIC_LOGGING 32 | namespace only_change_at_initialization { 33 | 34 | void addLogLevel(LEVELS lvl, bool enabled) { 35 | int value = lvl.value; 36 | internal::g_log_levels[value] = {lvl, enabled}; 37 | } 38 | 39 | void addLogLevel(LEVELS level) { 40 | addLogLevel(level, true); 41 | } 42 | 43 | void reset() { 44 | g3::internal::g_log_levels = g3::internal::g_log_level_defaults; 45 | } 46 | } // namespace only_change_at_initialization 47 | 48 | namespace log_levels { 49 | 50 | void setHighest(LEVELS enabledFrom) { 51 | auto it = internal::g_log_levels.find(enabledFrom.value); 52 | if (it != internal::g_log_levels.end()) { 53 | for (auto& v : internal::g_log_levels) { 54 | if (v.first < enabledFrom.value) { 55 | disable(v.second.level); 56 | } else { 57 | enable(v.second.level); 58 | } 59 | } 60 | } 61 | } 62 | 63 | void set(LEVELS level, bool enabled) { 64 | auto it = internal::g_log_levels.find(level.value); 65 | if (it != internal::g_log_levels.end()) { 66 | internal::g_log_levels[level.value] = {level, enabled}; 67 | } 68 | } 69 | 70 | void disable(LEVELS level) { 71 | set(level, false); 72 | } 73 | 74 | void enable(LEVELS level) { 75 | set(level, true); 76 | } 77 | 78 | void disableAll() { 79 | for (auto& v : internal::g_log_levels) { 80 | v.second.status = false; 81 | } 82 | } 83 | 84 | void enableAll() { 85 | for (auto& v : internal::g_log_levels) { 86 | v.second.status = true; 87 | } 88 | } 89 | 90 | std::string to_string(std::map levelsToPrint) { 91 | std::string levels; 92 | for (auto& v : levelsToPrint) { 93 | levels += "name: " + v.second.level.text + " level: " + std::to_string(v.first) + " status: " + std::to_string(v.second.status.value()) + "\n"; 94 | } 95 | return levels; 96 | } 97 | 98 | std::string to_string() { 99 | return to_string(internal::g_log_levels); 100 | } 101 | 102 | std::map getAll() { 103 | return internal::g_log_levels; 104 | } 105 | 106 | // status : {Absent, Enabled, Disabled}; 107 | status getStatus(LEVELS level) { 108 | const auto it = internal::g_log_levels.find(level.value); 109 | if (internal::g_log_levels.end() == it) { 110 | return status::Absent; 111 | } 112 | 113 | return (it->second.status.get().load() ? status::Enabled : status::Disabled); 114 | } 115 | } // namespace log_levels 116 | 117 | #endif 118 | 119 | bool logLevel(const LEVELS& log_level) { 120 | #ifdef G3_DYNAMIC_LOGGING 121 | int level = log_level.value; 122 | bool status = internal::g_log_levels[level].status.value(); 123 | return status; 124 | #else 125 | return true; 126 | #endif 127 | } 128 | } // namespace g3 129 | -------------------------------------------------------------------------------- /src/logworker.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/logworker.hpp" 10 | #include "g3log/active.hpp" 11 | #include "g3log/crashhandler.hpp" 12 | #include "g3log/future.hpp" 13 | #include "g3log/g3log.hpp" 14 | #include "g3log/logmessage.hpp" 15 | 16 | #include 17 | 18 | namespace g3 { 19 | 20 | LogWorkerImpl::LogWorkerImpl() : 21 | _bg(kjellkod::Active::createActive()) {} 22 | 23 | void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) { 24 | std::unique_ptr uniqueMsg(std::move(msgPtr.get())); 25 | 26 | for (auto& sink : _sinks) { 27 | LogMessage msg(*(uniqueMsg)); 28 | sink->send(LogMessageMover(std::move(msg))); 29 | } 30 | 31 | if (_sinks.empty()) { 32 | std::string err_msg{"g3logworker has no sinks. Message: ["}; 33 | err_msg.append(uniqueMsg.get()->toString()).append("]\n"); 34 | std::cerr << err_msg; 35 | } 36 | } 37 | 38 | void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { 39 | // this will be the last message. Only the active logworker can receive a FATAL call so it's 40 | // safe to shutdown logging now 41 | g3::internal::shutDownLogging(); 42 | 43 | std::string reason = msgPtr.get()->reason(); 44 | const auto level = msgPtr.get()->_level; 45 | const auto fatal_id = msgPtr.get()->_signal_id; 46 | 47 | std::unique_ptr uniqueMsg(std::move(msgPtr.get())); 48 | uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); 49 | 50 | // Change output in case of a fatal signal (or windows exception) 51 | std::string exiting = {"Fatal type: "}; 52 | 53 | uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason).append("\nLog content flushed successfully to sink\n\n"); 54 | 55 | std::cerr << uniqueMsg->toString() << std::flush; 56 | for (auto& sink : _sinks) { 57 | LogMessage msg(*(uniqueMsg)); 58 | sink->send(LogMessageMover(std::move(msg))); 59 | } 60 | 61 | // This clear is absolutely necessary 62 | // All sinks are forced to receive the fatal message above before we continue 63 | _sinks.clear(); // flush all queues 64 | internal::exitWithDefaultSignalHandler(level, fatal_id); 65 | 66 | // should never reach this point 67 | perror("g3log exited after receiving FATAL trigger. Flush message status: "); 68 | } 69 | 70 | LogWorker::~LogWorker() { 71 | g3::internal::shutDownLoggingForActiveOnly(this); 72 | 73 | // The sinks WILL automatically be cleared at exit of this destructor 74 | // The waiting inside removeAllSinks ensures that all messages until this point are 75 | // taken care of before any internals/LogWorkerImpl of LogWorker starts to be destroyed. 76 | // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and 77 | // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly 78 | // deconstructed LogWorkerImpl" 79 | // 80 | // Any messages put into the queue will be OK due to: 81 | // *) If it is before the wait below then they will be executed 82 | // *) If it is AFTER the wait below then they will be ignored and NEVER executed 83 | removeAllSinks(); 84 | 85 | // The background worker WILL be automatically cleared at the exit of the destructor 86 | // However, the explicitly clearing of the background worker (below) makes sure that there can 87 | // be no thread that manages to add another sink after the call to clear the sinks above. 88 | // i.e. this manages the extremely unlikely case of another thread calling 89 | // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp 90 | // and be closely coupled with the existence of the LogWorker. Sharing this adding of sinks to 91 | // other threads that do not know the state of LogWorker is considered a bug but it is dealt with 92 | // nonetheless below. 93 | // 94 | // If sinks would already have been added after the sink clear above then this reset will deal with it 95 | // without risking lambda execution with a partially deconstructed LogWorkerImpl 96 | // Calling g3::spawn_task on a nullptr Active object will not crash but return 97 | // a future containing an appropriate exception. 98 | _impl._bg.reset(nullptr); 99 | } 100 | 101 | void LogWorker::save(LogMessagePtr msg) { 102 | _impl._bg->send([this, msg] { _impl.bgSave(msg); }); 103 | } 104 | 105 | void LogWorker::fatal(FatalMessagePtr fatal_message) { 106 | _impl._bg->send([this, fatal_message] { _impl.bgFatal(fatal_message); }); 107 | } 108 | 109 | void LogWorker::addWrappedSink(std::shared_ptr sink) { 110 | auto bg_addsink_call = [this, sink] { 111 | _impl._sinks.push_back(sink); 112 | }; 113 | auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get()); 114 | token_done.wait(); 115 | } 116 | 117 | std::unique_ptr LogWorker::createLogWorker() { 118 | return std::unique_ptr(new LogWorker); 119 | } 120 | 121 | std::unique_ptr LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) { 122 | return addSink(std::make_unique(log_prefix, log_directory, default_id), &FileSink::fileWrite); 123 | } 124 | 125 | } // namespace g3 126 | -------------------------------------------------------------------------------- /src/time.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/time.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef __MACH__ 19 | #include 20 | #endif 21 | 22 | namespace g3 { 23 | namespace internal { 24 | const std::string kFractionalIdentier = "%f"; 25 | const size_t kFractionalIdentierSize = 2; 26 | 27 | Fractional getFractional(const std::string& format_buffer, size_t pos) { 28 | char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0'); 29 | Fractional type = Fractional::NanosecondDefault; 30 | switch (ch) { 31 | case '3': 32 | type = Fractional::Millisecond; 33 | break; 34 | case '6': 35 | type = Fractional::Microsecond; 36 | break; 37 | case '9': 38 | type = Fractional::Nanosecond; 39 | break; 40 | default: 41 | type = Fractional::NanosecondDefault; 42 | break; 43 | } 44 | return type; 45 | } 46 | 47 | // Returns the fractional as a string with padded zeroes 48 | // 1 ms --> 001 49 | // 1 us --> 000001 50 | // 1 ns --> 000000001 51 | std::string to_string(const g3::system_time_point& ts, Fractional fractional) { 52 | auto duration = ts.time_since_epoch(); 53 | auto sec_duration = std::chrono::duration_cast(duration); 54 | duration -= sec_duration; 55 | auto ns = std::chrono::duration_cast(duration).count(); 56 | 57 | auto zeroes = 9; // default ns 58 | auto digitsToCut = 1; // default ns, divide by 1 makes no change 59 | switch (fractional) { 60 | case Fractional::Millisecond: { 61 | zeroes = 3; 62 | digitsToCut = 1000000; 63 | break; 64 | } 65 | case Fractional::Microsecond: { 66 | zeroes = 6; 67 | digitsToCut = 1000; 68 | break; 69 | } 70 | case Fractional::Nanosecond: 71 | case Fractional::NanosecondDefault: 72 | default: 73 | zeroes = 9; 74 | digitsToCut = 1; 75 | } 76 | 77 | ns /= digitsToCut; 78 | auto value = std::string(std::to_string(ns)); 79 | return std::string(zeroes - value.size(), '0') + value; 80 | } 81 | 82 | std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer) { 83 | // iterating through every "%f" instance in the format string 84 | auto identifierExtraSize = 0; 85 | for (size_t pos = 0; 86 | (pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos; 87 | pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) { 88 | // figuring out whether this is nano, micro or milli identifier 89 | auto type = g3::internal::getFractional(format_buffer, pos); 90 | auto value = g3::internal::to_string(ts, type); 91 | auto padding = 0; 92 | if (type != g3::internal::Fractional::NanosecondDefault) { 93 | padding = 1; 94 | } 95 | 96 | // replacing "%f[3|6|9]" with sec fractional part value 97 | format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value); 98 | } 99 | return format_buffer; 100 | } 101 | 102 | } // namespace internal 103 | } // namespace g3 104 | 105 | namespace g3 { 106 | // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" 107 | // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. 108 | // return value is SIMPLIFIED to only return a std::string 109 | std::string put_time(const struct tm* tmb, const char* c_time_format) { 110 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) 111 | std::ostringstream oss; 112 | oss.fill('0'); 113 | // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* " 114 | oss << std::put_time(const_cast(tmb), c_time_format); 115 | return oss.str(); 116 | #else // LINUX 117 | const size_t size = 1024; 118 | char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time. 119 | // ... also ... This is way more buffer space then we need 120 | 121 | auto success = std::strftime(buffer, size, c_time_format, tmb); 122 | // In DEBUG the assert will trigger a process exit. Once inside the if-statement 123 | // the 'always true' expression will be displayed as reason for the exit 124 | // 125 | // In Production mode 126 | // the assert will do nothing but the format string will instead be returned 127 | if (0 == success) { 128 | assert((0 != success) && "strftime fails with illegal formatting"); 129 | return c_time_format; 130 | } 131 | return buffer; 132 | #endif 133 | } 134 | 135 | tm localtime(std::time_t ts) { 136 | struct tm tm_snapshot; 137 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 138 | localtime_s(&tm_snapshot, &ts); // windsows 139 | #else 140 | localtime_r(&ts, &tm_snapshot); // POSIX 141 | #endif 142 | return tm_snapshot; 143 | } 144 | 145 | std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) { 146 | auto format_buffer = internal::localtime_formatted_fractions(ts, time_format); 147 | auto time_point = std::chrono::system_clock::to_time_t(ts); 148 | std::tm t = localtime(time_point); 149 | return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S"); 150 | } 151 | } // namespace g3 152 | -------------------------------------------------------------------------------- /test_main/test_main.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * ============================================================================*/ 6 | 7 | #include 8 | #include 9 | 10 | int main(int argc, char* argv[]) { 11 | testing::InitGoogleTest(&argc, argv); 12 | int return_value = RUN_ALL_TESTS(); 13 | std::cout << "FINISHED WITH THE TESTING " << std::endl; 14 | return return_value; 15 | } 16 | -------------------------------------------------------------------------------- /test_performance/Performance.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | 13 | 14 | # . performance test (average + worst case) for KjellKod's g3log 15 | # Do 'cmake -DADD_G3LOG_BENCH_PERFORMANCE=ON' to enable this 16 | option (ADD_G3LOG_BENCH_PERFORMANCE "g3log performance test" OFF) 17 | 18 | 19 | 20 | 21 | # create the g3log's performance tests 22 | # ========================= 23 | IF (ADD_G3LOG_BENCH_PERFORMANCE) 24 | set(DIR_PERFORMANCE ${g3log_SOURCE_DIR}/test_performance) 25 | 26 | message( STATUS "-DADD_G3LOG_BENCH_PERFORMANCE=ON" ) 27 | include_directories (${DIR_PERFORMANCE}) 28 | 29 | # MEAN PERFORMANCE TEST 30 | add_executable(g3log-performance-threaded_mean 31 | ${DIR_PERFORMANCE}/main_threaded_mean.cpp 32 | ${DIR_PERFORMANCE}/performance.h) 33 | # Turn on G3LOG performance flag 34 | set_target_properties(g3log-performance-threaded_mean PROPERTIES 35 | COMPILE_DEFINITIONS "G3LOG_PERFORMANCE=1") 36 | target_link_libraries(g3log-performance-threaded_mean 37 | ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES}) 38 | 39 | # WORST CASE PERFORMANCE TEST 40 | add_executable(g3log-performance-threaded_worst 41 | ${DIR_PERFORMANCE}/main_threaded_worst.cpp ${DIR_PERFORMANCE}/performance.h) 42 | # Turn on G3LOG performance flag 43 | set_target_properties(g3log-performance-threaded_worst PROPERTIES 44 | COMPILE_DEFINITIONS "G3LOG_PERFORMANCE=1") 45 | target_link_libraries(g3log-performance-threaded_worst 46 | ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES}) 47 | 48 | ELSE() 49 | message( STATUS "-DADD_G3LOG_BENCH_PERFORMANCE=OFF" ) 50 | ENDIF(ADD_G3LOG_BENCH_PERFORMANCE) 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test_performance/main_threaded_mean.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | // through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE 10 | #include 11 | #include 12 | #include 13 | #include "performance.h" 14 | 15 | #if defined(G3LOG_PERFORMANCE) 16 | const std::string title = "G3LOG"; 17 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 18 | const std::string title = "GOOGLE__GLOG"; 19 | #else 20 | #error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined 21 | #endif 22 | 23 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 24 | const std::string g_path = "./"; 25 | #else 26 | const std::string g_path = "/tmp/"; 27 | #endif 28 | using namespace g3_test; 29 | 30 | int main(int argc, char** argv) { 31 | #ifdef G3_DYNAMIC_LOGGING 32 | std::cerr << "G3_DYNAMIC_LOGGING is enabled" << std::endl; 33 | #else 34 | std::cerr << "G3_DYNAMIC_LOGGING is DISABLED" << std::endl; 35 | #endif 36 | 37 | size_t number_of_threads = 0; 38 | if (argc == 2) { 39 | number_of_threads = atoi(argv[1]); 40 | } 41 | if (argc != 2 || number_of_threads == 0) { 42 | std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl; 43 | return 1; 44 | } 45 | 46 | std::ostringstream thread_count_oss; 47 | thread_count_oss << number_of_threads; 48 | const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-MEAN_LOG"; 49 | const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt"; 50 | 51 | std::ostringstream oss; 52 | const uint64_t us_to_s = 1000000; 53 | oss << "\n\n" 54 | << title << " performance " << number_of_threads << " threads MEAN times\n"; 55 | oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry 56 | const uint64_t xtra_margin = 2; 57 | oss << "*** It can take som time. Please wait: Approximate wait time on MY PC was: " << number_of_threads * (uint64_t)(g_iterations * 10 * xtra_margin / us_to_s) << " seconds" << std::endl; 58 | writeTextToFile(g_measurement_dump, oss.str(), kAppend); 59 | oss.str(""); // clear the stream 60 | 61 | #if defined(G3LOG_PERFORMANCE) 62 | auto worker = g3::LogWorker::createLogWorker(); 63 | auto handle = worker->addDefaultLogger(g_prefix_log_name, g_path); 64 | g3::initializeLogging(worker.get()); 65 | 66 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 67 | google::InitGoogleLogging(argv[0]); 68 | #endif 69 | auto start_time = std::chrono::high_resolution_clock::now(); 70 | 71 | std::thread* threads = new std::thread[number_of_threads]; 72 | // kiss: just loop, create threads, store them then join 73 | // could probably do this more elegant with lambdas 74 | for (size_t idx = 0; idx < number_of_threads; ++idx) { 75 | std::ostringstream count; 76 | count << idx + 1; 77 | std::string thread_name = title + "_T" + count.str(); 78 | std::cout << "Creating thread: " << thread_name << std::endl; 79 | threads[idx] = std::thread(doLogWrites, thread_name); 80 | } 81 | for (size_t idx = 0; idx < number_of_threads; ++idx) { 82 | threads[idx].join(); 83 | } 84 | auto application_end_time = std::chrono::high_resolution_clock::now(); 85 | delete[] threads; 86 | 87 | #if defined(G3LOG_PERFORMANCE) 88 | worker.reset(); // will flush anything in the queue to file 89 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 90 | google::ShutdownGoogleLogging(); 91 | #endif 92 | 93 | auto worker_end_time = std::chrono::high_resolution_clock::now(); 94 | uint64_t application_time_us = std::chrono::duration_cast(application_end_time - start_time).count(); 95 | uint64_t total_time_us = std::chrono::duration_cast(worker_end_time - start_time).count(); 96 | 97 | oss << "\n" 98 | << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk" << std::endl; 99 | oss << "[Application(" << number_of_threads << "):\t\t:" << application_time_us / 1000 << " ms]" << std::endl; 100 | oss << "[Background thread to finish\t:" << total_time_us / uint64_t(1000) << " ms]" << std::endl; 101 | oss << "\nAverage time per log entry:" << std::endl; 102 | oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; 103 | oss << "[Background+Application: " << total_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; 104 | writeTextToFile(g_measurement_dump, oss.str(), kAppend); 105 | std::cout << "Result can be found at:" << g_measurement_dump << std::endl; 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /test_performance/performance.h: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #if defined(G3LOG_PERFORMANCE) 23 | #include 24 | #include 25 | using namespace g3::internal; 26 | 27 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 28 | #include 29 | #else 30 | #error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined 31 | #endif 32 | 33 | typedef std::chrono::high_resolution_clock::time_point time_point; 34 | typedef std::chrono::duration > millisecond; 35 | typedef std::chrono::duration > microsecond; 36 | 37 | namespace g3_test 38 | { 39 | enum WriteMode 40 | { 41 | kAppend = 0, 42 | kTruncate = 1 43 | }; 44 | 45 | const uint64_t g_loop{1}; 46 | const uint64_t g_iterations{1000000}; 47 | const char* charptrmsg = "\tmessage by char*"; 48 | const std::string strmsg{"\tmessage by string"}; 49 | float pi_f{3.1415926535897932384626433832795f}; 50 | 51 | 52 | bool writeTextToFile(const std::string& filename, const std::string& msg, const WriteMode write_mode, bool push_out = true) 53 | { 54 | if(push_out) 55 | { 56 | std::cout << msg << std::flush; 57 | } 58 | 59 | std::ofstream out; 60 | std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream 61 | (kTruncate == write_mode) ? mode |= std::ios_base::trunc : mode |= std::ios_base::app; 62 | out.open(filename.c_str(), mode); 63 | if (!out.is_open()) 64 | { 65 | std::ostringstream ss_error; 66 | ss_error << "Fatal error could not open log file:[" << filename << "]"; 67 | ss_error << "\n\t\t std::ios_base state = " << out.rdstate(); 68 | std::cerr << ss_error.str().c_str() << std::endl << std::flush; 69 | return false; 70 | } 71 | 72 | out << msg; 73 | return true; 74 | } 75 | 76 | uint64_t mean(const std::vector &v) 77 | { 78 | uint64_t total = std::accumulate(v.begin(), v.end(), uint64_t(0) ); // '0' is the initial value 79 | return total/v.size(); 80 | } 81 | 82 | 83 | 84 | 85 | void measurePeakDuringLogWrites(const std::string& title, std::vector& result); 86 | inline void measurePeakDuringLogWrites(const std::string& title, std::vector& result) 87 | { 88 | 89 | 90 | #if defined(G3LOG_PERFORMANCE) 91 | std::cout << "G3LOG (" << title << ") WORST_PEAK PERFORMANCE TEST" << std::endl; 92 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 93 | std::cout << "GOOGLE_GLOG (" << title << ") WORST_PEAK PERFORMANCE TEST" << std::endl; 94 | #else 95 | std::cout << "ERROR no performance type chosen" << std::endl; 96 | assert(false); 97 | #endif 98 | for(uint64_t count = 0; count < g_iterations; ++count) 99 | { 100 | auto start_time = std::chrono::high_resolution_clock::now(); 101 | LOG(INFO) << title << " iteration #" << count << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f; 102 | auto stop_time = std::chrono::high_resolution_clock::now(); 103 | uint64_t time_us = std::chrono::duration_cast(stop_time - start_time).count(); 104 | result.push_back(time_us); 105 | } 106 | } 107 | 108 | 109 | void doLogWrites(const std::string& title); 110 | inline void doLogWrites(const std::string& title) 111 | { 112 | #if defined(G3LOG_PERFORMANCE) 113 | std::cout << "G3LOG (" << title << ") PERFORMANCE TEST" << std::endl; 114 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 115 | std::cout << "GOOGLE_GLOG (" << title << ") PERFORMANCE TEST" << std::endl; 116 | #else 117 | std::cout << "ERROR no performance type chosen" << std::endl; 118 | assert(false); 119 | #endif 120 | for(uint64_t count = 0; count < g_iterations; ++count) 121 | { 122 | LOG(INFO) << title << " iteration #" << count << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f; 123 | } 124 | } 125 | 126 | 127 | } // end namespace 128 | -------------------------------------------------------------------------------- /test_unit/Test.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | # ============================================================================ 13 | # TEST OPTIONS: Turn OFF the ones that is of no interest to you 14 | # ---- by default unit tests and g3log-FATAL-example are enabled. 15 | # Performance tests are turned off by default since they were not tested on Windows. 16 | # ============================================================================ 17 | 18 | 19 | # Unit test for g3log (cmake -DUSE_G3LOG_UNIT_TEST=ON ..) 20 | option (ADD_G3LOG_UNIT_TEST "g3log unit tests" ON) 21 | 22 | 23 | # 4. create the unit tests for g3log --- ONLY TESTED THE UNIT TEST ON LINUX 24 | # ========================= 25 | IF (ADD_G3LOG_UNIT_TEST) 26 | # Download and unpack googletest at configure time 27 | configure_file(CMakeLists.txt.in 28 | googletest-download/CMakeLists.txt) 29 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 30 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 31 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 32 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 33 | 34 | # Prevent GoogleTest from overriding our compiler/linker options 35 | # when building with Visual Studio 36 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 37 | 38 | # Add googletest directly to our build. This adds 39 | # the following targets: gtest, gtest_main, gmock 40 | # and gmock_main 41 | add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src 42 | ${CMAKE_BINARY_DIR}/googletest-build) 43 | 44 | # The gtest/gmock targets carry header search path 45 | # dependencies automatically when using CMake 2.8.11 or 46 | # later. Otherwise we have to add them here ourselves. 47 | if (CMAKE_VERSION VERSION_LESS 2.8.11) 48 | include_directories("${gtest_SOURCE_DIR}/include" 49 | "${gmock_SOURCE_DIR}/include") 50 | endif() 51 | 52 | enable_testing() 53 | 54 | set(DIR_UNIT_TEST ${g3log_SOURCE_DIR}/test_unit) 55 | message( STATUS "-DADD_G3LOG_UNIT_TEST=ON" ) 56 | 57 | # obs see this: http://stackoverflow.com/questions/9589192/how-do-i-change-the-number-of-template-arguments-supported-by-msvcs-stdtupl 58 | # and this: http://stackoverflow.com/questions/2257464/google-test-and-visual-studio-2010-rc 59 | 60 | 61 | IF (MSVC OR MINGW) 62 | SET(OS_SPECIFIC_TEST test_crashhandler_windows) 63 | ENDIF(MSVC OR MINGW) 64 | 65 | SET(tests_to_run test_message test_filechange test_io test_fatal test_signal test_cpp_future_concepts test_concept_sink test_sink ${OS_SPECIFIC_TEST}) 66 | SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp) 67 | include_directories(${DIR_UNIT_TEST}) 68 | 69 | FOREACH(test ${tests_to_run} ) 70 | SET(all_tests ${all_tests} ${DIR_UNIT_TEST}/${test}.cpp ) 71 | IF(${test} STREQUAL "test_filechange") 72 | add_executable(test_filechange ${DIR_UNIT_TEST}/${test}.cpp ${helper}) 73 | ELSE() 74 | add_executable(${test} ${g3log_SOURCE_DIR}/test_main/test_main.cpp ${DIR_UNIT_TEST}/${test}.cpp ${helper}) 75 | ENDIF(${test} STREQUAL "test_filechange") 76 | 77 | set_target_properties(${test} PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0") 78 | set_target_properties(${test} PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0") 79 | IF( NOT(MSVC)) 80 | set_target_properties(${test} PROPERTIES COMPILE_FLAGS "-isystem -pthread ") 81 | ENDIF( NOT(MSVC)) 82 | target_link_libraries(${test} g3log gtest_main) 83 | add_test( ${test} ${test} ) 84 | ENDFOREACH(test) 85 | 86 | # 87 | # Test for Linux, runtime loading of dynamic libraries 88 | # 89 | IF (NOT WIN32 AND NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") AND G3_SHARED_LIB) 90 | add_library(tester_sharedlib SHARED ${DIR_UNIT_TEST}/tester_sharedlib.h ${DIR_UNIT_TEST}/tester_sharedlib.cpp) 91 | target_link_libraries(tester_sharedlib ${G3LOG_LIBRARY}) 92 | 93 | add_executable(test_dynamic_loaded_shared_lib ${g3log_SOURCE_DIR}/test_main/test_main.cpp ${DIR_UNIT_TEST}/test_linux_dynamic_loaded_sharedlib.cpp) 94 | set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0") 95 | set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0") 96 | target_link_libraries(test_dynamic_loaded_shared_lib ${G3LOG_LIBRARY} -ldl gtest_main) 97 | ENDIF() 98 | ELSE() 99 | message( STATUS "-DADD_G3LOG_UNIT_TEST=OFF" ) 100 | ENDIF (ADD_G3LOG_UNIT_TEST) 101 | -------------------------------------------------------------------------------- /test_unit/test_cpp_future_concepts.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "g3log/future.hpp" 19 | #include "g3log/time.hpp" 20 | 21 | std::future sillyFutureReturn() { 22 | std::packaged_task task([]() { return std::string("Hello Future"); }); // wrap the function 23 | std::future result = task.get_future(); // get a future 24 | std::thread(std::move(task)).detach(); // launch on a thread 25 | std::cout << "Waiting..."; 26 | result.wait(); 27 | return result; // already wasted 28 | } 29 | 30 | TEST(Configuration, FutureSilly) { 31 | std::string hello = sillyFutureReturn().get(); 32 | ASSERT_STREQ(hello.c_str(), "Hello Future"); 33 | } 34 | 35 | struct MsgType { 36 | std::string msg_; 37 | MsgType(std::string m) : 38 | msg_(m){}; 39 | std::string msg() { return msg_; } 40 | }; 41 | 42 | TEST(TestOf_CopyableCall, Expecting_SmoothSailing) { 43 | using namespace kjellkod; 44 | const std::string str("Hello from struct"); 45 | MsgType type(str); 46 | std::unique_ptr bgWorker(Active::createActive()); 47 | std::future fstring = 48 | g3::spawn_task(std::bind(&MsgType::msg, type), bgWorker.get()); 49 | ASSERT_STREQ(str.c_str(), fstring.get().c_str()); 50 | } 51 | 52 | TEST(TestOf_CopyableLambdaCall, Expecting_AllFine) { 53 | using namespace kjellkod; 54 | std::unique_ptr bgWorker(Active::createActive()); 55 | 56 | // lambda task 57 | const std::string str_standalone("Hello from standalone"); 58 | auto msg_lambda = [=]() { 59 | return (str_standalone + str_standalone); 60 | }; 61 | std::string expected(str_standalone + str_standalone); 62 | 63 | auto fstring_standalone = g3::spawn_task(msg_lambda, bgWorker.get()); 64 | ASSERT_STREQ(expected.c_str(), fstring_standalone.get().c_str()); 65 | } 66 | 67 | template 68 | std::future> ObsoleteSpawnTask(F f) { 69 | typedef std::invoke_result_t result_type; 70 | typedef std::packaged_task task_type; 71 | 72 | task_type task(std::move(f)); 73 | std::future result = task.get_future(); 74 | 75 | std::vector> vec; 76 | vec.push_back(g3::MoveOnCopy(std::move(task))); 77 | std::thread(std::move(vec.back())).detach(); 78 | result.wait(); 79 | return std::move(result); 80 | } 81 | 82 | TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString) { 83 | std::string str("Hello"); 84 | std::string expected(str + str); 85 | auto msg_lambda = [=]() { 86 | return (str + str); 87 | }; 88 | auto future_string = ObsoleteSpawnTask(msg_lambda); 89 | 90 | ASSERT_STREQ(expected.c_str(), future_string.get().c_str()); 91 | } 92 | // gcc thread example below 93 | // tests code below copied from mail-list conversion between 94 | // Lars Gullik Bjønnes and Jonathan Wakely 95 | // http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html 96 | 97 | // -------------------------------------------------------------- 98 | namespace WORKING { 99 | using namespace g3; 100 | 101 | #include 102 | 103 | #include 104 | #include 105 | #include 106 | #include 107 | 108 | std::vector> vec; 109 | 110 | template 111 | std::future> spawn_task(F f) { 112 | typedef std::invoke_result_t result_type; 113 | typedef std::packaged_task task_type; 114 | 115 | task_type task(std::move(f)); 116 | std::future res = task.get_future(); 117 | 118 | vec.push_back( 119 | MoveOnCopy( 120 | std::move(task))); 121 | 122 | std::thread([]() { 123 | auto task = std::move(vec.back()); 124 | vec.pop_back(); 125 | task(); 126 | }).detach(); 127 | 128 | return std::move(res); 129 | } 130 | 131 | double get_res() { 132 | return 42.2; 133 | } 134 | 135 | std::string msg3() { 136 | return "msg3"; 137 | } 138 | } // namespace WORKING 139 | 140 | TEST(Yalla, Testar) { 141 | using namespace WORKING; 142 | auto f = spawn_task(get_res); 143 | ASSERT_EQ(42.2, f.get()); 144 | 145 | auto f2 = spawn_task(msg3); 146 | ASSERT_EQ("msg3", f2.get()); 147 | 148 | ASSERT_TRUE(true); 149 | } 150 | -------------------------------------------------------------------------------- /test_unit/test_crashhandler_windows.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | 11 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 12 | #include 13 | #include "g3log/stacktrace_windows.hpp" 14 | 15 | 16 | TEST(CrashHandler_Windows, ExceptionType) { 17 | EXPECT_EQ(stacktrace::exceptionIdToText(123), "UNKNOWN EXCEPTION:123"); 18 | EXPECT_EQ(stacktrace::exceptionIdToText(1), "UNKNOWN EXCEPTION:1"); 19 | 20 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ACCESS_VIOLATION), "EXCEPTION_ACCESS_VIOLATION"); 21 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); 22 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_BREAKPOINT), "EXCEPTION_BREAKPOINT"); 23 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_DATATYPE_MISALIGNMENT), "EXCEPTION_DATATYPE_MISALIGNMENT"); 24 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DENORMAL_OPERAND), "EXCEPTION_FLT_DENORMAL_OPERAND"); 25 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DIVIDE_BY_ZERO), "EXCEPTION_FLT_DIVIDE_BY_ZERO"); 26 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT), "EXCEPTION_FLT_INEXACT_RESULT"); 27 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT), "EXCEPTION_FLT_INEXACT_RESULT"); 28 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INVALID_OPERATION), "EXCEPTION_FLT_INVALID_OPERATION"); 29 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_OVERFLOW), "EXCEPTION_FLT_OVERFLOW"); 30 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_STACK_CHECK), "EXCEPTION_FLT_STACK_CHECK"); 31 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_UNDERFLOW), "EXCEPTION_FLT_UNDERFLOW"); 32 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ILLEGAL_INSTRUCTION), "EXCEPTION_ILLEGAL_INSTRUCTION"); 33 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_IN_PAGE_ERROR), "EXCEPTION_IN_PAGE_ERROR"); 34 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_DIVIDE_BY_ZERO), "EXCEPTION_INT_DIVIDE_BY_ZERO"); 35 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_OVERFLOW), "EXCEPTION_INT_OVERFLOW"); 36 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INVALID_DISPOSITION), "EXCEPTION_INVALID_DISPOSITION"); 37 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_NONCONTINUABLE_EXCEPTION), "EXCEPTION_NONCONTINUABLE_EXCEPTION"); 38 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_PRIV_INSTRUCTION), "EXCEPTION_PRIV_INSTRUCTION"); 39 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_SINGLE_STEP), "EXCEPTION_SINGLE_STEP"); 40 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_STACK_OVERFLOW), "EXCEPTION_STACK_OVERFLOW"); 41 | } 42 | 43 | #endif // defined WIN32 44 | -------------------------------------------------------------------------------- /test_unit/test_filechange.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include "g3log/g3log.hpp" 18 | #include "g3log/logworker.hpp" 19 | #include "testing_helpers.h" 20 | 21 | using namespace testing_helpers; 22 | 23 | namespace { // anonymous 24 | const char* name_path_1 = "./(some_fake_DirectoryOrName_1_)"; 25 | const std::string kReplaceFileName = "(ReplaceLogFile)"; 26 | g3::LogWorker* g_logger_ptr = nullptr; 27 | g3::SinkHandle* g_filesink_handler = nullptr; 28 | LogFileCleaner* g_cleaner_ptr = nullptr; 29 | 30 | std::string setLogNameAndAddCount(std::string new_file_to_create, std::string logger_id = "g3log") { 31 | static std::mutex m; 32 | static int count; 33 | std::string add_count; 34 | std::lock_guard lock(m); 35 | { 36 | add_count = std::to_string(++count) + "_"; 37 | auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create + add_count, logger_id); 38 | auto new_log = future_new_log.get(); 39 | if (!new_log.empty()) { 40 | g_cleaner_ptr->addLogToClean(new_log); 41 | } else { 42 | std::cout << "\nFailed to set filename: " << new_file_to_create << std::endl; 43 | } 44 | return new_log; 45 | } 46 | return add_count; 47 | } 48 | 49 | std::string setLogName(std::string new_file_to_create, std::string logger_id = "g3log") { 50 | auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create, logger_id); 51 | auto new_log = future_new_log.get(); 52 | if (!new_log.empty()) 53 | g_cleaner_ptr->addLogToClean(new_log); 54 | return new_log; 55 | } 56 | 57 | std::string getLogName() { 58 | return g_filesink_handler->call(&g3::FileSink::fileName).get(); 59 | } 60 | 61 | } // namespace 62 | 63 | TEST(TestOf_GetFileName, Expecting_ValidLogFile) { 64 | 65 | LOG(INFO) << "test_filechange, Retrieving file name: "; 66 | ASSERT_NE(g_logger_ptr, nullptr); 67 | ASSERT_FALSE(getLogName().empty()); 68 | } 69 | 70 | TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed) { 71 | auto old_log = getLogName(); 72 | std::string name = setLogNameAndAddCount(name_path_1); 73 | auto new_log = setLogName(name); 74 | ASSERT_NE(old_log, new_log); 75 | } 76 | 77 | TEST(TestOf_ChangingLogFile_Id, Expecting_NewLogFileUsed1) { 78 | auto old_log = getLogName(); 79 | setLogNameAndAddCount(name_path_1); 80 | auto new_log = setLogName("foo", "new_logger_id"); 81 | ASSERT_NE(old_log, new_log); 82 | std::string new_name = getLogName(); 83 | auto expected_part_of__new_name = std::string("foo") + kReplaceFileName + ".new_logger_id"; 84 | auto extracted_name = new_name.substr(0, expected_part_of__new_name.size()); 85 | ASSERT_EQ(extracted_name.c_str(), expected_part_of__new_name); 86 | } 87 | 88 | TEST(TestOf_ChangingLogFile_NoId, Expecting_NewLogFileUsed2) { 89 | auto old_log = getLogName(); 90 | setLogNameAndAddCount(name_path_1); 91 | auto new_log = setLogName("foo", ""); 92 | ASSERT_NE(old_log, new_log); 93 | std::string new_name = getLogName(); 94 | auto expected_part_of__new_name = std::string("foo") + kReplaceFileName; 95 | auto extracted_name = new_name.substr(0, expected_part_of__new_name.size()); 96 | ASSERT_EQ(extracted_name.c_str(), expected_part_of__new_name); 97 | } 98 | 99 | TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) { 100 | auto old_log = g_filesink_handler->call(&g3::FileSink::fileName).get(); 101 | if (!old_log.empty()) 102 | g_cleaner_ptr->addLogToClean(old_log); 103 | 104 | LOG(INFO) << "SoManyThreadsAllDoingChangeFileName"; 105 | std::vector threads; 106 | auto max = 2; 107 | auto size = g_cleaner_ptr->size(); 108 | for (auto count = 0; count < max; ++count) { 109 | std::string drive = ((count % 2) == 0) ? "./_threadEven_" : "./_threaOdd_"; 110 | std::string logger_id = std::to_string(count); 111 | threads.push_back(std::thread(setLogNameAndAddCount, drive, logger_id)); 112 | } 113 | for (auto& thread : threads) 114 | thread.join(); 115 | 116 | // check that all logs were created 117 | ASSERT_EQ(size + max, g_cleaner_ptr->size()); 118 | } 119 | 120 | TEST(TestOf_IllegalLogFileName, Expecting_NoChangeToOriginalFileName) { 121 | std::string original = getLogName(); 122 | auto perhaps_a_name = setLogName("XY:/"); // does not exist 123 | ASSERT_TRUE(perhaps_a_name.empty()); 124 | std::string post_illegal = getLogName(); 125 | ASSERT_STREQ(original.c_str(), post_illegal.c_str()); 126 | } 127 | 128 | TEST(TestOf_SinkHandleDifferentId, Expecting_DifferentId) { 129 | auto sink = std::make_unique("AnotherLogFile", name_path_1, "logger_id"); 130 | auto name = sink->fileName(); 131 | ASSERT_STREQ(name.substr(0, 26).c_str(), "./AnotherLogFile.logger_id"); 132 | g_cleaner_ptr->addLogToClean(name); 133 | } 134 | 135 | TEST(TestOf_LegalLogFileNam, With_parenthesis) { 136 | std::string original = getLogName(); 137 | auto perhaps_a_name = setLogName("(test)"); // does not exist 138 | EXPECT_NE(original, perhaps_a_name); 139 | std::string post_legal = getLogName(); 140 | EXPECT_TRUE(std::string::npos != post_legal.find("(test)")) << "filename was: " << post_legal; 141 | } 142 | 143 | int main(int argc, char* argv[]) { 144 | LogFileCleaner cleaner; 145 | g_cleaner_ptr = &cleaner; 146 | int return_value = 1; 147 | std::stringstream cerrDump; 148 | 149 | std::string last_log_file; 150 | { 151 | 152 | testing_helpers::ScopedOut scopedCerr(std::cerr, &cerrDump); 153 | 154 | auto worker = g3::LogWorker::createLogWorker(); 155 | auto handle = worker->addDefaultLogger(kReplaceFileName, name_path_1); 156 | g_logger_ptr = worker.get(); 157 | g_filesink_handler = handle.get(); 158 | last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get(); 159 | std::cout << "log file at: " << last_log_file << std::endl; 160 | cleaner.addLogToClean(last_log_file); 161 | 162 | g3::initializeLogging(g_logger_ptr); 163 | LOG(INFO) << "test_filechange demo*" << std::endl; 164 | 165 | testing::InitGoogleTest(&argc, argv); 166 | return_value = RUN_ALL_TESTS(); 167 | 168 | last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get(); 169 | std::cout << "log file at: " << last_log_file << std::endl; 170 | //g3::internal::shutDownLogging(); 171 | } 172 | std::cout << "FINISHED WITH THE TESTING" << std::endl; 173 | // cleaning up 174 | cleaner.addLogToClean(last_log_file); 175 | return return_value; 176 | } -------------------------------------------------------------------------------- /test_unit/test_linux_dynamic_loaded_sharedlib.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include "tester_sharedlib.h" 20 | 21 | struct LogMessageCounter { 22 | std::vector& bank; 23 | LogMessageCounter(std::vector& storeMessages) : 24 | bank(storeMessages) { 25 | } 26 | 27 | void countMessages(std::string msg) { 28 | bank.push_back(msg); 29 | } 30 | }; 31 | 32 | TEST(DynamicLoadOfLibrary, JustLoadAndExit) { 33 | std::vector receiver; 34 | 35 | { // scope to flush logs at logworker exit 36 | auto worker = g3::LogWorker::createLogWorker(); 37 | auto handle = worker->addSink(std::make_unique(std::ref(receiver)), &LogMessageCounter::countMessages); 38 | 39 | // add another sink just for more throughput of data 40 | auto fileHandle = worker->addSink(std::make_unique("runtimeLoadOfDynamiclibs", "/tmp"), &g3::FileSink::fileWrite); 41 | g3::initializeLogging(worker.get()); 42 | 43 | void* libHandle = dlopen("libtester_sharedlib.so", RTLD_LAZY | RTLD_GLOBAL); 44 | EXPECT_FALSE(nullptr == libHandle); 45 | LibraryFactory* factory = reinterpret_cast((dlsym(libHandle, "testRealFactory"))); 46 | EXPECT_FALSE(nullptr == factory); 47 | SomeLibrary* loadedLibrary = factory->CreateLibrary(); 48 | 49 | for (auto i = 0; i < 300; ++i) { 50 | loadedLibrary->action(); 51 | } 52 | 53 | delete loadedLibrary; 54 | dlclose(libHandle); 55 | } // scope exit. All log entries must be flushed now 56 | const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library 57 | EXPECT_EQ(receiver.size(), numberOfMessages); 58 | } 59 | -------------------------------------------------------------------------------- /test_unit/test_signal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "g3log/crashhandler.hpp" 3 | 4 | #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 5 | #include 6 | #include 7 | 8 | class SignalHandlingTest : public ::testing::Test { 9 | protected: 10 | int original_stderr; 11 | int pipefd[2]; 12 | FILE* temp_stderr; 13 | 14 | void SetUp() override { 15 | // Redirect stderr to a pipe 16 | ASSERT_EQ(pipe(pipefd), 0); 17 | original_stderr = dup(STDERR_FILENO); 18 | ASSERT_NE(original_stderr, -1); 19 | ASSERT_NE(dup2(pipefd[1], STDERR_FILENO), -1); 20 | temp_stderr = fdopen(pipefd[1], "w"); 21 | setvbuf(temp_stderr, NULL, _IONBF, 0); // Disable buffering 22 | 23 | // Set the read end of the pipe to non-blocking mode 24 | // so we can verify when buffer is empty 25 | int flags = fcntl(pipefd[0], F_GETFL, 0); 26 | fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK); 27 | } 28 | 29 | void TearDown() override { 30 | // Restore the original stderr 31 | fclose(temp_stderr); 32 | close(pipefd[0]); 33 | close(pipefd[1]); 34 | dup2(original_stderr, STDERR_FILENO); 35 | close(original_stderr); 36 | } 37 | 38 | std::string ReadStderr() { 39 | char buffer[1024]; 40 | ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1); 41 | if (bytes_read >= 0) { 42 | buffer[bytes_read] = '\0'; // Null-terminate the string 43 | return std::string(buffer); 44 | } 45 | return ""; 46 | } 47 | }; 48 | 49 | TEST_F(SignalHandlingTest, WriteErrorMessage_WritesToStderr) { 50 | const char* test_message = "Test error message"; 51 | g3::internal::writeErrorMessage(test_message); 52 | std::string output = ReadStderr(); 53 | ASSERT_EQ(output, test_message); 54 | } 55 | 56 | TEST_F(SignalHandlingTest, WriteErrorMessage_Nullptr_DoesNotWriteToStderr) { 57 | g3::internal::writeErrorMessage(nullptr); 58 | std::string output = ReadStderr(); 59 | ASSERT_TRUE(output.empty()); 60 | } 61 | 62 | #endif // #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) -------------------------------------------------------------------------------- /test_unit/tester_sharedlib.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "tester_sharedlib.h" 10 | #include 11 | #include 12 | 13 | struct RuntimeLoadedLib : public SomeLibrary { 14 | 15 | RuntimeLoadedLib() { 16 | LOG(INFO) << "Library was created"; 17 | LOGF(INFO, "Ready for testing"); 18 | } 19 | 20 | ~RuntimeLoadedLib() { 21 | LOG(G3LOG_DEBUG) << "Library destroyed"; 22 | } 23 | 24 | void action() { 25 | LOG(WARNING) << "Action, action, action. Safe for LOG calls by runtime dynamically loaded libraries"; 26 | } 27 | }; 28 | 29 | struct RealLibraryFactory : public LibraryFactory { 30 | SomeLibrary* CreateLibrary() { 31 | return new RuntimeLoadedLib; 32 | } 33 | }; 34 | 35 | RealLibraryFactory testRealFactory; 36 | -------------------------------------------------------------------------------- /test_unit/tester_sharedlib.h: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #pragma once 11 | 12 | struct SomeLibrary { 13 | 14 | SomeLibrary() {}; 15 | 16 | virtual ~SomeLibrary() {}; 17 | virtual void action() = 0; 18 | }; 19 | 20 | class LibraryFactory { 21 | public: 22 | virtual SomeLibrary* CreateLibrary() = 0; 23 | }; 24 | -------------------------------------------------------------------------------- /test_unit/testing_helpers.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "testing_helpers.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace std; 20 | using namespace g3; 21 | 22 | namespace testing_helpers { 23 | 24 | std::string g_mockFatal_message = {}; 25 | int g_mockFatal_signal = -1; 26 | bool g_mockFatalWasCalled = false; 27 | const size_t kFlushToDiskWithThisInterval = 2; 28 | 29 | std::string mockFatalMessage() { 30 | return g_mockFatal_message; 31 | } 32 | 33 | int mockFatalSignal() { 34 | return g_mockFatal_signal; 35 | } 36 | 37 | bool mockFatalWasCalled() { 38 | return g_mockFatalWasCalled; 39 | } 40 | 41 | void mockFatalCall(FatalMessagePtr fatal_message) { 42 | g_mockFatal_message = fatal_message.get()->toString(); 43 | g_mockFatal_signal = fatal_message.get()->_signal_id; 44 | g_mockFatalWasCalled = true; 45 | LogMessagePtr message{fatal_message.release()}; 46 | g3::internal::pushMessageToLogger(message); //fatal_message.copyToLogMessage()); 47 | } 48 | 49 | void clearMockFatal() { 50 | g_mockFatal_message.clear(); 51 | g_mockFatal_signal = -1; 52 | g_mockFatalWasCalled = false; 53 | } 54 | 55 | bool removeFile(std::string path_to_file) { 56 | return (0 == std::remove(path_to_file.c_str())); 57 | } 58 | 59 | bool verifyContent(const std::string& total_text, std::string msg_to_find) { 60 | std::string content(total_text); 61 | size_t location = content.find(msg_to_find); 62 | return (location != std::string::npos); 63 | } 64 | 65 | std::string readFileToText(std::string filename) { 66 | std::ifstream in; 67 | in.open(filename.c_str(), std::ios_base::in); 68 | if (!in.is_open()) { 69 | return {}; // error just return empty string - test will 'fault' 70 | } 71 | std::ostringstream oss; 72 | oss << in.rdbuf(); 73 | return oss.str(); 74 | // RAII of std::ifstream will automatically close the file 75 | } 76 | 77 | size_t LogFileCleaner::size() { 78 | return logs_to_clean_.size(); 79 | } 80 | 81 | LogFileCleaner::~LogFileCleaner() { 82 | std::lock_guard lock(g_mutex); 83 | { 84 | for (const auto& file : logs_to_clean_) { 85 | if (!removeFile(file)) { 86 | ADD_FAILURE() << "UNABLE to remove: " << file << std::endl; 87 | } 88 | } 89 | logs_to_clean_.clear(); 90 | } // mutex 91 | } 92 | 93 | void LogFileCleaner::addLogToClean(std::string path_to_log) { 94 | std::lock_guard lock(g_mutex); 95 | { 96 | if (std::find(logs_to_clean_.begin(), logs_to_clean_.end(), path_to_log.c_str()) == logs_to_clean_.end()) 97 | logs_to_clean_.push_back(path_to_log); 98 | } 99 | } 100 | 101 | ScopedLogger::ScopedLogger() : 102 | _currentWorker(g3::LogWorker::createLogWorker()) {} 103 | ScopedLogger::~ScopedLogger() {} 104 | 105 | g3::LogWorker* ScopedLogger::get() { 106 | return _currentWorker.get(); 107 | } 108 | 109 | RestoreFileLogger::RestoreFileLogger(std::string directory) : 110 | _scope(new ScopedLogger), 111 | _handle(_scope->get()->addSink(std::make_unique("UNIT_TEST_LOGGER", directory, "g3log", kFlushToDiskWithThisInterval), &g3::FileSink::fileWrite)) { 112 | using namespace g3; 113 | g3::initializeLogging(_scope->_currentWorker.get()); 114 | clearMockFatal(); 115 | setFatalExitHandler(&mockFatalCall); 116 | 117 | auto filename = _handle->call(&FileSink::fileName); 118 | if (!filename.valid()) 119 | ADD_FAILURE(); 120 | _log_file = filename.get(); 121 | 122 | #ifdef G3_DYNAMIC_LOGGING 123 | g3::only_change_at_initialization::addLogLevel(INFO, true); 124 | g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, true); 125 | g3::only_change_at_initialization::addLogLevel(WARNING, true); 126 | g3::only_change_at_initialization::addLogLevel(FATAL, true); 127 | #endif 128 | } 129 | 130 | RestoreFileLogger::~RestoreFileLogger() { 131 | g3::internal::shutDownLogging(); // is done at reset. Added for test clarity 132 | reset(); 133 | 134 | if (!removeFile(_log_file)) 135 | ADD_FAILURE(); 136 | } 137 | 138 | std::string RestoreFileLogger::logFile() { 139 | if (_scope) { 140 | // beware for race condition 141 | // example: 142 | // LOG(INFO) << ... 143 | // auto file = logger.logFile() 144 | // auto content = ReadContentFromFile(file) 145 | // ... it is not guaranteed that the content will contain (yet) the LOG(INFO) 146 | std::future filename = _handle->call(&g3::FileSink::fileName); 147 | _log_file = filename.get(); 148 | } 149 | return _log_file; 150 | } 151 | 152 | // Beware of race between LOG(...) and this function. 153 | // since LOG(...) passes two queues but the handle::call only passes one queue 154 | // the handle::call can happen faster 155 | std::string RestoreFileLogger::resetAndRetrieveContent() { 156 | std::future filename = _handle->call(&g3::FileSink::fileName); 157 | reset(); // flush all queues to sinks 158 | EXPECT_TRUE(filename.valid()); 159 | auto file = filename.get(); 160 | return readFileToText(file); 161 | } 162 | } // namespace testing_helpers 163 | -------------------------------------------------------------------------------- /test_unit/testing_helpers.h: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "g3log/logworker.hpp" 18 | #include "g3log/logmessage.hpp" 19 | #include "g3log/filesink.hpp" 20 | 21 | namespace testing_helpers { 22 | 23 | std::string mockFatalMessage(); 24 | int mockFatalSignal(); 25 | bool mockFatalWasCalled(); 26 | void mockFatalCall(g3::FatalMessagePtr fatal_message); 27 | void clearMockFatal(); 28 | 29 | bool removeFile(std::string path_to_file); 30 | bool verifyContent(const std::string& total_text, std::string msg_to_find); 31 | std::string readFileToText(std::string filename); 32 | 33 | 34 | 35 | /** After initializing ScopedCout all std::couts is redirected to the buffer 36 | @verbatim 37 | Example: 38 | stringstream buffer; 39 | ScopedCout guard(std::cout, &buffer); // std::cerr is also fine 40 | cout << "Hello World"; 41 | ASSERT_STREQ(buffer.str().c_str(), "Hello World"); */ 42 | class ScopedOut { 43 | std::ostream& _out_type; 44 | std::streambuf* _old_cout; 45 | public: 46 | explicit ScopedOut(std::ostream& out_type, std::stringstream* buffer) 47 | : _out_type(out_type) 48 | , _old_cout(_out_type.rdbuf()) { 49 | _out_type.rdbuf(buffer->rdbuf()); 50 | } 51 | 52 | virtual ~ScopedOut() { 53 | _out_type.rdbuf(_old_cout); 54 | } 55 | }; 56 | 57 | /// RAII cluttering files cleanup 58 | class LogFileCleaner { 59 | private: 60 | std::vector logs_to_clean_; 61 | std::mutex g_mutex; 62 | public: 63 | size_t size(); 64 | LogFileCleaner() {} 65 | virtual ~LogFileCleaner(); 66 | void addLogToClean(std::string path_to_log); 67 | }; 68 | 69 | 70 | struct ScopedLogger { 71 | ScopedLogger(); 72 | virtual ~ScopedLogger(); 73 | 74 | g3::LogWorker* get(); 75 | std::unique_ptr _currentWorker; 76 | }; 77 | 78 | /** RAII temporarily replace of logger 79 | * and restoration of original logger at scope end*/ 80 | struct RestoreFileLogger { 81 | explicit RestoreFileLogger(std::string directory); 82 | ~RestoreFileLogger(); 83 | 84 | std::unique_ptr _scope; 85 | void reset() { _scope.reset();} 86 | 87 | 88 | template 89 | std::invoke_result_t callToLogger(Call call, Args&& ... args) { 90 | auto func = std::bind(call, _scope->get(), std::forward(args)...); 91 | return func(); 92 | } 93 | 94 | std::string logFile(); 95 | std::string resetAndRetrieveContent(); 96 | std::unique_ptr> _handle; 97 | std::string _log_file; 98 | }; 99 | 100 | typedef std::shared_ptr> AtomicBoolPtr; 101 | typedef std::shared_ptr> AtomicIntPtr; 102 | struct ScopedSetTrue { 103 | AtomicBoolPtr _flag; 104 | AtomicIntPtr _count; 105 | 106 | explicit ScopedSetTrue(AtomicBoolPtr flag, AtomicIntPtr count) 107 | : _flag(flag), _count(count) { 108 | } 109 | 110 | void ReceiveMsg(std::string message) { 111 | std::chrono::milliseconds wait{100}; 112 | std::this_thread::sleep_for(wait); 113 | ++(*_count); 114 | } 115 | 116 | ~ScopedSetTrue() { 117 | (*_flag) = true; 118 | } 119 | }; 120 | } // testing_helpers 121 | 122 | #ifdef CHANGE_G3LOG_DEBUG_TO_DBUG 123 | #undef DEBUG 124 | #define DEBUG DBUG 125 | #endif 126 | 127 | 128 | --------------------------------------------------------------------------------