Compare commits

..

No commits in common. "master" and "1.2" have entirely different histories.
master ... 1.2

103 changed files with 3069 additions and 6298 deletions

View File

@ -1,83 +0,0 @@
# Google C/C++ Code Style settings
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# Author: Kehan Xue, kehan.xue (at) gmail.com
Language: Cpp
BasedOnStyle: Google
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: None
AlignOperands: Align
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never # To avoid conflict, set this "Never" and each "if statement" should include brace when coding
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: true
PackConstructorInitializers: Never
BreakBeforeBraces: Attach
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterStruct: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: BeforeColon
ColumnLimit: 0
CompactNamespaces: false
ContinuationIndentWidth: 3
Cpp11BracedListStyle: true
DerivePointerAlignment: false # Make sure the * or & align on the left
EmptyLineBeforeAccessModifier: LogicalBlock
FixNamespaceComments: true
IncludeBlocks: Preserve
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 3
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Left
ReflowComments: false
# SeparateDefinitionBlocks: Always # Only support since clang-format 14
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++11
TabWidth: 3
UseTab: Never

View File

@ -1,19 +0,0 @@
# Latest Debian
FROM mcr.microsoft.com/devcontainers/cpp:debian
ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none"
# Optionally install the cmake for vcpkg
COPY ./reinstall_cmake.sh /tmp/
RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \
chmod +x /tmp/reinstall_cmake.sh && /tmp/reinstall_cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \
fi \
&& rm -f /tmp/reinstall_cmake.sh
# [Optional] Uncomment this section to install additional vcpkg ports.
# RUN su vscode -c "${VCPKG_ROOT}/vcpkg install <your-port-name-here>"
# [Optional] Uncomment this section to install additional packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>

View File

@ -1,34 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/cpp
{
"name": "C++",
"build": {
"dockerfile": "Dockerfile"
},
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
"settings": {},
"extensions": [
"streetsidesoftware.code-spell-checker",
"genieai.chatgpt-vscode",
"ms-vscode.cpptools-extension-pack",
"ms-vscode.cpptools"
]
}
}
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "gcc -v",
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@ -1,60 +0,0 @@
#!/usr/bin/env bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# testing, unsure if it's needed. following steps from example: https://github.com/microsoft/vscode-remote-try-cpp/tree/main/.devcontainer
set -e
CMAKE_VERSION=${1:-"none"}
if [ "${CMAKE_VERSION}" = "none" ]; then
echo "No CMake version specified, skipping CMake reinstallation"
exit 0
fi
# Cleanup temporary directory and associated files when exiting the script.
cleanup() {
EXIT_CODE=$?
set +e
if [[ -n "${TMP_DIR}" ]]; then
echo "Executing cleanup of tmp files"
rm -Rf "${TMP_DIR}"
fi
exit $EXIT_CODE
}
trap cleanup EXIT
echo "Installing CMake..."
apt-get -y purge --auto-remove cmake
mkdir -p /opt/cmake
architecture=$(dpkg --print-architecture)
case "${architecture}" in
arm64)
ARCH=aarch64 ;;
amd64)
ARCH=x86_64 ;;
*)
echo "Unsupported architecture ${architecture}."
exit 1
;;
esac
CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh"
CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt"
TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX)
echo "${TMP_DIR}"
cd "${TMP_DIR}"
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O
curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O
sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}"
sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake

View File

@ -1,19 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: under investigation
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
- What system, compiler etc was used to run this?
- Is something done differently on your setup than what the documentation specifies?
- Have you provided a code snippet that can be tested?
**Additional context**
Add any other context about the problem here.

View File

@ -1,21 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
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.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -1,20 +0,0 @@
---
name: Question
about: Question to the community, request for help to troubleshoot or undersand
title: "[Question]"
labels: question
assignees: ''
---
### Uncertain what's going on? Do you need advice or help troubleshooting?
A clear and concise description of what the problem is.
### Describe the solution you'd like
A clear and concise description of what you want to happen.
### Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
### Additional context
Add any other context or screenshots that might help.

View File

@ -1,49 +0,0 @@
name: ci/action ctest Ubuntu v3
on:
push:
paths-ignore:
- docs/**
- '**.md'
- '**.markdown'
- '**.yml'
branches: [ master ]
pull_request:
paths-ignore:
- docs/**
- '**.md'
- '**.markdown'
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Print env
run: |
echo github.event.action: ${{ github.event.action }}
echo github.event_name: ${{ github.event_name }}
gcc --version
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install ninja-build cmake gcc-9 g++-9
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
echo gcc version after
gcc --version
cmake --version
# example: https://gist.github.com/NickNaso/0d478f1481686d5bcc868cac06620a60
- name: configure
shell: bash
# run: ./configure
run: ./scripts/buildAndRunTests.sh
#- name: make
# run: make
#- name: make check
# run: make check
#- name: make distcheck
# run: make distcheck

View File

@ -1,76 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "ci/action CodeQL"
on:
push:
paths-ignore:
- docs/**
- '**.md'
- '**.markdown'
- '**.yml'
branches: [ master ]
pull_request:
paths-ignore:
- docs/**
- '**.md'
- '**.markdown'
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '17 8 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'cmake' ]
# Learn more:
# 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
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View File

@ -1,87 +0,0 @@
name: matrix (ubuntu, macos, windows) ctes
on:
push:
paths-ignore:
- docs/**
- "**.md"
- "**.markdown"
branches:
- master
pull_request:
paths-ignore:
- docs/**
- "**.md"
- "**.markdown"
- '**.yml'
branches:
- master
env:
BUILD_TYPE: Release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
runs-on: ${{ matrix.os }}
steps:
# checkout full depth of history.
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Linux Build
if: matrix.os == 'ubuntu-latest'
run: echo "Ubuntu Latest" > release_ubuntu
- name: Run Mac Build
if: matrix.os == 'macos-latest'
run: echo "MacOS Latest" > release_mac
- name: Run Windows Build
if: matrix.os == 'windows-latest'
run: echo "Windows Latest" > release_windows
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build
- name: Configure Linux/OSX CMake
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
shell: bash
working-directory: ${{github.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DADD_G3LOG_UNIT_TEST=ON
- name: Configure Windows CMake
if: matrix.os == 'windows-latest'
shell: cmd
working-directory: ${{github.workspace}}/build
run: ls && cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DADD_G3LOG_UNIT_TEST=ON ..
- name: Build Linux/OSx
working-directory: ${{github.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE
- name: Test
working-directory: ${{github.workspace}}/build
shell: bash
run: ctest -V
- name: Fatal Exit Example Linux/OSX
working-directory: ${{github.workspace}}/build
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
shell: bash
run: ./g3log-FATAL-sigsegv || true && echo -e
"\n\nverifying SIGSEGV existed in stackdump\n\n\n\n" && cat /tmp/*3log*FATAL*.log && cat /tmp/g3log*FATAL*.log | grep "SIGSEGV"
- name: Fatal Exit Example Windows
working-directory: ${{github.workspace}}/build
if: matrix.os == 'windows-latest'
shell: bash
run: ./Release/g3log-FATAL-sigsegv.exe || true && echo -e "\n\nverifying SIGSEGV - EXCEPTION_ACCESS_VIOLATION existed in
stackdump\n\n\n\n" && cat *3log*FATAL*.log && cat *3log*FATAL*.log | grep "EXCEPTION_ACCESS_VIOLATION"

View File

@ -1,31 +0,0 @@
# Locally you can try this out also with `mkdocs serve`
# Remember if doing changes that github pages, need to deploy branch gh_pages
# which points to root.
name: Dockumentation Publish v2
on:
push:
branches:
- master
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure Git Credentials
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
key: mkdocs-material-${{ env.cache_id }}
path: .cache
restore-keys: |
mkdocs-material-
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force

2
.gitignore vendored
View File

@ -1,8 +1,10 @@
syntax: glob
sinks/*
3rdParty/*
build/*
nbproject/*
build_clang
build_travis
gtest-1.7.0
generated_definitions.hpp
*~

58
.travis.yml Normal file
View File

@ -0,0 +1,58 @@
language: cpp
os:
- linux
compiler:
- gcc
#- clang
# whitelist
branches:
only:
- master
before_install:
# use http://lint.travis-ci.org/ to validate changes
# sudo add-apt-repository -y ppa:h-rayflood/llvm;
# sudo apt-get install --allow-unauthenticated -qq clang-3.4
if [ ${TRAVIS_OS_NAME} = 'linux' ];
then
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test;
sudo apt-get update -qq;
sudo apt-get install python-software-properties;
sudo apt-get update;
sudo apt-get install gcc-4.8 g++-4.8;
sudo add-apt-repository --yes ppa:kalakris/cmake;
sudo apt-get update -qq;
sudo apt-get install cmake;
fi
install:
# gcc 4.8
- if [ "$CXX" == "g++" ]; then sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 10; fi
- if [ "$CXX" == "g++" ]; then sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 10; fi
# clang 3.4
- if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi
- if [ "$CXX" == "clang++" ]; then sudo apt-get install libc++1 libc++abi-dev || true; fi
- if [ "$CXX" == "clang++" ]; then export CXXFLAGS="-std=c++0x -stdlib=libc++"; fi
- if [ "$CXX" == "clang++" ]; then svn co --quiet http://llvm.org/svn/llvm-project/libcxx/trunk libcxx; fi
- if [ "$CXX" == "clang++" ]; then cd libcxx/lib && bash buildit; fi
- if [ "$CXX" == "clang++" ]; then sudo cp ./libc++.so.1.0 /usr/lib/; fi
- if [ "$CXX" == "clang++" ]; then sudo mkdir /usr/include/c++/v1; fi
- if [ "$CXX" == "clang++" ]; then cd .. && sudo cp -r include/* /usr/include/c++/v1/; fi
- if [ "$CXX" == "clang++" ]; then cd /usr/lib && sudo ln -sf libc++.so.1.0 libc++.so; fi
- if [ "$CXX" == "clang++" ]; then sudo ln -sf libc++.so.1.0 libc++.so.1 && cd $cwd; fi
# - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then brew update; fi
- echo $PWD
- echo $CXX
- if [ "$CXX" == "clang++" ]; then cd build/KjellKod/g3log || true; fi
- echo $PWD
script: "./scripts/buildAndRunTests.sh"

View File

@ -1,13 +0,0 @@
node_modules/
dist/
vendor/
cache/
.*/
*.min.*
*.test.*
*.spec.*
*.bundle.*
*.bundle-min.*
*.*.js
*.*.ts
*.log

23
.vscode/launch.json vendored
View File

@ -1,23 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// Remember to build the specific part of cmake with
// mkdir build; cd build
// "cmake -DCMAKE_BUILD_TYPE=Debug .. " if you want to be able to debug it.
// don't forget to inspect the cmake output for more configuration options
"configurations": [
{
"name": "(lldb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/test_signal",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
}
]
}

View File

@ -1,7 +0,0 @@
{
"cmake.configureOnOpen": false,
"editor.formatOnSave": true,
"files.associations": {
"ostream": "cpp"
}
}

BIN
3rdParty/gtest/gtest-1.7.0.zip vendored Normal file

Binary file not shown.

187
API.markdown Normal file
View File

@ -0,0 +1,187 @@
# API description
Most of the API that you need for using g3log is described in this readme. For more API documentation and examples please continue to read the [API readme](API.markdown). Examples of what you will find here are:
* Logging API: LOG calls
* Contract API: CHECK calls
* Logging levels
* disable/enabled levels at runtime
* custom logging levels
* Sink [creation](#sink_creation) and utilization
* LOG [flushing](#log_flushing)
* Fatal handling
* [Linux/*nix](#fatal_handling_linux)
* <strike>[TOWRITE: Windows](#fatal_handling_windows)</strike>
* <strike>[TOWRITE: Custom fatal handling](#fatal_custom_handling)</strike>
* <strike>[TOWRITE: Pre fatal hook](#fatal_pre_hook)</strike>
* <strike>[TOWRITE: Override of signal handling](#fatal_signalhandler_override)</strike>
* <strike>[TOWRITE: Disable fatal handling](#fatal_handling_disabled)</strike>
* Build Options
## Logging API: LOG calls
It is optional to use either streaming ```LOG(INFO) << "some text" ``` or printf-like syntax ```LOGF(WARNING, "some number %d", 123); ```
Conditional logging is made with ```LOG_IF(INFO, <boolean-expression>) << " some text" ``` or ```LOGF_IF(WARNING, <boolean-expression>) << " some text".``` Only if the expressions evaluates to ```true``` will the logging take place.
Example:
```LOG_IF(INFO, 1 != 200) << " some text";``` or ```LOG_IF(FATAL, SomeFunctionCall()) << " some text";```
*<a name="fatal_logging">A call using FATAL</a> logging level, such as the ```LOG_IF(FATAL,...)``` example above, will after logging the message at ```FATAL```level also kill the process. It is essentially the same as a ```CHECK(<boolea-expression>) << ...``` with the difference that the ```CHECK(<boolean-expression)``` triggers when the expression evaluates to ```false```.*
## Contract API: CHECK calls
The contract API follows closely the logging API with ```CHECK(<boolean-expression>) << ...``` for streaming or (*) ```CHECKF(<boolean-expression>, ...);``` for printf-style.
If the ```<boolean-expression>``` evaluates to false then the the message for the failed contract will be logged in FIFO order with previously made messages. The process will then shut down after the message is sent to the sinks and the sinks have dealt with the fatal contract message.
(\* * ```CHECK_F(<boolean-expression>, ...);``` was the the previous API for printf-like CHECK. It is still kept for backwards compatability but is exactly the same as ```CHECKF``` *)
## Logging levels ```
The default logging levels are ```DEBUG```, ```INFO```, ```WARNING``` and ```FATAL``` (see FATAL usage [above](#fatal_logging)). The logging levels are defined in [loglevels.hpp](src/g3log/loglevels.hpp).
For some windows framework there is a clash with the ```DEBUG``` logging level. One of the CMake [Build options](#build_options) can be used to then change offending default level from ```DEBUG``` TO ```DBUG```.
**CMake option: (default OFF) ** ```cmake -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON ..```
### disable/enabled levels at runtime
Logging levels can be disabled at runtime. The logic for this happens in
[loglevels.hpp](src/g3log/loglevels.hpp), [loglevels.cpp](src/loglevels.cpp) and [g3log.hpp](src/g3log/g3log.hpp).
There is a cmake option to enable the dynamic enable/disable of levels.
When the option is enabled there will be a slight runtime overhead for each ```LOG``` call when the enable/disable status is checked. For most intent and purposes this runtime overhead is negligable.
There is **no** runtime overhead for internally checking if a level is enabled//disabled if the cmake option is turned off. If the dynamic logging cmake option is turned off then all logging levels are enabled.
**CMake option: (default OFF)** ```cmake -DUSE_DYNAMIC_LOGGING_LEVELS=ON ..```
### custom logging levels
Custom logging levels can be created and used. When defining a custom logging level you set the value for it as well as the text for it. You can re-use values for other levels such as *INFO*, *WARNING* etc or have your own values.
Any value with equal or higher value than the *FATAL* value will be considered a *FATAL* logging level.
Example:
```
// In CustomLoggingLevels.hpp
#include <g3log/loglevels.hpp>
// all values with a + 1 higher than their closest equivalet
// they could really have the same value as well.
const LEVELS FYI {DEBUG.value + 1, {"For Your Information"}};
const LEVELS CUSTOM {INFO.value + 1, {"CUSTOM"}};
const LEVELS SEVERE {WARNING.value +1, {"SEVERE"}};
const LEVELS DEADLY {FATAL.value + 1, {"DEADLY"}};
```
## Sink <a name="sink_creation">creation</a> and utilization
The default sink for g3log is the one as used in g2log. It is a simple file sink with a limited API. The details for the default file sink can be found in [filesink.hpp](src/g3log/filesink.hpp), [filesink.cpp](src/filesink.cpp), [filesinkhelper.ipp](src/filesinkhelper.ipp)
More sinks can be found at [g3sinks](http://www.github.com/KjellKod/g3sinks) (log rotate, log rotate with filtering on levels)
A logging sink is not required to be a subclass of a specific type. The only requirement of a logging sink is that it can receive a logging message of
### Using the default sink
Sink creation is defined in [logworker.hpp](src/g3log/logworker.hpp) and used in [logworker.cpp](src/logworker.cpp). For in-depth knowlege regarding sink implementation details you can look at [sinkhandle.hpp](src/g3log/sinkhandle.hpp) and [sinkwrapper.hpp](src/g3log/sinkwrapper.hpp)
```
std::unique_ptr<FileSinkHandle> addDefaultLogger(
const std::string& log_prefix
, const std::string& log_directory
, const std::string& default_id = "g3log");
```
With the default id left as is (i.e. "g3log") a creation of the logger in the unit test "test_filechange" would look like this
```
const std::string directory = "./";
const std::string name = "(ReplaceLogFile)";
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(name, directory);
```
The resulting filename would be something like:
```
./(ReplaceLogFile).g3log.20160217-001406.log
```
## LOG <a name="log_flushing">flushing</a>
The default file sink will flush each log entry as it comes in. For different flushing policies please take a look at g3sinks [logrotate and LogRotateWithFilters](http://www.github.com/KjellKod/g3sinks/logrotate).
At shutdown all enqueued logs will be flushed to the sink.
At a discovered fatal event (SIGSEGV et.al) all enqueued logs will be flushed to the sink.
A programmatically triggered abrupt process exit such as a call to ```exit(0)``` will of course not get the enqueued log entries flushed. Similary a bug that does not trigger a fatal signal but a process exit will also not get the enqueued log entries flushed. G3log can catch several fatal crashes and it deals well with RAII exits but magic is so far out of its' reach.
## Fatal handling
The default behaviour for G3log is to catch several fatal events before they force the process to exit. After <i>catching</i> a fatal event a stack dump is generated and all log entries, up to the point of the stack dump are together with the dump flushed to the sink(s).
### <a name="fatal_handling_linux">Linux/*nix</a>
The default fatal handling on Linux deals with fatal signals. At the time of writing these signals were ```SIGABRT, SIGFPE, SIGILL, SIGILL, SIGSEGV, SIGSEGV, SIGTERM```. The Linux fatal handling is handled in [crashhandler.hpp](src/g3log/crashhandler.hpp) and [crashhandler_unix.cpp](src/crashhandler_unix.cpp)
A signal that commonly is associated with voluntarily process exit is ```SIGINT``` (ctrl + c) G3log does not deal with it.
The fatal signals can be [disabled](#fatal_handling_disabled) or [changed/added ](#fatal_signalhandler_override).
An example of a Linux stackdump as shown in the output from the fatal example <i>g3log-FATAL-sigsegv</i>.
```
***** FATAL SIGNAL RECEIVED *******
"Received fatal signal: SIGSEGV(11) PID: 6571
***** SIGNAL SIGSEGV(11)
******* STACKDUMP *******
stack dump [1] ./g3log-FATAL-sigsegv() [0x42a500]
stack dump [2] /lib/x86_64-linux-gnu/libpthread.so.0+0x10340 [0x7f83636d5340]
stack dump [3] ./g3log-FATAL-sigsegv : example_fatal::tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)+0x119 [0x4107b9]
stack dump [4] ./g3log-FATAL-sigsegvmain+0xdec [0x40e51c]
stack dump [5] /lib/x86_64-linux-gnu/libc.so.6__libc_start_main+0xf5 [0x7f8363321ec5]
stack dump [6] ./g3log-FATAL-sigsegv() [0x40ffa2]
Exiting after fatal event (FATAL_SIGNAL). Fatal type: SIGSEGV
Log content flushed flushed sucessfully to sink
"
g3log g3FileSink shutdown at: 16:33:18
```
<strikte>
### <a name="fatal_handling_windows">TOWRITE: Windows</a>
Windows fatal handling also deals with fatal signals just like Linux. In addition to fatal signals it also deals with unhandled exceptions, vectored exceptions. Windows fatal handling is handled in [crashhandler.hpp](src/g3log/crashhandler.hpp), [crashhandler_windows.cpp](src/crashhandler_windows.cpp), [stacktrace_windows.hpp](src/g3log/stacktrace_windows.hpp), [stacktrace_windows.cpp](src/stacktrace_windows.cpp)
An example of a Windows stackdump as shown in the output from the fatal example <i>g3log-FATAL-sigsegv</i>.
.... MISSING CONTENT..... since my Windows computer is gone!
</strike>
### <strike><a name="fatal_custom_handling">TOWRITE: Custom fatal handling</a></strike>
### <strike><a name="fatal_pre_hook">TOWRITE: Pre fatal hook</a> </strike>
### <strike><a name="fatal_signalhandler_override">TOWRITE: Override of signal handling</a> </strike>
### <strike><a name="fatal_handling_disabled">TOWRITE: Disable fatal handling</a> </strike>
## <a name="build_options">Build Options</a>
The build options are defined in the file [Options.cmake](Options.cmake)
build options are generated and saved to a header file. This avoid having to set the define options in the client source code

View File

@ -5,169 +5,116 @@
# risk and comes with no warranties.
#
# This code is yours to share, use and modify with no strings attached
# and no restrictions or obligations
# and no restrictions or obligations.
# ===================================================================
# GENERIC STEPS
SET(LOG_SRC ${g3log_SOURCE_DIR}/src)
file(GLOB SRC_FILES ${LOG_SRC}/*.cpp ${LOG_SRC}/*.ipp)
file(GLOB HEADER_FILES ${LOG_SRC}/g3log/*.hpp)
list( APPEND HEADER_FILES ${GENERATED_G3_DEFINITIONS} )
list( APPEND SRC_FILES ${GENERATED_G3_DEFINITIONS} )
IF (MSVC OR MINGW)
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_unix.cpp)
ELSE()
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_windows.cpp ${LOG_SRC}/g3log/stacktrace_windows.hpp ${LOG_SRC}/stacktrace_windows.cpp)
ENDIF (MSVC OR MINGW)
set(SRC_FILES ${SRC_FILES} ${SRC_PLATFORM_SPECIFIC})
# Create the g3log library
SET(G3LOG_LIBRARY g3log)
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
message("CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
IF( NOT CMAKE_INSTALL_PREFIX)
SET(CMAKE_INSTALL_PREFIX /usr/local)
ENDIF()
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX})
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
message("Install rpath location: ${CMAKE_INSTALL_RPATH}")
ENDIF()
IF( G3_SHARED_LIB )
IF( WIN32 )
IF(NOT(${CMAKE_VERSION} VERSION_LESS "3.4"))
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
ELSE()
message( FATAL_ERROR "Need CMake version >=3.4 to build shared windows library!" )
ENDIF()
ENDIF()
ADD_LIBRARY(${G3LOG_LIBRARY} SHARED ${SRC_FILES})
ELSE()
IF(MSVC)
IF(NOT G3_SHARED_RUNTIME)
SET(CompilerFlags
CMAKE_CXX_FLAGS
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
)
foreach(CompilerFlag ${CompilerFlags})
string(REPLACE "/MDd" "/MTd" ${CompilerFlag} "${${CompilerFlag}}")
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
endforeach()
ENDIF()
ENDIF()
ADD_LIBRARY(${G3LOG_LIBRARY} STATIC ${SRC_FILES})
ENDIF()
SET(${G3LOG_LIBRARY}_VERSION_STRING ${VERSION})
MESSAGE( STATUS "Creating ${G3LOG_LIBRARY} VERSION: ${VERSION}" )
MESSAGE( STATUS "Creating ${G3LOG_LIBRARY} SOVERSION: ${MAJOR_VERSION}" )
SET_TARGET_PROPERTIES(${G3LOG_LIBRARY} PROPERTIES
LINKER_LANGUAGE CXX
OUTPUT_NAME g3log
CLEAN_DIRECT_OUTPUT 1
SOVERSION ${MAJOR_VERSION}
VERSION ${VERSION}
)
IF(APPLE)
SET_TARGET_PROPERTIES(${G3LOG_LIBRARY} PROPERTIES MACOSX_RPATH TRUE)
ENDIF()
# require here some proxy for c++14/c++17? standard to avoid problems TARGET_PROPERTY CXX_STANDARD
TARGET_COMPILE_FEATURES(${G3LOG_LIBRARY} PUBLIC cxx_variable_templates)
TARGET_INCLUDE_DIRECTORIES(${G3LOG_LIBRARY}
PUBLIC
$<BUILD_INTERFACE:${LOG_SRC}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
)
include_directories(${LOG_SRC})
SET(ACTIVE_CPP0xx_DIR "Release")
# find corresponding thread lib (e.g. whether -lpthread is needed or not)
FIND_PACKAGE(Threads REQUIRED)
TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} Threads::Threads )
# check for backtrace and cxa_demangle only in non-Windows dev environments
IF(NOT(MSVC OR MINGW))
# the backtrace module does not provide a modern cmake target
FIND_PACKAGE(Backtrace REQUIRED)
if(Backtrace_FOUND)
TARGET_INCLUDE_DIRECTORIES(${G3LOG_LIBRARY} PRIVATE ${Backtrace_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} ${Backtrace_LIBRARIES})
else()
message( FATAL_ERROR "Could not find Library to create backtraces")
endif()
INCLUDE(CheckLibraryExists)
INCLUDE(CheckCXXSymbolExists)
#if demangle is in c++ runtime lib
CHECK_CXX_SYMBOL_EXISTS(abi::__cxa_demangle "cxxabi.h" DEMANGLE_EXISTS)
IF( NOT (DEMANGLE_EXISTS))
#try to link against c++abi to get demangle
CHECK_LIBRARY_EXISTS(c++abi abi::__cxa_demangle "cxxabi.h" NEED_C++ABI)
IF( NEED_C++ABI)
TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} c++abi)
ELSE()
message( FATAL_ERROR "Could not find function abi::__cxa_demangle")
ENDIF()
endif()
ENDIF()
# add Warnings
target_compile_options(${G3LOG_LIBRARY} PRIVATE
# clang/GCC warnings
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:-Wall -Wunused>
# MSVC warnings
$<$<CXX_COMPILER_ID:MSVC>:/W4>)
# add GCC specific stuff
target_compile_options(${G3LOG_LIBRARY} PRIVATE
# clang/GCC warnings
$<$<AND:$<CXX_COMPILER_ID:GNU>,$<NOT:$<BOOL:${MINGW}>>>:-rdynamic>
)
#cmake -DCMAKE_CXX_COMPILER=clang++ ..
# WARNING: If Clang for Linux does not work with full c++14 support it might be your
# WARNING: If Clang for Linux does not work with full C++11 support it might be your
# installation that is faulty. When I tested Clang on Ubuntu I followed the following
# description
# 1) http://kjellkod.wordpress.com/2013/09/23/experimental-g3log-with-clang/
# 2) https://github.com/maidsafe/MaidSafe/wiki/Hacking-with-Clang-llvm-abi-and-llvm-libc
IF ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang")
MESSAGE("")
MESSAGE("cmake for Clang ")
SET(CMAKE_CXX_FLAGS "-Wall -std=c++11 -Wunused -D_GLIBCXX_USE_NANOSLEEP")
IF (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++ -pthread")
ELSE()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
ENDIF()
IF (${CMAKE_SYSTEM} MATCHES "FreeBSD-([0-9]*)\\.(.*)")
IF (${CMAKE_MATCH_1} GREATER 9)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(PLATFORM_LINK_LIBRIES execinfo)
ENDIF()
ELSEIF (APPLE)
set(PLATFORM_LINK_LIBRIES c++abi)
ELSEIF (NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Linux"))
set(PLATFORM_LINK_LIBRIES rt c++abi)
ELSE()
set(PLATFORM_LINK_LIBRIES rt)
ENDIF()
# Windows Stuff
IF(MSVC OR MINGW)
TARGET_COMPILE_DEFINITIONS(${G3LOG_LIBRARY} PRIVATE NOGDI)
TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} dbghelp)
# VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408
# add_definition(-D_VARIADIC_MAX=10)
# https://github.com/anhstudios/swganh/pull/186/files
TARGET_COMPILE_DEFINITIONS(${G3LOG_LIBRARY} PRIVATE _VARIADIC_MAX=10)
MESSAGE(STATUS "- MSVC: Set variadic max to 10 for MSVC compatibility")
# Remember to set set target properties if using GTEST similar to done below on target "unit_test"
# "set_target_properties(unit_test PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0")
message( STATUS "" )
message( STATUS "Windows: Run cmake with the appropriate Visual Studio generator" )
message( STATUS "The generator is one number below the official version number. I.e. VS2013 -> Generator 'Visual Studio 12'" )
MESSAGE( STATUS "I.e. if VS2013: Please run the command [cmake -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 12\" ..]")
message( STATUS "if cmake finishes OK, do 'msbuild g3log.sln /p:Configuration=Release'" )
message( STATUS "then run 'Release\\g3log-FATAL-*' examples" )
message( STATUS "" )
ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
MESSAGE("cmake for GCC ")
IF (APPLE)
set(CMAKE_CXX_FLAGS "-Wall -Wunused -std=c++11 -pthread -D_GLIBCXX_USE_NANOSLEEP")
ELSEIF (MINGW)
set(CMAKE_CXX_FLAGS "-Wall -Wunused -std=c++11 -pthread -D_GLIBCXX_USE_NANOSLEEP -D_GLIBCXX_USE_SCHED_YIELD")
ELSE()
set(PLATFORM_LINK_LIBRIES rt)
set(CMAKE_CXX_FLAGS "-Wall -rdynamic -Wunused -std=c++11 -pthread -D_GLIBCXX_USE_NANOSLEEP -D_GLIBCXX_USE_SCHED_YIELD")
ENDIF()
ENDIF()
TARGET_COMPILE_OPTIONS(${G3LOG_LIBRARY} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/utf-8> # source code already in utf-8, force it for compilers in non-utf8_windows_locale
$<$<CXX_COMPILER_ID:MSVC>:$<$<EQUAL:4,${CMAKE_SIZEOF_VOID_P}>:/arch:IA32>>
)
IF (MSVC OR MINGW)
set(PLATFORM_LINK_LIBRIES dbghelp)
# VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408
# add_definition(-D_VARIADIC_MAX=10)
# https://github.com/anhstudios/swganh/pull/186/files
ADD_DEFINITIONS (/D_VARIADIC_MAX=10)
MESSAGE(STATUS "- MSVC: Set variadic max to 10 for MSVC compatibility")
# Remember to set set target properties if using GTEST similar to done below on target "unit_test"
# "set_target_properties(unit_test PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0")
MESSAGE("")
MESSAGE("Windows: Run cmake with the appropriate Visual Studio generator")
MESSAGE("The generator is one number below the official version number. I.e. VS2013 -> Generator 'Visual Studio 12'")
MESSAGE("I.e. if VS2013: Please run the command [cmake -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 12\" ..]")
MESSAGE("if cmake finishes OK, do 'msbuild g3log.sln /p:Configuration=Release'")
MESSAGE("then run 'Release\\g3log-FATAL-*' examples")
MESSAGE("")
ENDIF()
# GENERIC STEPS
file(GLOB SRC_FILES ${LOG_SRC}/g3log/*.h ${LOG_SRC}/g3log/*.hpp ${LOG_SRC}/*.cpp ${LOG_SRC}/*.ipp)
file(GLOB HEADER_FILES ${LOG_SRC}/g3log/*.hpp ${LOG_SRC}/*.hpp)
#MESSAGE(" HEADER FILES ARE: ${HEADER_FILES}")
IF (MSVC OR MINGW)
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_unix.cpp)
ELSE()
list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_windows.cpp ${LOG_SRC}/g3log/stacktrace_windows.hpp ${LOG_SRC}/stacktrace_windows.cpp)
ENDIF (MSVC OR MINGW)
set(SRC_FILES ${SRC_FILES} ${SRC_PLATFORM_SPECIFIC})
# Create the g3log library
include_directories(${LOG_SRC})
#MESSAGE(" g3logger files: [${SRC_FILES}]")
add_library(g3logger ${SRC_FILES})
set_target_properties(g3logger PROPERTIES
LINKER_LANGUAGE CXX
OUTPUT_NAME g3logger
CLEAN_DIRECT_OUTPUT 1)
target_link_libraries(g3logger ${PLATFORM_LINK_LIBRIES})
SET(G3LOG_LIBRARY g3logger)
if(ADD_BUILD_WIN_SHARED OR NOT(MSVC OR MINGW))
IF(NOT(CMAKE_VERSION LESS 3.4) AND MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
ENDIF()
add_library(g3logger_shared SHARED ${SRC_FILES})
set_target_properties(g3logger_shared PROPERTIES
LINKER_LANGUAGE CXX
OUTPUT_NAME g3logger
CLEAN_DIRECT_OUTPUT 1)
IF(APPLE)
set_target_properties(g3logger_shared PROPERTIES MACOSX_RPATH TRUE)
ENDIF(APPLE)
target_link_libraries(g3logger_shared ${PLATFORM_LINK_LIBRIES})
SET(G3LOG_SHARED_LIBRARY g3logger_shared)
endif()

View File

@ -1,172 +1,147 @@
# =============================================================================
# ==========================================================================
# 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
# with no warranties. This code is yours to share, use and modify with no
# strings attached and no restrictions or obligations.
#
# For more information see g3log/LICENSE or refer refer to http://unlicense.org
# ==============================================================================
# ============================================================================*/
# Below are details for compiling on Windows and Linux by default only an
# example g3log binary is created the performance and unit tests creation can be
# enabled by switching their OPTIONs from OFF to ON --- See below at around line
# 110
# Below are details for compiling on Windows and Linux
# by default only an example g3log binary is created
# the performance and unit tests creation can be enabled by switching their
# OPTIONs from OFF to ON --- See below at around line 110
# === WINDOWS ===
# Example for: Visual Studio 2013 (earlier should work too) 1. please use the
# "Visual Studio Command Prompt 12 (2013)" 2. from the g3log folder mkdir build
# cd build; 3. cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio XXX" .. (cmake
# -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 12") MAKE SURE you check the
# CMake documentation so you are using the correct bit flags(64 bit etc). The
# "XXX" needs tto be replaced for your specific build system, ref: cmake docs.
# Example for: Visual Studio 2013 (earlier should work too)
# 1. please use the "Visual Studio Command Prompt 12 (2013)"
# 2. from the g3log folder
# mkdir build
# cd build;
# 3. cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio XXX" ..
# (cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 12")
# (XXX is the Visual Studio version you are running)
# 4. msbuild g3log.sln /p:Configuration=Release
#
# (Example from Appveyor Ci:
# https://github.com/KjellKod/g3log/blob/master/appveyor.yml cmake -G "Visual
# Studio 14 2015 Win64" -DADD_G3LOG_UNIT_TEST=ON ..)
# Try to run an example, such as:
# 5. Release\g3log-FATAL-contract.exe
#
# 1. msbuild g3log.sln /p:Configuration=Release
#
# Try to run an example, such as: 5. Release\g3log-FATAL-contract.exe
#
# === LINUX: === To try this out from folder g3log: mkdir build cd build >>
# create makefiles in g3log/build directory cmake -DCMAKE_BUILD_TYPE=Release ..
# make -jN (where N stands for number of cores you want to utilize)
# === LINUX: === To try this out from folder g3log:
# mkdir build
# cd build
# >> create makefiles in g3log/build directory
# cmake -DCMAKE_BUILD_TYPE=Release ..
# make -jN (where N stands for number of cores you want to utilize)
#
#
#
# === Clang on Linux ===
# From g3log mkdir build && cd build cmake -DCMAKE_CXX_COMPILER=clang++ .. if
# you want to double-check settings: "VERBOSE=1 make" otherwise just run:
# "make -j"
# From g3log
# mkdir build && cd build
# cmake -DCMAKE_CXX_COMPILER=clang++ ..
# if you want to double-check settings: "VERBOSE=1 make"
# otherwise just run: "make -j"
#
# ============================================================================
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required (VERSION 2.8)
ENABLE_LANGUAGE(CXX)
set(CMAKE_BUILD_TYPE Release)
project(g3log CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CMAKE_BUILD_TYPE AND NOT (MSVC_IDE OR XCODE))
set(CMAKE_BUILD_TYPE
Release
CACHE STRING "Build type, one of: Release, Debug" FORCE)
endif()
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Configuration types: ${CMAKE_CONFIGURATION_TYPES}")
project (g3log)
# Detect 64 or 32 bit
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
# 64-bit project
set(64_BIT_OS TRUE)
message(STATUS "A 64-bit OS detected")
else()
set(64_BIT_OS FALSE)
message(STATUS "A 32-bit OS detected")
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
# 64-bit project
SET(64_BIT_OS TRUE)
MESSAGE("A 64-bit OS detected")
else()
SET(64_BIT_OS FALSE)
MESSAGE("A 32-bit OS detected")
endif()
# Calculate the version number
SET(MAJOR_VERSION 2)
SET(MINOR_VERSION 6)
IF ( NOT VERSION )
IF ( "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows" )
message("windows: Extracting git software version")
execute_process(COMMAND cmd /c "git rev-list ${MAJOR_VERSION}.${MINOR_VERSION}..HEAD | find /v " " /c" OUTPUT_VARIABLE GIT_RELEASE_COMMITS WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
ELSE()
IF(UNIX OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message( STATUS "nix: Extracting git software version" )
ELSE()
message( STATUS "unknown platform: extracting git software version" )
ENDIF()
execute_process(COMMAND bash "-c" "git rev-list ${MAJOR_VERSION}.${MINOR_VERSION}..HEAD | wc -l" OUTPUT_VARIABLE GIT_RELEASE_COMMITS WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
ENDIF()
# ============================================================================
# G3LOG OPTIONAL FEATURES
# ============================================================================
INCLUDE (${g3log_SOURCE_DIR}/Options.cmake)
string(STRIP ${GIT_RELEASE_COMMITS} GIT_RELEASE_COMMITS)
message( STATUS "git build version: ${GIT_VERSION}" )
message( STATUS "version base: ${VERSION-BASE}" )
message( STATUS "version remainder: ${VERSION-REMAINDER}" )
SET(BUILD_NUMBER ${VERSION-BASE})
SET(VERSION ${MAJOR_VERSION}.${MINOR_VERSION}-${GIT_RELEASE_COMMITS}) #-${VERSION-REMAINDER})
# =========================================================================
# G3 Macro definitions in Options.cmake are written to file
# this avoids having to re-state your definitions in your source code
# or compile options
#==========================================================================
INCLUDE (${g3log_SOURCE_DIR}/GenerateMacroDefinitionsFile.cmake)
option (ADD_BUILD_WIN_SHARED "Build shared library on Windows" OFF)
# =========================================================================
# G3LOG BUILD
#==========================================================================
INCLUDE (${g3log_SOURCE_DIR}/Build.cmake)
# ============================================================================
# EXAMPLE OPTIONS: By defauls is ON. This will create 'g3log-FATAL-* examples'
# ============================================================================
# DISABLE WITH: -DADD_FATAL_EXAMPLE=OFF
INCLUDE (${g3log_SOURCE_DIR}/example/Example.cmake)
# ============================================================================
# PERFORMANCE TEST OPTIONS: Performance operations for g3log
# ============================================================================
# ENABLE WITH: -DADD_G3LOG_PERFORMANCE=ON
INCLUDE (${g3log_SOURCE_DIR}/test_performance/Performance.cmake)
# ==========================================================================
# UNIT TEST OPTIONS:
# ============================================================================
# ENABLE WITH: -DADD_G3LOG_UNIT_TEST=ON
INCLUDE (${g3log_SOURCE_DIR}/test_unit/Test.cmake)
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# ==========================================================================
# BETA : package manager for G3Log,. not yet reliable. Use at your own risk
# only tested on Ubuntu and CentOS
#
# For OSX you can install an older version using 'brew install'
#
# ==========================================================================
# Package handling is done AFTER all other CMake setup
# usage: make package
# Check the output result and install accordingly.
# ==========================================================================
INCLUDE (${g3log_SOURCE_DIR}/CPackLists.txt)
ENDIF()
message( STATUS "Software Version: ${VERSION}" )
# ============================================================================
# G3LOG OPTIONAL FEATURES
# ============================================================================
include(${g3log_SOURCE_DIR}/Options.cmake)
# ============================================================================
# G3LOG iOS BUILD SUPPORT
# ============================================================================
include(${g3log_SOURCE_DIR}/iOSBuild.cmake)
if(G3_IOS_LIB)
# G3_IOS_LIB is the pass used to generate all the other cmakefiles for the
# different architectures needed for the universal library. So we're done at
# here.
return()
endif()
# =========================================================================
# G3 Macro definitions in Options.cmake are written to file this avoids having
# to re-state your definitions in your source code or compile options
# ==========================================================================
include(${g3log_SOURCE_DIR}/GenerateMacroDefinitionsFile.cmake)
# =========================================================================
# G3LOG BUILD
# ==========================================================================
include(${g3log_SOURCE_DIR}/Build.cmake)
# ============================================================================
# EXAMPLE OPTIONS: By defauls is ON. This will create 'g3log-FATAL-* examples'
# ============================================================================
# DISABLE WITH: -DADD_FATAL_EXAMPLE=OFF
include(${g3log_SOURCE_DIR}/example/Example.cmake)
# ============================================================================
# PERFORMANCE TEST OPTIONS: Performance operations for g3log
# ============================================================================
# ENABLE WITH: -DADD_G3LOG_PERFORMANCE=ON
include(${g3log_SOURCE_DIR}/test_performance/Performance.cmake)
# ==========================================================================
# UNIT TEST OPTIONS:
# ============================================================================
# ENABLE WITH: -DADD_G3LOG_UNIT_TEST=ON
include(${g3log_SOURCE_DIR}/test_unit/Test.cmake)
# ==========================================================================
# CMAKE INSTALL AND CPACK OPTIONS:
# ==========================================================================
#
# Alternative 1: Package handling is done AFTER all other CMake setup usage:
# make package Check the output result and install accordingly.
#
# Alternative 2: usage: make; sudo make install
#
# For OSX you can also install an older version using 'brew install'
#
# ==========================================================================
include(${g3log_SOURCE_DIR}/CPackLists.txt)
if(MINGW)
# this enables strerror_s
add_definitions(-DMINGW_HAS_SECURE_API)
endif()
if(NOT MSVC)
message(
STATUS
"\n\n
IF (NOT MSVC)
MESSAGE("\n\n
*******************************************************************
Please do 'make clean-cmake' before next cmake generation.
It is a good idea to purge your build directory of CMake
generated cache files
*******************************************************************
")
add_custom_target(clean-cmake COMMAND ${CMAKE_COMMAND} -P
${g3log_SOURCE_DIR}/CleanAll.cmake)
endif()
add_custom_target(clean-cmake
COMMAND ${CMAKE_COMMAND} -P ${g3log_SOURCE_DIR}/CleanAll.cmake
)
ENDIF()

View File

@ -1,15 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
URL https://github.com/google/googletest/archive/refs/heads/main.zip
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@ -1,95 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
kjell.hedstrom+g3log_code_of_conduct@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Warning
**Community Impact**: A violation through a single incident or series
of actions. Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested. Violating these terms may lead to a permanent ban.
### 2. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -1,13 +0,0 @@
# New Code
1. Please support all changed functionality with unit testing. A TDD approach usually leaves a cleaner end result than writing test afterwards
# Issues
1. Please explain your environment in the ticket. Frequently initialization issues due to not following best practices or the documentation are the causes.
Check the documentation and search previous issues before opening up a new one.
1. Don't be afraid of adding additional contexst of an old issue in case you think
this will improve things for the community going forward.
# Community Driven
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.

View File

@ -6,99 +6,64 @@
# For more information see g3log/LICENSE or refer refer to http://unlicense.org
# ============================================================================*/
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
IF(NOT CPACK_PACKAGING_INSTALL_PREFIX)
IF(NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
# set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX})
ELSE()
SET(CPACK_PACKAGING_INSTALL_PREFIX /usr/local)
ENDIF()
ENDIF()
# message("Install rpath location: ${CMAKE_INSTALL_RPATH}")
ENDIF()
INCLUDE(CMakePackageConfigHelpers)
INCLUDE(GNUInstallDirs)
SET(CPACK_PACKAGE_NAME g3log)
SET(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION})
SET(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
SET(CPACK_PACKAGE_VERSION_PATCH ${GIT_RELEASE_COMMITS})
SET(CPACK_PACKAGE_DESCRIPTION "Asynchronous 'crash safe' logger
License: http://unlicense.org
Repository: https://github.com/KjellKod/g3log")
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CPACK_PACKAGE_DESCRIPTION})
SET(CPACK_PACKAGE_CONTACT "Kjell Hedstrom hedstrom@kjellkoc.cc")
SET(CPACK_RESOURCE_FILE_LICENSE ${g3log_SOURCE_DIR}/LICENSE)
SET(CPACK_PACKAGE_VENDOR "KjellKod")
# INSTALL( TARGETS g3logger_shared
# ARCHIVE
# LIBRARY DESTINATION lib/g3log
# COMPONENT libraries)
IF(INSTALL_G3LOG)
INSTALL( TARGETS g3log
EXPORT g3log-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# INSTALL( FILES ${HEADER_FILES}
# DESTINATION include
# COMPONENT headers)
SET(CMAKE_INSTALL_LIBDIR lib CACHE PATH "Output dir for libraries")
SET(CMAKE_INSTALL_HEADERDIR include CACHE PATH "Output dir for headers")
INSTALL( TARGETS g3logger g3logger_shared
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT libraries)
INSTALL( FILES ${HEADER_FILES}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/g3log
COMPONENT headers)
INSTALL(
EXPORT g3log-targets
FILE g3logTargets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log
)
CONFIGURE_PACKAGE_CONFIG_FILE(
${PROJECT_SOURCE_DIR}/cmake/g3logConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/g3logConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/g3logConfig.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log
)
ENDIF()
DESTINATION ${CMAKE_INSTALL_HEADERDIR}/g3log
COMPONENT headers)
SET(CPACK_COMPONENTS_ALL libraries headers)
SET(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "g3log libraries")
SET(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "g3log C++ headers")
SET(CPACK_COMPONENTS_ALL libraries headers)
SET(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "G3Log libraries")
SET(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "G3Log C++ headers")
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
SET(CPACK_GENERATOR "DEB")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "KjellKod - Kjell Hedstrom")
ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
SET(CPACK_GENERATOR "ZIP") # Otherwise, NSIS is needed.
ENDIF()
SET(CPACK_GENERATOR "DEB")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kjell Hedstrom")
message( STATUS "\nTo create installation package: " )
message( STATUS "make package" )
message( STATUS "\nOption to install using 'make install'" )
message( STATUS "Installation locations: " )
message( STATUS "====================" )
message( STATUS "Headers: ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/g3log" )
message( STATUS "Library installation directory: ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" )
set(CPACK_PACKAGE_VERSION_MAJOR "1")
set(CPACK_PACKAGE_VERSION_MINOR "1")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_DESCRIPTION "Asynchronous 'crash safe' logger")
set(CPACK_PACKAGE_CONTACT "Kjell Hedstrom hedstrom@kjellkod.cc")
message( STATUS "For more information please see g3log/CPackLists.txt\n\n" )
IF(NOT MINGW)
message( STATUS "To install: sudo dpkg -i g3log-***Linux.deb" )
message( STATUS "To list package contents: sudo dpkg --contents g3log-***Linux.deb" )
message( STATUS "List content of the installed package: sudo dpkg -L g3log" )
message( STATUS "To remove: sudo dpkg -r g3log" )
ENDIF()
MESSAGE("\n\nTo install on Ubuntu\t\t(after cmake with options && make)")
MESSAGE("make package")
MESSAGE("Headers installation directory: ${CPACK_INSTALL_PREFIX}/${CMAKE_INSTALL_HEADERDIR}")
MESSAGE("Library installation directory: ${CPACK_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
MESSAGE("For more information please see g3log/CpackLists.txt")
MESSAGE("To install: sudo dpkg -i g3log-***Linux.deb\n\n")
MESSAGE("To list installed files: sudo dpkg -L g3log\n\n")
MESSAGE("To remove: sudo dpkg -r g3log\n\n")
MESSAGE("To remove: sudo dpkg -r g3log\n\n")
# NOTE: to change installation locations you can use the settings below
# examples:
# CPACK_PACKAGING_INSTALL_PREFIX
# CPACK_OUTPUT_FILE_PREFIX
# CMAKE_INSTALL_PREFIX
INCLUDE(CPack)
# CPACK_INSTALL_PREFIX
# CPACK_PACKAGE_INSTALL_DIRECTORY
# CMAKE_INSTALL_HEADERDIR
# CMAKE_INSTALL_LIBDIR
INCLUDE(CPack)

View File

@ -15,7 +15,7 @@ set(cmake_generated ${CMAKE_BINARY_DIR}/CMakeCache.txt
foreach(file ${cmake_generated})
if (EXISTS ${file})
message( STATUS "Removing: ${file}" )
MESSAGE("Removing: ${file}")
file(REMOVE_RECURSE ${file})
endif()
endforeach(file)

View File

@ -18,36 +18,23 @@
message( STATUS "" )
message( STATUS "COMPILE_DEFINITIONS:\n\t[${G3_DEFINITIONS}]" )
message( STATUS "" )
SET(GENERATED_G3_DEFINITIONS "${CMAKE_CURRENT_BINARY_DIR}/include/g3log/generated_definitions.hpp")
# If it exists, read existing file
set(current_content "")
if(EXISTS ${GENERATED_G3_DEFINITIONS})
file(READ ${GENERATED_G3_DEFINITIONS} current_content)
endif()
set(generated_content "// AUTO GENERATED MACRO DEFINITIONS FOR G3LOG\n\n")
set(generated_content "${generated_content}\n${HEADER}\n")
set(generated_content "${generated_content}\n#pragma once\n\n")
set(generated_content "${generated_content}\n// CMake induced definitions below. See g3log/Options.cmake for details.\n\n")
MESSAGE("COMPILE_DEFINITIONS: ${G3_DEFINITIONS}")
MESSAGE("End of COMPILE_DEFINITIONS")
SET(GENERATED_G3_DEFINITIONS src/g3log/generated_definitions.hpp)
file(REMOVE ${GENERATED_G3_DEFINITIONS} )
FILE(WRITE ${GENERATED_G3_DEFINITIONS} "// AUTO GENERATED MACRO DEFINITIONS FOR G3LOG\n\n")
FILE(APPEND ${GENERATED_G3_DEFINITIONS} "${HEADER}\n")
FILE(APPEND ${GENERATED_G3_DEFINITIONS} "#pragma once\n\n")
FILE(APPEND ${GENERATED_G3_DEFINITIONS} "// CMake induced definitions below. See g3log/Options.cmake for details.\n\n")
FOREACH(definition ${G3_DEFINITIONS} )
set(generated_content "${generated_content}\n#define ${definition}\n")
FILE(APPEND ${GENERATED_G3_DEFINITIONS} "#define ${definition}\n")
ENDFOREACH(definition)
if(NOT "${current_content}" STREQUAL "${generated_content}")
message( STATUS "Generated ${GENERATED_G3_DEFINITIONS}" )
message( STATUS "******************** START *************************" )
message(${generated_content})
message( STATUS "******************** END *************************" )
file(WRITE ${GENERATED_G3_DEFINITIONS} ${generated_content})
endif()
MESSAGE("Generated ${GENERATED_G3_DEFINITIONS}")
file(READ ${GENERATED_G3_DEFINITIONS} generated_content)
MESSAGE("******************** START *************************")
MESSAGE(${generated_content})
MESSAGE("******************** END *************************")

View File

@ -14,12 +14,9 @@
# to the auto generated file src/g3log/generated_definitions.hpp
# add_definitions(-DG3_DYNAMIC_LOGGING)
# add_definitions(-DCHANGE_G3LOG_DEBUG_TO_DBUG)
# add_definitions(-DWINDOWS_FUNCSIG)
# add_definitions(-DPRETTY_FUNCTION)
# add_definitions(-DDISABLE_FATAL_SIGNALHANDLING)
# add_definitions(-DDISABLE_VECTORED_EXCEPTIONHANDLING)
# add_definitions(-DDEBUG_BREAK_AT_FATAL_SIGNAL)
# add_definitions(-DG3_DYNAMIC_MAX_MESSAGE_SIZE)
@ -28,118 +25,49 @@
# compiling your binary (if done in a separate build step from the g3log library)
SET(G3_DEFINITIONS "")
# -DG3_IOS_LIB=ON : iOS version of library
option(G3_IOS_LIB
"iOS version of library." OFF)
IF(G3_IOS_LIB)
MESSAGE("-DG3_IOS_LIB=ON\t\t\t\tBuilding iOS version")
ENDIF(G3_IOS_LIB)
# -DUSE_DYNAMIC_LOGGING_LEVELS=ON : run-type turn on/off levels
option (USE_DYNAMIC_LOGGING_LEVELS
"Turn ON/OFF log levels. An disabled level will not push logs of that level to the sink. By default dynamic logging is disabled" OFF)
IF(USE_DYNAMIC_LOGGING_LEVELS)
LIST(APPEND G3_DEFINITIONS G3_DYNAMIC_LOGGING)
message( STATUS "-DUSE_DYNAMIC_LOGGING_LEVELS=ON" )
message( STATUS "\tDynamic logging levels is used" )
message( STATUS "\tUse [g3::addLogLevel(LEVEL boolean)] to enable/disable logging on specified levels\n\n" )
MESSAGE("-DUSE_DYNAMIC_LOGGING_LEVELS=ON")
MESSAGE("\tDynamic logging levels is used")
MESSAGE("\tUse [g3::setLogLevel(LEVEL boolean)] to enable/disable logging on specified levels\n\n")
ELSE()
message( STATUS "-DUSE_DYNAMIC_LOGGING_LEVELS=OFF" )
MESSAGE("-DUSE_DYNAMIC_LOGGING_LEVELS=OFF")
ENDIF(USE_DYNAMIC_LOGGING_LEVELS)
# -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON : change the DEBUG logging level to be DBUG to avoid clash with other libraries that might have
# predefined DEBUG for their own purposes
option (CHANGE_G3LOG_DEBUG_TO_DBUG
"Use DBUG logging level instead of DEBUG. By default DEBUG is the debugging level" OFF)
IF(CHANGE_G3LOG_DEBUG_TO_DBUG)
LIST(APPEND G3_DEFINITIONS CHANGE_G3LOG_DEBUG_TO_DBUG)
LIST(APPEND G3_DEFINITIONS "G3LOG_DEBUG DBUG")
message( STATUS "-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON DBUG instead of DEBUG logging level is used" )
ELSE()
LIST(APPEND G3_DEFINITIONS "G3LOG_DEBUG DEBUG")
message( STATUS "-DCHANGE_G3LOG_DEBUG_TO_DBUG=OFF \t(Debuggin logging level is 'DEBUG')" )
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON DBUG instead of DEBUG logging level is used")
ELSE()
MESSAGE("-DCHANGE_G3LOG_DEBUG_TO_DBUG=OFF \t(Debuggin logging level is 'DEBUG')")
ENDIF(CHANGE_G3LOG_DEBUG_TO_DBUG)
# -DWINDOWS_USE_FUNCSIG=ON : (Default OFF) Override the use of __FUNCTION__ for Windows platform and instead use __FUNCSIG__
option (WINDOWS_FUNCSIG
"Windows __FUNCSIG__ to expand `Function` location of the LOG call instead of the default __FUNCTION__" OFF)
IF(WINDOWS_FUNCSIG)
LIST(APPEND G3_DEFINITIONS WINDOWS_FUNCSIG)
message( STATUS "-DWINDOWS_FUNCSIG=ON\t\t__SIGFUNC__ is used instead of the default __FUNCTION__ for LOG call locations" )
ELSE()
message( STATUS "-DWINDOWS_FUNCSIG=OFF")
ENDIF(WINDOWS_FUNCSIG)
# -DPRETTY_FUNCTION=ON : (Default OFF) Override the use of __FUNCTION__ for Windows platform and instead use __FUNCSIG__
# NOTE: heavy templated integrations such as boost log calls that shows the function name can cause function name expansion
# to "spam" the LOG output with the now visible template arguments.
option (PRETTY_FUNCTION
"Windows __PRETTY_FUNCTION__ to expand `Function` location of the LOG call instead of the default __FUNCTION__" OFF)
IF(PRETTY_FUNCTION)
LIST(APPEND G3_DEFINITIONS PRETTY_FUNCTION)
message( STATUS "-DPRETTY_FUNCTION=ON\t\t__PRETTY_FUNCTION__ is used instead of the default __FUNCTION__ for LOG call locations" )
ELSE()
message( STATUS "-DPRETTY_FUNCTION=OFF")
ENDIF(PRETTY_FUNCTION)
# -DG3_DYNAMIC_MAX_MESSAGE_SIZE : use dynamic memory for final_message in logcapture.cpp
option (USE_G3_DYNAMIC_MAX_MESSAGE_SIZE
"Use dynamic memory for message buffer during log capturing" OFF)
IF(USE_G3_DYNAMIC_MAX_MESSAGE_SIZE)
LIST(APPEND G3_DEFINITIONS G3_DYNAMIC_MAX_MESSAGE_SIZE)
message( STATUS "-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=ON\t\tDynamic memory used during log capture" )
ELSE()
message( STATUS "-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=OFF" )
ENDIF(USE_G3_DYNAMIC_MAX_MESSAGE_SIZE)
# G3LOG_FULL_FILENAME logs full file name instead of short filename. This makes it
# easier to copy filenames to open them without needing to search.
option (G3_LOG_FULL_FILENAME "Log full filename" OFF)
IF(G3_LOG_FULL_FILENAME)
LIST(APPEND G3_DEFINITIONS G3_LOG_FULL_FILENAME)
message( STATUS "-DG3_LOG_FULL_FILENAME=ON\t\tShowing full filenames with logs")
ELSE()
message( STATUS "-DG3_LOG_FULL_FILENAME=OFF")
ENDIF(G3_LOG_FULL_FILENAME)
# -DENABLE_FATAL_SIGNALHANDLING=ON : default change the
# -DENABLE_FATAL_SIGNALHANDLING=ON : defualt change the
# By default fatal signal handling is enabled. You can disable it with this option
# enumerated in src/stacktrace_windows.cpp
option (ENABLE_FATAL_SIGNALHANDLING
"Vectored exception / crash handling with improved stack trace" ON)
IF(NOT ENABLE_FATAL_SIGNALHANDLING)
LIST(APPEND G3_DEFINITIONS DISABLE_FATAL_SIGNALHANDLING)
LIST(APPEND G3_DEFINITIONS DISABLE_FATAL_SIGNALHANDLING)
message( STATUS "-DENABLE_FATAL_SIGNALHANDLING=OFF Fatal signal handler is disabled" )
ELSE()
message( STATUS "-DENABLE_FATAL_SIGNALHANDLING=ON\tFatal signal handler is enabled" )
MESSAGE("-DENABLE_FATAL_SIGNALHANDLING=OFF Fatal signal handler is disabled")
ELSE()
MESSAGE("-DENABLE_FATAL_SIGNALHANDLING=ON\tFatal signal handler is enabled")
ENDIF(NOT ENABLE_FATAL_SIGNALHANDLING)
# Option for building as a static or shared library in all platforms
option (G3_SHARED_LIB "Build shared library" ON)
IF(G3_SHARED_LIB)
message( STATUS "-DG3_SHARED_LIB=ON\tBuild shared library" )
ELSE()
MESSAGE( STATUS "-DG3_SHARED_LIB=OFF\tBuild static library")
ENDIF()
# Option for building as a static or shared runtime library in MS VC++
option (G3_SHARED_RUNTIME "Build shared runtime library MS VC" ON)
IF(G3_SHARED_RUNTIME)
message( STATUS "-DG3_SHARED_RUNTIME=ON\tBuild shared runtime library" )
ELSE()
message( STATUS "-DG3_SHARED_RUNTIME=OFF\tBuild static runtime library")
ENDIF()
# WINDOWS OPTIONS
IF (MSVC OR MINGW)
# -DENABLE_VECTORED_EXCEPTIONHANDLING=ON : defualt change the
@ -149,12 +77,12 @@ IF (MSVC OR MINGW)
option (ENABLE_VECTORED_EXCEPTIONHANDLING
"Vectored exception / crash handling with improved stack trace" ON)
IF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING)
IF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING)
LIST(APPEND G3_DEFINITIONS DISABLE_VECTORED_EXCEPTIONHANDLING)
message( STATUS "-DENABLE_VECTORED_EXCEPTIONHANDLING=OFF Vectored exception handling is disabled" )
ELSE()
message( STATUS "-DENABLE_VECTORED_EXCEPTIONHANDLING=ON\t\t\tVectored exception handling is enabled" )
ENDIF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING)
MESSAGE("-DENABLE_VECTORED_EXCEPTIONHANDLING=OFF Vectored exception handling is disabled")
ELSE()
MESSAGE("-DENABLE_VECTORED_EXCEPTIONHANDLING=ON\t\t\tVectored exception handling is enabled")
ENDIF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING)
@ -164,17 +92,16 @@ IF (MSVC OR MINGW)
#
option (DEBUG_BREAK_AT_FATAL_SIGNAL
"Enable Visual Studio break point when receiving a fatal exception. In __DEBUG mode only" OFF)
IF(DEBUG_BREAK_AT_FATAL_SIGNAL)
LIST(APPEND G3_DEFINITIONS DEBUG_BREAK_AT_FATAL_SIGNAL)
message( STATUS "-DDEBUG_BREAK_AT_FATAL_SIGNAL=ON Break point for fatal signal is enabled for __DEBUG." )
ELSE()
message( STATUS "-DDEBUG_BREAK_AT_FATAL_SIGNAL=OFF\t\t\tBreak point for fatal signal is disabled" )
ENDIF(DEBUG_BREAK_AT_FATAL_SIGNAL)
IF(DEBUG_BREAK_AT_FATAL_SIGNAL)
LIST(APPEND G3_DEFINITIONS DEBUG_BREAK_AT_FATAL_SIGNAL)
MESSAGE("-DDEBUG_BREAK_AT_FATAL_SIGNAL=ON Break point for fatal signal is enabled for __DEBUG.")
ELSE()
MESSAGE("-DDEBUG_BREAK_AT_FATAL_SIGNAL=OFF\t\t\tBreak point for fatal signal is disabled")
ENDIF(DEBUG_BREAK_AT_FATAL_SIGNAL)
ENDIF (MSVC OR MINGW)
message( STATUS "\n\n\n" )
option(INSTALL_G3LOG "Enable installation of g3log. (Projects embedding g3log may want to turn this OFF.)" ON)
MESSAGE("\n\n\n")

View File

@ -1,36 +0,0 @@
# PULL REQUEST DESCRIPTION
`ADD CONTENT HERE TO DESCRIBE THE PURPOSE OF THE PULL REQUEST`
# Formatting
- [ ] I am following the formatting style of the existing codebase.
_a clang-format configuration file is available in the root of g3log._
- _Use VSCode with clang-formatter or commandline:_
`clang-format -i path_to_file`
- _or recursive throughout the whole repo:_ `find . -iname "*.hpp" -o -iname "*.cpp" | xargs clang-format -i`
# Testing
- [ ] This new/modified code was covered by unit tests.
- [ ] (insight) Was all tests written using TDD (Test Driven Development) style?
- [ ] The CI (Windows, Linux, OSX) are working without issues.
- [ ] Was new functionality documented?
- [ ] The testing steps 1 - 2 below were followed
_step 1_
```bash
mkdir build; cd build; cmake -DADD_G3LOG_UNIT_TEST=ON ..
// linux/osx alternative, simply run: ./scripts/buildAndRunTests.sh
```
_step 2: use one of these alternatives to run tests:_
- Cross-Platform: `ctest`
- or `ctest -V` for verbose output
- Linux: `make test`

View File

@ -1,221 +1,263 @@
[introduction](index.md) | [**detailed information**](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)
# How to use g3log
G3log is an asynchronous logger with dynamic sinks
## Example USAGE
#### Optional to use either streaming or printf-like syntax
```
LOG(INFO) << "streaming API is as easy as ABC or " << 123;
LOGF(WARNING, "Printf-style syntax is also %s", "available");
```
## <a name="what-g3log-is">What g3Log is</a>
* ***G3log*** is the acting name for the third version of g2log and it stands for **g3log with dynamic sinks**
* G3log is an asynchronous, "crash-safe" logger. You can read more about it here [[g2log version]](
http://www.codeproject.com/Articles/288827/g2log-An-efficient-asynchronous-logger-using-Cplus)
* You can choose to use the default log receiver which saves all LOG calls to file, **or** you can choose to use your own custom made log receiver(s), **or** both, **or** as many sinks as you need.
#### <a name="#conditional-logging">Conditional logging</a>
int less = 1; int more = 2
LOG_IF(INFO, (less<more)) <<"If [true], then this text will be logged";
// or with printf-like syntax
LOGF_IF(INFO, (less<more), "if %d<%d then this text will be logged", less,more);
#### <a name="design-by-contract">Design-by-Contract</a>
*CHECK(false)* will trigger a "fatal" message. It will be logged, and then the
application will exit.
```
CHECK(less != more); // not FATAL
CHECK(less > more) << "CHECK(false) triggers a FATAL message";
```
### Detailed API documentation
Please look at [API.md](API.md) for detailed API documentation
## <a name="benefits-with-g3log">Benefits you get when using g3log</a>
1. Easy to use, clean syntax and a blazing fast logger.
2. All the slow log I/O disk access is done in a background thread. This ensures that the LOG caller can immediately continue with other tasks and do not have to wait for the LOG call to finish.
3. G3log provides logging, Design-by-Contract [#CHECK], and flush of log to file at
shutdown. Buffered logs will be written to the sink before the application shuts down.
4. It is thread safe, so using it from multiple threads is completely fine.
5. It is *CRASH SAFE*. It will save the made logs to the sink before it shuts down.
The logger will catch certain fatal events *(Linux/OSX: signals, Windows: fatal OS exceptions and signals)* , so if your application crashes due to, say a segmentation fault, *SIGSEGV*, it will log and save the crash and all previously buffered log entries before exiting.
6. It is cross platform. Tested and used by me or by clients on OSX, Windows, Ubuntu, CentOS
7. G3log and G2log are used worldwide in commercial products as well as hobby projects. G2log was introduced in early 2011 and is now retired.
8. The code is given for free as public domain. This gives the option to change, use, and do whatever with it, no strings attached.
9. *[g3log](https://github.com/KjellKod/g3log)* : is made to facilitate easy adding of custom log receivers. Its tested on at least the following platforms with **Linux**(Clang/gcc), **Windows** (mingw, visual studio) and **OSX**. My recommendation is to go with g3log if you have full C++17 support
C++11 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.1).
C++14 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.4
## <a name="g3log-with-sinks">G3log with sinks</a>
[Sinks](http://en.wikipedia.org/wiki/Sink_(computing)) are receivers of LOG calls. G3log comes with a default sink (*the same as g3log uses*) that can be used to save log to file. A sink can be of *any* class type without restrictions as long as it can either receive a LOG message as a *std::string* **or** as a *g3::LogMessageMover*.
The *std::string* comes pre-formatted. The *g3::LogMessageMover* is a wrapped struct that contains the raw data for custom handling in your own sink.
A sink is *owned* by the g3log and is added to the logger inside a ```std::unique_ptr```. The sink can be called though its public API through a *handler* which will asynchronously forward the call to the receiving sink.
It is <a name="crazy-simple">crazy simple to create a custom sink</a>. This example show what is needed to make a custom sink that is using custom log formatting but only using that
for adding color to the default log formatting. The sink forwards the colored log to cout
```cpp
// in file Customsink.hpp
#pragma once
#include <string>
#include <iostream>
#include <g3log/logmessage.hpp>
struct CustomSink {
// Linux xterm color
// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};
FG_Color GetColor(const LEVELS level) const {
if (level.value == WARNING.value) { return YELLOW; }
if (level.value == DEBUG.value) { return GREEN; }
if (g3::internal::wasFatal(level)) { return RED; }
return WHITE;
}
void ReceiveLogMessage(g3::LogMessageMover logEntry) {
auto level = logEntry.get()._level;
auto color = GetColor(level);
std::cout << "\033[" << color << "m"
<< logEntry.get().toString() << "\033[m" << std::endl;
}
};
// in main.cpp, main() function
auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
```
## Adding and Removing Sinks
You can safely remove and add sinks during the running of your program.
**Keep in mind**
- *Initialization of the logger should happen before you have started any other threads that may call the logger.*
- *Destruction of the logger (RAII concept) should happen AFTER shutdown of other threads that are calling the logger.*
**Adding Sinks**
```cpp
auto sinkHandle1 = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
auto sinkHandle2 = logworker->addDefaultLogger(argv[0],
path_to_log_file);
logworker->removeSink(std::move(sinkHandle1)); // this will in a thread-safe manner remove the sinkHandle1
logworker->removeAllSinks(); // this will in a thread-safe manner remove any sinks.
```
**More sinks** can be found in the repository **[github.com/KjellKod/g3sinks](https://github.com/KjellKod/g3sinks)**.
## <a name="code-examples">Code Examples</a>
Example usage where a <a name="custom-sink">custom sink</a> is added. A function is called though the sink handler to the actual sink object.
```cpp
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
// initialize the logger before it can receive LOG calls
initializeLogging(logworker.get());
LOG(WARNING) << "This log call, may or may not happend before"
<< "the sinkHandle->call below";
// You can call in a thread safe manner public functions on your sink
// The call is asynchronously executed on your custom sink.
std::future<void> received = sinkHandle->call(&CustomSink::Foo,
param1, param2);
// If the LogWorker is initialized then at scope exit the g3::internal::shutDownLogging() will be called.
// This is important since it protects from LOG calls from static or other entities that will go out of
// scope at a later time.
//
// It can also be called manually:
g3::internal::shutDownLogging();
}
// some_file.cpp : To show how easy it is to get the logger to work
// in other parts of your software
#include <g3log/g3log.hpp>
void SomeFunction() {
...
LOG(INFO) << "Hello World";
}
```
Example usage where a the <a name="default-file-logger">default file logger</a> is used **and** a custom sink is added
```cpp
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
auto worker = LogWorker::createLogWorker();
auto defaultHandler = worker->addDefaultLogger(argv[0],
path_to_log_file);
// logger is initialized
g3::initializeLogging(worker.get());
LOG(DEBUG) << "Make log call, then add another sink";
worker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
...
}
```
[introduction](index.md) | [**detailed information**](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)
# G3log : Asynchronous logger with Dynamic Sinks
## EXAMPLE USAGE
#### Optional to use either streaming or printf-like syntax
```
LOG(INFO) << "streaming API is as easy as ABC or " << 123;
LOGF(WARNING, "Printf-style syntax is also %s", "available");
```
#### Conditional logging
int less = 1; int more = 2
LOG_IF(INFO, (less<more)) <<"If [true], then this text will be logged";
// or with printf-like syntax
LOGF_IF(INFO, (less<more), "if %d<%d then this text will be logged", less,more);
#### Design-by-Contract
*CHECK(false)* will trigger a "fatal" message. It will be logged, and then the
application will exit.
```
CHECK(less != more); // not FATAL
CHECK(less > more) << "CHECK(false) triggers a FATAL message";
```
## What G3Log is:
* ***G3log*** is the acting name for the third version of g2log and it stands for **g3log with dynamic sinks**
* G3log is an asynchronous, "crash-safe" logger. You can read more about it here [[g2log version]](
http://www.codeproject.com/Articles/288827/g2log-An-efficient-asynchronous-logger-using-Cplus)
* You can choose to use the default log receiver which saves all LOG calls to file, **or** you can choose to use your own custom made log receiver(s), **or** both, **or** as many sinks as you need.
## Detailed API documentation
Please look at [API.markdown](API.markdown) for detailed API documentation
## Benefits you get when using G3log ##
1. Easy to use, clean syntax and a blazing fast logger.
2. All the slow log I/O disk access is done in a background thread. This ensures that the LOG caller can immediately continue with other tasks and do not have to wait for the LOG call to finish.
3. G3log provides logging, Design-by-Contract [#CHECK], and flush of log to file at
shutdown. Buffered logs will be written to the sink before the application shuts down.
4. It is thread safe, so using it from multiple threads is completely fine.
5. It is *CRASH SAFE*. It will save the made logs to the sink before it shuts down.
The logger will catch certain fatal events *(Linux/OSX: signals, Windows: fatal OS exceptions and signals)* , so if your application crashes due to, say a segmentation fault, *SIGSEGV*, it will log and save the crash and all previously buffered log entries before exiting.
6. It is cross platform. Tested and used by me or by clients on OSX, Windows, Ubuntu, CentOS
7. G3log and G2log is used world wide in commercial products as well as hobby projects. G2log is used since early 2011.
8. The code is given for free as public domain. This gives the option to change, use, and do whatever with it, no strings attached.
9. Two versions of g3log exist that are under active development.
* This version: *[g3log](https://github.com/KjellKod/g3log)* : which is made to facilitate easy adding of custom log receivers. Its tested on at least the following platforms with Linux(Clang/gcc), Windows (mingw, visual studio 2013). My recommendation is to go with g3log if you have full C++11 support.
* *[g2log](https://bitbucket.org/KjellKod/g2log)*: The original. Simple, easy to modify and with the most OS support. Clients use g2log on environments such as OSX/Clang, Ubuntu, CentOS, Windows/mingw, Windows/Visual Studio. The focus on g2log is "slow to change" and compiler support. Only well, time tested, features from g3log will make it into g2log.
# G3log with sinks
[Sinks](http://en.wikipedia.org/wiki/Sink_(computing)) are receivers of LOG calls. G3log comes with a default sink (*the same as G3log uses*) that can be used to save log to file. A sink can be of *any* class type without restrictions as long as it can either receive a LOG message as a *std::string* **or** as a *g3::LogMessageMover*.
The *std::string* comes pre-formatted. The *g3::LogMessageMover* is a wrapped struct that contains the raw data for custom handling in your own sink.
A sink is *owned* by the G3log and is added to the logger inside a ```std::unique_ptr```. The sink can be called though its public API through a *handler* which will asynchronously forward the call to the receiving sink.
Silly example to show what is needed to make a custom sink that is using custom log formatting but only using that
for adding color to the default log formatting. The sink forwards the colored log to cout
```cpp
// in file Customsink.hpp
#pragma once
#include <string>
#include <iostream>
#include <g3log/logmessage.hpp>
struct CustomSink {
// Linux xterm color
// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};
FG_Color GetColor(const LEVELS level) const {
if (level.value == WARNING.value) { return YELLOW; }
if (level.value == DEBUG.value) { return GREEN; }
if (g3::internal::wasFatal(level)) { return RED; }
return WHITE;
}
void ReceiveLogMessage(g3::LogMessageMover logEntry) {
auto level = logEntry.get()._level;
auto color = GetColor(level);
std::cout << "\033[" << color << "m"
<< logEntry.get().toString() << "\033[m" << std::endl;
}
};
// in main.cpp, main() function
auto sinkHandle = logworker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
```
**More sinks** can be found in the repository **[github.com/KjellKod/g3sinks](https://github.com/KjellKod/g3sinks)**.
#Code Examples
Example usage where a custom sink is added. A function is called though the sink handler to the actual sink object.
```
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3log/std2_make_unique.hpp>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
auto sinkHandle = logworker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
// initialize the logger before it can receive LOG calls
initializeLogging(logworker.get());
LOG(WARNING) << "This log call, may or may not happend before"
<< "the sinkHandle->call below";
// You can call in a thread safe manner public functions on your sink
// The call is asynchronously executed on your custom sink.
std::future<void> received = sinkHandle->call(&CustomSink::Foo,
param1, param2);
// If the LogWorker is initialized then at scope exit the g3::shutDownLogging() will be called.
// This is important since it protects from LOG calls from static or other entities that will go out of
// scope at a later time.
//
// It can also be called manually:
g3::shutDownLogging();
}
// some_file.cpp : To show how easy it is to get the logger to work
// in other parts of your software
#include <g3log/g3log.hpp>
void SomeFunction() {
...
LOG(INFO) << "Hello World";
}
```
Example usage where a the default file logger is used **and** a custom sink is added
```
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3log/std2_make_unique.hpp>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
auto worker = LogWorker::createLogWorker();
auto defaultHandler = worker->addDefaultLogger(argv[0],
path_to_log_file);
// logger is initialized
g3::initializeLogging(worker.get());
LOG(DEBUG) << "Make log call, then add another sink";
worker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
...
}
```
# BUILDING g3log:
-----------
The default is to build an example binary 'g3log-FATAL-contract' and 'g3log-FATAL-sigsegv'. I suggest you start with that, run it and view the created log also.
If you are interested in the performance or unit tests then you can
enable the creation of them in the g3log/CMakeLists.txt file. See that file for
more details
```
cd g3log
mkdir build
cd build
```
## Building on Linux
```
cmake -DCMAKE_BUILD_TYPE=Release ..
make
```
## Building on Windows
Please use the Visual Studio 12 (2013) command prompt "Developer command prompt"
```
cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 12" ..
msbuild g3log.sln /p:Configuration=Release
```
## Building on *nix with Clang
```
cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release ..
make
```
# API description
Most of the API that you need for using g3log is described in this readme. For more API documentation and examples please continue to read the [API readme](API.markdown). Examples of what you will find here are:
* Sink creation and utilization
* Logging levels
* disable/enabled levels at runtime
* custom logging levels
* Fatal handling
* custom fatal handling
* pre fatal hook
* override of signal handling
* disable fatal handling
* LOG calls
* CHECK calls
#Performance
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 laptop the average call, when doing extreme performance testing, will be about ~2 us.
The worst case latency is kept stabile 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.
You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
#Enjoy
If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and g2log and it is also nice to see if someone is using it.
If you have ANY questions or problems please do not hesitate in contacting me on my blog
http://kjellkod.wordpress.com/2011/11/17/kjellkods-g2log-vs-googles-glog-are-asynchronous-loggers-taking-over
or at ```Hedstrom at KjellKod dot cc```
Cheers
Kjell *(a.k.a. KjellKod)*

136
README.md
View File

@ -1,136 +0,0 @@
**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)
# Contents
[**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)
# Welcome to g3log
### Use [kjellkod.github.io/g3log/](https://kjellkod.github.io/g3log/) for best reading / searching / navigating of g3log's documentation
G3log is an asynchronous logger with three main features:
1. Intuitive `LOG(...)` API
2. `Design-by-contract` `CHECK(...)` functionality
3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash.
The super quick introduction to g3log can be seen in the steps 1 - 9 below.
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.
## Experiment and try-out g3log in Github Codespaces
ref: [codespaces.md](docs/codespaces.md)
## 1. Easy usage in files
Avoid deep dependency injection complexity and instead get access to the logger as easy as:
```
#include <g3log/g3log.hpp>
```
## 2. Access to streaming and print_f log call syntax
Both streaming syntax `LOG` and print_f `LOGF` syntax are available:
```
LOGF(INFO, "Hi log %d", 123);
LOG(INF) << "Hi log " << 123;
```
## 3. Conditional logging
```
LOG_IF(INFO, (1 < 2)) << "If true this message will be logged";
LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message");
```
## 4. Design-by-contract framework
```
CHECK(less != more); // not fatal
CHECK_F(less > more, "CHECK(false) will trigger a fatal message")
```
## 5. Handling of fatal
By default g3log will capture fatal events such as `LOG(FATAL)`, `CHECK(false)` and otherwise fatal signals such as:
```
SIGABRT
SIGFPE
SIGILL
SIGSEGV
SIGTERM
```
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.
If `object` symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink.
#### 5b. Overriding and customization of fatal event handling
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.
## 6. Default and Custom logging levels
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)
### 7. Log filtering
Log filtering is handled in g3log if dynamic logging levels are enabled
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)
## 8. 3rd party and custom logging sinks
The default logging sink has no external 3rd party dependencies. For more logging sinks please see [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks)
- log rotate
- log to syslog
- log to colored terminal output
- log rotate with filter
See the [API.md](docs/API.md) for more information about the simple steps to creating your own logging sink.
## 9. Log instantiation
With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this:
```cpp
const std::string directory = "./";
const std::string name = "TestLogFile";
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(name, directory);
```
The resulting filename would be something like:
```
./TestLogFile.g3log.20160217-001406.log
```
## <a name="performance">Performance</a>
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.
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.
You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
## <a name="continuous_integration">Continuous Integration</a>
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.
See `Actions` for matrix (ubuntu, macos, windows) testing as well as other actions for doc publishing.
## <a name="feedback">Feedback</a>
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.
If you have ANY questions or problems please do not hesitate in contacting me at
`Hedstrom @ Kjellod. cc`
# <a name="say-thanks">Say Thanks</a>
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.
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/g3log/25)
* $5 for a cup of coffee
* $25 for a late evening coding with takeout
Cheers
Kjell *(a.k.a. KjellKod)*
[**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)

View File

@ -1,53 +0,0 @@
#.rst:
# FindG3log
# -------
#
# Find libg3log, G3log is an asynchronous, "crash safe", logger that is easy to use with default logging sinks or you can add your own.
#
# This defines the cmake import target "g3log" you can use like this
#```
# target_link_libraries(YourTarget PUBLIC g3log)
#```
# Variables and features
# ----------------------
# * ``G3LOG`` -- if this environment variable is set, it'll be used as a hint as to where the g3log files are.
# * ``G3LOG_INCLUDE_DIRS`` -- raw cmake variable with include path
# * ``G3LOG_LIBRARIES`` -- raw cmake variable with library link line
# * ``G3LOG_FOUND`` -- check if the lib was found without using the newer ``if(TARGET g3log)...``
include(FindPackageHandleStandardArgs)
include(SelectLibraryConfigurations)
@PACKAGE_INIT@
find_package(Threads REQUIRED)
if (NOT TARGET g3log)
include("${CMAKE_CURRENT_LIST_DIR}/g3logTargets.cmake")
get_target_property(G3LOG_INCLUDE_DIR g3log INTERFACE_INCLUDE_DIRECTORIES)
get_target_property(G3LOG_LIBRARY_DEBUG g3log IMPORTED_IMPLIB_DEBUG)
if (G3LOG_LIBRARY_DEBUG MATCHES ".*-NOTFOUND")
get_target_property(G3LOG_LIBRARY_DEBUG g3log IMPORTED_LOCATION_DEBUG)
endif ()
get_target_property(G3LOG_LIBRARY_RELEASE g3log IMPORTED_IMPLIB_RELEASE)
if (G3LOG_LIBRARY_RELEASE MATCHES ".*-NOTFOUND")
get_target_property(G3LOG_LIBRARY_RELEASE g3log IMPORTED_LOCATION_RELEASE)
endif ()
select_library_configurations(G3LOG)
if (G3LOG_LIBRARY)
list(APPEND G3LOG_LIBRARY Threads::Threads)
if (WIN32)
list(APPEND G3LOG_LIBRARY DbgHelp.lib)
endif ()
endif ()
endif ()
find_package_handle_standard_args(g3log REQUIRED_VARS G3LOG_INCLUDE_DIR G3LOG_LIBRARY)
mark_as_advanced(G3LOG_INCLUDE_DIR G3LOG_LIBRARY)
set(G3LOG_INCLUDE_DIRS ${G3LOG_INCLUDE_DIR})
set(G3LOG_LIBRARIES ${G3LOG_LIBRARY})

BIN
docs/.DS_Store vendored

Binary file not shown.

View File

@ -1 +0,0 @@

View File

@ -1,307 +0,0 @@
[introduction](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [**API description**](API.md) | [Custom log formatting](API_custom_formatting.md)
# High Level Description of g3log
The `g3log` logger is an asynchronous, crash-safe logging library designed for C++ applications. It allows for logging messages to various sinks without blocking the main application thread. Below is a high-level overview of how the `g3log` logger works.
## Asynchronous Logging
The logger operates on a separate thread, ensuring that the main application thread is not blocked by I/O operations when logging messages. This is achieved by using a background worker ([`LogWorker`](../src/g3log/logworker.hpp)) that queues log messages and processes them asynchronously.
## LogWorker and Sinks
The `LogWorker` is responsible for managing the logging sinks. A sink is an object that defines where and how log messages are outputted (e.g., to a file, console, or over the network). Users can add custom sinks to the `LogWorker` using the `addSink` method, which takes a unique pointer to a sink object and a function pointer to the method that will save the log message.
## Signal Handling
The logger includes a signal handler for Unix-like systems that captures fatal signals (e.g., `SIGSEGV`, `SIGABRT`) and ensures that all pending log messages are flushed to the sinks before the application exits. The signal handler function ([`signalHandler`](../src/crashhandler_unix.cpp)) is registered to handle these signals and will attempt to generate a stack trace when a fatal signal is received. This stack trace is then logged, providing valuable debugging information.
## Stack Trace Generation
Upon receiving a fatal signal, the `signalHandler` function will call [`stackdump`](../src/crashhandler_unix.cpp) to generate a stack trace. This function uses platform-specific calls to retrieve the stack frames and attempts to demangle the function names to make the stack trace more readable.
## Log Message Formatting
Log messages can be formatted using either a streaming API (e.g., `LOG(INFO) << "message";`) or a printf-like syntax (e.g., `LOGF(INFO, "message %d", value);`). This provides flexibility in how messages are constructed.
## Log Levels
The library supports various log levels (e.g., `DEBUG`, `INFO`, `WARNING`, `FATAL`). Users can define custom log levels or modify the existing ones. The log levels can be dynamically enabled or disabled at runtime if the `G3_DYNAMIC_LOGGING` preprocessor definition is set.
## Crash Safety
In the event of a crash, the logger is designed to be crash-safe by catching fatal events and ensuring that all log messages are flushed to the sinks before the process exits.
## Customization
The library allows for extensive customization, including adding custom log levels, creating custom sinks, and overriding the default signal handling behavior.
## Thread Safety
The `g3log` logger is thread-safe, meaning it can be used from multiple threads without the need for additional synchronization.
## Public Domain Software
The `g3log` code is released into the public domain, allowing users to use, modify, and distribute it freely without restrictions.
## Logging and Fatal Events Explained
_diagrams created with https://mermaid.live_
![G3Log sequence view](event_sequence.png)
# API description
Most of the API that you need for using g3log is described in this readme. For more API documentation and examples please continue to read the [API readme](API.md). Examples of what you will find here are:
## Logging API: LOG calls
LOG calls can follow streaming ```LOG(INFO) << "some text" ``` or printf-like syntax ```LOGF(WARNING, "some number %d", 123); ```
Conditional logging is made with ```LOG_IF(INFO, <boolean-expression>) << " some text" ``` or ```LOGF_IF(WARNING, <boolean-expression>) << " some text".``` Only if the expressions evaluates to ```true``` will the logging take place.
Example:
```LOG_IF(INFO, 1 != 200) << " some text";``` or ```LOG_IF(FATAL, SomeFunctionCall()) << " some text";```
*<a name="fatal_logging">A call using FATAL</a> logging level, such as the ```LOG_IF(FATAL,...)``` example above, will after logging the message at ```FATAL```level also kill the process. It is essentially the same as a ```CHECK(<boolea-expression>) << ...``` with the difference that the ```CHECK(<boolean-expression)``` triggers when the expression evaluates to ```false```.*
## Contract API: CHECK calls
The contract API follows closely the logging API with ```CHECK(<boolean-expression>) << ...``` for streaming or (*) ```CHECKF(<boolean-expression>, ...);``` for printf-style.
If the ```<boolean-expression>``` evaluates to false then the the message for the failed contract will be logged in FIFO order with previously made messages. The process will then shut down after the message is sent to the sinks and the sinks have dealt with the fatal contract message.
```CHECK_F(<boolean-expression>, ...);``` was the the previous API for printf-like CHECK. It is still kept for backwards compatability but is exactly the same as ```CHECKF```
# LOG(fATAL) or CHECK(false)
Fatal logging or failed `CHECK calls follows the same handling.
![CHECK(false) or LOG(FATAL)](fatal_log_sequence.png)
## Logging levels
The default logging levels are ```DEBUG```, ```INFO```, ```WARNING``` and ```FATAL``` (see FATAL usage [above](#fatal_logging)). The logging levels are defined in [loglevels.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/loglevels.hpp).
For some windows framework there is a clash with the ```DEBUG``` logging level. One of the CMake [Build options](#build_options) can be used to then change offending default level from ```DEBUG``` TO ```DBUG```.
**CMake option: (default OFF) ** ```cmake -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON ..```
### disable/enabled levels at runtime
Logging levels can be disabled at runtime. The logic for this happens in
[loglevels.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/loglevels.hpp), [loglevels.cpp](https://github.com/KjellKod/g3log/tree/master/src/loglevels.cpp) and [g3log.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/g3log.hpp).
There is a cmake option to enable the dynamic enable/disable of levels.
When the option is enabled there will be a slight runtime overhead for each ```LOG``` call when the enable/disable status is checked. For most intent and purposes this runtime overhead is negligable.
There is **no** runtime overhead for internally checking if a level is enabled//disabled if the cmake option is turned off. If the dynamic logging cmake option is turned off then all logging levels are enabled.
**CMake option: (default OFF)** ```cmake -DUSE_DYNAMIC_LOGGING_LEVELS=ON ..```
### custom logging levels
Custom logging levels can be created and used. When defining a custom logging level you set the value for it as well as the text for it. You can re-use values for other levels such as *INFO*, *WARNING* etc or have your own values. Any value with equal or higher value than the *FATAL* value will be considered a *FATAL* logging level.
**To keep in mind when adding your own custom levels.**
1. If the cmake option `G3_DYNAMIC_LOGGING` is enabled then you must use `g3::only_change_at_initialization::addLogLevel(...)` to give g3log a record of your logging level and if it is an enabled or disbled logging level.
1. If the cmake `G3_DYNAMIC_LOGGING` is turned OFF, then giving g3log a record of your logging level with 'addLogLevel(...) is **not needed** since no `"disbled/enabled"` check will happen - all logging levels will be considered enabled.
Example:
```cpp
// In CustomLoggingLevels.hpp
#include <g3log/loglevels.hpp>
// all values with a + 1 higher than their closest equivalet
// they could really have the same value as well.
const LEVELS FYI {DEBUG.value + 1, {"For Your Information"}};
const LEVELS CUSTOM {INFO.value + 1, {"CUSTOM"}};
const LEVELS SEVERE {WARNING.value +1, {"SEVERE"}};
const LEVELS DEADLY {FATAL.value + 1, {"DEADLY"}};
```
More examples can be viwed in the [unit tests](https://github.com/KjellKod/g3log/blob/master/test_unit/test_io.cpp).
## Sink <a name="sink_creation">creation</a> and utilization
The default sink for g3log is the one as used in g2log. It is a simple file sink with a limited API. The details for the default file sink can be found in [filesink.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/filesink.hpp), [filesink.cpp](https://github.com/KjellKod/g3log/tree/master/src/filesink.cpp), [filesinkhelper.ipp](https://github.com/KjellKod/g3log/tree/master/src/filesinkhelper.ipp)
More sinks can be found at [g3sinks](http://www.github.com/KjellKod/g3sinks) (log rotate, log rotate with filtering on levels)
A logging sink is not required to be a subclass of a specific type. The only requirement of a logging sink is that it can receive a logging message of
### Using the default sink
Sink creation is defined in [logworker.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/logworker.hpp) and used in [logworker.cpp](https://github.com/KjellKod/g3log/tree/master/src/logworker.cpp). For in-depth knowlege regarding sink implementation details you can look at [sinkhandle.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/sinkhandle.hpp) and [sinkwrapper.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/sinkwrapper.hpp)
```cpp
std::unique_ptr<FileSinkHandle> addDefaultLogger(
const std::string& log_prefix
, const std::string& log_directory
, const std::string& default_id = "g3log");
```
With the default id left as is (i.e. "g3log") a creation of the logger in the unit test "test_filechange" would look like this
```cpp
const std::string directory = "./";
const std::string name = "(ReplaceLogFile)";
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(name, directory);
```
The resulting filename would be something like:
```
./(ReplaceLogFile).g3log.20160217-001406.log
```
## Designate the sink function's log entry receving function
The default log formatting look can be overriden by any sink.
If the sink receiving function calls `toString()` then the default log formatting will be used.
If the sink receiving function calls `toString(&XFunc)` then the `XFunc`will be used instead (see `LogMessage.h/cpp` for code details if it is not clear). (`XFunc` is a place holder for *your* formatting function of choice).
The API for the function-ptr to pass in is
```cpp
std::string (*) (const LogMessage&)
```
or for short as defined in `LogMessage.h`
```cpp
using LogDetailsFunc = std::string (*) (const LogMessage&);
```
## Log format customization
Please see[API_custom_formatting.md](API_custom_formatting.md)
## LOG <a name="log_flushing">flushing</a>
The default file sink will flush each log entry at set intervals. The default buffer size for flushing is set to 100 entries. You can adjust this down to 1, or as high as makes sense for your system. Please see [FileSink](https://github.com/KjellKod/g3log/blob/master/src/g3log/filesink.hpp#L18)
Even more flushing policies and log rotations can be found at g3sinks [logrotate and LogRotateWithFilters](https://github.com/KjellKod/g3sinks/tree/master/sink_logrotate).
At shutdown all enqueued logs will be flushed to the sink.
At a discovered fatal event (SIGSEGV et.al) all enqueued logs will be flushed to the sink.
A programmatically triggered abrupt process exit such as a call to ```exit(0)``` will of course not get the enqueued log entries flushed. Similary a bug that does not trigger a fatal signal but a process exit will also not get the enqueued log entries flushed. G3log can catch several fatal crashes and it deals well with RAII exits but magic is so far out of its' reach.
![log sequence](log_sequence.png)
## G3log and Sink Usage Code Example
Example usage where a [logrotate sink (g3sinks)](https://github.com/KjellKod/g3sinks) is added. In the example it is shown how the logrotate API is called. The logrotate limit is changed from the default to instead be 10MB. The limit is changed by calling the sink handler which passes the function call through to the actual logrotate sink object.
```cpp
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.h>
#include <g3sinks/LogRotate.h>
#include <memory>
int main(int argc, char**argv) {
using namespace g3;
std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
auto sinkHandle = logworker->addSink(std::make_unique<LogRotate>(),
&LogRotate::save);
// initialize the logger before it can receive LOG calls
initializeLogging(logworker.get());
// You can call in a thread safe manner public functions on the logrotate sink
// The call is asynchronously executed on your custom sink.
const int k10MBInBytes = 10 * 1024 * 1024;
std::future<void> received = sinkHandle->call(&LogRotate::setMaxLogSize, k10MBInBytes);
// Run the main part of the application. This can be anything of course, in this example
// we'll call it "RunApplication". Once this call exits we are in shutdown mode
RunApplication();
// If the LogWorker is initialized then at scope exit the g3::shutDownLogging() will be
// called automatically.
//
// This is important since it protects from LOG calls from static or other entities that will go out of
// scope at a later time.
//
// It can also be called manually if for some reason your setup is different then the one highlighted in
// this example
g3::shutDownLogging();
}
```
## Dynamic Message Sizing <a name="dynamic_message_sizing"></a>
The default build uses a fixed size buffer for formatting messages. The size of this buffer is 2048 bytes. If an incoming message results in a formatted message that is greater than 2048 bytes, it will be bound to 2048 bytes and will have the string ```[...truncated...]``` appended to the end of the bound message. There are cases where one would like to dynamically change the size at runtime. For example, when debugging payloads for a server, it may be desirable to handle larger message sizes in order to examine the whole payload. Rather than forcing the developer to rebuild the server, dynamic message sizing could be used along with a config file which defines the message size at runtime.
This feature supported as a CMake option:
**CMake option: (default OFF)** ```cmake -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=ON ..```
The following is an example of changing the size for the message.
```cpp
g3::only_change_at_initialization::setMaxMessageSize(10000);
```
## Fatal handling
The default behaviour for G3log is to catch several fatal events before they force the process to exit. After <i>catching</i> a fatal event a stack dump is generated and all log entries, up to the point of the stack dump are together with the dump flushed to the sink(s).
![fatal signal](fatal_signal_sequence.png)
### <a name="fatal_handling_linux">Linux/*nix</a>
The default fatal handling on Linux deals with fatal signals. At the time of writing these signals were ```SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM```. The Linux fatal handling is handled in [crashhandler.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/crashhandler.hpp) and [crashhandler_unix.cpp](https://github.com/KjellKod/g3log/tree/master/src/crashhandler_unix.cpp)
A signal that commonly is associated with voluntarily process exit is ```SIGINT``` (ctrl + c) G3log does not deal with it.
The fatal signals can be [disabled](#fatal_handling_disabled) or [changed/added ](#fatal_signalhandler_override).
An example of a Linux stackdump as shown in the output from the fatal example <i>g3log-FATAL-sigsegv</i>.
```
***** FATAL SIGNAL RECEIVED *******
"Received fatal signal: SIGSEGV(11) PID: 6571
***** SIGNAL SIGSEGV(11)
******* STACKDUMP *******
stack dump [1] ./g3log-FATAL-sigsegv() [0x42a500]
stack dump [2] /lib/x86_64-linux-gnu/libpthread.so.0+0x10340 [0x7f83636d5340]
stack dump [3] ./g3log-FATAL-sigsegv : example_fatal::tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::default_delete<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)+0x119 [0x4107b9]
stack dump [4] ./g3log-FATAL-sigsegvmain+0xdec [0x40e51c]
stack dump [5] /lib/x86_64-linux-gnu/libc.so.6__libc_start_main+0xf5 [0x7f8363321ec5]
stack dump [6] ./g3log-FATAL-sigsegv() [0x40ffa2]
Exiting after fatal event (FATAL_SIGNAL). Fatal type: SIGSEGV
Log content flushed sucessfully to sink
"
g3log g3FileSink shutdown at: 16:33:18
```
### <a name="fatal_custom_handling">Custom fatal handling - override defaults</a>
By <a name="fatal_signalhandler_override">default</a> the fatal signals are defined in [https://github.com/KjellKod/g3log/tree/master/src/g3log.cpp](https://github.com/KjellKod/g3log/tree/master/src/g3log.cpp) as
```
SIGABRT
SIGFPE
SIGILL
SIGSEGV
SIGTERM
```
If you want to define your own set of fatal signals, override the default ones, then this can be done as shown in [src/g3log/crashhandler.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/crashhandler.hpp)
```cpp
// Example when SIGTERM is skipped due to ZMQ usage
g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"},
{SIGFPE, "SIGFPE"},
{SIGILL, "SIGILL"},
{SIGSEGV, "SIGSEGV"}});
```
### <a name="fatal_pre_hook">Pre fatal hook</a>
You can define a custom call back function that will be called before the fatal signal handling re-emits the `fatal` signal. See [src/g3log/g3log.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/g3log.hpp) for details.
```
// Example of how to enforce important shutdown cleanup even in the event of a fatal crash:
g3::setFatalPreLoggingHook([]{ cleanup(); });
```
### <a name="fatal_handling_disabled">Disable fatal handling</a>
Fatal signal handling can be disabled with a CMake option: `ENABLE_FATAL_SIGNALHANDLING`. See [Options.cmake](https://github.com/KjellKod/g3log/Options.cmake) for more details
### <a name="PID1">PID1 Fatal Signal Recommendations</a>
If you are using g3log on a PID1 process then you absolutely should provide your own signal handling (ref: [issue 269](https://github.com/KjellKod/g3log/issues/269)) as g3log re-emits the fatal signal after it has restored the previous signal handler for that signal. PID1 processed do *not* shutdown the process for a normal fatal signal so the choice to exit the PID1 process after such a signal must be taken by the coder - not by g3log.
## <a name="fatal_handling_windows">Windows</a>
Windows fatal handling also deals with fatal signals just like Linux. In addition to fatal signals it also deals with unhandled exceptions, vectored exceptions. Windows fatal handling is handled in [crashhandler.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/crashhandler.hpp), [crashhandler_windows.cpp](https://github.com/KjellKod/g3log/tree/master/src/crashhandler_windows.cpp), [stacktrace_windows.hpp](https://github.com/KjellKod/g3log/tree/master/src/g3log/stacktrace_windows.hpp), [stacktrace_windows.cpp](https://github.com/KjellKod/g3log/tree/master/src/stacktrace_windows.cpp)
An example of a Windows stackdump as shown in the output from the fatal example <i>g3log-FATAL-sigsegv</i>.
[introduction](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [**API description**](API.md) | [Custom log formatting](API_custom_formatting.md)

View File

@ -1,69 +0,0 @@
[introduction](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [**Custom log formatting**](API_custom_formatting.md)
# Custom LOG <a name="log_formatting">formatting</a>
### Overriding the Default File Sink's file header
The default file header can be customized in the default file sink in calling
```cpp
FileSink::overrideLogHeader(std::string);
```
### Overriding the Default FileSink's log formatting
The default log formatting is defined in `LogMessage.hpp`
```cpp
static std::string DefaultLogDetailsToString(const LogMessage& msg);
```
### Adding thread ID to the log formatting
An "all details" log formatting function is also defined - this one also adds the "calling thread's ID"
```cpp
static std::string FullLogDetailsToString(const LogMessage& msg);
```
### Override default sink log formatting
For convenience the *Default* sink has a function
for doing exactly this
```cpp
void overrideLogDetails(LogMessage::LogDetailsFunc func);
```
Example code for replacing the default log formatting for "full details" formatting (it adds thread ID)
```cpp
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], path_to_log_file);
g3::initializeLogging(worker.get());
handle->call(&g3::FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString);
```
See [test_message.cpp](https://github.com/KjellKod/g3log/tree/master/test_unit/test_message.cpp) for details and testing
Example code for overloading the formatting of a custom sink. The log formatting function will be passed into the
`LogMessage::toString(...)` this will override the default log formatting
Example
```cpp
namespace {
std::string MyCustomFormatting(const LogMessage& msg) {
... how you want it ...
}
}
void MyCustomSink::ReceiveLogEntry(LogMessageMover message) {
std::string formatted = message.get().toString(&MyCustomFormatting) << std::flush;
}
...
...
auto worker = g3::LogWorker::createLogWorker();
auto sinkHandle = worker->addSink(std::make_unique<MyCustomSink>(),
&MyCustomSink::ReceiveLogMessage);
// ReceiveLogMessage(...) will used the custom formatting function "MyCustomFormatting(...)
```
[introduction](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [**Custom log formatting**](API_custom_formatting.md)

View File

@ -1,252 +0,0 @@
[introduction](index.md) | [detailed information](g3log_usage.md) | [**Configure & Build**](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)
# <a name="building-g3log">Configure, build, package, install and test g3log</a>
## Example Project with g3log
An example project integration of g3log, both statially and dynamically built can be found at [g3log_example_integration](https://github.com/KjellKod/g3log_example_integration/blob/master/README.md)
## Building it standalone to try out is as easy as:
```
git clone https://github.com/KjellKod/g3log
cd g3log
mkdir build
cd build
```
## <a name="prerequisites">Prerequisites</a>
You also need these tools to build g3log from source:
- CMake (*Required*)
g3log uses CMake as a one-stop solution for configuring, building, installing, packaging and testing on Windows, Linux and OSX.
- Git (*Optional but Recommended*)
When building g3log it uses git to calculate the software version from the commit history of this repository. If you don't want that, or your setup does not have access to git, or you download g3log source archive from the GitHub Releases page so that you do not have the commit history downloaded, you can instead pass in the version as part of the CMake build arguments. See this [_issue_](https://github.com/KjellKod/g3log/issues/311#issuecomment-488829282) for more information.
```
cmake -DVERSION=1.3.2 ..
```
## <a name="configuration">Configuration Options</a>
g3log provides following CMake options (and default values):
```
$ cmake -LAH # List non-advanced cached variables. See `cmake --help` for more details.
...
// Fatal (fatal-crashes/contract) examples
ADD_FATAL_EXAMPLE:BOOL=ON
// g3log performance test
ADD_G3LOG_BENCH_PERFORMANCE:BOOL=OFF
// g3log unit tests
ADD_G3LOG_UNIT_TEST:BOOL=OFF
// Use DBUG logging level instead of DEBUG.
// By default DEBUG is the debugging level
CHANGE_G3LOG_DEBUG_TO_DBUG:BOOL=OFF
// Windows only: Use __FUNCSIG__ instead of the default __FUNCTION__
// to show LOG function location
// WARNING: using this in heavily templated code, like boost can expand
// the function name into massive size
WINDOWS_FUNCSIG:BOOL=OFF
// gcc/clang only: Use __PRETTY_FUNCTION__ instead of the default __FUNCTION__
// to show LOG function location
// WARNING: using this in heavily templated code, like boost can expand
// the function name into massive size
PRETTY_FUNCTION:BOOL=OFF
// Specifies the build type on single-configuration generators.
// Possible values are empty, Debug, Release, RelWithDebInfo, MinSizeRel, …
CMAKE_BUILD_TYPE:STRING=
// Install path prefix, prepended onto install directories.
// This variable defaults to /usr/local on UNIX
// and c:/Program Files/${PROJECT_NAME} on Windows.
CMAKE_INSTALL_PREFIX:PATH=
// The prefix used in the built package.
// On Linux, if this option is not set:
// 1) If CMAKE_INSTALL_PREFIX is given, then it will be
// set with the value of CMAKE_INSTALL_PREFIX by g3log.
// 2) Otherwise, it will be set as /usr/local by g3log.
CPACK_PACKAGING_INSTALL_PREFIX:PATH=
// Enable Visual Studio break point when receiving a fatal exception.
// In __DEBUG mode only
DEBUG_BREAK_AT_FATAL_SIGNAL:BOOL=OFF
// Vectored exception / crash handling with improved stack trace
ENABLE_FATAL_SIGNALHANDLING:BOOL=ON
// Vectored exception / crash handling with improved stack trace
ENABLE_VECTORED_EXCEPTIONHANDLING:BOOL=ON
// iOS version of library.
G3_IOS_LIB:BOOL=OFF
// Log full filename
G3_LOG_FULL_FILENAME:BOOL=OFF
// Build shared library
G3_SHARED_LIB:BOOL=ON
// Build shared runtime library MSVC
G3_SHARED_RUNTIME:BOOL=ON
// Turn ON/OFF log levels.
// An disabled level will not push logs of that level to the sink.
// By default dynamic logging is disabled
USE_DYNAMIC_LOGGING_LEVELS:BOOL=OFF
// Use dynamic memory for message buffer during log capturing
USE_G3_DYNAMIC_MAX_MESSAGE_SIZE:BOOL=OFF
...
```
For additional option context and comments please also see [Options.cmake](https://github.com/KjellKod/g3log/blob/master/Options.cmake)
If you want to leave everything as it was, then you should:
```
cmake ..
```
You may also specify one or more of those options listed above from the command line.
For example, on Windows:
```
cmake .. -G "Visual Studio 15 2017"
-DG3_SHARED_LIB=OFF
-DCMAKE_INSTALL_PREFIX=C:/g3log
-DADD_G3LOG_UNIT_TEST=ON
-DADD_FATAL_EXAMPLE=OFF
```
will use a Visual Studio 2017 solution generator, build g3log as a static library, headers and libraries will be installed to `C:\g3log` when installed from source, enable unit testing, but do not build fatal example.
MinGW users on Windows may find they should use a different generator:
```
cmake .. -G "MinGW Makefiles"
```
By default, headers and libraries will be installed to `/usr/local` on Linux when installed from build tree via `make install`. You may overwrite it by:
```
cmake .. -DCMAKE_INSTALL_PREFIX=/usr
```
This will install g3log to `/usr` instead of `/usr/local`.
Linux/OSX package maintainers may be interested in the `CPACK_PACKAGING_INSTALL_PREFIX`. For example:
```
cmake .. -DCPACK_PACKAGING_INSTALL_PREFIX=/usr/local
```
## <a name="build-commands">Build Commands</a>
Once the configuration is done, you may build g3log with:
```
# Suppose you are still in the `build` directory. I won't repeat it anymore!
cmake --build . --config Release
```
You may also build it with a system-specific way.
On Linux, OSX and MinGW:
```
make
```
On Windows:
```
msbuild g3log.sln /p:Configuration=Release
```
Windows users can also open the generated Visual Studio solution file and build it happily.
## <a name="installing">Installation</a>
Install from source in a CMake way:
```
cmake --build . --target install
```
Linux users may also use:
```
sudo make install
```
You may also create a package first and install g3log with it. See the next section.
## <a name=packaging>Packaging</a>
A CMake way:
```
cmake --build . --config Release --target package
```
or
```
cpack -C Release
```
if the whole library has been built in the previous step.
It will generate a ZIP package on Windows, and a DEB package on Linux.
Linux users may also use a Linux way:
```
make package
```
If you want to use a different package generator, you should specify a `-G` option.
On Windows:
```
cpack -C Release -G NSIS;7Z
```
this will create a installable NSIS package and a 7z package.
*Note:* To use the NSIS generator, you should install [```NSIS```](https://nsis.sourceforge.io/Download) first.
On Linux:
```
cpack -C Release -G TGZ
```
this will create a .tar.gz archive for you.
Once done, you may install or uncompress the package file to the target machine. For example, on Debian or Ubuntu:
```
sudo dpkg -i g3log-<version>-Linux.deb
```
will install the g3log library to `CPACK_PACKAGING_INSTALL_PREFIX`.
## <a name="testing">Testing</a>
By default, tests will be built. To disable unit testing, you should turn off `ADD_G3LOG_UNIT_TEST`.
Suppose the build process has completed, then you can run the tests with:
```
ctest -C Release
```
or:
```
make test
```
for Linux users.
or for a detailed gtest output of all the tests:
```
cd build;
../scripts/runAllTests.sh
```
## <a name="cmake-module">CMake module</a>
g3log comes with a CMake module. Once installed, it can be found under `${CMAKE_INSTALL_PREFIX}/lib/cmake/g3log`. Users can use g3log in a CMake-based project this way:
```
find_package(g3log CONFIG REQUIRED)
target_link_libraries(main PRIVATE g3log)
```
To make sure that CMake can find g3log, you also need to tell CMake where to search for it:
```
cmake .. -DCMAKE_PREFIX_PATH=<g3log's install prefix>
```
## <a name="build_options">Build Options</a>
The build options are defined in the file [Options.cmake](https://github.com/KjellKod/g3log/blob/master/Build.cmake)
build options are generated and saved to a header file. This avoid having to set the define options in the client source code
[introduction](index.md) | [detailed information](g3log_usage.md) | [**Configure & Build**](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)

View File

@ -1,85 +0,0 @@
# Codespaces
You can experiment with codespaces and g3log.
## Learn about Github Codespaces
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)
# Commandline codespaces Quick Reference
1. List all your codespaces `gh codespace list`
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)
3. View codebase details `gh codespace view`
4. Stop `gh codespace stop -c CODESPACE-NAME`
5. Delete `gh codespace delete -c CODESPACE-NAME`
6. Rebuild `gh codespace rebuild`
7. Rename `gh codespace edit -c CODESPACE-NAME -d DISPLAY-NAME`
8. SSH into REMOTE codespace `gh codespace ssh -c CODESPACE-NAME`
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))
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))
# Try g3log in a local dev container.
Please note that this will build g3log as if it's on a Debian Linux platform.
1. Clone this repository to your local filesystem.
2. Start Visual Studio Code. Press F1 and select the `Dev Containers: Open Folder in Container...` command.
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.
### Example cmake configuration and build
```
Open a terminal in Visual Studio Code
mkdir debianbuild
cd debianbuild
cmake -DADD_G3LOG_UNIT_TEST=ON -DADD_G3LOG_BENCH_PERFORMANCE=ON ..
make -j
```
### Example runs
1. performance test in the container `./g3log-performance-threaded_mean 4`
2. unit tests `ctest -v`
3. Try a fatal example with dumped stack trace `./g3log-FATAL-contract`
### Example with Debugging.
Without any need to set up environment on your local machine you can also use Codespaces to debug examples, unit tests etc of g3log.
The pesky thing with VSCode, especially with cmake is to set up the launh.json.
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
contain information about the pecific executable.
Here we try out the `g3log-FATAL-contract` after cmake configure with `-DCMAKE_BUILD_TYPE=Debug`
```
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// Remember to build the specific part of cmake with
// "cmake -DCMAKE_BUILD_TYPE=Debug" if you want to be able to debug it.
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Start",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/g3log-FATAL-contract",
"MIMode": "gdb",
"cwd": "${workspaceFolder}/build"
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
```

View File

@ -1,15 +0,0 @@
# Information for contributing to g3log
## License
[LICENSE](https://github.com/KjellKod/g3log/blob/master/LICENSE)
## Contributing
[CONTRIBUTING.md](https://github.com/KjellKod/g3log/blob/master/CONTRIBUTING.md)
### Code of conduct
[CODE_OF_CONDUCT.md](https://github.com/KjellKod/g3log/blob/master/CODE_OF_CONDUCT.md)
### Pull request template
[PULL_REQUEST_TEMPLATE.md](https://github.com/KjellKod/g3log/blob/master/PULL_REQUEST_TEMPLATE.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

View File

@ -1,132 +0,0 @@
[**introduction**](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)
# Welcome to g3log
G3log is an asynchronous logger with three main features:
1. Intuitive `LOG(...)` API
2. `Design-by-contract` `CHECK(...)` functionality
3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash
The super quick introduction to g3log can be seen in the steps 1 - 9 below.
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)
## 1. Easy usage in files
Avoid deep dependency injection complexity and instead get access to the logger as easy as
```
#include <g3log/g3log.hpp>
```
## 2. Access to streaming and print_f log call syntax
Both streaming syntax `LOG` and print_f `LOGF` syntax are available.
```
LOGF(INFO, "Hi log %d", 123);
LOG(INF) << "Hi log " << 123;
```
## 3. Conditional logging
```
LOG_IF(INFO, (1 < 2)) << "If true this message will be logged";
LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message");
```
## 4. Design-by-contract framework
```
CHECK(less != more); // not fatal
CHECK_F(less > more, "CHECK(false) will trigger a fatal message")
```
## 5. Handling of fatal
By default g3log will capture fatal events such as `LOG(FATAL)`, `CHECK(false)` and otherwise fatal signals such as:
```
SIGABRT
SIGFPE
SIGILL
SIGSEGV
SIGTERM
```
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.
If `object` symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink.
#### 5b. Overriding and customization of fatal event handling
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.
## 6. Default and Custom logging levels
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)
### 7. Log filtering
Log filtering is handled in g3log if dynamic logging levels are enabled
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)
## 8. 3rd party and custom logging sinks
The default logging sink has no external 3rd party dependencies. For more logging sinks please see [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks)
- log rotate
- log to syslog
- log to colored terminal output
- log rotate with filter
See the [API.md](API.md) for more information about the simple steps to creating your own logging sink.
## 9. Log instantiation
With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this:
```cpp
const std::string directory = "./";
const std::string name = "TestLogFile";
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(name, directory);
```
The resulting filename would be something like:
```
./TestLogFile.g3log.20160217-001406.log
```
## <a name="performance">Performance</a>
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.
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.
You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
## <a name="continuos_integration">Continuos Integration</a>
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.
```
1. appveyor --> g3log/appveyor.yml
2. circleCI --> g3log/.circleci/config.yml
3. github actions --> g3log/.github/workflows/*.yml
```
## <a name="feedback">Feedback</a>
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.
If you have ANY questions or problems please do not hesitate in contacting me at
`Hedstrom @ Kjellod. cc`
# <a name="say-thanks">Say Thanks</a>
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.
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/g3log/25)
* $5 for a cup of coffee
* $25 for a late evening coding with takeout
Cheers
Kjell *(a.k.a. KjellKod)*
[**introduction**](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

View File

@ -22,25 +22,21 @@
#
# ==============================================================
IF (MSVC OR MINGW)
set(EXAMPLE_PLATFORM_LINK_LIBRIES dbghelp)
ENDIF()
set(DIR_EXAMPLE ${g3log_SOURCE_DIR}/example)
option (ADD_FATAL_EXAMPLE "Fatal (fatal-crashes/contract) examples " ON)
IF (ADD_FATAL_EXAMPLE)
message( STATUS "-DADD_FATAL_EXAMPLE=ON" )
message( STATUS "\t\t[contract][sigsegv][fatal choice] are examples of when g3log comes in handy\n" )
MESSAGE("-DADD_FATAL_EXAMPLE=ON\t\t[contract][sigsegv][fatal choice] are examples of when g3log comes in handy")
include_directories (${DIR_EXAMPLE})
add_executable(g3log-FATAL-contract ${DIR_EXAMPLE}/main_contract.cpp)
add_executable(g3log-FATAL-sigsegv ${DIR_EXAMPLE}/main_sigsegv.cpp)
add_executable(g3log-FATAL-choice ${DIR_EXAMPLE}/main_fatal_choice.cpp)
target_link_libraries(g3log-FATAL-contract ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES})
target_link_libraries(g3log-FATAL-sigsegv ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES})
target_link_libraries(g3log-FATAL-choice ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES})
target_link_libraries(g3log-FATAL-contract ${G3LOG_LIBRARY})
target_link_libraries(g3log-FATAL-sigsegv ${G3LOG_LIBRARY})
target_link_libraries(g3log-FATAL-choice ${G3LOG_LIBRARY})
ELSE()
message( STATUS "-DADD_SIMPLE_EXAMPLE=OFF" )
MESSAGE("-DADD_SIMPLE_EXAMPLE=OFF")
ENDIF (ADD_FATAL_EXAMPLE)

View File

@ -9,57 +9,51 @@
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <iomanip>
#include <iostream>
#include <thread>
#include <iostream>
namespace {
namespace
{
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string path_to_log_file = "./";
#else
const std::string path_to_log_file = "/tmp/";
#endif
} // namespace
}
namespace example_fatal {
void killWithContractIfNonEqual(int first, int second) {
namespace example_fatal
{
void killWithContractIfNonEqual(int first, int second)
{
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";
}
} // namespace example_fatal
} // example fatal
int main(int argc, char** argv) {
int main(int argc, char **argv)
{
double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f;
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(argv[0], path_to_log_file);
auto handle= worker->addDefaultLogger(argv[0], path_to_log_file);
g3::initializeLogging(worker.get());
std::future<std::string> log_file_name = handle->call(&g3::FileSink::fileName);
// Exmple of overriding the default formatting of log entry
auto changeFormatting = handle->call(&g3::FileSink::overrideLogDetails, g3::LogMessage::FullLogDetailsToString);
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";
// example of ovrriding the default formatting of header
auto changeHeader = handle->call(&g3::FileSink::overrideLogHeader, newHeader);
changeFormatting.wait();
changeHeader.wait();
std::cout << "* This is an example of g3log. It WILL exit by a failed CHECK(...)" << std::endl;
std::cout << "* that acts as a FATAL trigger. Please see the generated log and " << std::endl;
std::cout << "* compare to the code at:\n* \t g3log/test_example/main_contract.cpp" << std::endl;
std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n"
<< std::endl;
std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl;
LOGF(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO";
LOG(G3LOG_DEBUG) << "Test SLOG DEBUG";
LOG(DEBUG) << "Test SLOG DEBUG";
LOG(INFO) << "one: " << 1;
LOG(INFO) << "two: " << 2;
LOG(INFO) << "one and two: " << 1 << " and " << 2;
LOG(G3LOG_DEBUG) << "float 2.14: " << 1000 / 2.14f;
LOG(G3LOG_DEBUG) << "pi double: " << pi_d;
LOG(G3LOG_DEBUG) << "pi float: " << pi_f;
LOG(G3LOG_DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOG(DEBUG) << "float 2.14: " << 1000 / 2.14f;
LOG(DEBUG) << "pi double: " << pi_d;
LOG(DEBUG) << "pi float: " << pi_f;
LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOGF(INFO, "pi float printf:%f", pi_f);
// FATAL SECTION
@ -67,3 +61,4 @@ int main(int argc, char** argv) {
int larger = 2;
example_fatal::killWithContractIfNonEqual(smaller, larger);
}

View File

@ -9,14 +9,14 @@
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <cctype>
#include <chrono>
#include <exception>
#include <future>
#include <iostream>
#include <string>
#include <thread>
#include <cctype>
#include <future>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
#include <exception>
#ifndef _MSC_VER
#define NOEXCEPT noexcept
@ -24,48 +24,50 @@
#define NOEXCEPT throw()
#endif
namespace {
namespace
{
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string path_to_log_file = "./";
#else
const std::string path_to_log_file = "/tmp/";
#endif
void ToLower(std::string& str) {
for (auto& character : str) {
void ToLower(std::string &str)
{
for (auto &character : str) {
character = std::tolower(character);
}
}
void RaiseSIGABRT() {
raise(SIGABRT);
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
LOG(WARNING) << "Expected to have died by now...";
}
void RaiseSIGFPE() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by");
raise(SIGFPE);
LOG(WARNING) << "Expected to have died by now...";
}
void RaiseSIGSEGV() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(G3LOG_DEBUG) << "Exit by SIGSEGV";
LOG(DEBUG) << " trigger exit";
LOG(DEBUG) << "Exit by SIGSEGV";
raise(SIGSEGV);
LOG(WARNING) << "Expected to have died by now...";
}
void RaiseSIGILL() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOGF(G3LOG_DEBUG, "Exit by %s", "SIGILL");
LOG(DEBUG) << " trigger exit";
LOGF(DEBUG, "Exit by %s", "SIGILL");
raise(SIGILL);
LOG(WARNING) << "Expected to have died by now...";
}
void RAiseSIGTERM() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
LOGF_IF(INFO, (false != true), "Exiting %s SIGFPE", "by");
raise(SIGTERM);
LOG(WARNING) << "Expected to have died by now...";
@ -73,7 +75,7 @@ namespace {
int gShouldBeZero = 1;
void DivisionByZero() {
LOG(G3LOG_DEBUG) << " trigger exit Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero;
LOG(DEBUG) << " trigger exit Executing DivisionByZero: gShouldBeZero: " << gShouldBeZero;
LOG(INFO) << "Division by zero is a big no-no";
int value = 3;
auto test = value / gShouldBeZero;
@ -81,34 +83,35 @@ namespace {
}
void IllegalPrintf() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(G3LOG_DEBUG) << "Impending doom due to illeteracy";
LOG(DEBUG) << " trigger exit";
LOG(DEBUG) << "Impending doom due to illeteracy";
LOGF(INFO, "2nd attempt at ILLEGAL PRINTF_SYNTAX %d EXAMPLE. %s %s", "hello", 1);
LOG(WARNING) << "Expected to have died by now...";
}
void OutOfBoundsArrayIndexing() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
std::vector<int> v;
v[0] = 5;
LOG(WARNING) << "Expected to have died by now...";
}
void AccessViolation() {
LOG(G3LOG_DEBUG) << " trigger exit";
char* ptr = 0;
LOG(DEBUG) << " trigger exit";
char *ptr = 0;
LOG(INFO) << "Death by access violation is imminent";
*ptr = 0;
LOG(WARNING) << "Expected to have died by now...";
}
void NoExitFunction() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
CHECK(false) << "This function should never be called";
}
void RaiseSIGABRTAndAccessViolation() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
auto f1 = std::async(std::launch::async, &RaiseSIGABRT);
auto f2 = std::async(std::launch::async, &AccessViolation);
@ -116,9 +119,10 @@ namespace {
f2.wait();
}
using deathfunc = void (*)(void);
using deathfunc = void (*) (void);
void Death_x10000(deathfunc func, std::string funcname) NOEXCEPT {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
std::vector<std::future<void>> asyncs;
asyncs.reserve(10000);
for (auto idx = 0; idx < 10000; ++idx) {
@ -132,20 +136,19 @@ namespace {
std::cout << __FUNCTION__ << " unexpected result. Death by " << funcname << " did not crash and exit the system" << std::endl;
}
void Throw() NOEXCEPT {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
std::future<int> empty;
empty.get();
empty.get();
// --> thows future_error http://en.cppreference.com/w/cpp/thread/future_error
// example of std::exceptions can be found here: http://en.cppreference.com/w/cpp/error/exception
}
void SegFaultAttempt_x10000() NOEXCEPT {
deathfunc f = [] {
char* ptr = 0;
*ptr = 1;
};
void SegFaultAttempt_x10000() NOEXCEPT {
deathfunc f = []{char* ptr = 0; *ptr = 1; };
Death_x10000(f, "throw uncaught exception... and then some sigsegv calls");
}
@ -154,7 +157,7 @@ namespace {
}
void FailedCHECK() {
LOG(G3LOG_DEBUG) << " trigger exit";
LOG(DEBUG) << " trigger exit";
CHECK(false) << "This is fatal";
}
@ -166,57 +169,28 @@ namespace {
CallActualExitFunction(fatal_function);
}
void ExecuteDeathFunction(const bool runInNewThread, int fatalChoice) {
LOG(G3LOG_DEBUG) << "trigger exit";
LOG(DEBUG) << "trigger exit";
auto exitFunction = &NoExitFunction;
switch (fatalChoice) {
case 1:
exitFunction = &RaiseSIGABRT;
break;
case 2:
exitFunction = &RaiseSIGFPE;
break;
case 3:
exitFunction = &RaiseSIGSEGV;
break;
case 4:
exitFunction = &RaiseSIGILL;
break;
case 5:
exitFunction = &RAiseSIGTERM;
break;
case 6:
exitFunction = &DivisionByZero;
gShouldBeZero = 0;
DivisionByZero();
break;
case 7:
exitFunction = &IllegalPrintf;
break;
case 8:
exitFunction = &OutOfBoundsArrayIndexing;
break;
case 9:
exitFunction = &AccessViolation;
break;
case 10:
exitFunction = &RaiseSIGABRTAndAccessViolation;
break;
case 11:
exitFunction = &Throw;
break;
case 12:
exitFunction = &FailedCHECK;
break;
case 13:
exitFunction = &AccessViolation_x10000;
break;
case 14:
exitFunction = &SegFaultAttempt_x10000;
break;
default:
break;
case 1: exitFunction = &RaiseSIGABRT; break;
case 2: exitFunction = &RaiseSIGFPE; break;
case 3: exitFunction = &RaiseSIGSEGV; break;
case 4: exitFunction = &RaiseSIGILL; break;
case 5: exitFunction = &RAiseSIGTERM; break;
case 6: exitFunction = &DivisionByZero; gShouldBeZero = 0; DivisionByZero(); break;
case 7: exitFunction = &IllegalPrintf; break;
case 8: exitFunction = &OutOfBoundsArrayIndexing; break;
case 9: exitFunction = &AccessViolation; break;
case 10: exitFunction = &RaiseSIGABRTAndAccessViolation; break;
case 11: exitFunction = &Throw; break;
case 12: exitFunction = &FailedCHECK; break;
case 13: exitFunction = &AccessViolation_x10000; break;
case 14: exitFunction = &SegFaultAttempt_x10000; break;
default: break;
}
if (runInNewThread) {
auto dieInNearFuture = std::async(std::launch::async, CallExitFunction, exitFunction);
@ -226,10 +200,12 @@ namespace {
}
std::string unexpected = "Expected to exit by FATAL event. That did not happen (printf choice in Windows?).";
unexpected.append("Choice was: ").append(std::to_string(fatalChoice)).append(", async?: ").append(std::to_string(runInNewThread)).append("\n\n***** TEST WILL RUN AGAIN *****\n\n");
unexpected.append("Choice was: ").append(std::to_string(fatalChoice)).append(", async?: ")
.append(std::to_string(runInNewThread)).append("\n\n***** TEST WILL RUN AGAIN *****\n\n");
std::cerr << unexpected << std::endl;
std::cerr << unexpected << std::endl;
LOG(WARNING) << unexpected;
}
bool AskForAsyncDeath() {
@ -248,6 +224,8 @@ namespace {
return ("yes" == option);
}
int ChoiceOfFatalExit() {
std::string option;
int choice = {0};
@ -279,7 +257,7 @@ namespace {
choice = std::stoi(option);
if (choice <= 0 || choice > 14) {
std::cout << "Invalid choice: [" << option << "\n\n";
} else {
} else {
return choice;
}
} catch (...) {
@ -297,21 +275,22 @@ namespace {
const int exitChoice = ChoiceOfFatalExit();
ForwardChoiceForFatalExit(runInNewThread, exitChoice);
}
} // namespace
} // namespace
void breakHere() {
std::ostringstream oss;
oss << "Fatal hook function: " << __FUNCTION__ << ":" << __LINE__ << " was called";
oss << "Fatal hook function: " << __FUNCTION__ << ":" << __LINE__ << " was called";
oss << " through g3::setFatalPreLoggingHook(). setFatalPreLoggingHook should be called AFTER g3::initializeLogging()" << std::endl;
LOG(G3LOG_DEBUG) << oss.str();
LOG(DEBUG) << oss.str();
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
__debugbreak();
#endif
}
int main(int argc, char** argv) {
int main(int argc, char **argv)
{
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(argv[0], path_to_log_file);
auto handle= worker->addDefaultLogger(argv[0], path_to_log_file);
g3::initializeLogging(worker.get());
g3::setFatalPreLoggingHook(&breakHere);
std::future<std::string> log_file_name = handle->call(&g3::FileSink::fileName);
@ -319,10 +298,10 @@ int main(int argc, char** argv) {
std::cout << "**** G3LOG FATAL EXAMPLE ***\n\n"
<< "Choose your type of fatal exit, then "
<< " read the generated log and backtrace.\n"
<< "The logfile is generated at: [" << log_file_name.get() << "]\n\n"
<< std::endl;
<< "The logfile is generated at: [" << log_file_name.get() << "]\n\n" << std::endl;
LOGF(G3LOG_DEBUG, "Fatal exit example starts now, it's as easy as %d", 123);
LOGF(DEBUG, "Fatal exit example starts now, it's as easy as %d", 123);
LOG(INFO) << "Feel free to read the source code also in g3log/example/main_fatal_choice.cpp";
while (true) {
@ -332,4 +311,6 @@ int main(int argc, char** argv) {
LOG(WARNING) << "Expected to exit by fatal event, this code line should never be reached";
CHECK(false) << "Forced death";
return 0;
}

View File

@ -10,55 +10,63 @@
#include <g3log/logworker.hpp>
#include <iomanip>
#include <thread>
#include <iostream>
#include <memory>
#include <thread>
namespace {
namespace
{
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string path_to_log_file = "./";
#else
const std::string path_to_log_file = "/tmp/";
#endif
} // namespace
}
namespace example_fatal {
namespace example_fatal
{
// on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected)
// On windows it'll probably crash too.
void tryToKillWithIllegalPrintout() {
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl
<< std::flush;
std::cout << "************************************************************\n\n"
<< std::endl
<< std::flush;
void tryToKillWithIllegalPrintout()
{
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush;
std::cout << "************************************************************\n\n" << std::endl << std::flush;
std::this_thread::sleep_for(std::chrono::seconds(1));
const std::string logging = "logging";
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());
LOGF(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());
}
// The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a segmentation
// The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a SIGSEGV
// fault as expected by the illegal printf-format usage. just in case we exit by zero division"
void killByZeroDivision(int value) {
int zero = 0; // trying to fool the compiler to automatically warn
void killByZeroDivision(int value)
{
int zero = 0; // trying to fool the compiler to automatically warn
LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero;
}
void tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::string> badStringPtr) {
auto badPtr = std::move(badStringPtr);
LOG(INFO) << "Function calls through a nullptr object will trigger segmentation fault";
LOG(INFO) << "Function calls through a nullptr object will trigger SIGSEGV";
badStringPtr->append("crashing");
}
} // namespace example_fatal
int main(int argc, char** argv) {
} // example fatal
int main(int argc, char **argv)
{
double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f;
using namespace g3;
std::unique_ptr<LogWorker> logworker{LogWorker::createLogWorker()};
auto sinkHandle = logworker->addSink(std::make_unique<FileSink>(argv[0], path_to_log_file),
std::unique_ptr<LogWorker> logworker {LogWorker::createLogWorker()};
auto sinkHandle = logworker->addSink(std2::make_unique<FileSink>(argv[0], path_to_log_file),
&FileSink::fileWrite);
initializeLogging(logworker.get());
@ -66,19 +74,19 @@ int main(int argc, char** argv) {
std::cout << "* This is an example of g3log. It WILL exit by a FATAL trigger" << std::endl;
std::cout << "* Please see the generated log and compare to the code at" << std::endl;
std::cout << "* g3log/test_example/main.cpp" << std::endl;
std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n"
<< std::endl;
std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl;
LOGF(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO";
LOG(G3LOG_DEBUG) << "Test SLOG DEBUG";
LOG(DEBUG) << "Test SLOG DEBUG";
LOG(INFO) << "one: " << 1;
LOG(INFO) << "two: " << 2;
LOG(INFO) << "one and two: " << 1 << " and " << 2;
LOG(G3LOG_DEBUG) << "float 2.14: " << 1000 / 2.14f;
LOG(G3LOG_DEBUG) << "pi double: " << pi_d;
LOG(G3LOG_DEBUG) << "pi float: " << pi_f;
LOG(G3LOG_DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOG(DEBUG) << "float 2.14: " << 1000 / 2.14f;
LOG(DEBUG) << "pi double: " << pi_d;
LOG(DEBUG) << "pi float: " << pi_f;
LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOGF(INFO, "pi float printf:%f", pi_f);
//
@ -91,8 +99,8 @@ int main(int argc, char** argv) {
LOG_IF(INFO, (1 < 2)) << "If true this text will be logged";
LOGF_IF(INFO, (1 < 2), "if %d<%d : then this text will be logged", 1, 2);
LOG_IF(FATAL, (2 > 3)) << "This message should NOT throw";
LOGF(G3LOG_DEBUG, "This API is popular with some %s", "programmers");
LOGF_IF(G3LOG_DEBUG, (1 < 2), "If true, then this %s will be logged", "message");
LOGF(DEBUG, "This API is popular with some %s", "programmers");
LOGF_IF(DEBUG, (1 < 2), "If true, then this %s will be logged", "message");
// OK --- on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected)
@ -103,8 +111,8 @@ int main(int argc, char** argv) {
std::unique_ptr<std::string> badStringPtr;
example_fatal::tryToKillWithAccessingIllegalPointer(std::move(badStringPtr));
// what happened? OK. let us just exit with SIGFPE
int value = 1; // system dependent but it SHOULD never reach this line
// what happened? OK. let us just exit with SIGFPE
int value = 1; // system dependent but it SHOULD never reach this line
example_fatal::killByZeroDivision(value);
return 0;
}

219
iOS.cmake
View File

@ -1,219 +0,0 @@
# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
# files which are included with CMake 2.8.4
# It has been altered for iOS development
# Options:
#
# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64
# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
#
# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
# By default this location is automatcially chosen based on the IOS_PLATFORM value above.
# If set manually, it will override the default location and force the user of a particular Developer Platform
#
# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
# If set manually, this will force the use of a specific SDK version
# Macros:
#
# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE)
# A convenience macro for setting xcode specific properties on targets
# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1")
#
# find_host_package (PROGRAM ARGS)
# A macro used to find executable programs on the host system, not within the iOS environment.
# Thanks to the android-cmake project for providing the command
# Standard settings
set (CMAKE_SYSTEM_NAME Darwin)
set (CMAKE_SYSTEM_VERSION 1)
set (UNIX True)
set (APPLE True)
set (IOS True)
# Required as of cmake 2.8.10
set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
# Allow external setting of CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET. This is clumsy
# but it provides flexibility of not having to hardcode the deployment version in the file or have
# numerous copies of the file
if ($ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET})
set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET $ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET})
endif ($ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET})
# Determine the cmake host system version so we know where to find the iOS SDKs
find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin)
if (CMAKE_UNAME)
exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION)
string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
endif (CMAKE_UNAME)
# Use clang. Use xcrun to determine the location
EXEC_PROGRAM(xcrun ARGS -find clang OUTPUT_VARIABLE APPLE_CLANG)
EXEC_PROGRAM(xcrun ARGS -find clang++ OUTPUT_VARIABLE APPLE_CLANGPP)
set(CMAKE_C_COMPILER ${APPLE_CLANG})
set(CMAKE_CXX_COMPILER ${APPLE_CLANGPP})
set(CMAKE_AR ar CACHE FILEPATH "" FORCE)
# Skip the platform compiler checks for cross compiling
set (CMAKE_CXX_COMPILER_WORKS TRUE)
set (CMAKE_C_COMPILER_WORKS TRUE)
# All iOS/Darwin specific settings - some may be redundant
set (CMAKE_SHARED_LIBRARY_PREFIX "lib")
set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
set (CMAKE_SHARED_MODULE_PREFIX "lib")
set (CMAKE_SHARED_MODULE_SUFFIX ".so")
set (CMAKE_MODULE_EXISTS 1)
set (CMAKE_DL_LIBS "")
set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
# Hidden visibilty is required for cxx on iOS
set (CMAKE_C_FLAGS_INIT "")
set (CMAKE_CXX_FLAGS_INIT "-fvisibility=hidden")
set (CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "" CACHE STRING "Force unset of CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN" FORCE)
set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
set (CMAKE_PLATFORM_HAS_INSTALLNAME 1)
set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names")
set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names")
set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
set (CMAKE_MACOSX_BUNDLE ON)
set (CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED OFF)
# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree
# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache
# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun)
# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex
if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
# Setup iOS platform unless specified manually with IOS_PLATFORM
if (NOT DEFINED IOS_PLATFORM)
set (IOS_PLATFORM "OS")
endif (NOT DEFINED IOS_PLATFORM)
set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
# Setup building for arm64 or not
if (NOT DEFINED BUILD_ARM64)
set (BUILD_ARM64 true)
endif (NOT DEFINED BUILD_ARM64)
set (BUILD_ARM64 ${BUILD_ARM64} CACHE STRING "Build arm64 arch or not")
# Check the platform selection and setup for developer root
if (${IOS_PLATFORM} STREQUAL "OS")
set (IOS_PLATFORM_LOCATION "iPhoneOS.platform")
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos")
elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR")
set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64")
set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
# This causes the installers to properly locate the output libraries
set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
else (${IOS_PLATFORM} STREQUAL "OS")
message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR")
endif (${IOS_PLATFORM} STREQUAL "OS")
# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT
# Note Xcode 4.3 changed the installation location, choose the most recent one available
exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR)
set (XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
if (EXISTS ${XCODE_POST_43_ROOT})
set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT})
elseif(EXISTS ${XCODE_PRE_43_ROOT})
set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT})
endif (EXISTS ${XCODE_POST_43_ROOT})
endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
if (NOT DEFINED CMAKE_IOS_SDK_ROOT)
file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
if (_CMAKE_IOS_SDKS)
list (SORT _CMAKE_IOS_SDKS)
list (REVERSE _CMAKE_IOS_SDKS)
list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
else (_CMAKE_IOS_SDKS)
message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
endif (_CMAKE_IOS_SDKS)
message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
endif (NOT DEFINED CMAKE_IOS_SDK_ROOT)
set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
# Set the sysroot default to the most recent SDK
set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
# set the architecture for iOS
if (${IOS_PLATFORM} STREQUAL "OS")
set (IOS_ARCH armv7 armv7s arm64 arm64e)
elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR")
set (IOS_ARCH i386)
elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64")
set (IOS_ARCH x86_64)
endif (${IOS_PLATFORM} STREQUAL "OS")
set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS")
# Set the find root to the iOS developer roots and to user defined paths
set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root")
# default to searching for frameworks first
set (CMAKE_FIND_FRAMEWORK FIRST)
# set up the default search directories for frameworks
set (CMAKE_SYSTEM_FRAMEWORK_PATH
${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
)
# only search the iOS sdks, not the remainder of the host filesystem
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# This little macro lets you set any XCode specific property
macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE)
set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE})
endmacro (set_xcode_property)
# This macro lets you find executable programs on the host system
macro (find_host_package)
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
set (IOS FALSE)
find_package(${ARGN})
set (IOS TRUE)
set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endmacro (find_host_package)

View File

@ -1,121 +0,0 @@
if(IOS_PLATFORM)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries)
endif(IOS_PLATFORM)
if(G3_IOS_LIB)
if (CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET)
set (ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET} ${CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET})
endif(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET)
set(TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/iOS.cmake")
set(SIM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.i386" CACHE INTERNAL "")
set(SIM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "")
set(SIM64_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.x86_64" CACHE INTERNAL "")
set(SIM64_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "")
set(ARM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.arm" CACHE INTERNAL "")
set(ARM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "")
file(MAKE_DIRECTORY ${SIM_BINARY_DIR})
execute_process(WORKING_DIRECTORY ${SIM_BINARY_DIR}
COMMAND ${CMAKE_COMMAND}
-GXcode
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}
-DIOS_PLATFORM=SIMULATOR
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DADD_FATAL_EXAMPLE=OFF
-DADD_G3LOG_BENCH_PERFORMANCE=OFF
-DADD_G3LOG_UNIT_TEST=OFF
-DG3_SHARED_LIB=OFF
-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON
-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE}
-DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING}
"${SIM_SOURCE_DIR}"
)
file(MAKE_DIRECTORY ${SIM64_BINARY_DIR})
execute_process(WORKING_DIRECTORY ${SIM64_BINARY_DIR}
COMMAND ${CMAKE_COMMAND}
-GXcode
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}
-DIOS_PLATFORM=SIMULATOR64
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DADD_FATAL_EXAMPLE=OFF
-DG3_SHARED_LIB=OFF
-DADD_G3LOG_BENCH_PERFORMANCE=OFF
-DADD_G3LOG_UNIT_TEST=OFF
-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON
-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE}
-DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING}
"${SIM64_SOURCE_DIR}"
)
file(MAKE_DIRECTORY ${ARM_BINARY_DIR})
execute_process(WORKING_DIRECTORY ${ARM_BINARY_DIR}
COMMAND ${CMAKE_COMMAND}
-GXcode
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE}
-DIOS_PLATFORM=OS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DADD_FATAL_EXAMPLE=OFF
-DG3_SHARED_LIB=OFF
-DADD_G3LOG_BENCH_PERFORMANCE=OFF
-DADD_G3LOG_UNIT_TEST=OFF
-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON
-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE}
-DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING}
"${ARM_SOURCE_DIR}"
)
## Simulator i386 version
add_custom_target(sim
COMMAND ${CMAKE_COMMAND}
--build ${SIM_BINARY_DIR}
--config ${CMAKE_BUILD_TYPE}
COMMENT "Building for i386 (simulator)"
VERBATIM
)
## Simulator x86_64 version
add_custom_target(sim64
COMMAND ${CMAKE_COMMAND}
--build ${SIM64_BINARY_DIR}
--config ${CMAKE_BUILD_TYPE}
COMMENT "Building for x86_64 (simulator)"
VERBATIM
)
## ARM version
add_custom_target(arm
COMMAND ${CMAKE_COMMAND}
--build ${ARM_BINARY_DIR}
--config ${CMAKE_BUILD_TYPE}
COMMENT "Building for armv7, armv7s, arm64, arm64e"
VERBATIM
)
set(LIB_G3 libg3log.a)
add_custom_command(
OUTPUT ${LIB_G3}
COMMAND lipo -create
-output "${CMAKE_CURRENT_BINARY_DIR}/${LIB_G3}"
${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}
${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}
${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}
DEPENDS
sim
sim64
arm
"${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}"
"${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}"
"${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}"
VERBATIM
)
add_custom_target(g3log ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${LIB_G3})
endif(G3_IOS_LIB)

View File

@ -1,17 +0,0 @@
site_name: G3log, an asynchronous "crash-safe" logger
site_author: 'Kjell Hedstrom'
site_url: https://kjellkod.github.io/g3log/
theme:
name: material
docs_dir: docs/
nav:
- Introduction to G3log: index.md
- G3log usage: g3log.md
- API description: API.md
- API for custom log formatting: API_custom_formatting.md
- Configure, Build, Package, Install and Test: building.md
- License and contribution: contributing.md

View File

@ -1,18 +0,0 @@
#!/bin/bash
set -ev
set -x
apt-get update -y
apt-get install -y apt-utils | true
apt-get install -y software-properties-common | true
apt-get install -y python-software-properties
apt-get update -y
add-apt-repository -y ppa:jonathonf/gcc
apt-get update -y
apt-get install -y cmake software-properties-common git make
apt-get install -y gcc-7 g++-7
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 90
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90
apt-get install -y unzip zlib1g-dev
apt-get install -y libboost-all-dev

View File

@ -1,27 +1,41 @@
#!/bin/bash
set -ev
set -x
unzip -o 3rdParty/gtest/gtest-1.7.0.zip -d 3rdParty/gtest
if [ "$CXX" = "g++" ]; then export CXX=g++-4.8; fi
if [ "$CXX" = "clang++" ]; then export CXX=clang++-3.4; fi
echo $TRAVIS_OS_NAME
echo $CXX
mkdir -p build_travis
cd build_travis
cmake -DADD_G3LOG_BENCH_PERFORMANCE=ON -DPRETTY_FUNCTION=ON -DADD_G3LOG_UNIT_TEST=ON -DCMAKE_INSTALL_PREFIX=./install -DCPACK_PACKAGING_INSTALL_PREFIX=/opt/g3log ..
cmake --build . --target install
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
cpack -G "ZIP"
unzip g3log-*-Darwin.zip
if [[ $CXX == *"g++"* ]]
then
echo "Testing with g++"
cmake -DUSE_DYNAMIC_LOGGING_LEVELS=ON -DADD_G3LOG_UNIT_TEST=ON ..
make -j
./test_concept_sink
./test_configuration
./test_dynamic_loaded_shared_lib
./test_filechange
./test_io
./test_sink
fi
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
cpack -G "DEB;TGZ"
tar zxvf g3log-*-Linux.tar.gz
fi
# LINUX OR OSX
makeArg=`grep -c ^processor /proc/cpuinfo || sysctl -n hw.ncpu`
make -j$makeArg
ctest -V
if [ "$CXX" = "clang++-3.4" ]
then
echo "Testing with Clang++"
cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=-std=gnu++11 -DUSE_G3LOG_UNIT_TEST=ON ..
make -j
./test_concept_sink
./test_configuration
#./test_dynamic_loaded_shared_lib
./test_filechange
./test_io
./test_sink
fi

View File

@ -7,26 +7,27 @@
* ============================================================================*/
#include "g3log/crashhandler.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/logcapture.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logmessage.hpp"
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
#error "crashhandler_unix.cpp used but it's a windows system"
#endif
#include <cxxabi.h>
#include <execinfo.h>
#include <unistd.h>
#include <atomic>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <execinfo.h>
#include <cxxabi.h>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <thread>
#include <atomic>
#include <map>
#include <mutex>
#include <sstream>
#include <thread>
// Linux/Clang, OSX/Clang, OSX/gcc
#if (defined(__clang__) || defined(__APPLE__))
@ -35,9 +36,8 @@
#include <ucontext.h>
#endif
namespace {
std::atomic<bool> gBlockForFatal{true};
namespace {
const std::map<int, std::string> kSignals = {
{SIGABRT, "SIGABRT"},
@ -48,7 +48,7 @@ namespace {
};
std::map<int, std::string> gSignals = kSignals;
std::map<int, struct sigaction> gSavedSigActions;
bool shouldDoExit() {
static std::atomic<uint64_t> firstExit{0};
@ -56,60 +56,75 @@ namespace {
return (0 == count);
}
void restoreSignalHandler(int signal_number) {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
struct sigaction action;
memset(&action, 0, sizeof (action)); //
sigemptyset(&action.sa_mask);
action.sa_handler = SIG_DFL; // take default action for the signal
sigaction(signal_number, &action, NULL);
#endif
}
// Dump of stack,. then exit through g3log background worker
// ALL thanks to this thread at StackOverflow. Pretty much borrowed from:
// Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes
void signalHandler(int signal_number, siginfo_t* info, void* /*unused_context*/) {
using namespace g3::internal;
void signalHandler(int signal_number, siginfo_t* info, void* unused_context) {
// Only one signal will be allowed past this point
if (false == shouldDoExit()) {
while (shouldBlockForFatalHandling()) {
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
using namespace g3::internal;
{
const auto dump = stackdump();
std::ostringstream fatal_stream;
const auto fatal_reason = exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
fatal_stream << "Received fatal signal: " << fatal_reason;
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
fatal_stream << "\n***** SIGNAL " << fatal_reason << "(signo= " << signal_number << " si_errno= " << info->si_errno << " si_code = " << info->si_code << ")" << std::endl;
fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl;
LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
trigger.stream() << fatal_stream.str();
} // message sent to g3LogWorker
} // message sent to g3LogWorker
// wait to die
}
//
// Installs FATAL signal handler that is enough to handle most fatal events
// on *NIX systems
void installSignalHandler() {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
struct sigaction action;
memset(&action, 0, sizeof (action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = &signalHandler; // callback to crashHandler for fatal signals
action.sa_sigaction = &signalHandler; // callback to crashHandler for fatal signals
// sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction
action.sa_flags = SA_SIGINFO;
// do it verbose style - install all signal actions
for (const auto& sig_pair : gSignals) {
struct sigaction old_action;
memset(&old_action, 0, sizeof(old_action));
if (sigaction(sig_pair.first, &action, &old_action) < 0) {
std::string signalerror = "sigaction - " + sig_pair.second;
perror(signalerror.c_str());
} else {
gSavedSigActions[sig_pair.first] = old_action;
if (sigaction(sig_pair.first, &action, nullptr) < 0) {
const std::string error = "sigaction - " + sig_pair.second;
perror(error.c_str());
}
}
#endif
}
} // end anonymous namespace
} // end anonymous namespace
// Redirecting and using signals. In case of fatal signals g3log should log the fatal signal
// and flush the log queue and then "rethrow" the signal to exit
@ -128,183 +143,107 @@ namespace g3 {
namespace internal {
bool shouldBlockForFatalHandling() {
return gBlockForFatal;
return true; // For windows we will after fatal processing change it to false
}
/// Generate stackdump. Or in case a stackdump was pre-generated and non-empty just use that one
/// i.e. the latter case is only for Windows and test purposes
std::string stackdump(const char* rawdump) {
if (nullptr != rawdump && !std::string(rawdump).empty()) {
return {rawdump};
}
const size_t max_dump_size = 50;
void* dump[max_dump_size];
const size_t size = backtrace(dump, max_dump_size);
char** messages = backtrace_symbols(dump, static_cast<int>(size)); // overwrite sigaction with caller's address
size_t size = backtrace(dump, max_dump_size);
char** messages = backtrace_symbols(dump, size); // overwrite sigaction with caller's address
// dump stack: skip first frame, since that is here
std::ostringstream oss;
for (size_t idx = 1; idx < size && messages != nullptr; ++idx) {
std::string strMessage{messages[idx]};
std::string mangled_name, offset;
/// first look for format that includes brackets "(mangled_name+offset)""
const auto firstBracket = strMessage.find_last_of('(');
const auto secondBracket = strMessage.find_last_of(')');
if (firstBracket != strMessage.npos && secondBracket != strMessage.npos) {
const auto betweenBrackets = strMessage.substr(firstBracket + 1, secondBracket - firstBracket - 1);
const auto plusSign = betweenBrackets.find_first_of('+');
if (plusSign != betweenBrackets.npos) {
mangled_name = betweenBrackets.substr(0, plusSign);
offset = betweenBrackets.substr(plusSign + 1, betweenBrackets.npos);
}
} else {
/// we did not found brackets, looking for "_mangled_name + offset"
const auto plusSign = strMessage.find_first_of('+');
const auto lastUnderscore = strMessage.rfind(" _");
if (plusSign != strMessage.npos && lastUnderscore != strMessage.npos) {
mangled_name = strMessage.substr(lastUnderscore + 1, plusSign - lastUnderscore - 2);
offset = strMessage.substr(plusSign + 2, strMessage.npos);
char* mangled_name = 0, *offset_begin = 0, *offset_end = 0;
// find parantheses and +address offset surrounding mangled name
for (char* p = messages[idx]; *p; ++p) {
if (*p == '(') {
mangled_name = p;
} else if (*p == '+') {
offset_begin = p;
} else if (*p == ')') {
offset_end = p;
break;
}
}
// if the line could be processed, attempt to demangle the symbol
if (!mangled_name.empty() && !offset.empty()) {
if (mangled_name && offset_begin && offset_end &&
mangled_name < offset_begin) {
*mangled_name++ = '\0';
*offset_begin++ = '\0';
*offset_end++ = '\0';
int status;
char* real_name = abi::__cxa_demangle(mangled_name.c_str(), 0, 0, &status);
char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
// if demangling is successful, output the demangled function name
if (status == 0) {
oss << "\tstack dump [" << idx << "] " << real_name << " + " << offset << std::endl;
} // otherwise, output the mangled function name
oss << "\n\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+";
oss << offset_begin << offset_end << std::endl;
}// otherwise, output the mangled function name
else {
oss << "\tstack dump [" << idx << "] " << mangled_name << " + " << offset << std::endl;
oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+";
oss << offset_begin << offset_end << std::endl;
}
free(real_name); // mallocated by abi::__cxa_demangle(...)
free(real_name); // mallocated by abi::__cxa_demangle(...)
} else {
// no demangling done -- just dump the whole line
oss << "\tstack dump [" << idx << "] " << strMessage << std::endl;
oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
}
} // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
} // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
free(messages);
return oss.str();
}
/// string representation of signal ID
std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
int signal_number = static_cast<int>(fatal_id);
switch (signal_number) {
case SIGABRT:
return "SIGABRT";
break;
case SIGFPE:
return "SIGFPE";
break;
case SIGSEGV:
return "SIGSEGV";
break;
case SIGILL:
return "SIGILL";
break;
case SIGTERM:
return "SIGTERM";
break;
default:
std::ostringstream oss;
oss << "UNKNOWN SIGNAL(" << signal_number << ") for " << level.text;
return oss.str();
case SIGABRT: return "SIGABRT";
break;
case SIGFPE: return "SIGFPE";
break;
case SIGSEGV: return "SIGSEGV";
break;
case SIGILL: return "SIGILL";
break;
case SIGTERM: return "SIGTERM";
break;
default:
std::ostringstream oss;
oss << "UNKNOWN SIGNAL(" << signal_number << ") for " << level.text;
return oss.str();
}
}
// Triggered by g3log->g3LogWorker after receiving a FATAL trigger
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
// --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType fatal_signal_id) {
const int signal_number = static_cast<int>(fatal_signal_id);
restoreSignalHandler(signal_number);
std::cerr << "\n\n" << __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n" << std::flush;
// Restore all saved signal handlers. If handling a signal which causes exiting
// than let the original signal handlers to handle other signals.
for (const auto& sig : gSignals) {
restoreSignalHandler(sig.first);
}
std::cerr << "\n\n"
<< __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n"
<< std::flush;
raise(signal_number);
// When running as PID1 the above kill doesn't have any effect (execution simply passes through it, contrary
// to a non-PID1 process where execution stops at kill and switches over to signal handling). Also as PID1
// we must unblock the thread that received the original signal otherwise the process will never terminate.
gBlockForFatal = false;
kill(getpid(), signal_number);
exit(signal_number);
}
} // end g3::internal
// This function is intended to be async-signal-safe. Using write and STDERR_FILENO should be
// safe in a signal handler. ref: http://pubs.opengroup.org/onlinepubs/009695399/functions/write.html
// This function is intended to be async-signal-safe.
// It writes an error message to stderr using the write system call,
// which is listed as async-signal-safe by POSIX.
size_t writeErrorMessage(const char* message) {
if (message == nullptr) {
return 0;
}
// Calculate the length of the message without using std library strlen or similar
// this is to ensure async-signal-safe by POSIX
size_t length = 0;
for (const char* p = message; *p != '\0'; ++p) {
++length;
}
// Write the message to STDERR_FILENO in a single call.
// This assumes that the message is not too large for a single write.
auto bytes_written = write(STDERR_FILENO, message, length);
return bytes_written;
}
// restores the signal handler back to default
void restoreFatalHandlingToDefault() {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
overrideSetupSignals(kSignals);
#endif
}
} // namespace internal
std::string signalToStr(int signal_number) {
std::string signal_name;
const char* signal_name_sz = strsignal(signal_number);
// From strsignal(3): On some systems (but not on Linux), NULL may instead
// be returned for an invalid signal number.
if (nullptr == signal_name_sz) {
signal_name = "Unknown signal " + std::to_string(signal_number);
} else {
signal_name = signal_name_sz;
}
return signal_name;
}
void restoreSignalHandler(int signal_number) {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
auto old_action_it = gSavedSigActions.find(signal_number);
if (old_action_it == gSavedSigActions.end()) {
return;
}
if (sigaction(signal_number, &(old_action_it->second), nullptr) < 0) {
auto signalname = std::string("sigaction - ") + signalToStr(signal_number);
// https://man7.org/linux/man-pages/man7/signal-safety.7.html (see signal-safety)
internal::writeErrorMessage(signalname.c_str());
}
gSavedSigActions.erase(old_action_it);
#endif
}
// This will override the default signal handler setup and instead
// install a custom set of signals to handle
@ -316,13 +255,20 @@ namespace g3 {
}
gSignals = overrideSignals;
installCrashHandler(); // installs all the signal handling for gSignals
installCrashHandler(); // installs all the signal handling for gSignals
}
// restores the signal handler back to default
void restoreSignalHandlerToDefault() {
overrideSetupSignals(kSignals);
}
// installs the signal handling for whatever signal set that is currently active
// If you want to setup your own signal handling then
// You should instead call overrideSetupSignals()
void installCrashHandler() {
installSignalHandler();
}
} // end namespace g3
} // end namespace g3

View File

@ -10,21 +10,19 @@
#error "crashhandler_windows.cpp used but not on a windows system"
#endif
#include <process.h> // getpid
#include <windows.h>
#include <atomic>
#include <csignal>
#include <cstring>
#include <sstream>
#include <atomic>
#include <process.h> // getpid
#include "g3log/crashhandler.hpp"
#include "g3log/g3log.hpp"
#include "g3log/logcapture.hpp"
#include "g3log/stacktrace_windows.hpp"
#include "g3log/logcapture.hpp"
#define getpid _getpid
namespace {
std::atomic<bool> gBlockForFatal{true};
std::atomic<bool> gBlockForFatal {true};
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
@ -32,9 +30,39 @@ namespace {
#endif
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
void* g_vector_exception_handler = nullptr;
void *g_vector_exception_handler = nullptr;
#endif
// Restore back to default fatal event handling
void ReverseToOriginalFatalHandling() {
SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler);
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
RemoveVectoredExceptionHandler (g_vector_exception_handler);
#endif
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGILL, SIG_DFL))
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
perror("signal - SIGABRT");
#endif
}
// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
void signalHandler(int signal_number) {
using namespace g3::internal;
@ -44,11 +72,12 @@ namespace {
fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
trigger.stream() << fatal_stream.str();
// Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens.
// Be patient. The "Debug" dialog should pop-up eventually if you doing it in Visual Studio.
// Be patient. The "Debug" dialogue should pop-up eventually if you doing it in Visual Studio.
// For fatal signals only, not exceptions.
// This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint
// Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger
@ -57,10 +86,12 @@ namespace {
#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
__debugbreak();
#endif
} // scope exit - message sent to LogWorker, wait to die...
} // scope exit - message sent to LogWorker, wait to die...
// Unhandled exception catching
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) {
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS *info, const std::string &handler) {
std::string dump = stacktrace::stackdump(info);
std::ostringstream fatal_stream;
@ -71,21 +102,23 @@ namespace {
const auto fatal_id = static_cast<g3::SignalType>(exception_code);
LogCapture trigger(g3::internal::FATAL_EXCEPTION, fatal_id, dump.c_str());
trigger.stream() << fatal_stream.str();
// FATAL Exception: It doesn't necessarily stop here. we pass on continue search
// FATAL Exception: It doesn't necessarily stop here we pass on continue search
// if no one else will catch that then it's goodbye anyhow.
// The RISK here is if someone is catching this and returning "EXCEPTION_EXECUTE_HANDLER"
// The RISK here is if someone is cathing this and returning "EXCEPTION_EXECUTE_HANDLER"
// but does not shutdown then the software will be running with g3log shutdown.
// .... However... this must be seen as a bug from standard handling of fatal exceptions
// https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx
return EXCEPTION_CONTINUE_SEARCH;
}
// Unhandled exception catching
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
g3::internal::restoreFatalHandlingToDefault();
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS *info) {
ReverseToOriginalFatalHandling();
return exceptionHandling(info, "Unexpected Exception Handler");
}
/// Setup through (Windows API) AddVectoredExceptionHandler
/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
@ -97,12 +130,17 @@ namespace {
// responsibility to deal with this by the client software.
return EXCEPTION_CONTINUE_SEARCH;
} else {
g3::internal::restoreFatalHandlingToDefault();
ReverseToOriginalFatalHandling();
return exceptionHandling(p, "Vectored Exception Handler");
}
}
#endif
} // end anonymous namespace
} // end anonymous namespace
namespace g3 {
namespace internal {
@ -112,10 +150,11 @@ namespace g3 {
return gBlockForFatal;
}
/// Generate stackdump. Or in case a stackdump was pre-generated and
/// non-empty just use that one. i.e. the latter case is only for
/// Windows and test purposes
std::string stackdump(const char* dump) {
std::string stackdump(const char *dump) {
if (nullptr != dump && !std::string(dump).empty()) {
return {dump};
}
@ -123,28 +162,20 @@ namespace g3 {
return stacktrace::stackdump();
}
/// string representation of signal ID or Windows exception id
std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
std::string exitReasonName(const LEVELS &level, g3::SignalType fatal_id) {
if (level == g3::internal::FATAL_EXCEPTION) {
return stacktrace::exceptionIdToText(fatal_id);
}
switch (fatal_id) {
case SIGABRT:
return "SIGABRT";
break;
case SIGFPE:
return "SIGFPE";
break;
case SIGSEGV:
return "SIGSEGV";
break;
case SIGILL:
return "SIGILL";
break;
case SIGTERM:
return "SIGTERM";
break;
case SIGABRT: return "SIGABRT"; break;
case SIGFPE: return "SIGFPE"; break;
case SIGSEGV: return "SIGSEGV"; break;
case SIGILL: return "SIGILL"; break;
case SIGTERM: return "SIGTERM"; break;
default:
std::ostringstream oss;
oss << "UNKNOWN SIGNAL(" << fatal_id << ")";
@ -152,14 +183,16 @@ namespace g3 {
}
}
// Triggered by g3log::LogWorker after receiving a FATAL trigger
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
// --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType fatal_signal_id) {
restoreFatalHandlingToDefault();
void exitWithDefaultSignalHandler(const LEVELS &level, g3::SignalType fatal_signal_id) {
ReverseToOriginalFatalHandling();
// For windows exceptions we want to continue the possibility of
// exception handling now when the log and stacktrace are flushed
// to sinks. We therefore avoid to kill the process here. Instead
// to sinks. We therefore avoid to kill the preocess here. Instead
// it will be the exceptionHandling functions above that
// will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH
if (g3::internal::FATAL_EXCEPTION == level) {
@ -167,49 +200,20 @@ namespace g3 {
return;
}
// for a signal however, we exit through that fatal signal
// for a sigal however, we exit through that fatal signal
const int signal_number = static_cast<int>(fatal_signal_id);
raise(signal_number);
}
// FYI: Concept of async-signal-safe operations does not exist on windows
// we stick to perror for lack of better alternatives.
size_t writeErrorMessage(const char* message) {
perror(message);
return std::strlen(message);
}
// Restore back to default fatal event handling
void restoreFatalHandlingToDefault() {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
SetUnhandledExceptionFilter(g_previous_unexpected_exception_handler);
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
RemoveVectoredExceptionHandler(g_vector_exception_handler);
#endif
if (SIG_ERR == signal(SIGABRT, SIG_DFL))
internal::writeErrorMessage("signal - SIGABRT");
if (SIG_ERR == signal(SIGFPE, SIG_DFL))
internal::writeErrorMessage("signal - SIGABRT");
if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
internal::writeErrorMessage("signal - SIGABRT");
if (SIG_ERR == signal(SIGILL, SIG_DFL))
internal::writeErrorMessage("signal - SIGABRT");
if (SIG_ERR == signal(SIGTERM, SIG_DFL))
internal::writeErrorMessage("signal - SIGABRT");
#endif
}
void installSignalHandler() {
g3::installSignalHandlerForThread();
}
} // namespace internal
} // end g3::internal
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
/// on Windows. This is automatically done if you do at least one LOG(...) call
@ -220,15 +224,15 @@ namespace g3 {
if (!g_installed_thread_signal_handler) {
g_installed_thread_signal_handler = true;
if (SIG_ERR == signal(SIGTERM, signalHandler))
internal::writeErrorMessage("signal - SIGTERM");
perror("signal - SIGTERM");
if (SIG_ERR == signal(SIGABRT, signalHandler))
internal::writeErrorMessage("signal - SIGABRT");
perror("signal - SIGABRT");
if (SIG_ERR == signal(SIGFPE, signalHandler))
internal::writeErrorMessage("signal - SIGFPE");
perror("signal - SIGFPE");
if (SIG_ERR == signal(SIGSEGV, signalHandler))
internal::writeErrorMessage("signal - SIGSEGV");
perror("signal - SIGSEGV");
if (SIG_ERR == signal(SIGILL, signalHandler))
internal::writeErrorMessage("signal - SIGILL");
perror("signal - SIGILL");
}
#endif
}
@ -239,11 +243,11 @@ namespace g3 {
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
// const size_t kFirstExceptionHandler = 1;
// kFirstExceptionsHandler is kept here for documentation purposes.
// The last exception seems more like what we want.
// kFirstExeptionsHandler is kept here for documentational purposes.
// The last exception seems more what we want
const size_t kLastExceptionHandler = 0;
g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
#endif
}
} // end namespace g3
} // end namespace g3

View File

@ -7,22 +7,18 @@
* ============================================================================*/
#include "g3log/filesink.hpp"
#include <cassert>
#include <chrono>
#include "filesinkhelper.ipp"
#include <cassert>
namespace g3 {
using namespace internal;
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) :
_log_details_func(&LogMessage::DefaultLogDetailsToString),
_log_file_with_path(log_directory),
_log_prefix_backup(log_prefix),
_outptr(new std::ofstream),
_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"),
_firstEntry(true),
_write_counter(0),
_write_to_log_every_x_message(write_to_log_every_x_message) {
FileSink::FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string& logger_id)
: _log_file_with_path(log_directory)
, _log_prefix_backup(log_prefix)
, _outptr(new std::ofstream)
{
_log_prefix_backup = prefixSanityFix(log_prefix);
if (!isValidFilename(_log_prefix_backup)) {
std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl;
@ -39,48 +35,36 @@ namespace g3 {
_outptr = createLogFile(_log_file_with_path);
}
assert(_outptr && "cannot open log file at startup");
addLogFileHeader();
}
FileSink::~FileSink() {
std::string exit_msg = {"g3log g3FileSink shutdown at: "};
auto now = std::chrono::system_clock::now();
exit_msg.append(localtime_formatted(now, internal::time_formatted)).append("\n");
std::string exit_msg {"\ng3log g3FileSink shutdown at: "};
exit_msg.append(localtime_formatted(systemtime_now(), internal::time_formatted));
filestream() << exit_msg << std::flush;
// write anything buffered up and then end with the exit msg
filestream() << _write_buffer << exit_msg << std::flush;
exit_msg.append("Log file at: [").append(_log_file_with_path).append("]\n");
exit_msg.append({"\nLog file at: ["}).append(_log_file_with_path).append({"]\n\n"});
std::cerr << exit_msg << std::flush;
}
// The actual log receiving function
void FileSink::fileWrite(LogMessageMover message) {
if (_firstEntry) {
addLogFileHeader();
_firstEntry = false;
}
auto data = message.get().toString(_log_details_func);
_write_buffer.append(data);
if (++_write_counter % _write_to_log_every_x_message == 0) {
filestream() << _write_buffer << std::flush;
_write_buffer.clear();
}
std::ofstream &out(filestream());
out << message.get().toString() << std::flush;
}
std::string FileSink::changeLogFile(const std::string& directory, const std::string& logger_id) {
std::string FileSink::changeLogFile(const std::string &directory, const std::string &logger_id) {
auto now = std::chrono::system_clock::now();
auto now = g3::systemtime_now();
auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted});
std::string file_name = createLogFileName(_log_prefix_backup, logger_id);
std::string prospect_log = directory + file_name;
std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
if (nullptr == log_stream) {
filestream() << "\n"
<< now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
return {}; // no success
filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
return {}; // no success
}
addLogFileHeader();
@ -91,27 +75,18 @@ namespace g3 {
ss_change.str("");
std::string old_log = _log_file_with_path;
_log_file_with_path = std::move(prospect_log);
_log_file_with_path = prospect_log;
_outptr = std::move(log_stream);
ss_change << "\n\tNew log file. The previous log file was at: ";
ss_change << old_log << "\n";
ss_change << old_log;
filestream() << now_formatted << ss_change.str();
return _log_file_with_path;
}
std::string FileSink::fileName() {
return _log_file_with_path;
}
void FileSink::overrideLogDetails(LogMessage::LogDetailsFunc func) {
_log_details_func = func;
}
void FileSink::overrideLogHeader(const std::string& change) {
_header = change;
}
void FileSink::addLogFileHeader() {
filestream() << header(_header);
filestream() << header();
}
} // namespace g3
} // g3

View File

@ -51,7 +51,7 @@ namespace g3 {
return prefix;
}
std::string pathSanityFix(std::string path, const std::string &file_name) {
std::string pathSanityFix(std::string path, std::string file_name) {
// Unify the delimeters,. maybe sketchy solution but it seems to work
// on at least win7 + ubuntu. All bets are off for older windows
std::replace(path.begin(), path.end(), '\\', '/');
@ -76,23 +76,20 @@ namespace g3 {
return path;
}
std::string header(const std::string& headerFormat) {
std::string header() {
std::ostringstream ss_entry;
// 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
auto now = std::chrono::system_clock::now();
ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n";
ss_entry << headerFormat;
ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(g3::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message";
ss_entry << "\t\t(uuu*: microsecond counter since initialization of log worker)\n\n";
return ss_entry.str();
}
std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) {
std::stringstream oss_name;
oss_name << verified_prefix << ".";
if( !logger_id.empty() ) {
oss_name << logger_id << ".";
}
auto now = std::chrono::system_clock::now();
oss_name << g3::localtime_formatted(now, file_name_time_formatted);
if( logger_id != "" ) oss_name << logger_id << ".";
oss_name << g3::localtime_formatted(g3::systemtime_now(), file_name_time_formatted);
oss_name << ".log";
return oss_name.str();
}
@ -117,7 +114,7 @@ namespace g3 {
std::ofstream &stream(*(out.get()));
bool success_with_open_file = openLogFile(file_with_full_path, stream);
if (false == success_with_open_file) {
out.reset();
out.release();
}
return out;
}

View File

@ -16,7 +16,8 @@
// Btw: replacing g2log for g3log include is easy on Linux
// find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g'
#include <g3log/filesink.hpp>
#include <g3log/g3log.hpp>
#include <g3log/loglevels.hpp>
#include <g3log/logworker.hpp>
#include <g3log/loglevels.hpp>
#include <g3log/filesink.hpp>

View File

@ -9,7 +9,7 @@
* Filename:g3log.cpp Framework for Logging and Design By Contract
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copyrighted since it was built on public-domain software and at least in "spirit" influenced
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and at least in "spirit" influenced
* from the following sources
* 1. kjellkod.cc ;)
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
@ -19,32 +19,38 @@
* ********************************************* */
#include "g3log/g3log.hpp"
#include "g3log/crashhandler.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/std2_make_unique.hpp"
#include "g3log/logworker.hpp"
#include "g3log/crashhandler.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/loglevels.hpp"
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <memory>
#include <iostream>
#include <thread>
#include <atomic>
namespace {
std::once_flag g_initialize_flag;
g3::LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
g3::LogWorker *g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
std::mutex g_logging_init_mutex;
std::unique_ptr<g3::LogMessage> g_first_uninitialized_msg = {nullptr};
std::unique_ptr<g3::LogMessage> g_first_unintialized_msg = {nullptr};
std::once_flag g_set_first_uninitialized_flag;
std::once_flag g_save_first_uninitialized_flag;
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */ };
std::once_flag g_save_first_unintialized_flag;
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */};
std::function<void(void)> g_fatal_pre_logging_hook;
std::atomic<size_t> g_fatal_hook_recursive_counter = {0};
} // namespace
}
namespace g3 {
// signalhandler and internal clock is only needed to install once
@ -52,25 +58,18 @@ namespace g3 {
// several times...
// for all other practical use, it shouldn't!
void initializeLogging(LogWorker* bgworker) {
void initializeLogging(LogWorker *bgworker) {
std::call_once(g_initialize_flag, [] {
installCrashHandler();
});
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
if (internal::isLoggingInitialized() || nullptr == bgworker) {
std::ostringstream exitMsg;
exitMsg << __FILE__ "->" << __FUNCTION__ << ":" << __LINE__ << std::endl;
exitMsg << "\tFatal exit due to illegal initialization of g3::LogWorker\n";
exitMsg << "\t(due to multiple initializations? : " << std::boolalpha << internal::isLoggingInitialized();
exitMsg << ", due to nullptr == bgworker? : " << std::boolalpha << (nullptr == bgworker) << ")";
std::cerr << exitMsg.str() << std::endl;
std::exit(EXIT_FAILURE);
}
CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr);
// Save the first uninitialized message, if any
std::call_once(g_save_first_uninitialized_flag, [&bgworker] {
if (g_first_uninitialized_msg) {
bgworker->save(LogMessagePtr{std::move(g_first_uninitialized_msg)});
std::call_once(g_save_first_unintialized_flag, [&bgworker] {
if (g_first_unintialized_msg) {
bgworker->save(LogMessagePtr {std::move(g_first_unintialized_msg)});
}
});
@ -78,33 +77,41 @@ namespace g3 {
// by default the pre fatal logging hook does nothing
// if it WOULD do something it would happen in
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
// recursive crash counter re-set to zero
// recurvise crash counter re-set to zero
g_fatal_hook_recursive_counter.store(0);
}
/**
* default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing
* It will be called just before sending the fatal message, @ref pushFatalmessageToLogger
* It will be reset to do nothing in ::initializeLogging(...)
* so please call this function, if you ever need to, after initializeLogging(...)
*/
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook) {
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook) {
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
g_fatal_pre_logging_hook = pre_fatal_hook;
}
// By default this function pointer goes to \ref pushFatalMessageToLogger;
std::function<void(FatalMessagePtr)> g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger;
std::function<void(FatalMessagePtr) > g_fatal_to_g3logworker_function_ptr = internal::pushFatalMessageToLogger;
/** REPLACE fatalCallToLogger for fatalCallForUnitTest
* This function switches the function pointer so that only
* 'unitTest' mock-fatal calls are made.
* */
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call) {
void setFatalExitHandler(std::function<void(FatalMessagePtr) > fatal_call) {
g_fatal_to_g3logworker_function_ptr = fatal_call;
}
namespace internal {
bool isLoggingInitialized() {
@ -118,6 +125,7 @@ namespace g3 {
void shutDownLogging() {
std::lock_guard<std::mutex> lock(g_logging_init_mutex);
g_logger_instance = nullptr;
}
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
@ -126,7 +134,7 @@ namespace g3 {
* and the logging continues to be active.
* @return true if the correct worker was given,. and shutDownLogging was called
*/
bool shutDownLoggingForActiveOnly(LogWorker* active) {
bool shutDownLoggingForActiveOnly(LogWorker *active) {
if (isLoggingInitialized() && nullptr != active && (active != g_logger_instance)) {
LOG(WARNING) << "\n\t\tAttempted to shut down logging, but the ID of the Logger is not the one that is active."
<< "\n\t\tHaving multiple instances of the g3::LogWorker is likely a BUG"
@ -138,69 +146,69 @@ namespace g3 {
return true;
}
/** explicitly copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
/** explicits copy of all input. This is makes it possibly to use g3log across dynamically loaded libraries
* i.e. (dlopen + dlsym) */
void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level,
const char* boolean_expression, int fatal_signal, const char* stack_trace) {
LEVELS msgLevel{level};
LogMessagePtr message{std::make_unique<LogMessage>(file, line, function, msgLevel)};
void saveMessage(const char *entry, const char *file, int line, const char *function, const LEVELS &level,
const char *boolean_expression, int fatal_signal, const char *stack_trace) {
LEVELS msgLevel {level};
LogMessagePtr message {std2::make_unique<LogMessage>(file, line, function, msgLevel)};
message.get()->write().append(entry);
message.get()->setExpression(boolean_expression);
if (internal::wasFatal(level)) {
saveFatalMessage(stack_trace, message, fatal_signal);
auto fatalhook = g_fatal_pre_logging_hook;
// In case the fatal_pre logging actually will cause a crash in its turn
// let's not do recursive crashing!
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
++g_fatal_hook_recursive_counter; // thread safe counter
// "benign" race here. If two threads crashes, with recursive crashes
// then it's possible that the "other" fatal stack trace will be shown
// that's OK since it was anyhow the first crash detected
static const std::string first_stack_trace = stack_trace;
fatalhook();
message.get()->write().append(stack_trace);
if (g_fatal_hook_recursive_counter.load() > 1) {
message.get()->write()
.append("\n\n\nWARNING\n"
"A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n")
.append("---First crash stacktrace: ").append(first_stack_trace).append("\n---End of first stacktrace\n");
}
FatalMessagePtr fatal_message { std2::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal) };
// At destruction, flushes fatal message to g3LogWorker
// either we will stay here until the background worker has received the fatal
// message, flushed the crash message to the sinks and exits with the same fatal signal
//..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
fatalCall(fatal_message);
} else {
pushMessageToLogger(message);
}
}
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal) {
auto fatalhook = g_fatal_pre_logging_hook;
// In case the fatal_pre logging actually will cause a crash in its turn
// let's not do recursive crashing!
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
++g_fatal_hook_recursive_counter; // thread safe counter
// "benign" race here. If two threads crashes, with recursive crashes
// then it's possible that the "other" fatal stack trace will be shown
// that's OK since it was anyhow the first crash detected
static const std::string first_stack_trace = stack_trace;
fatalhook();
message.get()->write().append(stack_trace);
if (g_fatal_hook_recursive_counter.load() > 1) {
message.get()->write().append(
"\n\n\nWARNING\n"
"A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\n")
.append("---First crash stacktrace: ")
.append(first_stack_trace)
.append("\n---End of first stacktrace\n");
}
FatalMessagePtr fatal_message{std::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal)};
// At destruction, flushes fatal message to g3LogWorker
// either we will stay here until the background worker has received the fatal
// message, flushed the crash message to the sinks and exits with the same fatal signal
//..... OR it's in unit-test mode then we throw a std::runtime_error (and never hit sleep)
fatalCall(fatal_message);
}
/**
* save the message to the logger. In case of called before the logger is instantiated
* the first message will be saved. Any following subsequent uninitialized log calls
* will be ignored.
*
* The first initialized log entry will also save the first uninitialized log message, if any
* @param log_entry to save to logger
*/
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
* save the message to the logger. In case of called before the logger is instantiated
* the first message will be saved. Any following subsequent unitnialized log calls
* will be ignored.
*
* The first initialized log entry will also save the first uninitialized log message, if any
* @param log_entry to save to logger
*/
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
// Uninitialized messages are ignored but does not CHECK/crash the logger
if (!internal::isLoggingInitialized()) {
std::call_once(g_set_first_uninitialized_flag, [&] {
g_first_uninitialized_msg = incoming.release();
g_first_unintialized_msg = incoming.release();
std::string err = {"LOGGER NOT INITIALIZED:\n\t\t"};
err.append(g_first_uninitialized_msg->message());
std::string& str = g_first_uninitialized_msg->write();
err.append(g_first_unintialized_msg->message());
std::string &str = g_first_unintialized_msg->write();
str.clear();
str.append(err); // replace content
std::cerr << str << std::endl; });
std::cerr << str << std::endl;
});
return;
}
@ -209,17 +217,16 @@ namespace g3 {
}
/** Fatal call saved to logger. This will trigger SIGABRT or other fatal signal
* to exit the program. After saving the fatal message the calling thread
* will sleep forever (i.e. until the background thread catches up, saves the fatal
* message and kills the software with the fatal signal.
*/
* to exit the program. After saving the fatal message the calling thread
* will sleep forever (i.e. until the background thread catches up, saves the fatal
* message and kills the software with the fatal signal.
*/
void pushFatalMessageToLogger(FatalMessagePtr message) {
if (!isLoggingInitialized()) {
std::ostringstream error;
error << "FATAL CALL but logger is NOT initialized\n"
<< "CAUSE: " << message.get()->reason()
<< "\nMessage: \n"
<< message.get()->toString() << std::flush;
<< "\nMessage: \n" << message.get()->toString() << std::flush;
std::cerr << error.str() << std::flush;
internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
}
@ -230,13 +237,17 @@ namespace g3 {
}
/** The default, initial, handling to send a 'fatal' event to g3logworker
* the caller will stay here, eternally, until the software is aborted
* ... in the case of unit testing it is the given "Mock" fatalCall that will
* define the behaviour.
*/
* the caller will stay here, eternally, until the software is aborted
* ... in the case of unit testing it is the given "Mock" fatalCall that will
* define the behaviour.
*/
void fatalCall(FatalMessagePtr message) {
g_fatal_to_g3logworker_function_ptr(FatalMessagePtr{std::move(message)});
g_fatal_to_g3logworker_function_ptr(FatalMessagePtr {std::move(message)});
}
} // namespace internal
} // namespace g3
} // internal
} // g3

View File

@ -19,20 +19,19 @@
#pragma once
#include <thread>
#include <functional>
#include <memory>
#include <thread>
#include "g3log/shared_queue.hpp"
namespace kjellkod {
typedef std::function<void()> Callback;
typedef std::function<void() > Callback;
class Active {
private:
Active() :
done_(false) {} // Construction ONLY through factory createActive();
Active(const Active&) = delete;
Active& operator=(const Active&) = delete;
private:
Active() : done_(false) {} // Construction ONLY through factory createActive();
Active(const Active &) = delete;
Active &operator=(const Active &) = delete;
void run() {
while (!done_) {
@ -46,9 +45,10 @@ namespace kjellkod {
std::thread thd_;
bool done_;
public:
public:
virtual ~Active() {
send([this]() noexcept { done_ = true; });
send([this] { done_ = true;});
thd_.join();
}
@ -64,4 +64,6 @@ namespace kjellkod {
}
};
} // namespace kjellkod
} // kjellkod

View File

@ -1,47 +0,0 @@
/** ==========================================================================
* 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#pragma once
#include <atomic>
namespace g3 {
/// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c
struct atomicbool {
private:
std::atomic<bool> value_;
public:
atomicbool() :
value_{false} {}
atomicbool(bool value) :
value_{value} {}
atomicbool(const std::atomic<bool>& value) :
value_{value.load(std::memory_order_acquire)} {}
atomicbool(const atomicbool& other) :
value_{other.value_.load(std::memory_order_acquire)} {}
atomicbool& operator=(const atomicbool& other) {
value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release);
return *this;
}
atomicbool& operator=(const bool other) {
value_.store(other, std::memory_order_release);
return *this;
}
bool operator==(const atomicbool& rhs) const {
return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire));
}
bool value() { return value_.load(std::memory_order_acquire); }
std::atomic<bool>& get() { return value_; }
};
} // namespace g3
// explicit whitespace/EOF for VS15

View File

@ -8,10 +8,10 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <cstdio>
#include <map>
#include <string>
#include "g3log/generated_definitions.hpp"
#include <map>
#include "g3log/loglevels.hpp"
#include "g3log/generated_definitions.hpp"
// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
// implementationsfilen kan vara den samma
@ -20,7 +20,7 @@ namespace g3 {
// PUBLIC API:
/** Install signal handler that catches FATAL C-runtime or OS signals
See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE
See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling
See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling.hpptm
SIGABRT ABORT (ANSI), abnormal termination
SIGFPE Floating point exception (ANSI)
SIGILL ILlegal instruction (ANSI)
@ -28,37 +28,35 @@ namespace g3 {
SIGTERM TERMINATION (ANSI) */
void installCrashHandler();
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
typedef unsigned long SignalType;
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
/// on Windows. This is automatically done if you do at least one LOG(...) call
/// you can also use this function call, per thread so make sure these three
/// fatal signals are covered in your thread (even if you don't do a LOG(...) call
void installSignalHandlerForThread();
#else
typedef int SignalType;
std::string signalToStr(int signal_number);
// restore to whatever signal handler was used before signal handler installation
void restoreSignalHandler(int signal_number);
/// Probably only needed for unit testing. Resets the signal handling back to default
/// which might be needed in case it was previously overridden
/// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
void restoreSignalHandlerToDefault();
/// Overrides the existing signal handling for custom signals
/// For example: usage of zcmq relies on its own signal handler for SIGTERM
/// so users of g3log with zcmq should then use the @ref overrideSetupSignals
/// , likely with the original set of signals but with SIGTERM removed
///
///
/// call example:
/// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"},
// {SIGSEGV, "SIGSEGV"},});
void overrideSetupSignals(const std::map<int, std::string> overrideSignals);
#endif
namespace internal {
/// Resets the fatal signal/exception handling back to default
/// which might be needed in case it was previously overridden
/// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
void restoreFatalHandlingToDefault();
namespace internal {
/** return whether or any fatal handling is still ongoing
* this is used by g3log::fatalCallToLogger
* only in the case of Windows exceptions (not fatal signals)
@ -77,6 +75,5 @@ namespace g3 {
* This is an internal only function. Do not use it elsewhere. It is triggered
* from g3log, g3LogWorker after flushing messages to file */
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
size_t writeErrorMessage(const char* message);
} // namespace internal
} // namespace g3
} // end g3::internal
} // g3

View File

@ -7,40 +7,36 @@
* ============================================================================*/
#pragma once
#include <memory>
#include <string>
#include <memory>
#include "g3log/logmessage.hpp"
namespace g3 {
class FileSink {
public:
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);
public:
FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string &logger_id="g3log");
virtual ~FileSink();
void fileWrite(LogMessageMover message);
std::string changeLogFile(const std::string& directory, const std::string& logger_id);
std::string changeLogFile(const std::string &directory, const std::string &logger_id);
std::string fileName();
void overrideLogDetails(LogMessage::LogDetailsFunc func);
void overrideLogHeader(const std::string& change);
private:
LogMessage::LogDetailsFunc _log_details_func;
private:
std::string _log_file_with_path;
std::string _log_prefix_backup; // needed in case of future log file changes of directory
std::string _log_prefix_backup; // needed in case of future log file changes of directory
std::unique_ptr<std::ofstream> _outptr;
std::string _header;
bool _firstEntry;
std::string _write_buffer;
size_t _write_counter;
size_t _write_to_log_every_x_message;
void addLogFileHeader();
std::ofstream& filestream() {
std::ofstream &filestream() {
return *(_outptr.get());
}
FileSink& operator=(const FileSink&) = delete;
FileSink(const FileSink& other) = delete;
FileSink &operator=(const FileSink &) = delete;
FileSink(const FileSink &other) = delete;
};
} // namespace g3
} // g3

View File

@ -23,6 +23,8 @@
* PUBLIC DOMAIN and NOT under copywrite protection.
* ********************************************* */
#include <future>
#include "g3log/active.hpp"
#include "g3log/moveoncopy.hpp"
@ -40,8 +42,9 @@ namespace g3 {
// auto msg_call=[=](){return ("Hello from the Background");};
// auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get());
template <typename Func, class BgWorker>
std::future<std::invoke_result_t<Func>> spawn_task(Func func, BgWorker* worker) {
typedef std::invoke_result_t<Func> result_type;
std::future<typename std::result_of<Func()>::type> spawn_task(Func func, BgWorker *worker)
{
typedef typename std::result_of<Func()>::type result_type;
typedef std::packaged_task<result_type()> task_type;
if (nullptr == worker) {
@ -55,6 +58,6 @@ namespace g3 {
std::future<result_type> result = task.get_future();
worker->send(MoveOnCopy<task_type>(std::move(task)));
return result;
return std::move(result);
}
} // end namespace g3
} // end namespace g3

View File

@ -18,22 +18,19 @@
* 5. Various Q&A at StackOverflow
* ********************************************* */
#pragma once
#include "g3log/generated_definitions.hpp"
#include "g3log/logcapture.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logcapture.hpp"
#include "g3log/logmessage.hpp"
#include <functional>
#include <string>
#include <functional>
#if defined(_MSC_VER) && (defined(WINDOWS_FUNCSIG)) // Microsoft
#define G3LOG_PRETTY_FUNCTION __FUNCSIG__
#elif defined(__GNUC__) && defined(PRETTY_FUNCTION) // GCC compatible
#define G3LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__
#else
#define G3LOG_PRETTY_FUNCTION __FUNCTION__
#if !(defined(__PRETTY_FUNCTION__))
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif
// thread_local doesn't exist before VS2013
@ -42,14 +39,15 @@
#define thread_local __declspec(thread)
#endif
/** namespace for LOG() and CHECK() frameworks
* History lesson: Why the names 'g3' and 'g3log'?:
* The framework was made in my own free time as PUBLIC DOMAIN but the
* first commercial project to use it used 'g3' as an internal denominator for
* the current project. g3 as in 'generation 2'. I decided to keep the g3 and g3log names
* to give credit to the people in that project (you know who you are :) and I guess also
* for 'sentimental' reasons. That a big influence was Google's glog is just a happy
* coincidence or subconscious choice. Either way g3log became the name for this logger.
* for 'sentimental' reasons. That a big influence was google's glog is just a happy
* concidence or subconscious choice. Either way g3log became the name for this logger.
*
* --- Thanks for a great 2011 and good luck with 'g3' --- KjellKod
*/
@ -59,14 +57,15 @@ namespace g3 {
struct FatalMessage;
/** Should be called at very first startup of the software with \ref g3LogWorker
* pointer. Ownership of the \ref g3LogWorker is the responsibility of the caller */
void initializeLogging(LogWorker* logger);
* pointer. Ownership of the \ref g3LogWorker is the responsibilkity of the caller */
void initializeLogging(LogWorker *logger);
/** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called
*
* Set a function-hook before a fatal message will be sent to the logger
* i.e. this is a great place to put a break point, either in your debugger
* or programmatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal)
* or programatically to catch LOG(FATAL), CHECK(...) or an OS fatal event (exception or signal)
* This will be reset to default (does nothing) at initializeLogging(...);
*
* Example usage:
@ -77,7 +76,7 @@ namespace g3 {
*
* Linux: g3::setFatalPreLoggingHook([]{ raise(SIGTRAP); });
*/
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook);
void setFatalPreLoggingHook(std::function<void(void)> pre_fatal_hook);
/** If the @ref setFatalPreLoggingHook is not enough and full fatal exit handling is needed then
* use "setFatalExithandler". Please see g3log.cpp and crashhandler_windows.cpp or crashhandler_unix for
@ -85,16 +84,8 @@ namespace g3 {
*/
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call);
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
// only_change_at_initialization namespace is for changes to be done only during initialization. More specifically
// items here would be called prior to calling other parts of g3log
namespace only_change_at_initialization {
// Sets the MaxMessageSize to be used when capturing log messages. Currently this value is set to 2KB. Messages
// Longer than this are bound to 2KB with the string "[...truncated...]" at the end. This function allows
// this limit to be changed.
void setMaxMessageSize(size_t max_size);
} // namespace only_change_at_initialization
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
// internal namespace is for completely internal or semi-hidden from the g3 namespace due to that it is unlikely
// that you will use these
@ -103,24 +94,24 @@ namespace g3 {
bool isLoggingInitialized();
// Save the created LogMessage to any existing sinks
void saveMessage(const char* message, const char* file, int line, const char* function, const LEVELS& level,
const char* boolean_expression, int fatal_signal, const char* stack_trace);
void saveFatalMessage(const char* stack_trace, g3::LogMessagePtr& message, int& fatal_signal);
void saveMessage(const char *message, const char *file, int line, const char *function, const LEVELS &level,
const char *boolean_expression, int fatal_signal, const char *stack_trace);
// forwards the message to all sinks
void pushMessageToLogger(LogMessagePtr log_entry);
// forwards a FATAL message to all sinks,. after which the g3logworker
// will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler
//
// By default the "fatalCall" will forward a FatalMessageptr to this function
// this behavior can be changed if you set a different fatal handler through
// By default the "fatalCall" will forward a Fatalessageptr to this function
// this behaviour can be changed if you set a different fatal handler through
// "setFatalExitHandler"
void pushFatalMessageToLogger(FatalMessagePtr message);
// Saves the created FatalMessage to any existing sinks and exits with
// the originating fatal signal,. or SIGABRT if it originated from a broken contract.
// Save the created FatalMessage to any existing sinks and exit with
// the originating fatal signal,. or SIGABRT if it originated from a broken contract
// By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override
//
// If you override it then you probably want to call "pushFatalMessageToLogger" after your
@ -132,29 +123,31 @@ namespace g3 {
void shutDownLogging();
// Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized
bool shutDownLoggingForActiveOnly(LogWorker* active);
bool shutDownLoggingForActiveOnly(LogWorker *active);
} // namespace internal
} // namespace g3
// clang-format off
#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, static_cast<const char*>(G3LOG_PRETTY_FUNCTION), level)
} // internal
} // g3
#define INTERNAL_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, level)
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
LogCapture(__FILE__, __LINE__, G3LOG_PRETTY_FUNCTION, g3::internal::CONTRACT, boolean_expression)
LogCapture(__FILE__, __LINE__, __PRETTY_FUNCTION__, g3::internal::CONTRACT, boolean_expression)
// LOG(level) is the API for the stream log
#define LOG(level) if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).stream()
#define LOG(level) if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
// 'Conditional' stream log
#define LOG_IF(level, boolean_expression) \
if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).stream()
if(true == boolean_expression) \
if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).stream()
// 'Design By Contract' stream API. Broken Contracts will exit the application by using fatal signal SIGABRT
// For unit testing, you can override the fatal handling using setFatalExitHandler(...). See tes_io.cpp for examples
// 'Design By Contract' stream API. For Broken Contracts:
// unit testing: it will throw std::runtime_error when a contract breaks
// I.R.L : it will exit the application by using fatal signal SIGABRT
#define CHECK(boolean_expression) \
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).stream()
/** For details please see this
@ -206,22 +199,22 @@ And here is possible output
: Width trick: 10
: A string \endverbatim */
#define LOGF(level, printf_like_message, ...) \
if (!g3::logLevel(level)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
// Conditional log printf syntax
#define LOGF_IF(level,boolean_expression, printf_like_message, ...) \
if (!g3::logLevel(level) || false == (boolean_expression)) {} else INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
if(true == boolean_expression) \
if(g3::logLevel(level)) INTERNAL_LOG_MESSAGE(level).capturef(printf_like_message, ##__VA_ARGS__)
// Design By Contract, printf-like API syntax with variadic input parameters.
// Calls the signal handler if the contract failed with the default exit for a failed contract. This is typically SIGABRT
// See g3log, setFatalExitHandler(...) which can be overriden for unit tests (ref test_io.cpp)
// Throws std::runtime_eror if contract breaks
#define CHECKF(boolean_expression, printf_like_message, ...) \
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
// Backwards compatible. The same as CHECKF.
// Backwards compatible. The same as CHECKF.
// Design By Contract, printf-like API syntax with variadic input parameters.
// Calls the signal handler if the contract failed. See g3log, setFatalExitHandler(...) which can be overriden for unit tests
// (ref test_io.cpp)
// Throws std::runtime_eror if contract breaks
#define CHECK_F(boolean_expression, printf_like_message, ...) \
if (true == (boolean_expression)) {} else INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)
// clang-format on
if (false == (boolean_expression)) INTERNAL_CONTRACT_MESSAGE(#boolean_expression).capturef(printf_like_message, ##__VA_ARGS__)

View File

@ -8,16 +8,14 @@
#pragma once
#include "g3log/crashhandler.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/g3log.hpp"
#include "g3log/crashhandler.hpp"
#include <csignal>
#include <cstdarg>
#include <sstream>
#include <string>
#ifdef _MSC_VER
#include <sal.h>
#endif
#include <sstream>
#include <cstdarg>
#include <csignal>
/**
* Simple struct for capturing log/fatal entries. At destruction the captured message is
@ -27,7 +25,8 @@
*/
struct LogCapture {
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump = nullptr);
LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump = nullptr);
/**
* @file, line, function are given in g3log.hpp from macros
@ -35,41 +34,40 @@ struct LogCapture {
* @expression for CHECK calls
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
*/
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);
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);
// At destruction the message will be forwarded to the g3log worker.
// In the case of dynamically (at runtime) loaded libraries, the important thing to know is that
// all strings are copied, so the original are not destroyed at the receiving end, only the copy
virtual ~LogCapture() noexcept(false);
// in case of dynamically (at runtime) loaded libraries the important thing to know is that
// all strings are copied so the original are not destroyed at the receiving end, only the copy
virtual ~LogCapture();
#ifdef _MSC_VER
#if _MSC_VER >= 1400
#define G3LOG_FORMAT_STRING _Printf_format_string_
#else
#define G3LOG_FORMAT_STRING __format_string
#endif
void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...);
#else
#define G3LOG_FORMAT_STRING
// Use "-Wall" to generate warnings in case of illegal printf format.
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
[[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char* printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18
#ifndef __GNUC__
#define __attribute__(x) // Disable 'attributes' if compiler does not support 'em
#endif
void capturef(const char *printf_like_message, ...) __attribute__((format(printf, 2, 3))); // 2,3 ref: http://www.codemaestro.com/reviews/18
/// prettifying API for this completely open struct
std::ostringstream& stream() {
std::ostringstream &stream() {
return _stream;
}
std::ostringstream _stream;
std::string _stack_trace;
const char* _file;
const char *_file;
const int _line;
const char* _function;
const LEVELS& _level;
const char* _expression;
const char *_function;
const LEVELS &_level;
const char *_expression;
const g3::SignalType _fatal_signal;
};
//} // g3

View File

@ -14,7 +14,7 @@
// "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
#if (defined(DBUG))
#error "DBUG is already defined elsewhere which clashes with G3Log's log level DBUG"
#error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG"
#endif
#else
#if (defined(DEBUG))
@ -22,26 +22,19 @@
#endif
#endif
#include <string>
#include <algorithm>
#include <atomic>
#include <g3log/atomicbool.hpp>
#include <map>
#include <string>
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
struct LEVELS {
// force internal copy of the const char*. This is a simple safeguard for when g3log is used in a
// "dynamic, runtime loading of shared libraries"
LEVELS(const LEVELS& other) :
value(other.value),
text(other.text.c_str()) {}
LEVELS(const LEVELS& other): value(other.value), text(other.text.c_str()) {}
LEVELS(int id, const char* idtext) : value(id), text(idtext) {}
LEVELS(int id, const std::string& idtext) :
value(id),
text(idtext) {}
bool operator==(const LEVELS& rhs) const {
bool operator==(const LEVELS& rhs) const {
return (value == rhs.value && text == rhs.text);
}
@ -55,141 +48,78 @@ struct LEVELS {
swap(first.text, second.text);
}
LEVELS& operator=(LEVELS other) {
swap(*this, other);
return *this;
}
int value;
std::string text;
};
// If you want to add any extra logging level then please add to your own source file the logging level you need
// 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...).
// to give g3log a record of your logging level and if it is an enabled or disbled logging level.
//
// 2. If the cmake dynamic logging option is turned OFF
// then giving g3log a record of your logging level with 'addLogLevel(...) is NOT needed since no "disbled/enabled"
// check will happen - all logging levels will be considered enabled.
// 3. See also the [g3log/API.markdown](https://github.com/KjellKod/g3log/blob/master/API.markdown) for for information.
//
// example: MyLoggingLevel.h
// #pragma once
// const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"};
// const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"};
//
// ... somewhere else when G3_DYNAMIC_LOGGING is enabled
// addLogLevel(MYINFO, true);
// LOG(MYINFO) << "some text";
//
// ... another example, when G3_DYNAMIC_LOGGING is enabled
// 'addLogLevel' is NOT required
// LOG(MYFATAL) << "this will just work, and it will be counted as a FATAL event";
namespace g3 {
static const int kDebugValue = 100;
static const int kInfoValue = 300;
static const int kDebugVaulue = 0;
static const int kInfoValue = 100;
static const int kWarningValue = 500;
static const int kFatalValue = 1000;
static const int kInternalFatalValue = 2000;
} // namespace g3
}
const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"},
INFO{g3::kInfoValue, "INFO"},
WARNING{g3::kWarningValue, "WARNING"},
FATAL{g3::kFatalValue, "FATAL"};
#if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG))
const LEVELS DBUG {g3::kDebugVaulue, {"DEBUG"}},
#else
const LEVELS DEBUG {g3::kDebugVaulue, {"DEBUG"}},
#endif
INFO {g3::kInfoValue, {"INFO"}},
WARNING {g3::kWarningValue, {"WARNING"}},
namespace g3 {
// Logging level and atomic status collection struct
struct LoggingLevel {
atomicbool status;
LEVELS level;
// default operator needed for std::map compliance
LoggingLevel() :
status(false),
level(INFO){};
LoggingLevel(const LoggingLevel& lvl) :
status(lvl.status),
level(lvl.level) {}
LoggingLevel(const LEVELS& lvl) :
status(true),
level(lvl){};
LoggingLevel(const LEVELS& lvl, bool enabled) :
status(enabled),
level(lvl){};
~LoggingLevel() = default;
LoggingLevel& operator=(const LoggingLevel& other) {
status = other.status;
level = other.level;
return *this;
}
// Insert here *any* extra logging levels that is needed. You can do so in your own source file
// If it is a FATAL you should keep it above (FATAL.value and below internal::CONTRACT.value
// If it is a non-fatal you can keep it above (WARNING.value and below FATAL.value)
//
// example: MyLoggingLevel.h
// #pragma once
// const LEVELS MYINFO {WARNING.value +1, {"MyInfoLevel"}};
// const LEVELS MYFATAL {FATAL.value +1, {"MyFatalLevel"}};
//
// IMPORTANT: As of yet dynamic on/off of logging is NOT changed automatically
// any changes of this, if you use dynamic on/off must be done in loglevels.cpp,
// g_log_level_status and
// void setLogLevel(LEVELS log_level, bool enabled) {...}
// bool logLevel(LEVELS log_level){...}
bool operator==(const LoggingLevel& rhs) const {
return (status == rhs.status && level == rhs.level);
}
};
} // namespace g3
// 1) Remember to update the FATAL initialization below
// 2) Remember to update the initialization of "g3loglevels.cpp/g_log_level_status"
FATAL {g3::kFatalValue, {"FATAL"}};
namespace g3 {
namespace internal {
const LEVELS CONTRACT{g3::kInternalFatalValue, {"CONTRACT"}},
FATAL_SIGNAL{g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION{kInternalFatalValue + 2, {"FATAL_EXCEPTION"}};
const LEVELS CONTRACT {g3::kInternalFatalValue, {"CONTRACT"}},
FATAL_SIGNAL {g3::kInternalFatalValue +1, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION {kInternalFatalValue +2, {"FATAL_EXCEPTION"}};
/// helper function to tell the logger if a log message was fatal. If it is it will force
/// a shutdown after all log entries are saved to the sinks
bool wasFatal(const LEVELS& level);
} // namespace internal
}
#ifdef G3_DYNAMIC_LOGGING
// Only safe if done at initialization in a single-thread context
namespace only_change_at_initialization {
/// add a custom level - enabled or disabled
void addLogLevel(LEVELS level, bool enabled);
/// add a custom level - enabled
void addLogLevel(LEVELS level);
/// reset all default logging levels to enabled
/// remove any added logging levels so that the only ones left are
/// {DEBUG,INFO,WARNING,FATAL}
// Enable/Disable a log level {DEBUG,INFO,WARNING,FATAL}
void setLogLevel(LEVELS level, bool enabled_status);
std::string printLevels();
void reset();
} // namespace only_change_at_initialization
namespace log_levels {
/// Enable log level >= log_level.
/// log levels below will be disabled
/// log levels equal or higher will be enabled.
void setHighest(LEVELS level);
void set(LEVELS level, bool enabled);
void disable(LEVELS level);
void enable(LEVELS level);
/// WARNING: This will also disable FATAL events from being logged
void disableAll();
void enableAll();
/// print all levels with their disabled or enabled status
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint);
/// print snapshot of system levels with their
/// disabled or enabled status
std::string to_string();
/// Snapshot view of the current logging levels' status
std::map<int, g3::LoggingLevel> getAll();
enum class status { Absent,
Enabled,
Disabled };
status getStatus(LEVELS level);
} // namespace log_levels
} // only_change_at_initialization
#endif
/// Enabled status for the given logging level
bool logLevel(const LEVELS& level);
bool logLevel(LEVELS level);
} // g3
} // namespace g3

View File

@ -8,15 +8,17 @@
#pragma once
#include "g3log/crashhandler.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/moveoncopy.hpp"
#include "g3log/time.hpp"
#include <memory>
#include <sstream>
#include "g3log/loglevels.hpp"
#include "g3log/time.hpp"
#include "g3log/moveoncopy.hpp"
#include "g3log/crashhandler.hpp"
#include <string>
#include <sstream>
#include <thread>
#include <memory>
namespace g3 {
@ -29,9 +31,6 @@ namespace g3 {
* desired way.
*/
struct LogMessage {
std::string file_path() const {
return _file_path;
}
std::string file() const {
return _file;
}
@ -48,15 +47,18 @@ namespace g3 {
/// use a different format string to get a different look on the time.
// default look is Y/M/D H:M:S
std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
std::string microseconds() const {
return std::to_string(_microseconds);
}
std::string message() const {
std::string message() const {
return _message;
}
std::string& write() const {
return _message;
}
std::string expression() const {
std::string expression() const {
return _expression;
}
bool wasFatal() const {
@ -65,59 +67,44 @@ namespace g3 {
std::string threadID() const;
void setExpression(std::string expression) {
_expression = std::move(expression);
std::string toString() const;
void setExpression(const std::string expression) {
_expression = expression;
}
LogMessage& operator=(LogMessage other);
LogMessage(std::string file, const int line, std::string function, const LEVELS level);
LogMessage(const std::string& file, const int line, const std::string& function, const LEVELS& level);
explicit LogMessage(const std::string& fatalOsSignalCrashMessage);
LogMessage(const LogMessage& other);
LogMessage(LogMessage&& other);
virtual ~LogMessage() {}
// helper log printing functions used by "toString()"
static std::string splitFileName(const std::string& str);
static std::string fatalSignalToString(const LogMessage& msg);
// windows only: fatalExceptionToString
static std::string fatalExceptionToString(const LogMessage& msg);
static std::string fatalLogToString(const LogMessage& msg);
static std::string fatalCheckToString(const LogMessage& msg);
static std::string normalToString(const LogMessage& msg);
// the default formatting option
static std::string DefaultLogDetailsToString(const LogMessage& msg);
// this function can be used by the logging sink to add thread ID
// see this concept and it is easy to make your own custom formatting
static std::string FullLogDetailsToString(const LogMessage& msg);
using LogDetailsFunc = std::string (*)(const LogMessage&);
std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const;
void overrideLogDetailsFunc(LogDetailsFunc func) const;
//
// Complete access to the raw data in case the helper functions above
// are not enough.
//
mutable LogDetailsFunc _logDetailsToStringFunc;
g3::high_resolution_time_point _timestamp;
std::time_t _timestamp;
std::thread::id _call_thread_id;
int64_t _microseconds;
std::string _file;
std::string _file_path;
int _line;
std::string _function;
LEVELS _level;
std::string _expression; // only with content for CHECK(...) calls
std::string _expression; // only with content for CHECK(...) calls
mutable std::string _message;
friend void swap(LogMessage& first, LogMessage& second) {
// enable ADL (not necessary in our case, but good practice)
using std::swap;
swap(first._timestamp, second._timestamp);
swap(first._call_thread_id, second._call_thread_id);
swap(first._microseconds, second._microseconds);
swap(first._file, second._file);
swap(first._line, second._line);
swap(first._function, second._function);
@ -125,15 +112,18 @@ namespace g3 {
swap(first._expression, second._expression);
swap(first._message, second._message);
}
};
/** Trigger for flushing the message queue and exiting the application
* A thread that causes a FatalMessage will sleep forever until the
* application has exited (after message flush) */
struct FatalMessage : public LogMessage {
FatalMessage(const LogMessage& details, g3::SignalType signal_id);
FatalMessage(const FatalMessage&);
FatalMessage& operator=(const FatalMessage&) = delete;
virtual ~FatalMessage() {}
LogMessage copyToLogMessage() const;
@ -142,7 +132,8 @@ namespace g3 {
const SignalType _signal_id;
};
typedef MoveOnCopy<std::unique_ptr<FatalMessage>> FatalMessagePtr;
typedef MoveOnCopy<std::unique_ptr<LogMessage>> LogMessagePtr;
typedef MoveOnCopy<LogMessage> LogMessageMover;
} // namespace g3
} // g3

View File

@ -9,19 +9,20 @@
* Filename:g3logworker.h Framework for Logging and Design By Contract
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copyrighted. First published at KjellKod.cc
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#include <memory>
#include "g3log/filesink.hpp"
#include "g3log/g3log.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/sinkhandle.hpp"
#include "g3log/sinkwrapper.hpp"
#include "g3log/sinkhandle.hpp"
#include "g3log/filesink.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/std2_make_unique.hpp"
#include <memory>
#include <string>
#include <vector>
namespace g3 {
class LogWorker;
struct LogWorkerImpl;
@ -31,7 +32,7 @@ namespace g3 {
struct LogWorkerImpl final {
typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
std::vector<SinkWrapperPtr> _sinks;
std::unique_ptr<kjellkod::Active> _bg; // do not change declaration order. _bg must be destroyed before sinks
std::unique_ptr<kjellkod::Active> _bg; // do not change declaration order. _bg must be destroyed before sinks
LogWorkerImpl();
~LogWorkerImpl() = default;
@ -43,8 +44,10 @@ namespace g3 {
LogWorkerImpl& operator=(const LogWorkerImpl&) = delete;
};
/// Front end of the LogWorker. API that is useful is
/// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example
/// Front end of the LogWorker. API that is usefule is
/// addSink( sink, default_call ) which returns a handle to the sink. See below and REAME for usage example
/// save( msg ) : internal use
/// fatal ( fatal_msg ) : internal use
class LogWorker final {
@ -55,13 +58,15 @@ namespace g3 {
LogWorker(const LogWorker&) = delete;
LogWorker& operator=(const LogWorker&) = delete;
public:
public:
~LogWorker();
/// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it
/// Creates the LogWorker with no sinks. See exampel below on @ref addSink for how to use it
/// if you want to use the default file logger then see below for @ref addDefaultLogger
static std::unique_ptr<LogWorker> createLogWorker();
/**
A convenience function to add the default g3::FileSink to the log worker
@param log_prefix that you want
@ -79,56 +84,24 @@ namespace g3 {
std::cout << "The filename is: " << log_file_name.get() << std::endl;
// something like: /tmp/my_test_log.g3log.20150819-100300.log
*/
std::unique_ptr<FileSinkHandle> addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log");
std::unique_ptr<FileSinkHandle> addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log");
/// Adds a sink and returns the handle for access to the sink
/// @param real_sink unique_ptr ownership is passed to the log worker
/// @param call the default call that should receive either a std::string or a LogMessageMover message
/// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger
template <typename T, typename DefaultLogCall>
template<typename T, typename DefaultLogCall>
std::unique_ptr<g3::SinkHandle<T>> addSink(std::unique_ptr<T> real_sink, DefaultLogCall call) {
using namespace g3;
using namespace g3::internal;
auto sink = std::make_shared<Sink<T>>(std::move(real_sink), call);
auto sink = std::make_shared<Sink<T>> (std::move(real_sink), call);
addWrappedSink(sink);
return std::make_unique<SinkHandle<T>>(sink);
return std2::make_unique<SinkHandle<T>> (sink);
}
/// Removes a sink. This is a synchronous call.
/// You are guaranteed that the sink is removed by the time the call returns
/// @param sink_handle the ownership of the sink handle is given
template <typename T>
void removeSink(std::unique_ptr<SinkHandle<T>> sink_handle) {
if (sink_handle) {
// sink_handle->sink().use_count() is 1 at this point
// i.e. this would be safe as long as no other weak_ptr to shared_ptr conversion
// was made by the client: assert(sink_handle->sink().use_count() == 0);
auto weak_ptr_sink = sink_handle->sink();
{
auto bg_removesink_call = [this, weak_ptr_sink] {
auto shared_sink = weak_ptr_sink.lock();
if (shared_sink) {
_impl._sinks.erase(std::remove(_impl._sinks.begin(), _impl._sinks.end(), shared_sink), _impl._sinks.end());
}
};
auto token_done = g3::spawn_task(bg_removesink_call, _impl._bg.get());
token_done.wait();
}
// sink_handle->sink().use_count() is 1 at this point.
// i.e. this would be safe: assert(sink_handle->sink().use_count() == 0);
// as long as the client has not converted more instances from the weak_ptr
}
}
/// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink
/// handle then the sink will be removed internally but will live on in the client's instance
void removeAllSinks() {
auto bg_clear_sink_call = [this]() noexcept {
_impl._sinks.clear();
};
auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
token_cleared.wait();
}
/// internal:
/// pushes in background thread (asynchronously) input messages to log file
@ -139,5 +112,7 @@ namespace g3 {
/// this way it's ensured that all existing entries were flushed before 'fatal'
/// Will abort the application!
void fatal(FatalMessagePtr fatal_message);
};
} // namespace g3
} // g3

View File

@ -14,23 +14,20 @@ namespace g3 {
// not CopyConstructible or CopyAssignable. To put them in a std container they need
// to be wrapped and their internals "moved" when tried to be copied.
template <typename Moveable>
template<typename Moveable>
struct MoveOnCopy {
mutable Moveable _move_only;
explicit MoveOnCopy(Moveable&& m) :
_move_only(std::move(m)) {}
MoveOnCopy(MoveOnCopy const& t) :
_move_only(std::move(t._move_only)) {}
MoveOnCopy(MoveOnCopy&& t) :
_move_only(std::move(t._move_only)) {}
explicit MoveOnCopy(Moveable &&m) : _move_only(std::move(m)) {}
MoveOnCopy(MoveOnCopy const &t) : _move_only(std::move(t._move_only)) {}
MoveOnCopy(MoveOnCopy &&t) : _move_only(std::move(t._move_only)) {}
MoveOnCopy& operator=(MoveOnCopy const& other) {
MoveOnCopy &operator=(MoveOnCopy const &other) {
_move_only = std::move(other._move_only);
return *this;
}
MoveOnCopy& operator=(MoveOnCopy&& other) {
MoveOnCopy &operator=(MoveOnCopy && other) {
_move_only = std::move(other._move_only);
return *this;
}
@ -39,13 +36,13 @@ namespace g3 {
_move_only();
}
Moveable& get() {
Moveable &get() {
return _move_only;
}
Moveable release() {
return std::move(_move_only);
}
};
} // namespace g3
} // g3

View File

@ -11,29 +11,30 @@
* the help from the std::thread library from JustSoftwareSolutions
* ref: http://www.stdthread.co.uk/doc/headers/mutex.html
*
* This example was totally inspired by Anthony Williams lock-based data structures in
* This exampel was totally inspired by Anthony Williams lock-based data structures in
* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */
#pragma once
#include <condition_variable>
#include <exception>
#include <mutex>
#include <queue>
#include <mutex>
#include <exception>
#include <condition_variable>
/** Multiple producer, multiple consumer thread safe queue
* Since 'return by reference' is used this queue won't throw */
template <typename T>
class shared_queue {
template<typename T>
class shared_queue
{
std::queue<T> queue_;
mutable std::mutex m_;
std::condition_variable data_cond_;
shared_queue& operator=(const shared_queue&) = delete;
shared_queue(const shared_queue& other) = delete;
shared_queue &operator=(const shared_queue &) = delete;
shared_queue(const shared_queue &other) = delete;
public:
shared_queue() = default;
public:
shared_queue() {}
void push(T item) {
{
@ -44,7 +45,7 @@ class shared_queue {
}
/// \return immediately, with true if successful retrieval
bool try_and_pop(T& popped_item) {
bool try_and_pop(T &popped_item) {
std::lock_guard<std::mutex> lock(m_);
if (queue_.empty()) {
return false;
@ -55,9 +56,10 @@ class shared_queue {
}
/// Try to retrieve, if no items, wait till an item is available and try again
void wait_and_pop(T& popped_item) {
void wait_and_pop(T &popped_item) {
std::unique_lock<std::mutex> lock(m_);
while (queue_.empty()) {
while (queue_.empty())
{
data_cond_.wait(lock);
// This 'while' loop is equal to
// data_cond_.wait(lock, [](bool result){return !queue_.empty();});

View File

@ -8,57 +8,59 @@
#pragma once
#include "g3log/sinkwrapper.hpp"
#include "g3log/active.hpp"
#include "g3log/future.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/sinkwrapper.hpp"
#include <functional>
#include <memory>
#include <functional>
#include <type_traits>
namespace g3 {
namespace internal {
typedef std::function<void(LogMessageMover)> AsyncMessageCall;
typedef std::function<void(LogMessageMover) > AsyncMessageCall;
/// The asynchronous Sink has an active object, incoming requests for actions
// will be processed in the background by the specific object the Sink represents.
//
// The Sink will wrap either
// a Sink with Message object receiving call
// or a Sink with a LogEntry (string) receiving call
// or a Sink with a LogEntry (string) receving call
//
// The Sink can also be used through the SinkHandler to call Sink specific function calls
// Ref: send(Message) deals with incoming log entries (converted if necessary to string)
// Ref: send(Call call, Args... args) deals with calls
// to the real sink's API
template <class T>
template<class T>
struct Sink : public SinkWrapper {
std::unique_ptr<T> _real_sink;
std::unique_ptr<kjellkod::Active> _bg;
AsyncMessageCall _default_log_call;
template <typename DefaultLogCall>
Sink(std::unique_ptr<T> sink, DefaultLogCall call) :
SinkWrapper(),
_real_sink{std::move(sink)},
_bg(kjellkod::Active::createActive()),
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
template<typename DefaultLogCall >
Sink(std::unique_ptr<T> sink, DefaultLogCall call)
: SinkWrapper {},
_real_sink {std::move(sink)},
_bg(kjellkod::Active::createActive()),
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
}
Sink(std::unique_ptr<T> sink, void (T::*Call)(std::string)) :
SinkWrapper(),
_real_sink{std::move(sink)},
_bg(kjellkod::Active::createActive()) {
Sink(std::unique_ptr<T> sink, void(T::*Call)(std::string) )
: SinkWrapper {},
_real_sink {std::move(sink)},
_bg(kjellkod::Active::createActive()) {
std::function<void(std::string)> adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1);
_default_log_call = [=](LogMessageMover m) {
_default_log_call = [ = ](LogMessageMover m) {
adapter(m.get().toString());
};
}
virtual ~Sink() {
_bg.reset(); // TODO: to remove
_bg.reset(); // TODO: to remove
}
void send(LogMessageMover msg) override {
@ -67,10 +69,11 @@ namespace g3 {
});
}
template <typename Call, typename... Args>
auto async(Call call, Args&&... args) -> std::future<std::invoke_result_t<decltype(call), T, Args...>> {
template<typename Call, typename... Args>
auto async(Call call, Args &&... args)-> std::future< typename std::result_of<decltype(call)(T, Args...)>::type> {
return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
}
};
} // namespace internal
} // namespace g3
} // internal
} // g3

View File

@ -19,41 +19,36 @@ namespace g3 {
// Only through the Sinkhandle can, and should, the real sink's specific API
// be called.
//
// The real sink will be owned by g3log. If the real sink is deleted
// The real sink will be owned by the g3logger. If the real sink is deleted
// calls to sink's API through the SinkHandle will return an exception embedded
// in the resulting future. Ref: SinkHandle::call
template <class T>
template<class T>
class SinkHandle {
std::weak_ptr<internal::Sink<T>> _sink;
public:
SinkHandle(std::shared_ptr<internal::Sink<T>> sink) :
_sink(sink) {}
public:
SinkHandle(std::shared_ptr<internal::Sink<T>> sink)
: _sink(sink) {}
~SinkHandle() {}
~SinkHandle() = default;
// Asynchronous call to the real sink. If the real sink is already deleted
// the returned future will contain a bad_weak_ptr exception instead of the
// call result.
template <typename AsyncCall, typename... Args>
auto call(AsyncCall func, Args&&... args) -> std::future<std::invoke_result_t<decltype(func), T, Args...>> {
template<typename AsyncCall, typename... Args>
auto call(AsyncCall func , Args &&... args) -> std::future<typename std::result_of<decltype(func)(T, Args...)>::type> {
try {
std::shared_ptr<internal::Sink<T>> sink(_sink);
return sink->async(func, std::forward<Args>(args)...);
} catch (const std::bad_weak_ptr& e) {
typedef std::invoke_result_t<decltype(func), T, Args...> PromiseType;
} catch (const std::bad_weak_ptr &e) {
typedef typename std::result_of<decltype(func)(T, Args...)>::type PromiseType;
std::promise<PromiseType> promise;
promise.set_exception(std::make_exception_ptr(e));
return std::move(promise.get_future());
}
}
/// Get weak_ptr access to the sink(). Make sure to check that the returned pointer is valid,
/// auto p = sink(); auto ptr = p.lock(); if (ptr) { .... }
/// ref: https://en.cppreference.com/w/cpp/memory/weak_ptr/lock
std::weak_ptr<internal::Sink<T>> sink() {
return _sink.lock();
}
};
}
} // namespace g3

View File

@ -14,8 +14,9 @@ namespace g3 {
namespace internal {
struct SinkWrapper {
virtual ~SinkWrapper() {}
virtual ~SinkWrapper() { }
virtual void send(LogMessageMover msg) = 0;
};
} // namespace internal
} // namespace g3
}
}

View File

@ -11,6 +11,7 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#pragma once
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "stacktrace_win.cpp used but not on a windows system"
@ -18,8 +19,8 @@
#include "g3log/crashhandler.hpp"
#include <windows.h>
#include <string>
#include <windows.h>
namespace stacktrace {
/// return the text description of a Windows exception code
@ -33,9 +34,9 @@ namespace stacktrace {
std::string stackdump();
/// helper function: retrieve stackdump, starting from an exception pointer
std::string stackdump(EXCEPTION_POINTERS* info);
std::string stackdump(EXCEPTION_POINTERS *info);
/// main stackdump function. retrieve stackdump, from the given context
std::string stackdump(CONTEXT* context);
std::string stackdump(CONTEXT *context);
} // namespace stacktrace
} // stacktrace

View File

@ -0,0 +1,47 @@
/** ==========================================================================
* 2013 This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
*
* make_unique will be in C++14, this implementation is copied as I understood
* Stephan T. Lavavej's description of it.
*
* PUBLIC DOMAIN and NOT under copywrite protection.
*
*
* Example: usage
* auto an_int = make_unique<int>(123);
* auto a_string = make_unique<string>(5, 'x');
* auto an_int_array = make_unique<int[]>(11, 22, 33);
* ********************************************* */
#pragma once
#include <memory>
#include <type_traits>
namespace std2 {
namespace impl_fut_stl {
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args &&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args &&... args) {
static_assert(std::extent<T>::value == 0, "make_unique<T[N]>() is forbidden, please use make_unique<T[]>(),");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)] {std::forward<Args>(args)...});
}
}
template<typename T, typename ... Args>
std::unique_ptr<T> make_unique(Args &&... args) {
return impl_fut_stl::make_unique_helper<T>(
std::is_array<T>(), std::forward<Args>(args)...);
}
}

View File

@ -11,42 +11,45 @@
* 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
* ============================================================================*/
#pragma once
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800)
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
namespace std {
template <class... _ArgTypes>
class packaged_task<void(_ArgTypes...)> {
template<class... _ArgTypes>
class packaged_task<void(_ArgTypes...)>
{
promise<void> _my_promise;
function<void(_ArgTypes...)> _my_func;
public:
public:
packaged_task() {
}
template <class _Fty2>
explicit packaged_task(_Fty2&& _Fnarg) :
_my_func(_Fnarg) {
template<class _Fty2>
explicit packaged_task(_Fty2 &&_Fnarg)
: _my_func(_Fnarg) {
}
packaged_task(packaged_task&& _Other) :
_my_promise(move(_Other._my_promise)),
_my_func(move(_Other._my_func)) {
packaged_task(packaged_task &&_Other)
: _my_promise(move(_Other._my_promise)),
_my_func(move(_Other._my_func)) {
}
packaged_task& operator=(packaged_task&& _Other) {
packaged_task &operator=(packaged_task && _Other) {
_my_promise = move(_Other._my_promise);
_my_func = move(_Other._my_func);
return (*this);
}
packaged_task(const packaged_task&) = delete;
packaged_task& operator=(const packaged_task&) = delete;
packaged_task(const packaged_task &) = delete;
packaged_task &operator=(const packaged_task &) = delete;
~packaged_task() {
}
void swap(packaged_task& _Other) {
void swap(packaged_task &_Other) {
swap(_my_promise, _Other._my_promise);
swap(_my_func, _Other._my_func);
}
@ -74,5 +77,5 @@ namespace std {
}
};
}; // namespace std
#endif // defined(WIN32) ...
}; // namespace std
#endif // defined(WIN32) ...

View File

@ -13,66 +13,39 @@
* PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc
* ********************************************* */
#include <chrono>
#include <ctime>
#include <string>
#include <chrono>
// FYI:
// namespace g3::internal ONLY in g3time.cpp
// std::string put_time(const struct tm* tmb, const char* c_time_format)
namespace g3 {
typedef std::chrono::time_point<std::chrono::system_clock> system_time_point;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> high_resolution_time_point;
namespace g3
{
namespace internal
{
static const std::string date_formatted = "%Y/%m/%d";
static const std::string time_formatted = "%H:%M:%S";
}
typedef std::chrono::time_point<std::chrono::system_clock> system_time_point;
typedef std::chrono::milliseconds milliseconds;
typedef std::chrono::microseconds microseconds;
namespace internal {
enum class Fractional { Millisecond,
Microsecond,
Nanosecond,
NanosecondDefault };
Fractional getFractional(const std::string& format_buffer, size_t pos);
std::string to_string(const g3::system_time_point& ts, Fractional fractional);
std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer);
static const std::string date_formatted = "%Y/%m/%d";
// %f: fractions of seconds (%f is nanoseconds)
// %f3: milliseconds, 3 digits: 001
// %6: microseconds: 6 digits: 000001 --- default for the time_format
// %f9, %f: nanoseconds, 9 digits: 000000001
static const std::string time_formatted = "%H:%M:%S %f6";
} // namespace internal
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
// return value is SIMPLIFIED to only return a std::string
std::string put_time(const struct tm* tmb, const char* c_time_format);
// wrap for std::chrono::system_clock::now()
std::time_t systemtime_now();
/** return time representing POD struct (ref ctime + wchar) that is normally
* retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not.
* g3::localtime is probably used together with @ref g3::systemtime_now */
tm localtime(std::time_t time);
tm localtime(const std::time_t &time);
/** format string must conform to std::put_time's demands.
* WARNING: At time of writing there is only so-so compiler support for
* std::put_time. A possible fix if your c++11 library is not updated is to
* modify this to use std::strftime instead */
std::string localtime_formatted(const system_time_point& ts, const std::string& time_format);
std::string localtime_formatted(const std::time_t &time_snapshot, const std::string &time_format) ;
}
inline system_time_point to_system_time(const high_resolution_time_point& ts) {
// On some (windows) systems, the system_clock does not provide the highest possible time
// resolution. Thus g3log uses high_resolution_clock for message time stamps. However,
// unlike system_clock, high_resolution_clock cannot be converted to a time and date as
// it usually measures reflects the time since power-up.
// Thus, hrs_now and sys_now are recorded once when the program starts to be able to convert
// timestamps to dime and date using to_system_time(). The precision of the absolute time is
// of course that of system_clock() with some error added due to the non-simultaneous initialization
// of the two static variables but relative times within one log will be as precise as
// high_resolution_clock.
using namespace std::chrono;
static const auto hrs_now = high_resolution_clock::now();
static const auto sys_now = system_clock::now();
return time_point_cast<system_clock::duration>(sys_now + (ts - hrs_now));
}
} // namespace g3

View File

@ -8,47 +8,33 @@
#include "g3log/logcapture.hpp"
#include "g3log/crashhandler.hpp"
#include "g3log/g3log.hpp"
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
#include <vector>
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
// For Windows we need force a thread_local install per thread of three
// signals that must have a signal handler installed per thread-basis
// signals that must have a signal handler instealled per thread-basis
// It is really a royal pain. Seriously Microsoft? Seriously?
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread()
#else
// Does nothing --- enforces that semicolon must be written
#define SIGNAL_HANDLER_VERIFY() \
do { \
} while (0)
#define SIGNAL_HANDLER_VERIFY() do {} while(0)
#endif
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
// MaxMessageSize is message limit used with vsnprintf/vsnprintf_s
static int MaxMessageSize = 2048;
void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) {
MaxMessageSize = max_size;
}
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
/** logCapture is a simple struct for capturing log/fatal entries. At destruction the
* captured message is forwarded to background worker.
* As a safety precaution: No memory allocated here will be moved into the background
* worker in case of dynamic loaded library reasons instead the arguments are copied
* inside of g3log.cpp::saveMessage*/
LogCapture::~LogCapture() noexcept(false) {
LogCapture::~LogCapture() {
using namespace g3::internal;
SIGNAL_HANDLER_VERIFY();
saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str());
}
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc)
LogCapture::LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const char* dump) :
LogCapture("", 0, "", level, "", fatal_signal, dump) {
LogCapture::LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump) : LogCapture("", 0, "", level, "", fatal_signal, dump) {
}
/**
@ -57,57 +43,44 @@ LogCapture::LogCapture(const LEVELS& level, g3::SignalType fatal_signal, const c
* @expression for CHECK calls
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler
*/
LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS& level,
const char* expression, g3::SignalType fatal_signal, const char* dump) :
_file(file),
_line(line),
_function(function),
_level(level),
_expression(expression),
_fatal_signal(fatal_signal) {
LogCapture::LogCapture(const char *file, const int line, const char *function, const LEVELS &level,
const char *expression, g3::SignalType fatal_signal, const char *dump)
: _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) {
if (g3::internal::wasFatal(level)) {
_stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"};
_stack_trace = {"\n*******\tSTACKDUMP *******\n"};
_stack_trace.append(g3::internal::stackdump(dump));
}
}
/**
* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18
*/
void LogCapture::capturef(const char* printf_like_message, ...) {
static const std::string kTruncatedWarningText = "[...truncated...]";
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
std::vector<char> finished_message_backing(MaxMessageSize);
char* finished_message = finished_message_backing.data();
auto finished_message_len = MaxMessageSize;
#else
void LogCapture::capturef(const char *printf_like_message, ...) {
static const int kMaxMessageSize = 2048;
static const std::string kTruncatedWarningText = "[...truncated...]";
char finished_message[kMaxMessageSize];
#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__))
auto finished_message_len = _countof(finished_message);
#else
int finished_message_len = sizeof(finished_message);
#endif
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE*/
va_list arglist;
va_start(arglist, printf_like_message);
#if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__))
const int nbrcharacters = vsnprintf_s(finished_message, finished_message_len, _TRUNCATE, printf_like_message, arglist);
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
const int nbrcharacters = vsnprintf_s(finished_message, _countof(finished_message), _TRUNCATE, printf_like_message, arglist);
#else
const int nbrcharacters = vsnprintf(finished_message, finished_message_len, printf_like_message, arglist);
const int nbrcharacters = vsnprintf(finished_message, sizeof (finished_message), printf_like_message, arglist);
#endif
va_end(arglist);
if (nbrcharacters < 0) {
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to successfully parse the message";
if (nbrcharacters <= 0) {
stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
stream() << '"' << printf_like_message << '"' << std::endl;
} else if (nbrcharacters > finished_message_len) {
} else if (nbrcharacters > kMaxMessageSize) {
stream() << finished_message << kTruncatedWarningText;
} else {
stream() << finished_message;
}
}

View File

@ -7,122 +7,71 @@
* ============================================================================*/
#include "g3log/loglevels.hpp"
#include <atomic>
#include <cassert>
#include <map>
#include <iostream>
namespace {
namespace {
/// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c
struct atomicbool {
private:
std::atomic<bool> value_;
public:
atomicbool(): value_ {false} {}
atomicbool(const bool &value): value_ {value} {}
atomicbool(const std::atomic<bool> &value) : value_ {value.load(std::memory_order_acquire)} {}
atomicbool(const atomicbool &other): value_ {other.value_.load(std::memory_order_acquire)} {}
atomicbool &operator=(const atomicbool &other) {
value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release);
return *this;
}
bool value() {return value_.load(std::memory_order_acquire);}
std::atomic<bool>& get() {return value_;}
};
} // anonymous
}
namespace g3 {
namespace internal {
bool wasFatal(const LEVELS& level) {
bool wasFatal(const LEVELS &level) {
return level.value >= FATAL.value;
}
#ifdef G3_DYNAMIC_LOGGING
const std::map<int, LoggingLevel> g_log_level_defaults = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG}},
{INFO.value, {INFO}},
{WARNING.value, {WARNING}},
{FATAL.value, {FATAL}}};
std::map<int, g3::LoggingLevel> g_log_levels = g_log_level_defaults;
std::map<int, atomicbool> g_log_level_status = {{g3::kDebugVaulue, true}, {INFO.value, true}, {WARNING.value, true}, {FATAL.value, true}};
#endif
} // namespace internal
} // internal
#ifdef G3_DYNAMIC_LOGGING
namespace only_change_at_initialization {
void addLogLevel(LEVELS lvl, bool enabled) {
int value = lvl.value;
internal::g_log_levels[value] = {lvl, enabled};
void setLogLevel(LEVELS log_level, bool enabled) {
int level = log_level.value;
internal::g_log_level_status[level].get().store(enabled, std::memory_order_release);
}
void addLogLevel(LEVELS level) {
addLogLevel(level, true);
}
void reset() {
g3::internal::g_log_levels = g3::internal::g_log_level_defaults;
}
} // namespace only_change_at_initialization
namespace log_levels {
void setHighest(LEVELS enabledFrom) {
auto it = internal::g_log_levels.find(enabledFrom.value);
if (it != internal::g_log_levels.end()) {
for (auto& v : internal::g_log_levels) {
if (v.first < enabledFrom.value) {
disable(v.second.level);
} else {
enable(v.second.level);
}
}
}
}
void set(LEVELS level, bool enabled) {
auto it = internal::g_log_levels.find(level.value);
if (it != internal::g_log_levels.end()) {
internal::g_log_levels[level.value] = {level, enabled};
}
}
void disable(LEVELS level) {
set(level, false);
}
void enable(LEVELS level) {
set(level, true);
}
void disableAll() {
for (auto& v : internal::g_log_levels) {
v.second.status = false;
}
}
void enableAll() {
for (auto& v : internal::g_log_levels) {
v.second.status = true;
}
}
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint) {
std::string printLevels() {
std::string levels;
for (auto& v : levelsToPrint) {
levels += "name: " + v.second.level.text + " level: " + std::to_string(v.first) + " status: " + std::to_string(v.second.status.value()) + "\n";
for (auto& v : internal::g_log_level_status) {
levels += "value: " + std::to_string(v.first) + " status: " + std::to_string(v.second.value()) + "\n";
}
return levels;
}
std::string to_string() {
return to_string(internal::g_log_levels);
void reset() {
internal::g_log_level_status.clear();
internal::g_log_level_status = std::map<int, atomicbool>{{g3::kDebugVaulue, true}, {INFO.value, true}, {WARNING.value, true}, {FATAL.value, true}};
}
std::map<int, g3::LoggingLevel> getAll() {
return internal::g_log_levels;
}
// status : {Absent, Enabled, Disabled};
status getStatus(LEVELS level) {
const auto it = internal::g_log_levels.find(level.value);
if (internal::g_log_levels.end() == it) {
return status::Absent;
}
return (it->second.status.get().load() ? status::Enabled : status::Disabled);
}
} // namespace log_levels
} // only_change_at_initialization
#endif
bool logLevel(const LEVELS& log_level) {
bool logLevel(LEVELS log_level) {
#ifdef G3_DYNAMIC_LOGGING
int level = log_level.value;
bool status = internal::g_log_levels[level].status.value();
bool status = internal::g_log_level_status[level].value();
return status;
#else
return true;
#endif
return true;
}
} // namespace g3
} // g3

View File

@ -7,182 +7,192 @@
* ============================================================================*/
#include "g3log/logmessage.hpp"
#include <mutex>
#include "g3log/crashhandler.hpp"
#include "g3log/time.hpp"
#include <mutex>
namespace g3 {
namespace {
std::once_flag g_start_time_flag;
std::chrono::steady_clock::time_point g_start_time;
std::string LogMessage::splitFileName(const std::string& str) {
int64_t microsecondsCounter() {
std::call_once(g_start_time_flag, []() {
g_start_time = std::chrono::steady_clock::now();
});
auto now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now - g_start_time).count();
}
std::string splitFileName(const std::string& str) {
size_t found;
found = str.find_last_of("(/\\");
return str.substr(found + 1);
}
} // anonymous
namespace g3 {
// helper for setting the normal log details in an entry
std::string LogDetailsToString(const LogMessage& msg) {
std::string out;
out.append("\n" + msg.timestamp() + " " + msg.microseconds() + "\t"
+ msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
return out;
}
// helper for normal
std::string normalToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg);
out.append('"' + msg.message() + '"');
return out;
}
// helper for fatal signal
std::string LogMessage::fatalSignalToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting
out.append(msg.timestamp() + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + msg.message() + '\n');
std::string fatalSignalToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting
out.append("\n" + msg.timestamp() + "." + msg.microseconds()
+ "\n\n***** FATAL SIGNAL RECEIVED ******* \n"
+ '"' + msg.message() + '"');
return out;
}
// helper for fatal exception (windows only)
std::string LogMessage::fatalExceptionToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting
out.append(msg.timestamp() + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + msg.message() + '\n');
std::string fatalExceptionToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting
out.append("\n" + msg.timestamp() + "." + msg.microseconds()
+ "\n\n***** FATAL EXCEPTION RECEIVED ******* \n"
+ '"' + msg.message() + '"');
return out;
}
// helper for fatal LOG
std::string LogMessage::fatalLogToString(const LogMessage& msg) {
auto out = msg._logDetailsToStringFunc(msg);
std::string fatalLogToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg);
static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "};
out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"');
return out;
}
// helper for fatal CHECK
std::string LogMessage::fatalCheckToString(const LogMessage& msg) {
auto out = msg._logDetailsToStringFunc(msg);
std::string fatalCheckToString(const LogMessage& msg) {
auto out = LogDetailsToString(msg);
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" + '"' + msg.message() + '"');
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t"
+ '"' + msg. message() + '"');
return out;
}
// helper for setting the normal log details in an entry
std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) {
std::string out;
out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
return out;
}
std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) {
std::string out;
out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.threadID() + " " + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
return out;
}
// helper for normal
std::string LogMessage::normalToString(const LogMessage& msg) {
auto out = msg._logDetailsToStringFunc(msg);
out.append(msg.message() + '\n');
return out;
}
// end static functions section
void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const {
_logDetailsToStringFunc = func;
}
// Format the log message according to it's type
std::string LogMessage::toString(LogDetailsFunc formattingFunc) const {
overrideLogDetailsFunc(formattingFunc);
std::string LogMessage::toString() const {
if (false == wasFatal()) {
return LogMessage::normalToString(*this);
return normalToString(*this);
}
const auto level_value = _level.value;
if (internal::FATAL_SIGNAL.value == _level.value) {
return LogMessage::fatalSignalToString(*this);
return fatalSignalToString(*this);
}
if (internal::FATAL_EXCEPTION.value == _level.value) {
return LogMessage::fatalExceptionToString(*this);
return fatalExceptionToString(*this);
}
if (FATAL.value == _level.value) {
return LogMessage::fatalLogToString(*this);
return fatalLogToString(*this);
}
if (internal::CONTRACT.value == level_value) {
return LogMessage::fatalCheckToString(*this);
return fatalCheckToString(*this);
}
// What? Did we hit a custom made level?
auto out = _logDetailsToStringFunc(*this);
auto out = LogDetailsToString(*this);
static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"};
out.append("\t*******" + errorUnknown + "\n\t" + message() + '\n');
out.append("\n\t*******" + errorUnknown + "\t\n" + '"' + message() + '"');
return out;
}
std::string LogMessage::timestamp(const std::string& time_look) const {
return g3::localtime_formatted(to_system_time(_timestamp), time_look);
return localtime_formatted(_timestamp, time_look);
}
// By copy, not by reference. See this explanation for details:
// http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
// By copy, not by reference. See this explanation for details:
// http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
LogMessage& LogMessage::operator=(LogMessage other) {
swap(*this, other);
return *this;
}
LogMessage::LogMessage(std::string file, const int line,
std::string function, const LEVELS level) :
_logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString),
_timestamp(std::chrono::high_resolution_clock::now()),
_call_thread_id(std::this_thread::get_id())
#if defined(G3_LOG_FULL_FILENAME)
,
_file(file)
#else
,
_file(LogMessage::splitFileName(file))
#endif
,
_file_path(file),
_line(line),
_function(std::move(function)),
_level(level) {
}
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) :
LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) {
LogMessage::LogMessage(const std::string& file, const int line,
const std::string& function, const LEVELS& level)
: _timestamp(g3::systemtime_now())
, _call_thread_id(std::this_thread::get_id())
, _microseconds(microsecondsCounter())
, _file(splitFileName(file))
, _line(line)
, _function(function)
, _level(level)
{}
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage)
: LogMessage( {""}, 0, {""}, internal::FATAL_SIGNAL) {
_message.append(fatalOsSignalCrashMessage);
}
LogMessage::LogMessage(const LogMessage& other) :
_logDetailsToStringFunc(other._logDetailsToStringFunc),
_timestamp(other._timestamp),
_call_thread_id(other._call_thread_id),
_file(other._file),
_file_path(other._file_path),
_line(other._line),
_function(other._function),
_level(other._level),
_expression(other._expression),
_message(other._message) {
LogMessage::LogMessage(const LogMessage& other)
: _timestamp(other._timestamp)
, _call_thread_id(other._call_thread_id)
, _microseconds(other._microseconds)
, _file(other._file)
, _line(other._line)
, _function(other._function)
, _level(other._level)
, _expression(other._expression)
, _message(other._message) {
}
LogMessage::LogMessage(LogMessage&& other) :
_logDetailsToStringFunc(other._logDetailsToStringFunc),
_timestamp(other._timestamp),
_call_thread_id(other._call_thread_id),
_file(std::move(other._file)),
_file_path(std::move(other._file_path)),
_line(other._line),
_function(std::move(other._function)),
_level(other._level),
_expression(std::move(other._expression)),
_message(std::move(other._message)) {
LogMessage::LogMessage(LogMessage &&other)
: _timestamp(other._timestamp)
, _call_thread_id(other._call_thread_id)
, _microseconds(other._microseconds)
, _file(std::move(other._file))
, _line(other._line)
, _function(std::move(other._function))
, _level(other._level)
, _expression(std::move(other._expression))
, _message(std::move(other._message)) {
}
std::string LogMessage::threadID() const {
std::ostringstream oss;
oss << _call_thread_id;
return oss.str();
}
FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id) :
LogMessage(details),
_signal_id(signal_id) {}
FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id)
: LogMessage(details), _signal_id(signal_id) { }
FatalMessage::FatalMessage(const FatalMessage& other) :
LogMessage(other),
_signal_id(other._signal_id) {}
LogMessage FatalMessage::copyToLogMessage() const {
FatalMessage::FatalMessage(const FatalMessage& other)
: LogMessage(other), _signal_id(other._signal_id) {}
LogMessage FatalMessage::copyToLogMessage() const {
return LogMessage(*this);
}
@ -190,4 +200,5 @@ namespace g3 {
return internal::exitReasonName(_level, _signal_id);
}
} // namespace g3
} // g3

View File

@ -7,18 +7,17 @@
* ============================================================================*/
#include "g3log/logworker.hpp"
#include "g3log/active.hpp"
#include "g3log/crashhandler.hpp"
#include "g3log/future.hpp"
#include "g3log/g3log.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/active.hpp"
#include "g3log/g3log.hpp"
#include "g3log/future.hpp"
#include "g3log/crashhandler.hpp"
#include <iostream>
namespace g3 {
LogWorkerImpl::LogWorkerImpl() :
_bg(kjellkod::Active::createActive()) {}
LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { }
void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
@ -29,8 +28,8 @@ namespace g3 {
}
if (_sinks.empty()) {
std::string err_msg{"g3logworker has no sinks. Message: ["};
err_msg.append(uniqueMsg.get()->toString()).append("]\n");
std::string err_msg {"g3logworker has no sinks. Message: ["};
err_msg.append(uniqueMsg.get()->toString()).append({"]\n"});
std::cerr << err_msg;
}
}
@ -44,23 +43,27 @@ namespace g3 {
const auto level = msgPtr.get()->_level;
const auto fatal_id = msgPtr.get()->_signal_id;
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level());
// Change output in case of a fatal signal (or windows exception)
std::string exiting = {"Fatal type: "};
uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason).append("\nLog content flushed successfully to sink\n\n");
uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason)
.append("\nLog content flushed flushed sucessfully to sink\n\n");
std::cerr << uniqueMsg->toString() << std::flush;
std::cerr << uniqueMsg->message() << std::flush;
for (auto& sink : _sinks) {
LogMessage msg(*(uniqueMsg));
sink->send(LogMessageMover(std::move(msg)));
}
// This clear is absolutely necessary
// All sinks are forced to receive the fatal message above before we continue
_sinks.clear(); // flush all queues
_sinks.clear(); // flush all queues
internal::exitWithDefaultSignalHandler(level, fatal_id);
// should never reach this point
@ -71,23 +74,24 @@ namespace g3 {
g3::internal::shutDownLoggingForActiveOnly(this);
// The sinks WILL automatically be cleared at exit of this destructor
// The waiting inside removeAllSinks ensures that all messages until this point are
// taken care of before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
// i.e. this avoids a race with another thread slipping through the "shutdownLogging" and
// calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly
// deconstructed LogWorkerImpl"
// However, the waiting below ensures that all messages until this point are taken care of
// before any internals/LogWorkerImpl of LogWorker starts to be destroyed.
// i.e. this avoids a race with another thread slipping through the "shutdownLogging" and calling
// calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly deconstructed LogWorkerImpl"
//
// Any messages put into the queue will be OK due to:
// *) If it is before the wait below then they will be executed
// *) If it is AFTER the wait below then they will be ignored and NEVER executed
removeAllSinks();
auto bg_clear_sink_call = [this] { _impl._sinks.clear(); };
auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
token_cleared.wait();
// The background worker WILL be automatically cleared at the exit of the destructor
// However, the explicitly clearing of the background worker (below) makes sure that there can
// be no thread that manages to add another sink after the call to clear the sinks above.
// i.e. this manages the extremely unlikely case of another thread calling
// addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp
// and be closely coupled with the existence of the LogWorker. Sharing this adding of sinks to
// and be closely coupled with the existance of the LogWorker. Sharing this adding of sinks to
// other threads that do not know the state of LogWorker is considered a bug but it is dealt with
// nonetheless below.
//
@ -99,17 +103,15 @@ namespace g3 {
}
void LogWorker::save(LogMessagePtr msg) {
_impl._bg->send([this, msg] { _impl.bgSave(msg); });
_impl._bg->send([this, msg] {_impl.bgSave(msg); });
}
void LogWorker::fatal(FatalMessagePtr fatal_message) {
_impl._bg->send([this, fatal_message] { _impl.bgFatal(fatal_message); });
_impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); });
}
void LogWorker::addWrappedSink(std::shared_ptr<g3::internal::SinkWrapper> sink) {
auto bg_addsink_call = [this, sink] {
_impl._sinks.push_back(sink);
};
auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);};
auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
token_done.wait();
}
@ -118,8 +120,11 @@ namespace g3 {
return std::unique_ptr<LogWorker>(new LogWorker);
}
std::unique_ptr<FileSinkHandle> LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
return addSink(std::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) {
return addSink(std2::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
}
} // namespace g3
} // g3

View File

@ -14,51 +14,62 @@
#include "g3log/stacktrace_windows.hpp"
#include <dbghelp.h>
#include <windows.h>
#include <cassert>
#include <g3log/g3log.hpp>
#include <dbghelp.h>
#include <map>
#include <memory>
#include <mutex>
#include <cassert>
#include <vector>
#include <mutex>
#include <g3log/g3log.hpp>
#pragma comment(lib, "dbghelp.lib")
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "stacktrace_win.cpp used but not on a windows system"
#endif
#define g3_MAP_PAIR_STRINGIFY(x) \
{ x, #x }
#define g3_MAP_PAIR_STRINGIFY(x) {x, #x}
namespace {
thread_local size_t g_thread_local_recursive_crash_check = 0;
const std::map<g3::SignalType, std::string> kExceptionsAsText = {
g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK), g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION), g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW), g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT), g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)
g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT)
, g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP)
};
// Using the given context, fill in all the stack frames.
// Which then later can be interpreted to human readable text
void captureStackTrace(CONTEXT* context, std::vector<uint64_t>& frame_pointers) {
void captureStackTrace(CONTEXT *context, std::vector<uint64_t> &frame_pointers) {
DWORD machine_type = 0;
STACKFRAME64 frame = {}; // force zeroing
STACKFRAME64 frame = {}; // force zeroeing
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Mode = AddrModeFlat;
#if defined(_M_ARM64)
frame.AddrPC.Offset = context->Pc;
frame.AddrFrame.Offset = context->Fp;
frame.AddrStack.Offset = context->Sp;
machine_type = IMAGE_FILE_MACHINE_ARM64;
#elif defined(_M_ARM)
frame.AddrPC.Offset = context->Pc;
frame.AddrFrame.Offset = context->R11;
frame.AddrStack.Offset = context->Sp;
machine_type = IMAGE_FILE_MACHINE_ARM;
#elif defined(_M_X64)
#ifdef _M_X64
frame.AddrPC.Offset = context->Rip;
frame.AddrFrame.Offset = context->Rbp;
frame.AddrStack.Offset = context->Rsp;
@ -69,7 +80,8 @@ namespace {
frame.AddrPC.Offset = context->Esp;
machine_type = IMAGE_FILE_MACHINE_I386;
#endif
for (size_t index = 0; index < frame_pointers.size(); ++index) {
for (size_t index = 0; index < frame_pointers.size(); ++index)
{
if (StackWalk64(machine_type,
GetCurrentProcess(),
GetCurrentThread(),
@ -86,16 +98,18 @@ namespace {
}
}
// extract readable text from a given stack frame. All thanks to
// using SymFromAddr and SymGetLineFromAddr64 with the stack pointer
std::string getSymbolInformation(const size_t index, const std::vector<uint64_t>& frame_pointers) {
std::string getSymbolInformation(const size_t index, const std::vector<uint64_t> &frame_pointers) {
auto addr = frame_pointers[index];
std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t";
DWORD64 displacement64;
DWORD displacement;
alignas(SYMBOL_INFO) char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer);
char symbol_buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(symbol_buffer);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
@ -104,7 +118,7 @@ namespace {
std::string lineInformation;
std::string callInformation;
if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) {
callInformation.append(" ").append(std::string(symbol->Name, symbol->NameLen));
callInformation.append(" ").append({symbol->Name, symbol->NameLen});
if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) {
lineInformation.append("\t").append(line.FileName).append(" L: ");
lineInformation.append(std::to_string(line.LineNumber));
@ -114,9 +128,10 @@ namespace {
return frame_dump;
}
// Retrieves all the symbols for the stack frames, fills them within a text representation and returns it
std::string convertFramesToText(std::vector<uint64_t>& frame_pointers) {
std::string dump; // slightly more efficient than ostringstream
// Retrieves all the symbols for the stack frames, fills them witin a text representation and returns it
std::string convertFramesToText(std::vector<uint64_t> &frame_pointers) {
std::string dump; // slightly more efficient than ostringstream
const size_t kSize = frame_pointers.size();
for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) {
dump += getSymbolInformation(index, frame_pointers);
@ -124,7 +139,10 @@ namespace {
}
return dump;
}
} // namespace
} // anonymous
namespace stacktrace {
const std::string kUnknown = {"UNKNOWN EXCEPTION"};
@ -132,7 +150,7 @@ namespace stacktrace {
/// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
std::string exceptionIdToText(g3::SignalType id) {
const auto iter = kExceptionsAsText.find(id);
if (iter == kExceptionsAsText.end()) {
if ( iter == kExceptionsAsText.end()) {
std::string unknown = {kUnknown + ":" + std::to_string(id)};
return unknown;
}
@ -155,15 +173,17 @@ namespace stacktrace {
}
/// helper function: retrieve stackdump, starting from an exception pointer
std::string stackdump(EXCEPTION_POINTERS* info) {
std::string stackdump(EXCEPTION_POINTERS *info) {
auto context = info->ContextRecord;
return stackdump(context);
}
/// main stackdump function. retrieve stackdump, from the given context
std::string stackdump(CONTEXT* context) {
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarios we allow one extra pass
/// main stackdump function. retrieve stackdump, from the given context
std::string stackdump(CONTEXT *context) {
if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarious we allow one extra pass
std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
return recursive_crash;
@ -176,15 +196,16 @@ namespace stacktrace {
const BOOL kLoadSymModules = TRUE;
const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules);
if (TRUE != initialized) {
return {"Error: Cannot call SymInitialize(...) for retrieving symbols in stack"};
return { "Error: Cannot call SymInitialize(...) for retrieving symbols in stack" };
}
std::shared_ptr<void> RaiiSymCleaner(nullptr, [&](void*) {
std::shared_ptr<void> RaiiSymCleaner(nullptr, [&](void *) {
SymCleanup(GetCurrentProcess());
}); // Raii sym cleanup
}); // Raii sym cleanup
constexpr size_t kmax_frame_dump_size = 64;
std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
const size_t kmax_frame_dump_size = 64;
std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
// C++11: size set and values are zeroed
assert(frame_pointers.size() == kmax_frame_dump_size);
@ -193,4 +214,7 @@ namespace stacktrace {
}
}
} // namespace stacktrace
} // stacktrace

View File

@ -2,150 +2,75 @@
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include "g3log/time.hpp"
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <string>
#ifdef __MACH__
#include <sys/time.h>
#endif
#include <chrono>
#include <cassert>
#include <iomanip>
namespace g3 {
namespace internal {
const std::string kFractionalIdentier = "%f";
const size_t kFractionalIdentierSize = 2;
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
// return value is SIMPLIFIED to only return a std::string
Fractional getFractional(const std::string& format_buffer, size_t pos) {
char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0');
Fractional type = Fractional::NanosecondDefault;
switch (ch) {
case '3':
type = Fractional::Millisecond;
break;
case '6':
type = Fractional::Microsecond;
break;
case '9':
type = Fractional::Nanosecond;
break;
default:
type = Fractional::NanosecondDefault;
break;
std::string put_time(const struct tm *tmb, const char *c_time_format) {
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
std::ostringstream oss;
oss.fill('0');
// BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
oss << std::put_time(const_cast<struct tm *> (tmb), c_time_format);
return oss.str();
#else // LINUX
const size_t size = 1024;
char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time.
// ... also ... This is way more buffer space then we need
auto success = std::strftime(buffer, size, c_time_format, tmb);
if (0 == success)
{
assert((0 != success) && "strftime fails with illegal formatting");
return c_time_format;
}
return type;
return buffer;
#endif
}
} // internal
} // g3
// Returns the fractional as a string with padded zeroes
// 1 ms --> 001
// 1 us --> 000001
// 1 ns --> 000000001
std::string to_string(const g3::system_time_point& ts, Fractional fractional) {
auto duration = ts.time_since_epoch();
auto sec_duration = std::chrono::duration_cast<std::chrono::seconds>(duration);
duration -= sec_duration;
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
auto zeroes = 9; // default ns
auto digitsToCut = 1; // default ns, divide by 1 makes no change
switch (fractional) {
case Fractional::Millisecond: {
zeroes = 3;
digitsToCut = 1000000;
break;
}
case Fractional::Microsecond: {
zeroes = 6;
digitsToCut = 1000;
break;
}
case Fractional::Nanosecond:
case Fractional::NanosecondDefault:
default:
zeroes = 9;
digitsToCut = 1;
}
ns /= digitsToCut;
auto value = std::string(std::to_string(ns));
return std::string(zeroes - value.size(), '0') + value;
}
std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer) {
// iterating through every "%f" instance in the format string
auto identifierExtraSize = 0;
for (size_t pos = 0;
(pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos;
pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) {
// figuring out whether this is nano, micro or milli identifier
auto type = g3::internal::getFractional(format_buffer, pos);
auto value = g3::internal::to_string(ts, type);
auto padding = 0;
if (type != g3::internal::Fractional::NanosecondDefault) {
padding = 1;
}
// replacing "%f[3|6|9]" with sec fractional part value
format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value);
}
return format_buffer;
}
} // namespace internal
} // namespace g3
namespace g3 {
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
// return value is SIMPLIFIED to only return a std::string
std::string put_time(const struct tm* tmb, const char* c_time_format) {
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
std::ostringstream oss;
oss.fill('0');
// BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
oss << std::put_time(const_cast<struct tm*>(tmb), c_time_format);
return oss.str();
#else // LINUX
const size_t size = 1024;
char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time.
// ... also ... This is way more buffer space then we need
auto success = std::strftime(buffer, size, c_time_format, tmb);
// In DEBUG the assert will trigger a process exit. Once inside the if-statement
// the 'always true' expression will be displayed as reason for the exit
//
// In Production mode
// the assert will do nothing but the format string will instead be returned
if (0 == success) {
assert((0 != success) && "strftime fails with illegal formatting");
return c_time_format;
}
return buffer;
#endif
std::time_t systemtime_now() {
system_time_point system_now = std::chrono::system_clock::now();
return std::chrono::system_clock::to_time_t(system_now);
}
tm localtime(std::time_t ts) {
tm localtime(const std::time_t &time) {
struct tm tm_snapshot;
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
localtime_s(&tm_snapshot, &ts); // windsows
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
localtime_s(&tm_snapshot, &time); // windsows
#else
localtime_r(&ts, &tm_snapshot); // POSIX
localtime_r(&time, &tm_snapshot); // POSIX
#endif
return tm_snapshot;
}
std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) {
auto format_buffer = internal::localtime_formatted_fractions(ts, time_format);
auto time_point = std::chrono::system_clock::to_time_t(ts);
std::tm t = localtime(time_point);
return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
/// returns a std::string with content of time_t as localtime formatted by input format string
/// * format string must conform to std::put_time
/// This is similar to std::put_time(std::localtime(std::time_t*), time_format.c_str());
std::string localtime_formatted(const std::time_t &time_snapshot, const std::string &time_format) {
std::tm t = localtime(time_snapshot); // could be const, but cannot due to VS2012 is non conformant for C++11's std::put_time (see above)
return g3::internal::put_time(&t, time_format.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
}
} // namespace g3
} // g3

24
sublime.formatting Normal file
View File

@ -0,0 +1,24 @@
{
"AStyleFormatter":
{
"options_default":
{
"indent": "spaces",
"indent-modifiers": false,
"indent-namespaces": true,
"indent-preproc-block": true,
"indent-spaces": 3,
"style": "googles"
}
},
"color_scheme": "Packages/Color Scheme - Default/Twilight.tmTheme",
"font_size": 11,
"highlight_modified_tabs": true,
"ignored_packages":
[
"Vintage"
],
"tab_size": 3,
"translate_tabs_to_spaces": true,
"word_wrap": true
}

View File

@ -7,9 +7,11 @@
#include <gtest/gtest.h>
#include <iostream>
int main(int argc, char* argv[]) {
int main(int argc, char *argv[])
{
testing::InitGoogleTest(&argc, argv);
int return_value = RUN_ALL_TESTS();
std::cout << "FINISHED WITH THE TESTING " << std::endl;
std::cout << "FINISHED WITH THE TESTING" << std::endl;
return return_value;
}

View File

@ -12,8 +12,8 @@
# . performance test (average + worst case) for KjellKod's g3log
# Do 'cmake -DADD_G3LOG_BENCH_PERFORMANCE=ON' to enable this
option (ADD_G3LOG_BENCH_PERFORMANCE "g3log performance test" OFF)
# Do 'cmake -DUSE_G3LOG_PERFORMANCE=ON' to enable this
option (ADD_G3LOG_PERFORMANCE "g3log performance test" OFF)
@ -23,7 +23,7 @@
IF (ADD_G3LOG_BENCH_PERFORMANCE)
set(DIR_PERFORMANCE ${g3log_SOURCE_DIR}/test_performance)
message( STATUS "-DADD_G3LOG_BENCH_PERFORMANCE=ON" )
MESSAGE("-DADD_G3LOG_BENCH_PERFORMANCE=ON")
include_directories (${DIR_PERFORMANCE})
# MEAN PERFORMANCE TEST
@ -46,7 +46,7 @@
${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES})
ELSE()
message( STATUS "-DADD_G3LOG_BENCH_PERFORMANCE=OFF" )
MESSAGE("-DADD_G3LOG_BENCH_PERFORMANCE=OFF")
ENDIF(ADD_G3LOG_BENCH_PERFORMANCE)

View File

@ -7,10 +7,9 @@
* ============================================================================*/
// through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE
#include <algorithm>
#include <iostream>
#include <thread>
#include "performance.h"
#include <thread>
#include <iostream>
#if defined(G3LOG_PERFORMANCE)
const std::string title = "G3LOG";
@ -27,7 +26,8 @@ const std::string g_path = "/tmp/";
#endif
using namespace g3_test;
int main(int argc, char** argv) {
int main(int argc, char **argv)
{
#ifdef G3_DYNAMIC_LOGGING
std::cerr << "G3_DYNAMIC_LOGGING is enabled" << std::endl;
#else
@ -35,57 +35,61 @@ int main(int argc, char** argv) {
#endif
size_t number_of_threads = 0;
if (argc == 2) {
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0) {
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::ostringstream thread_count_oss;
thread_count_oss << number_of_threads;
const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-MEAN_LOG";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-MEAN_LOG";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
std::ostringstream oss;
const uint64_t us_to_s = 1000000;
oss << "\n\n"
<< title << " performance " << number_of_threads << " threads MEAN times\n";
oss << "\n\n" << title << " performance " << number_of_threads << " threads MEAN times\n";
oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const uint64_t xtra_margin = 2;
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;
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;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
oss.str(""); // clear the stream
#if defined(G3LOG_PERFORMANCE)
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(g_prefix_log_name, g_path);
auto handle= worker->addDefaultLogger(g_prefix_log_name, g_path);
g3::initializeLogging(worker.get());
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]);
#endif
auto start_time = std::chrono::high_resolution_clock::now();
std::thread* threads = new std::thread[number_of_threads];
std::thread *threads = new std::thread[number_of_threads];
// kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas
for (size_t idx = 0; idx < number_of_threads; ++idx) {
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
std::ostringstream count;
count << idx + 1;
std::string thread_name = title + "_T" + count.str();
std::string thread_name = title + "_T" + count.str();
std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(doLogWrites, thread_name);
}
for (size_t idx = 0; idx < number_of_threads; ++idx) {
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto application_end_time = std::chrono::high_resolution_clock::now();
delete[] threads;
delete [] threads;
#if defined(G3LOG_PERFORMANCE)
worker.reset(); // will flush anything in the queue to file
worker.reset(); // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
#endif
@ -94,10 +98,9 @@ int main(int argc, char** argv) {
uint64_t application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
uint64_t total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
oss << "\n"
<< number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk" << std::endl;
oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk" << std::endl;
oss << "[Application(" << number_of_threads << "):\t\t:" << application_time_us / 1000 << " ms]" << std::endl;
oss << "[Background thread to finish\t:" << total_time_us / uint64_t(1000) << " ms]" << std::endl;
oss << "[Background thread to finish\t:" << total_time_us / uint64_t(1000 ) << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl;
oss << "[Background+Application: " << total_time_us / (number_of_threads * g_iterations) << " us]" << std::endl;

View File

@ -9,69 +9,83 @@
// through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE
#include "performance.h"
#include <algorithm>
#include <cmath>
#include <map>
#include <thread>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#if defined(G3LOG_PERFORMANCE)
const std::string title{
"G3LOG"};
const std::string title {
"G3LOG"
};
#elif defined(GOOGLE_GLOG_PERFORMANCE)
const std::string title{
"GOOGLE__GLOG"};
const std::string title {
"GOOGLE__GLOG"
};
#else
#error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined
#endif
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string g_path{
"./"};
const std::string g_path {
"./"
};
#else
const std::string g_path{
"/tmp/"};
const std::string g_path {
"/tmp/"
};
#endif
using namespace g3_test;
//
// OK: The code below isn't pretty but it works. Lots and lots of log entries
// to keep track of!
//
int main(int argc, char** argv) {
size_t number_of_threads{0};
if (argc == 2) {
int main(int argc, char** argv)
{
size_t number_of_threads {0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0) {
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::ostringstream thread_count_oss;
thread_count_oss << number_of_threads;
const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-WORST_LOG";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
const std::string g_measurement_bucket_dump = g_path + g_prefix_log_name + "_RESULT_buckets.txt";
const uint64_t us_to_ms{
1000};
const uint64_t us_to_s{
1000000};
const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-WORST_LOG";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
const std::string g_measurement_bucket_dump = g_path + g_prefix_log_name + "_RESULT_buckets.txt";
const uint64_t us_to_ms {
1000
};
const uint64_t us_to_s {
1000000
};
std::ostringstream oss;
oss << "\n\n"
<< title << " performance " << number_of_threads << " threads WORST (PEAK) times\n";
oss << "\n\n" << title << " performance " << number_of_threads << " threads WORST (PEAK) times\n";
oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const uint64_t xtra_margin{
2};
const uint64_t xtra_margin {
2
};
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;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
oss.str(""); // clear the stream
#if defined(G3LOG_PERFORMANCE)
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(g_prefix_log_name, g_path);
auto handle= worker->addDefaultLogger(g_prefix_log_name, g_path);
g3::initializeLogging(worker.get());
#elif defined(GOOGLE_GLOG_PERFORMANCE)
@ -83,27 +97,31 @@ int main(int argc, char** argv) {
// kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas
for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads_result[idx].reserve(g_iterations);
}
auto start_time = std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::ostringstream count;
count << idx + 1;
std::string thread_name = title + "_T" + count.str();
std::string thread_name = title + "_T" + count.str();
std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(measurePeakDuringLogWrites, thread_name, std::ref(threads_result[idx]));
}
// wait for thread finishing
for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto application_end_time = std::chrono::high_resolution_clock::now();
delete[] threads;
delete [] threads;
#if defined(G3LOG_PERFORMANCE)
worker.reset(); // will flush anything in the queue to file
worker.reset(); // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
#endif
@ -112,17 +130,17 @@ int main(int argc, char** argv) {
uint64_t application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
uint64_t total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
oss << "\n"
<< number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / us_to_s << " s] to write to disk" << std::endl;
oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / us_to_s << " s] to write to disk" << std::endl;
oss << "[Application(" << number_of_threads << "_threads+overhead time for measurement):\t" << application_time_us / us_to_ms << " ms]" << std::endl;
oss << "[Background thread to finish:\t\t\t\t" << total_time_us / us_to_ms << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl;
for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
std::vector<uint64_t>& t_result = threads_result[idx];
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::vector<uint64_t> &t_result = threads_result[idx];
uint64_t worstUs = (*std::max_element(t_result.begin(), t_result.end()));
oss << "[Application t" << idx + 1 << " worst took: " << worstUs / uint64_t(1000) << " ms (" << worstUs << " us)] " << std::endl;
oss << "[Application t" << idx + 1 << " worst took: " << worstUs / uint64_t(1000) << " ms (" << worstUs << " us)] " << std::endl;
}
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl;
@ -130,19 +148,21 @@ int main(int argc, char** argv) {
// now split the result in buckets of 10ms each so that it's obvious how the peaks go
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations * number_of_threads);
for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
std::vector<uint64_t>& t_result = threads_result[idx];
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
std::vector<uint64_t> &t_result = threads_result[idx];
all_measurements.insert(all_measurements.end(), t_result.begin(), t_result.end());
}
delete[] threads_result; // finally get rid of them
delete [] threads_result; // finally get rid of them
std::sort(all_measurements.begin(), all_measurements.end());
std::sort (all_measurements.begin(), all_measurements.end());
std::map<uint64_t, uint64_t> value_amounts;
std::map<uint64_t, uint64_t> value_amounts_for_0ms_bucket;
for (auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter) {
uint64_t value = (*iter) / us_to_ms; // convert to ms
++value_amounts[value]; // asuming uint64_t is default 0 when initialized
for (auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter)
{
uint64_t value = (*iter) / us_to_ms; // convert to ms
++value_amounts[value]; // asuming uint64_t is default 0 when initialized
if (0 == value) {
++value_amounts_for_0ms_bucket[*iter];
@ -152,12 +172,11 @@ int main(int argc, char** argv) {
oss.str("");
oss << "Number of values rounded to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl;
if (1 == value_amounts.size()) {
oss << "Format: bucket of us inside bucket0 for ms\nFormat:bucket_of_ms, number_of_values_in_bucket\n\n"
<< std::endl;
oss << "Format: bucket of us inside bucket0 for ms\nFormat:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
oss << "\n";
} else {
oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n"
<< std::endl;
}
else {
oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl;
}
std::cout << oss.str() << std::endl;
@ -172,10 +191,12 @@ int main(int argc, char** argv) {
oss << "\n\n***** Millisecond bucket measurement ****\n";
}
for (auto ms_bucket : value_amounts) {
for (auto ms_bucket : value_amounts)
{
oss << ms_bucket.first << "\t, " << ms_bucket.second << std::endl;
}
writeTextToFile(g_measurement_bucket_dump, oss.str(), kAppend, false);
writeTextToFile(g_measurement_bucket_dump, oss.str(), kAppend, false);
return 0;
}

View File

@ -11,48 +11,32 @@
# ============================================================================
# TEST OPTIONS: Turn OFF the ones that is of no interest to you
# ---- by default unit tests and g3log-FATAL-example are enabled.
# Performance tests are turned off by default since they were not tested on Windows.
# ---- by default all is OFF: except 'g3log-FATAL-example -----
# ---- the reason for this is that
# ----- 1) the performance tests were only thoroughly tested on Ubuntu, not windows-
# (g3log windows/linux, but Google's glog only on linux)
#
# 2) The unit test were tested windows/linux,. but must be unzipped
# before it can be "cmake'd" and compiled --- leaving it as OFF for now
# ============================================================================
# Unit test for g3log (cmake -DUSE_G3LOG_UNIT_TEST=ON ..)
option (ADD_G3LOG_UNIT_TEST "g3log unit tests" ON)
# remember to unzip gtest at g3log/3rdParty/gtest
option (ADD_G3LOG_UNIT_TEST "g3log unit tests" OFF)
# 4. create the unit tests for g3log --- ONLY TESTED THE UNIT TEST ON LINUX
# =========================
IF (ADD_G3LOG_UNIT_TEST)
# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build)
# The gtest/gmock targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories("${gtest_SOURCE_DIR}/include"
"${gmock_SOURCE_DIR}/include")
endif()
enable_testing()
set(DIR_UNIT_TEST ${g3log_SOURCE_DIR}/test_unit)
message( STATUS "-DADD_G3LOG_UNIT_TEST=ON" )
MESSAGE("-DADD_G3LOG_UNIT_TEST=ON")
set(GTEST_DIR ${g3log_SOURCE_DIR}/3rdParty/gtest/gtest-1.7.0)
set(GTEST_INCLUDE_DIRECTORIES ${GTEST_DIR}/include ${GTEST_DIR} ${GTEST_DIR}/src)
include_directories(${GTEST_INCLUDE_DIRECTORIES})
add_library(gtest_170_lib ${GTEST_DIR}/src/gtest-all.cc)
set_target_properties(gtest_170_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0")
enable_testing(true)
# obs see this: http://stackoverflow.com/questions/9589192/how-do-i-change-the-number-of-template-arguments-supported-by-msvcs-stdtupl
# and this: http://stackoverflow.com/questions/2257464/google-test-and-visual-studio-2010-rc
@ -62,14 +46,14 @@
SET(OS_SPECIFIC_TEST test_crashhandler_windows)
ENDIF(MSVC OR MINGW)
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})
SET(tests_to_run test_filechange test_io test_configuration test_concept_sink test_sink ${OS_SPECIFIC_TEST})
SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp)
include_directories(${DIR_UNIT_TEST})
FOREACH(test ${tests_to_run} )
SET(all_tests ${all_tests} ${DIR_UNIT_TEST}/${test}.cpp )
IF(${test} STREQUAL "test_filechange")
add_executable(test_filechange ${DIR_UNIT_TEST}/${test}.cpp ${helper})
add_executable(${test} ${DIR_UNIT_TEST}/${test}.cpp ${helper})
ELSE()
add_executable(${test} ${g3log_SOURCE_DIR}/test_main/test_main.cpp ${DIR_UNIT_TEST}/${test}.cpp ${helper})
ENDIF(${test} STREQUAL "test_filechange")
@ -79,22 +63,21 @@
IF( NOT(MSVC))
set_target_properties(${test} PROPERTIES COMPILE_FLAGS "-isystem -pthread ")
ENDIF( NOT(MSVC))
target_link_libraries(${test} g3log gtest_main)
add_test( ${test} ${test} )
target_link_libraries(${test} g3logger gtest_170_lib)
ENDFOREACH(test)
#
# Test for Linux, runtime loading of dynamic libraries
#
IF (NOT WIN32 AND NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") AND G3_SHARED_LIB)
IF (NOT WIN32 AND NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang"))
add_library(tester_sharedlib SHARED ${DIR_UNIT_TEST}/tester_sharedlib.h ${DIR_UNIT_TEST}/tester_sharedlib.cpp)
target_link_libraries(tester_sharedlib ${G3LOG_LIBRARY})
target_link_libraries(tester_sharedlib ${G3LOG_SHARED_LIBRARY})
add_executable(test_dynamic_loaded_shared_lib ${g3log_SOURCE_DIR}/test_main/test_main.cpp ${DIR_UNIT_TEST}/test_linux_dynamic_loaded_sharedlib.cpp)
add_executable(test_dynamic_loaded_shared_lib ../test_main/test_main.cpp ${DIR_UNIT_TEST}/test_linux_dynamic_loaded_sharedlib.cpp)
set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0")
set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0")
target_link_libraries(test_dynamic_loaded_shared_lib ${G3LOG_LIBRARY} -ldl gtest_main)
target_link_libraries(test_dynamic_loaded_shared_lib ${G3LOG_SHARED_LIBRARY} -ldl gtest_170_lib )
ENDIF()
ELSE()
message( STATUS "-DADD_G3LOG_UNIT_TEST=OFF" )
MESSAGE("-DADD_G3LOG_UNIT_TEST=OFF")
ENDIF (ADD_G3LOG_UNIT_TEST)

View File

@ -2,52 +2,53 @@
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include <thread>
#include <chrono>
#include <atomic>
#include "g3log/generated_definitions.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/sink.hpp"
#include "g3log/sinkhandle.hpp"
#include "g3log/sinkwrapper.hpp"
#include "testing_helpers.h"
#include "g3log/std2_make_unique.hpp"
#include "g3log/sink.hpp"
#include "g3log/sinkwrapper.hpp"
#include "g3log/sinkhandle.hpp"
#include "g3log/logmessage.hpp"
using namespace std;
using namespace std2;
using namespace testing_helpers;
class CoutSink {
stringstream buffer;
unique_ptr<ScopedOut> scope_ptr;
CoutSink() :
scope_ptr(std::make_unique<ScopedOut>(std::cout, &buffer)) {}
public:
CoutSink() : scope_ptr(std2::make_unique<ScopedOut>(std::cout, &buffer)) {}
public:
void clear() { buffer.str(""); }
std::string string() { return buffer.str(); }
void save(g3::LogMessageMover msg) { std::cout << msg.get().message(); }
virtual ~CoutSink() final {}
static std::unique_ptr<CoutSink> createSink() { return std::unique_ptr<CoutSink>(new CoutSink); }
static std::unique_ptr<CoutSink> createSink() { return std::unique_ptr<CoutSink>(new CoutSink);}
};
struct StringSink {
std::string raw;
void append(g3::LogMessageMover entry) { raw.append(entry.get().message()); }
void append(g3::LogMessageMover entry) { raw.append(entry.get().message());}
std::string string() {
return raw;
}
};
namespace {
typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
}
@ -55,7 +56,7 @@ namespace {
namespace g3 {
class Worker {
std::vector<SinkWrapperPtr> _container; // should be hidden in a pimple with a bg active object
std::vector<SinkWrapperPtr> _container; // should be hidden in a pimple with a bg active object
std::unique_ptr<kjellkod::Active> _bg;
void bgSave(std::string msg) {
@ -66,37 +67,40 @@ namespace g3 {
}
}
public:
Worker() :
_bg{
kjellkod::Active::createActive()} {
public:
Worker() : _bg {
kjellkod::Active::createActive()
}
{
}
~Worker() {
_bg->send([this] {
_container.clear();
});
_container.clear(); });
}
void save(std::string msg) {
_bg->send([this, msg] { bgSave(msg); });
}
template <typename T, typename DefaultLogCall>
std::unique_ptr<SinkHandle<T>> addSink(std::unique_ptr<T> unique, DefaultLogCall call) {
auto sink = std::make_shared<internal::Sink<T>>(std::move(unique), call);
auto add_sink_call = [this, sink] {
_container.push_back(sink);
};
template<typename T, typename DefaultLogCall>
std::unique_ptr< SinkHandle<T> > addSink(std::unique_ptr<T> unique, DefaultLogCall call) {
auto sink = std::make_shared < internal::Sink<T> > (std::move(unique), call);
auto add_sink_call = [this, sink] { _container.push_back(sink); };
auto wait_result = g3::spawn_task(add_sink_call, _bg.get());
wait_result.wait();
auto handle = std::make_unique<SinkHandle<T>>(sink);
auto handle = std2::make_unique< SinkHandle<T> >(sink);
return handle;
}
};
} // namespace g3
} // g3
using namespace g3;
using namespace g3::internal;
@ -118,22 +122,25 @@ TEST(ConceptSink, OneSink__VerifyMsgIn) {
ASSERT_NE(pos, std::string::npos);
}
TEST(ConceptSink, DualSink__VerifyMsgIn) {
Worker worker;
auto h1 = worker.addSink(CoutSink::createSink(), &CoutSink::save);
auto h2 = worker.addSink(std::make_unique<StringSink>(), &StringSink::append);
auto h2 = worker.addSink(std2::make_unique<StringSink>(), &StringSink::append);
worker.save("Hello World!");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto first = h1->call(&CoutSink::string);
auto second = h2->call(&StringSink::string);
ASSERT_EQ("Hello World!", first.get());
ASSERT_EQ("Hello World!", second.get());
}
TEST(ConceptSink, DeletedSink__Exptect_badweak_ptr___exception) {
auto worker = std::make_unique<Worker>();
auto worker = std2::make_unique<Worker>();
auto h1 = worker->addSink(CoutSink::createSink(), &CoutSink::save);
worker->save("Hello World!");
worker.reset();
@ -143,20 +150,20 @@ TEST(ConceptSink, DeletedSink__Exptect_badweak_ptr___exception) {
}
namespace {
using AtomicBooleanPtr = std::shared_ptr<std::atomic<bool>>;
using AtomicIntegerPtr = std::shared_ptr<std::atomic<int>>;
using BoolList = std::vector<AtomicBooleanPtr>;
using IntVector = std::vector<AtomicIntegerPtr>;
} // namespace
typedef std::shared_ptr<std::atomic<bool >> AtomicBoolPtr;
typedef std::shared_ptr<std::atomic<int >> AtomicIntPtr;
typedef vector<AtomicBoolPtr> BoolList;
typedef vector<AtomicIntPtr> IntVector;
}
TEST(ConceptSink, OneHundredSinks_part1) {
TEST(ConceptSink, OneHundredSinks) {
BoolList flags;
IntVector counts;
size_t NumberOfItems = 100;
for (size_t index = 0; index < NumberOfItems; ++index) {
flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared<atomic<int>>(0));
flags.push_back(make_shared < atomic<bool >> (false));
counts.push_back(make_shared < atomic<int >> (0));
}
{
@ -165,12 +172,12 @@ TEST(ConceptSink, OneHundredSinks_part1) {
for (auto& flag : flags) {
auto& count = counts[index++];
// ignore the handle
worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
worker->addSink(std2::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
}
worker->save("Hello to 100 receivers :)");
worker->save("Hello to 100 receivers :)");
}
// at the curly brace above the ScopedLogger will go out of scope and all the
// at the curly brace above the ScopedLogger will go out of scope and all the
// 100 logging receivers will get their message to exit after all messages are
// are processed
size_t index = 0;
@ -180,66 +187,80 @@ TEST(ConceptSink, OneHundredSinks_part1) {
ASSERT_TRUE(2 == count->load()) << ", count : " << (index - 1);
}
cout << "test one hundred sinks is finished\n";
cout << "test one hundred sinks is finished finished\n";
}
TEST(ConceptSink, OneHundredSinks_part2) {
using BoolPtrVector = std::vector<AtomicBooleanPtr>;
using IntPtrVector = vector<AtomicIntegerPtr>;
BoolPtrVector flags;
IntPtrVector counts;
int NumberOfItems = 100;
for (int index = 0; index < NumberOfItems; ++index) {
flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared<atomic<int>>(0));
}
/*
TEST(Sink, OneSink) {
AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared<atomic<int>>(0);
{
auto worker = std::make_shared<g3LogWorker>();
worker->addSink(std2::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
worker->save("this message should trigger an atomic increment at the sink");
{
auto worker = g3::LogWorker::createLogWorker();
size_t index = 0;
for (auto& flag : flags) {
auto& count = counts[index++];
// ignore the handle
worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
}
EXPECT_FALSE(flag->load());
EXPECT_TRUE(0 == count->load());
}
EXPECT_TRUE(flag->load());
EXPECT_TRUE(1 == count->load());
}
// 100 logs
for (int index = 0; index < NumberOfItems; ++index) {
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
message.get()->write().append("Hello to 100 receivers :)");
worker->save(message);
}
} // RAII exit
TEST(Sink, OneSinkWithHandleOutOfScope) {
AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared<atomic<int>>(0);
{
auto worker = std::make_shared<g3LogWorker>();
{
auto handle = worker->addSink(std2::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
}
EXPECT_FALSE(flag->load());
EXPECT_TRUE(0 == count->load());
worker->save("this message should trigger an atomic increment at the sink");
}
EXPECT_TRUE(flag->load());
EXPECT_TRUE(1 == count->load());
}
// at the curly brace above the ScopedLogger will go out of scope and all the
// 100 logging receivers will get their message to exit after all messages are
// are processed at the curly brace above the ScopedLogger will go out of scope and all the
// 100 logging receivers will get their message to exit after all messages are
// are processed
size_t index = 0;
for (auto& flag : flags) {
//Perfect det här testet triggar felet
typedef vector<AtomicBoolPtr> BoolPtrVector;
typedef vector<AtomicIntPtr> IntPtrVector;
TEST(Sink, OneHundredSinks) {
BoolPtrVector flags;
IntPtrVector counts;
size_t NumberOfItems = 100;
for (size_t index = 0; index < NumberOfItems; ++index) {
flags.push_back(make_shared < atomic<bool >> (false));
counts.push_back(make_shared < atomic<int >> (0));
}
{
auto worker = std::make_shared<g3LogWorker>();
size_t index = 0;
for (auto& flag : flags) {
auto& count = counts[index++];
EXPECT_TRUE(flag->load());
EXPECT_EQ(NumberOfItems, count->load());
}
// ignore the handle
worker->addSink(std2::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
}
worker->save("Hello to 100 receivers :)");
}
// at the curly brace above the ScopedLogger will go out of scope and all the
// 100 logging receivers will get their message to exit after all messages are
// are processed // at the curly brace above the ScopedLogger will go out of scope and all the
// 100 logging receivers will get their message to exit after all messages are
// are processed
size_t index = 0;
for (auto& flag : flags) {
auto& count = counts[index++];
EXPECT_TRUE(flag->load());
EXPECT_EQ(100, count->load());
cout << "test one hundred sinks is finished finished\n";
}
}
TEST(ConceptSink, OneSinkWithHandleOutOfScope) {
AtomicBooleanPtr flag = make_shared<atomic<bool>>(false);
AtomicIntegerPtr count = make_shared<atomic<int>>(0);
{
auto worker = g3::LogWorker::createLogWorker();
{
auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
}
EXPECT_FALSE(flag->load());
EXPECT_TRUE(0 == count->load());
*/
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
message.get()->write().append("this message should trigger an atomic increment at the sink");
worker->save(message);
}
EXPECT_TRUE(flag->load());
EXPECT_TRUE(1 == count->load());
}

View File

@ -0,0 +1,197 @@
/** ==========================================================================
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <chrono>
#include <thread>
#include <future>
#include <string>
#include <exception>
#include <functional>
#include <memory>
#include "g3log/time.hpp"
#include "g3log/future.hpp"
TEST(Configuration, LOG)
{ // ref: http://www.cplusplus.com/reference/clibrary/ctime/strftime/
// ref: http://en.cppreference.com/w/cpp/io/manip/put_time
// 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
// --- WARNING: The try/catch setup does NOT work,. but for fun and for fake-clarity I leave it
// --- For formatting options to std::put_time that are NOT YET implemented on Windows fatal errors/assert will occurr
// --- the last example is such an example.
try
{
std::cout << g3::localtime_formatted(g3::systemtime_now(), "%a %b %d %H:%M:%S %Y") << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << g3::localtime_formatted(g3::systemtime_now(), "%%Y/%%m/%%d %%H:%%M:%%S = %Y/%m/%d %H:%M:%S") << std::endl;
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
std::cerr << "Formatting options skipped due to VS2012, C++11 non-conformance for" << std::endl;
std::cerr << " some formatting options. The skipped code was:\n\t\t %EX %Ec, \n(see http://en.cppreference.com/w/cpp/io/manip/put_time for details)" << std::endl;
#else
std::cout << "C++11 new formatting options:\n" << g3::localtime_formatted(g3::systemtime_now(), "%%EX: %EX\n%%z: %z\n%%Ec: %Ec") << std::endl;
#endif
}
// This does not work. Other kinds of fatal exits (on Windows) seems to be used instead of exceptions
// Maybe a signal handler catch would be better? --- TODO: Make it better, both failing and correct
catch(...)
{
ADD_FAILURE() << "On this platform the library does not support given (C++11?) specifiers";
return;
}
ASSERT_TRUE(true); // no exception. all good
}
std::future<std::string> sillyFutureReturn()
{
std::packaged_task<std::string()> task([](){return std::string("Hello Future");}); // wrap the function
std::future<std::string> result = task.get_future(); // get a future
std::thread(std::move(task)).detach(); // launch on a thread
std::cout << "Waiting...";
result.wait();
return result; // already wasted
}
TEST(Configuration, FutureSilly)
{
std::string hello = sillyFutureReturn().get();
ASSERT_STREQ(hello.c_str(), "Hello Future");
}
struct MsgType
{
std::string msg_;
MsgType(std::string m): msg_(m){};
std::string msg(){return msg_;}
};
TEST(TestOf_CopyableCall, Expecting_SmoothSailing)
{
using namespace kjellkod;
const std::string str("Hello from struct");
MsgType type(str);
std::unique_ptr<Active> bgWorker(Active::createActive());
std::future<std::string> fstring =
g3::spawn_task(std::bind(&MsgType::msg, type), bgWorker.get());
ASSERT_STREQ(str.c_str(), fstring.get().c_str());
}
TEST(TestOf_CopyableLambdaCall, Expecting_AllFine)
{
using namespace kjellkod;
std::unique_ptr<Active> bgWorker(Active::createActive());
// lambda task
const std::string str_standalone("Hello from standalone");
auto msg_lambda=[=](){return (str_standalone+str_standalone);};
std::string expected(str_standalone+str_standalone);
auto fstring_standalone = g3::spawn_task(msg_lambda, bgWorker.get());
ASSERT_STREQ(expected.c_str(), fstring_standalone.get().c_str());
}
template<typename F>
std::future<typename std::result_of<F()>::type> ObsoleteSpawnTask(F f)
{
typedef typename std::result_of<F()>::type result_type;
typedef std::packaged_task<result_type()> task_type;
task_type task(std::move(f));
std::future<result_type> result = task.get_future();
std::vector<std::function<void()>> vec;
vec.push_back(g3::MoveOnCopy<task_type>(std::move(task)));
std::thread(std::move(vec.back())).detach();
result.wait();
return std::move(result);
}
TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString)
{
std::string str("Hello");
std::string expected(str+str);
auto msg_lambda=[=](){return (str+str);};
auto future_string = ObsoleteSpawnTask(msg_lambda);
ASSERT_STREQ(expected.c_str(), future_string.get().c_str());
}
// gcc thread example below
// tests code below copied from mail-list conversion between
// Lars Gullik Bjønnes and Jonathan Wakely
// http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
// --------------------------------------------------------------
namespace WORKING
{
using namespace g3;
#include <gtest/gtest.h>
#include <iostream>
#include <future>
#include <thread>
#include <vector>
std::vector<std::function<void()>> vec;
template<typename F>
std::future<typename std::result_of<F()>::type> spawn_task(F f)
{
typedef typename std::result_of<F()>::type result_type;
typedef std::packaged_task<result_type()> task_type;
task_type task(std::move(f));
std::future<result_type> res = task.get_future();
vec.push_back(
MoveOnCopy<task_type>(
std::move(task)));
std::thread([]()
{
auto task = std::move(vec.back());
vec.pop_back();
task();
}
).detach();
return std::move(res);
}
double get_res()
{
return 42.2;
}
std::string msg3(){return "msg3";}
} // WORKING
TEST(Yalla, Testar)
{
using namespace WORKING;
auto f = spawn_task(get_res);
std::cout << "Res = " << f.get() << std::endl;
auto f2 = spawn_task(msg3);
std::cout << "Res2 = " << f2.get() << std::endl;
ASSERT_TRUE(true);
}

View File

@ -1,149 +0,0 @@
/** ==========================================================================
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <chrono>
#include <exception>
#include <functional>
#include <future>
#include <memory>
#include <string>
#include <thread>
#include "g3log/future.hpp"
#include "g3log/time.hpp"
std::future<std::string> sillyFutureReturn() {
std::packaged_task<std::string()> task([]() { return std::string("Hello Future"); }); // wrap the function
std::future<std::string> result = task.get_future(); // get a future
std::thread(std::move(task)).detach(); // launch on a thread
std::cout << "Waiting...";
result.wait();
return result; // already wasted
}
TEST(Configuration, FutureSilly) {
std::string hello = sillyFutureReturn().get();
ASSERT_STREQ(hello.c_str(), "Hello Future");
}
struct MsgType {
std::string msg_;
MsgType(std::string m) :
msg_(m){};
std::string msg() { return msg_; }
};
TEST(TestOf_CopyableCall, Expecting_SmoothSailing) {
using namespace kjellkod;
const std::string str("Hello from struct");
MsgType type(str);
std::unique_ptr<Active> bgWorker(Active::createActive());
std::future<std::string> fstring =
g3::spawn_task(std::bind(&MsgType::msg, type), bgWorker.get());
ASSERT_STREQ(str.c_str(), fstring.get().c_str());
}
TEST(TestOf_CopyableLambdaCall, Expecting_AllFine) {
using namespace kjellkod;
std::unique_ptr<Active> bgWorker(Active::createActive());
// lambda task
const std::string str_standalone("Hello from standalone");
auto msg_lambda = [=]() {
return (str_standalone + str_standalone);
};
std::string expected(str_standalone + str_standalone);
auto fstring_standalone = g3::spawn_task(msg_lambda, bgWorker.get());
ASSERT_STREQ(expected.c_str(), fstring_standalone.get().c_str());
}
template <typename F>
std::future<std::invoke_result_t<F>> ObsoleteSpawnTask(F f) {
typedef std::invoke_result_t<F> result_type;
typedef std::packaged_task<result_type()> task_type;
task_type task(std::move(f));
std::future<result_type> result = task.get_future();
std::vector<std::function<void()>> vec;
vec.push_back(g3::MoveOnCopy<task_type>(std::move(task)));
std::thread(std::move(vec.back())).detach();
result.wait();
return std::move(result);
}
TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString) {
std::string str("Hello");
std::string expected(str + str);
auto msg_lambda = [=]() {
return (str + str);
};
auto future_string = ObsoleteSpawnTask(msg_lambda);
ASSERT_STREQ(expected.c_str(), future_string.get().c_str());
}
// gcc thread example below
// tests code below copied from mail-list conversion between
// Lars Gullik Bjønnes and Jonathan Wakely
// http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
// --------------------------------------------------------------
namespace WORKING {
using namespace g3;
#include <gtest/gtest.h>
#include <future>
#include <iostream>
#include <thread>
#include <vector>
std::vector<std::function<void()>> vec;
template <typename F>
std::future<std::invoke_result_t<F>> spawn_task(F f) {
typedef std::invoke_result_t<F> result_type;
typedef std::packaged_task<result_type()> task_type;
task_type task(std::move(f));
std::future<result_type> res = task.get_future();
vec.push_back(
MoveOnCopy<task_type>(
std::move(task)));
std::thread([]() {
auto task = std::move(vec.back());
vec.pop_back();
task();
}).detach();
return std::move(res);
}
double get_res() {
return 42.2;
}
std::string msg3() {
return "msg3";
}
} // namespace WORKING
TEST(Yalla, Testar) {
using namespace WORKING;
auto f = spawn_task(get_res);
ASSERT_EQ(42.2, f.get());
auto f2 = spawn_task(msg3);
ASSERT_EQ("msg3", f2.get());
ASSERT_TRUE(true);
}

View File

@ -6,11 +6,12 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#include "g3log/stacktrace_windows.hpp"
#include <windows.h>
#include "g3log/stacktrace_windows.hpp"
TEST(CrashHandler_Windows, ExceptionType) {
@ -40,4 +41,4 @@ TEST(CrashHandler_Windows, ExceptionType) {
EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_STACK_OVERFLOW), "EXCEPTION_STACK_OVERFLOW");
}
#endif // defined WIN32
#endif // defined WIN32

View File

@ -1,349 +0,0 @@
/** ==========================================================================re
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include "g3log/g3log.hpp"
#include "g3log/generated_definitions.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logworker.hpp"
#include "testing_helpers.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <exception>
#include <iomanip>
#include <memory>
#include <string>
#include <thread>
namespace {
const std::string log_directory = "./";
const std::string t_info = "test INFO ";
const std::string t_info2 = "test INFO 123";
const std::string t_debug = "test DEBUG ";
const std::string t_debug3 = "test DEBUG 1.123456";
const std::string t_warning = "test WARNING ";
const std::string t_warning3 = "test WARNING yello";
std::atomic<size_t> g_fatal_counter = {0};
void fatalCounter() {
++g_fatal_counter;
}
} // end anonymous namespace
using namespace testing_helpers;
TEST(LogTest, LOGF__FATAL) {
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
LOGF(FATAL, "This message should throw %d", 0);
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw 0")) << "\n****" << mockFatalMessage();
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
auto file_content = logger.resetAndRetrieveContent();
EXPECT_TRUE(verifyContent(file_content, "This message should throw 0")) << "\n****" << file_content;
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
}
#ifndef DISABLE_FATAL_SIGNALHANDLING
TEST(LogTest, FatalSIGTERM__UsingDefaultHandler) {
RestoreFileLogger logger(log_directory);
g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
raise(SIGTERM);
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
}
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
namespace {
std::atomic<size_t> customFatalCounter = {0};
std::atomic<int> lastEncounteredSignal = {0};
void customSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
lastEncounteredSignal.store(signal_number);
++customFatalCounter;
}
void installCustomSIGTERM() {
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = &customSignalHandler;
action.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &action, nullptr);
}
std::atomic<bool> oldSigTermCheck = {false};
void customOldSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
lastEncounteredSignal.store(signal_number);
oldSigTermCheck.store(true);
}
void installCustomOldSIGTERM() {
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = &customOldSignalHandler;
action.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &action, nullptr);
}
} // namespace
// Override of signal handling and testing of it should be fairly easy to port to windows
// ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_windows.cpp
// what is missing is the override of signals and custom installation of signals
// ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_unix.cpp
// functions: void restoreFatalHandlingToDefault()
// void overrideSetupSignals(const std::map<int, std::string> overrideSignals)
// void restoreSignalHandler(int signal_number)
//
// It would require some adding of unit test (see the test below)
// and good Windows experience. Since I am not currently working much on the Windows
// side I am reaching out to the community for this one:
//
//
// For the test to work the following code should be added in this test
//void customSignalHandler(int signal_number) {
// lastEncounteredSignal.store(signal_number);
// ++customFatalCounter;
//}
//
//void installCustomSIGTERM() {
// ASSERT_TRUE(SIG_ERR != signal(SIGTERM, customSignalHandler));
//}
TEST(LogTest, FatalSIGTERM__UsingCustomHandler) {
RestoreFileLogger logger(log_directory);
g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
installCustomSIGTERM();
g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}});
installCustomSIGTERM();
EXPECT_EQ(customFatalCounter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), 0);
raise(SIGTERM);
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
EXPECT_EQ(customFatalCounter.load(), size_t{1});
}
TEST(LogTest, FatalSIGTERM__VerifyingOldCustomHandler) {
RestoreFileLogger logger(log_directory);
g_fatal_counter.store(0);
customFatalCounter.store(0);
lastEncounteredSignal.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
installCustomOldSIGTERM();
g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGTERM, "SIGTERM"}});
g3::restoreSignalHandler(SIGTERM); // revert SIGTERM installation
EXPECT_EQ(customFatalCounter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), 0);
EXPECT_FALSE(oldSigTermCheck.load());
raise(SIGTERM);
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
EXPECT_TRUE(oldSigTermCheck.load());
}
#endif // DISABLE_FATAL_SIGNALHANDLING
#endif // !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
TEST(LogTest, LOG_preFatalLogging_hook) {
{
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
LOG(FATAL) << "This message is fatal";
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
}
{
// Now with no fatal pre-logging-hook
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
g_fatal_counter.store(0);
LOG(FATAL) << "This message is fatal";
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
}
}
TEST(LogTest, LOG_FATAL) {
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
LOG(FATAL) << "This message is fatal";
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal"))
<< "\ncontent: [[" << mockFatalMessage() << "]]";
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(file_content, "This message is fatal"));
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
}
TEST(LogTest, LOGF_IF__FATAL) {
RestoreFileLogger logger(log_directory);
EXPECT_FALSE(mockFatalWasCalled());
LOGF_IF(FATAL, (2 < 3), "This message %s be worse", "could");
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")) << "\n"
<< mockFatalMessage();
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message could be worse"));
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
EXPECT_TRUE(verifyContent(file_content, "This message could be worse"));
}
TEST(LogTest, LOG_IF__FATAL) {
RestoreFileLogger logger(log_directory);
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
EXPECT_FALSE(mockFatalWasCalled());
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw. xyz ";
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw. xyz "));
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
EXPECT_TRUE(verifyContent(file_content, "This message should throw. xyz "));
}
TEST(LogTest, LOG_IF__FATAL__NO_THROW) {
RestoreFileLogger logger(log_directory);
LOG_IF(FATAL, (2 > 3)) << "This message%sshould NOT throw";
ASSERT_FALSE(mockFatalWasCalled());
}
// CHECK_F
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory);
EXPECT_TRUE(mockFatalMessage().empty());
EXPECT_FALSE(mockFatalWasCalled());
CHECK(1 == 2);
EXPECT_FALSE(mockFatalMessage().empty());
EXPECT_TRUE(mockFatalWasCalled());
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT")) << "**** " << mockFatalMessage();
}
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory);
std::string msg = "This message is added to throw %s and %s";
std::string arg1 = "message";
std::string arg3 = "log";
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg3.c_str());
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
}
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory);
std::string msg = "This message is added to throw message and log";
CHECK(1 >= 2) << msg;
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
EXPECT_TRUE(verifyContent(file_content, msg));
}
TEST(CHECK, CHECK_ThatWontThrow) {
RestoreFileLogger logger(log_directory);
std::string msg = "This %s should never appear in the %s";
std::string msg3 = "This message should never appear in the log";
CHECK(1 == 1);
CHECK_F(1 == 1, msg.c_str(), "message", "log");
logger.reset();
EXPECT_FALSE(mockFatalWasCalled());
std::string file_content = readFileToText(logger.logFile());
EXPECT_FALSE(verifyContent(file_content, msg3));
EXPECT_FALSE(verifyContent(mockFatalMessage(), msg3));
}
TEST(CHECK, CHECK_runtimeError) {
RestoreFileLogger logger(log_directory);
g3::setFatalExitHandler([](g3::FatalMessagePtr msg) {
throw std::runtime_error("fatal test handler");
});
class dynamic_int_array {
std::unique_ptr<int[]> data_;
const int size_;
public:
explicit dynamic_int_array(int size) :
data_{std::make_unique<int[]>(size)},
size_(size) {}
int& at(int i) {
CHECK(i < size_);
// unreachable if i >= size_
return data_[i];
}
};
dynamic_int_array arr{3};
EXPECT_THROW(arr.at(3) = 1, std::runtime_error);
}
// see also test_io -- AddNonFatal
TEST(CustomLogLevels, AddFatal) {
RestoreFileLogger logger(log_directory);
const LEVELS DEADLY{FATAL.value + 1, {"DEADLY"}};
EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
g_fatal_counter.store(0);
ASSERT_FALSE(mockFatalWasCalled());
g3::setFatalPreLoggingHook(fatalCounter);
#ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::addLogLevel(DEADLY, true);
#endif
// clang-format off
LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset();
ASSERT_TRUE(mockFatalWasCalled());
EXPECT_EQ(size_t{1}, g_fatal_counter.load());
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "DEADLY [test_fatal.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n"
<< expected;
g_fatal_counter.store(0); // restore
}

View File

@ -6,12 +6,15 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <fstream>
#include <future>
#include <memory>
#include <queue>
#include <fstream>
#include <string>
#include <memory>
#include <future>
#include <queue>
#include <thread>
#include "g3log/g3log.hpp"
@ -20,7 +23,8 @@
using namespace testing_helpers;
namespace { // anonymous
namespace { // anonymous
const char* name_path_1 = "./(some_fake_DirectoryOrName_1_)";
const std::string kReplaceFileName = "(ReplaceLogFile)";
g3::LogWorker* g_logger_ptr = nullptr;
@ -49,8 +53,7 @@ namespace { // anonymous
std::string setLogName(std::string new_file_to_create, std::string logger_id = "g3log") {
auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create, logger_id);
auto new_log = future_new_log.get();
if (!new_log.empty())
g_cleaner_ptr->addLogToClean(new_log);
if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
return new_log;
}
@ -58,7 +61,7 @@ namespace { // anonymous
return g_filesink_handler->call(&g3::FileSink::fileName).get();
}
} // namespace
} // anonymous
TEST(TestOf_GetFileName, Expecting_ValidLogFile) {
@ -76,7 +79,7 @@ TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed) {
TEST(TestOf_ChangingLogFile_Id, Expecting_NewLogFileUsed1) {
auto old_log = getLogName();
setLogNameAndAddCount(name_path_1);
std::string name = setLogNameAndAddCount(name_path_1);
auto new_log = setLogName("foo", "new_logger_id");
ASSERT_NE(old_log, new_log);
std::string new_name = getLogName();
@ -87,7 +90,7 @@ TEST(TestOf_ChangingLogFile_Id, Expecting_NewLogFileUsed1) {
TEST(TestOf_ChangingLogFile_NoId, Expecting_NewLogFileUsed2) {
auto old_log = getLogName();
setLogNameAndAddCount(name_path_1);
std::string name = setLogNameAndAddCount(name_path_1);
auto new_log = setLogName("foo", "");
ASSERT_NE(old_log, new_log);
std::string new_name = getLogName();
@ -98,8 +101,7 @@ TEST(TestOf_ChangingLogFile_NoId, Expecting_NewLogFileUsed2) {
TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) {
auto old_log = g_filesink_handler->call(&g3::FileSink::fileName).get();
if (!old_log.empty())
g_cleaner_ptr->addLogToClean(old_log);
if (!old_log.empty()) g_cleaner_ptr->addLogToClean(old_log);
LOG(INFO) << "SoManyThreadsAllDoingChangeFileName";
std::vector<std::thread> threads;
@ -119,27 +121,27 @@ TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) {
TEST(TestOf_IllegalLogFileName, Expecting_NoChangeToOriginalFileName) {
std::string original = getLogName();
auto perhaps_a_name = setLogName("XY:/"); // does not exist
auto perhaps_a_name = setLogName("XY:/"); // does not exist
ASSERT_TRUE(perhaps_a_name.empty());
std::string post_illegal = getLogName();
ASSERT_STREQ(original.c_str(), post_illegal.c_str());
}
TEST(TestOf_SinkHandleDifferentId, Expecting_DifferentId) {
auto sink = std::make_unique<g3::FileSink>("AnotherLogFile", name_path_1, "logger_id");
auto sink = std2::make_unique<g3::FileSink>("AnotherLogFile", name_path_1, "logger_id");
auto name = sink->fileName();
ASSERT_STREQ(name.substr(0, 26).c_str(), "./AnotherLogFile.logger_id");
ASSERT_STREQ( name.substr(0, 26).c_str(), "./AnotherLogFile.logger_id");
g_cleaner_ptr->addLogToClean(name);
}
TEST(TestOf_LegalLogFileNam, With_parenthesis) {
TEST(TestOf_LegalLogFileNam, With_parenthesis) {
std::string original = getLogName();
auto perhaps_a_name = setLogName("(test)"); // does not exist
EXPECT_NE(original, perhaps_a_name);
auto perhaps_a_name = setLogName("(test)"); // does not exist
std::string post_legal = getLogName();
EXPECT_TRUE(std::string::npos != post_legal.find("(test)")) << "filename was: " << post_legal;
}
int main(int argc, char* argv[]) {
LogFileCleaner cleaner;
g_cleaner_ptr = &cleaner;
@ -159,6 +161,7 @@ int main(int argc, char* argv[]) {
std::cout << "log file at: " << last_log_file << std::endl;
cleaner.addLogToClean(last_log_file);
g3::initializeLogging(g_logger_ptr);
LOG(INFO) << "test_filechange demo*" << std::endl;

View File

@ -8,19 +8,17 @@
#include <gtest/gtest.h>
#include "g3log/g3log.hpp"
#include "g3log/generated_definitions.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logworker.hpp"
#include "testing_helpers.h"
#include "g3log/loglevels.hpp"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <exception>
#include <iomanip>
#include <memory>
#include <string>
#include <cstdio>
#include <thread>
#include <chrono>
#include <exception>
#include <algorithm>
namespace {
const std::string log_directory = "./";
@ -36,10 +34,12 @@ namespace {
++g_fatal_counter;
}
} // end anonymous namespace
} // end anonymous namespace
using namespace testing_helpers;
/// THIS MUST BE THE FIRST UNIT TEST TO RUN! If any unit test run before this
/// one then it could fail. For dynamic levels all levels are turned on only AT
/// instantiation so we do different test for dynamic logging levels
@ -49,12 +49,12 @@ using namespace testing_helpers;
#ifdef G3_DYNAMIC_LOGGING
TEST(Initialization, No_Logger_Initialized___LevelsAreONByDefault) {
EXPECT_FALSE(g3::internal::isLoggingInitialized());
EXPECT_TRUE(g3::logLevel(G3LOG_DEBUG));
EXPECT_TRUE(g3::logLevel(DEBUG));
EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(WARNING));
EXPECT_TRUE(g3::logLevel(FATAL));
EXPECT_EQ(G3LOG_DEBUG.value, 100);
EXPECT_EQ(INFO.value, 300);
EXPECT_EQ(DEBUG.value, 0);
EXPECT_EQ(INFO.value, 100);
EXPECT_EQ(WARNING.value, 500);
EXPECT_EQ(FATAL.value, 1000);
EXPECT_EQ(g3::internal::CONTRACT.value, 2000);
@ -64,23 +64,23 @@ TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKi
EXPECT_FALSE(g3::internal::isLoggingInitialized());
EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(FATAL));
EXPECT_TRUE(g3::logLevel(G3LOG_DEBUG));
EXPECT_TRUE(g3::logLevel(DEBUG));
EXPECT_TRUE(g3::logLevel(WARNING));
std::string err_msg1 = "Hey. I am not instantiated but I still should not crash. (I am g3log)";
std::string err_msg1 = "Hey. I am not instantiated but I still should not crash. (I am g3logger)";
std::string err_msg3_ignored = "This uninitialized message should be ignored";
try {
LOG(INFO) << err_msg1; // nothing happened. level not ON
LOG(INFO) << err_msg3_ignored; // nothing happened. level not ON
LOG(INFO) << err_msg1; // nothing happened. level not ON
LOG(INFO) << err_msg3_ignored; // nothing happened. level not ON
} catch (std::exception& e) {
ADD_FAILURE() << "Should never have thrown even if it is not instantiated. Ignored exception: " << e.what();
}
RestoreFileLogger logger(log_directory); // now instantiate the logger
RestoreFileLogger logger(log_directory); // now instantiate the logger
std::string good_msg1 = "This message could have pulled in the uninitialized_call message";
LOG(INFO) << good_msg1;
auto content = logger.resetAndRetrieveContent(); // this synchronizes with the LOG(INFO) call if debug level would be ON.
auto content = logger.resetAndRetrieveContent(); // this synchronizes with the LOG(INFO) call if debug level would be ON.
ASSERT_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]";
ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]";
ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]";
@ -90,9 +90,9 @@ TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKi
EXPECT_FALSE(g3::internal::isLoggingInitialized());
EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(FATAL));
EXPECT_TRUE(g3::logLevel(G3LOG_DEBUG));
EXPECT_TRUE(g3::logLevel(DEBUG));
EXPECT_TRUE(g3::logLevel(WARNING));
std::string err_msg1 = "Hey. I am not instantiated but I still should not crash. (I am g3log)";
std::string err_msg1 = "Hey. I am not instantiated but I still should not crash. (I am g3logger)";
std::string err_msg3_ignored = "This uninitialized message should be ignored";
try {
@ -103,16 +103,16 @@ TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKi
ADD_FAILURE() << "Should never have thrown even if it is not instantiated: " << e.what();
}
RestoreFileLogger logger(log_directory); // now instantiate the logger
RestoreFileLogger logger(log_directory); // now instantiate the logger
std::string good_msg1 = "This message will pull in also the uninitialized_call message";
LOG(INFO) << good_msg1;
auto content = logger.resetAndRetrieveContent(); // this synchronizes with the LOG(INFO) call.
auto content = logger.resetAndRetrieveContent(); // this synchronizes with the LOG(INFO) call.
ASSERT_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]";
ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]";
ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]";
}
#endif // #ifdef G3_DYNAMIC_LOGGING
#endif // #ifdef G3_DYNAMIC_LOGGING
TEST(Basics, Levels_StdFind) {
std::vector<LEVELS> levels = {INFO, WARNING, FATAL};
@ -138,6 +138,7 @@ TEST(Basics, Levels_StdFind) {
EXPECT_FALSE(wasNotFoundIterator != levels.end());
}
TEST(Basics, Levels_Operator) {
auto info = INFO;
auto warning = WARNING;
@ -153,16 +154,12 @@ TEST(Basics, Shutdown) {
std::string file_content;
{
RestoreFileLogger logger(log_directory);
LOG(INFO) << "First message buffered, then flushed";
LOG(INFO) << "Second message still in the buffer";
LOG(INFO) << "Not yet shutdown. This message should make it";
logger.reset(); // force flush of logger (which will trigger a shutdown)
logger.reset(); // force flush of logger (which will trigger a shutdown)
LOG(INFO) << "Logger is shutdown,. this message will not make it (but it's safe to try)";
file_content = readFileToText(logger.logFile()); // logger is already reset
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
file_content = readFileToText(logger.logFile()); // logger is already reset
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
EXPECT_TRUE(verifyContent(file_content, "First message buffered, then flushed"));
EXPECT_TRUE(verifyContent(file_content, "Second message still in the buffer"));
EXPECT_TRUE(verifyContent(file_content, "Not yet shutdown. This message should make it"));
EXPECT_FALSE(verifyContent(file_content, "Logger is shutdown,. this message will not make it (but it's safe to try)"));
}
@ -172,11 +169,11 @@ TEST(Basics, Shutdownx2) {
{
RestoreFileLogger logger(log_directory);
LOG(INFO) << "Not yet shutdown. This message should make it";
logger.reset(); // force flush of logger (which will trigger a shutdown)
g3::internal::shutDownLogging(); // already called in reset, but safe to call again
logger.reset(); // force flush of logger (which will trigger a shutdown)
g3::internal::shutDownLogging(); // already called in reset, but safe to call again
LOG(INFO) << "Logger is shutdown,. this message will not make it (but it's safe to try)";
file_content = readFileToText(logger.logFile()); // already reset
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
file_content = readFileToText(logger.logFile()); // already reset
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
EXPECT_TRUE(verifyContent(file_content, "Not yet shutdown. This message should make it"));
EXPECT_FALSE(verifyContent(file_content, "Logger is shutdown,. this message will not make it (but it's safe to try)"));
@ -190,10 +187,9 @@ TEST(Basics, ShutdownActiveLogger) {
EXPECT_TRUE(g3::internal::shutDownLoggingForActiveOnly(logger._scope->get()));
LOG(INFO) << "Logger is shutdown,. this message will not make it (but it's safe to try)";
file_content = logger.resetAndRetrieveContent();
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
EXPECT_TRUE(verifyContent(file_content, "Not yet shutdown. This message should make it")) << "\n\n\n***************************\n"
<< file_content;
EXPECT_TRUE(verifyContent(file_content, "Not yet shutdown. This message should make it")) << "\n\n\n***************************\n" << file_content;
EXPECT_FALSE(verifyContent(file_content, "Logger is shutdown,. this message will not make it (but it's safe to try)"));
}
@ -206,12 +202,13 @@ TEST(Basics, DoNotShutdownActiveLogger) {
EXPECT_FALSE(g3::internal::shutDownLoggingForActiveOnly(duplicateLogWorker.get()));
LOG(INFO) << "Logger is (NOT) shutdown,. this message WILL make it";
file_content = logger.resetAndRetrieveContent();
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
EXPECT_TRUE(verifyContent(file_content, "Not yet shutdown. This message should make it"));
EXPECT_TRUE(verifyContent(file_content, "Logger is (NOT) shutdown,. this message WILL make it")) << file_content;
}
TEST(LOGTest, LOG) {
std::string file_content;
{
@ -219,17 +216,20 @@ TEST(LOGTest, LOG) {
EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(FATAL));
LOG(INFO) << "test LOG(INFO)";
logger.reset(); // force flush of logger
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
EXPECT_TRUE(verifyContent(file_content, "test LOG(INFO)"));
EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(FATAL));
}
// printf-type log
TEST(LogTest, LOG_F) {
std::string file_content;
{
@ -237,227 +237,422 @@ TEST(LogTest, LOG_F) {
std::cout << "logfilename: " << logger.logFile() << std::flush << std::endl;
LOGF(INFO, std::string(t_info + "%d").c_str(), 123);
LOGF(G3LOG_DEBUG, std::string(t_debug + "%f").c_str(), 1.123456);
LOGF(DEBUG, std::string(t_debug + "%f").c_str(), 1.123456);
LOGF(WARNING, std::string(t_warning + "%s").c_str(), "yello");
logger.reset(); // force flush of logger
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_TRUE(verifyContent(file_content, t_debug3));
ASSERT_TRUE(verifyContent(file_content, t_warning3));
}
// stream-type log
TEST(LogTest, LOG) {
std::string file_content;
{
RestoreFileLogger logger(log_directory);
LOG(INFO) << t_info << 123;
LOG(G3LOG_DEBUG) << t_debug << std::setprecision(7) << 1.123456f;
LOG(DEBUG) << t_debug << std::setprecision(7) << 1.123456f;
LOG(WARNING) << t_warning << "yello";
logger.reset(); // force flush of logger
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_TRUE(verifyContent(file_content, t_debug3));
ASSERT_TRUE(verifyContent(file_content, t_warning3));
}
TEST(LogTest, LOG_after_if) {
std::string file_content;
{
RestoreFileLogger logger(log_directory);
if (false == file_content.empty())
LOG(INFO) << "This-should-NOT-show-up";
else
LOG(INFO) << "This-should-show-up";
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
}
ASSERT_FALSE(verifyContent(file_content, "This-should-NOT-show-up"));
ASSERT_TRUE(verifyContent(file_content, "This-should-show-up"));
}
TEST(LogTest, LOG_after_if_with_parentesis) {
std::string file_content;
{
RestoreFileLogger logger(log_directory);
if (false == file_content.empty()) {
LOG(INFO) << "This-should-NOT-show-up";
} else {
LOG(INFO) << "This-should-show-up";
}
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
}
ASSERT_FALSE(verifyContent(file_content, "This-should-NOT-show-up"));
ASSERT_TRUE(verifyContent(file_content, "This-should-show-up"));
}
TEST(LogTest, LOG_F_IF) {
std::string file_content;
{
RestoreFileLogger logger(log_directory);
LOGF_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123);
LOGF_IF(G3LOG_DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456);
logger.reset(); // force flush of logger
LOGF_IF(DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456);
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_FALSE(verifyContent(file_content, t_debug3));
}
TEST(LogTest, LOG_IF) {
std::string file_content;
{
RestoreFileLogger logger(log_directory);
LOG_IF(INFO, (2 == 2)) << t_info << 123;
LOG_IF(G3LOG_DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f;
logger.reset(); // force flush of logger
LOG_IF(DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f;
logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile());
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
}
EXPECT_TRUE(verifyContent(file_content, t_info2));
EXPECT_FALSE(verifyContent(file_content, t_debug3));
}
TEST(LogTest, LOGF__FATAL) {
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
LOGF(FATAL, "This message should throw %d", 0);
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw 0")) << "\n****" << mockFatalMessage();
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
auto file_content = logger.resetAndRetrieveContent();
EXPECT_TRUE(verifyContent(file_content, "This message should throw 0")) << "\n****" << file_content;
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
}
TEST(LogTest, FatalSIGTERM__UsingDefaultHandler) {
RestoreFileLogger logger(log_directory);
g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
raise(SIGTERM);
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
}
namespace {
std::atomic<size_t> customFatalCounter = {0};
std::atomic<int> lastEncounteredSignal = {0};
void customSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
lastEncounteredSignal.store(signal_number);
++customFatalCounter;
}
void installCustomSIGTERM () {
struct sigaction action;
memset(&action, 0, sizeof (action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = &customSignalHandler;
action.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &action, nullptr);
}
} // anonymous
TEST(LogTest, FatalSIGTERM__UsingCustomHandler) {
RestoreFileLogger logger(log_directory);
g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
installCustomSIGTERM();
g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}});
installCustomSIGTERM();
EXPECT_EQ(customFatalCounter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), 0);
raise(SIGTERM);
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
EXPECT_EQ(customFatalCounter.load(), size_t{1});
}
TEST(LogTest, LOG_preFatalLogging_hook) {
{
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter);
LOG(FATAL) << "This message is fatal";
logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{1});
}
{
// Now with no fatal pre-logging-hook
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
g_fatal_counter.store(0);
LOG(FATAL) << "This message is fatal";
EXPECT_EQ(g_fatal_counter.load(), size_t{0});
}
}
TEST(LogTest, LOG_FATAL) {
RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled());
LOG(FATAL) << "This message is fatal";
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal"))
<< "\ncontent: [[" << mockFatalMessage() << "]]";
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(file_content, "This message is fatal"));
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
}
TEST(LogTest, LOGF_IF__FATAL) {
RestoreFileLogger logger(log_directory);
EXPECT_FALSE(mockFatalWasCalled());
LOGF_IF(FATAL, (2 < 3), "This message %s be worse", "could");
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")) << "\n" << mockFatalMessage();
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message could be worse"));
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
EXPECT_TRUE(verifyContent(file_content, "This message could be worse"));
}
TEST(LogTest, LOG_IF__FATAL) {
RestoreFileLogger logger(log_directory);
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
EXPECT_FALSE(mockFatalWasCalled());
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw. xyz ";
EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message should throw. xyz "));
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(file_content, "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "FATAL"));
EXPECT_TRUE(verifyContent(file_content, "This message should throw. xyz "));
}
TEST(LogTest, LOG_IF__FATAL__NO_THROW) {
RestoreFileLogger logger(log_directory);
LOG_IF(FATAL, (2 > 3)) << "This message%sshould NOT throw";
ASSERT_FALSE(mockFatalWasCalled());
}
// CHECK_F
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory);
EXPECT_TRUE(mockFatalMessage().empty());
EXPECT_FALSE(mockFatalWasCalled());
CHECK(1 == 2);
EXPECT_FALSE(mockFatalMessage().empty());
EXPECT_TRUE(mockFatalWasCalled());
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT")) << "**** " << mockFatalMessage();
}
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory);
std::string msg = "This message is added to throw %s and %s";
std::string msg3 = "This message is added to throw message and log";
std::string arg1 = "message";
std::string arg3 = "log";
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg3.c_str());
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
}
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory);
std::string msg = "This message is added to throw %s and %s";
std::string msg3 = "This message is added to throw message and log";
std::string arg1 = "message";
std::string arg3 = "log";
CHECK(1 >= 2) << msg3;
logger.reset();
std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
EXPECT_TRUE(verifyContent(file_content, msg3));
}
TEST(CHECK, CHECK_ThatWontThrow) {
RestoreFileLogger logger(log_directory);
std::string msg = "This %s should never appear in the %s";
std::string msg3 = "This message should never appear in the log";
std::string arg1 = "message";
std::string arg3 = "log";
CHECK(1 == 1);
CHECK_F(1 == 1, msg.c_str(), "message", "log");
logger.reset();
EXPECT_FALSE(mockFatalWasCalled());
std::string file_content = readFileToText(logger.logFile());
EXPECT_FALSE(verifyContent(file_content, msg3));
EXPECT_FALSE(verifyContent(mockFatalMessage(), msg3));
}
TEST(CustomLogLevels, AddANonFatal) {
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO{WARNING.value + 1, {"MY_INFO_LEVEL"}};
const LEVELS MYINFO {WARNING.value + 1, {"MY_INFO_LEVEL"}};
#ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::addLogLevel(MYINFO, true);
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
#endif
// clang-format off
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(__FUNCTION__) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n"
<< expected;
<< "\n\nExpected: \n" << expected;
}
TEST(CustomLogLevels, AddFatal) {
RestoreFileLogger logger(log_directory);
const LEVELS DEADLY {FATAL.value + 1, {"DEADLY"}};
EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
g_fatal_counter.store(0);
ASSERT_FALSE(mockFatalWasCalled());
g3::setFatalPreLoggingHook(fatalCounter);
#ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::setLogLevel(DEADLY, true);
#endif
LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__;
logger.reset();
ASSERT_TRUE(mockFatalWasCalled());
EXPECT_EQ(size_t{1}, g_fatal_counter.load());
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "DEADLY [test_io.cpp->" + std::string(__FUNCTION__) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected;
g_fatal_counter.store(0); // restore
}
#ifdef G3_DYNAMIC_LOGGING
namespace {
// Restore dynamic levels if turned off
struct RestoreDynamicLoggingLevels {
RestoreDynamicLoggingLevels(){};
RestoreDynamicLoggingLevels() {
};
~RestoreDynamicLoggingLevels() {
g3::only_change_at_initialization::reset();
g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false);
g3::only_change_at_initialization::addLogLevel(INFO, false);
g3::only_change_at_initialization::addLogLevel(WARNING, false);
g3::only_change_at_initialization::addLogLevel(FATAL, false);
g3::only_change_at_initialization::setLogLevel(DEBUG, false);
g3::only_change_at_initialization::setLogLevel(INFO, false);
g3::only_change_at_initialization::setLogLevel(WARNING, false);
g3::only_change_at_initialization::setLogLevel(FATAL, false);
}
};
} // namespace
} // anonymous
TEST(CustomLogLevels, AddANonFatal__ThenReset) {
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO{WARNING.value + 2, {"MY_INFO_LEVEL"}};
const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
EXPECT_FALSE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::addLogLevel(MYINFO, true);
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
EXPECT_TRUE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::reset();
EXPECT_FALSE(g3::logLevel(MYINFO));
}
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue1) {
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO{WARNING.value + 2, {"MY_INFO_LEVEL"}};
// clang-format off
const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp:" + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n"
<< expected << "\nLevels:\n"
<< g3::log_levels::to_string();
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels();
}
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2) {
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO{WARNING.value + 2, {"MY_INFO_LEVEL"}};
const LEVELS MYINFO {WARNING.value + 2, {"MY_INFO_LEVEL"}};
EXPECT_FALSE(g3::logLevel(MYINFO));
// clang-format off
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp:" + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n"
<< expected << "\nLevels:\n"
<< g3::log_levels::to_string();
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::only_change_at_initialization::printLevels();
}
TEST(CustomLogLevels, AddANonFatal__DidtAddItToEnabledValue) {
RestoreFileLogger logger(log_directory);
const LEVELS MYINFO{WARNING.value + 3, {"MY_INFO_LEVEL"}};
g3::only_change_at_initialization::addLogLevel(MYINFO, true);
// clang-format off
const LEVELS MYINFO {WARNING.value + 3, {"MY_INFO_LEVEL"}};
g3::only_change_at_initialization::setLogLevel(MYINFO, true);
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset();
std::string file_content = readFileToText(logger.logFile());
std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(__FUNCTION__) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n"
<< expected;
<< "\n\nExpected: \n" << expected;
}
TEST(DynamicLogging, DynamicLogging_IS_ENABLED) {
RestoreDynamicLoggingLevels raiiLevelRestore;
ASSERT_TRUE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(DEBUG));
ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::setLogLevel(DEBUG, false);
ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(INFO, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
g3::only_change_at_initialization::setLogLevel(INFO, false);
ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(WARNING, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
g3::only_change_at_initialization::setLogLevel(WARNING, false);
ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_FALSE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
g3::only_change_at_initialization::setLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(DEBUG));
ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_FALSE(g3::logLevel(WARNING));
ASSERT_FALSE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
ASSERT_FALSE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
}
TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
{
RestoreFileLogger logger(log_directory);
ASSERT_TRUE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(DEBUG));
ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL));
@ -467,31 +662,33 @@ TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
std::string msg_debugOn = "This %s SHOULD appear in the %s";
std::string msg_debugOff = "This message should never appear in the log";
std::string msg_info1 = "This info msg log";
try {
{
RestoreFileLogger logger(log_directory);
LOGF(G3LOG_DEBUG, msg_debugOn.c_str(), "msg", "log");
LOGF(DEBUG, msg_debugOn.c_str(), "msg", "log");
auto content = logger.resetAndRetrieveContent();
ASSERT_TRUE(verifyContent(content, "This msg SHOULD appear in the log")) << "Content: [" << content << "]";
}
{
RestoreFileLogger logger(log_directory);
g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false);
EXPECT_FALSE(g3::logLevel(G3LOG_DEBUG));
LOG(G3LOG_DEBUG) << msg_debugOff;
g3::only_change_at_initialization::setLogLevel(DEBUG, false);
EXPECT_FALSE(g3::logLevel(DEBUG));
LOG(DEBUG) << msg_debugOff;
auto content = logger.resetAndRetrieveContent();
ASSERT_FALSE(verifyContent(content, "This message should never appear in the log")) << "Content: [" << content << "]";
}
} catch (std::exception const& e) {
ADD_FAILURE() << "Should never have thrown: " << e.what();
std::cerr << e.what() << std::endl;
ADD_FAILURE() << "Should never have thrown";
}
}
TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
RestoreFileLogger logger(log_directory);
RestoreDynamicLoggingLevels raiiLevelRestore;
ASSERT_TRUE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(DEBUG));
ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL));
@ -506,13 +703,15 @@ TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
clearMockFatal();
EXPECT_FALSE(mockFatalWasCalled());
g3::only_change_at_initialization::addLogLevel(FATAL, false);
g3::only_change_at_initialization::setLogLevel(FATAL, false);
std::string msg3 = "This is NOT fatal (not crash, since it is unit test. FATAL is disabled";
LOG(FATAL) << msg3;
EXPECT_FALSE(mockFatalWasCalled());
EXPECT_TRUE(mockFatalMessage().empty());
}
TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disabled) {
RestoreFileLogger logger(log_directory);
RestoreDynamicLoggingLevels raiiLevelRestore;
@ -528,16 +727,22 @@ TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disab
EXPECT_FALSE(mockFatalWasCalled());
// Disable also CHECK calls
g3::only_change_at_initialization::addLogLevel(FATAL, false);
g3::only_change_at_initialization::setLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(FATAL));
LOG(FATAL) << msg3;
EXPECT_FALSE(mockFatalWasCalled());
}
#else
TEST(DynamicLogging, DynamicLogging_IS_NOT_ENABLED) {
ASSERT_TRUE(g3::logLevel(G3LOG_DEBUG));
//g3::addLogLevel(G3LOG_DEBUG, false); this line will not compile since G3_DYNAMIC_LOGGING is not enabled. Kept for show.
//ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(DEBUG));
//g3::setLogLevel(DEBUG, false); this line will not compile since G3_DYNAMIC_LOGGING is not enabled. Kept for show.
//ASSERT_FALSE(g3::logLevel(DEBUG));
}
#endif // Dynamic logging
#endif // Dynamic logging

View File

@ -6,22 +6,22 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <g3log/filesink.hpp>
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>
#include <g3log/filesink.hpp>
#include <g3log/std2_make_unique.hpp>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <dlfcn.h>
#include "tester_sharedlib.h"
#include <dlfcn.h>
struct LogMessageCounter {
std::vector<std::string>& bank;
LogMessageCounter(std::vector<std::string>& storeMessages) :
bank(storeMessages) {
LogMessageCounter(std::vector<std::string>& storeMessages) : bank(storeMessages) {
}
void countMessages(std::string msg) {
@ -31,18 +31,18 @@ struct LogMessageCounter {
TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
std::vector<std::string> receiver;
{ // scope to flush logs at logworker exit
{ // scope to flush logs at logworker exit
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addSink(std::make_unique<LogMessageCounter>(std::ref(receiver)), &LogMessageCounter::countMessages);
auto handle = worker->addSink(std2::make_unique<LogMessageCounter>(std::ref(receiver)), &LogMessageCounter::countMessages);
// add another sink just for more throughput of data
auto fileHandle = worker->addSink(std::make_unique<g3::FileSink>("runtimeLoadOfDynamiclibs", "/tmp"), &g3::FileSink::fileWrite);
auto fileHandle = worker->addSink(std2::make_unique<g3::FileSink>("runtimeLoadOfDynamiclibs", "/tmp"), &g3::FileSink::fileWrite);
g3::initializeLogging(worker.get());
void* libHandle = dlopen("libtester_sharedlib.so", RTLD_LAZY | RTLD_GLOBAL);
EXPECT_FALSE(nullptr == libHandle);
LibraryFactory* factory = reinterpret_cast<LibraryFactory*>((dlsym(libHandle, "testRealFactory")));
LibraryFactory* factory = reinterpret_cast<LibraryFactory*> ((dlsym(libHandle, "testRealFactory")));
EXPECT_FALSE(nullptr == factory);
SomeLibrary* loadedLibrary = factory->CreateLibrary();
@ -52,7 +52,7 @@ TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
delete loadedLibrary;
dlclose(libHandle);
} // scope exit. All log entries must be flushed now
const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library
} // scope exit. All log entries must be flushed now
const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library
EXPECT_EQ(receiver.size(), numberOfMessages);
}

View File

@ -1,584 +0,0 @@
/** ==========================================================================
* 2016 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <testing_helpers.h>
#include <cstdlib>
#include <ctime>
#include <g3log/filesink.hpp>
#include <g3log/g3log.hpp>
#include <g3log/generated_definitions.hpp>
#include <g3log/time.hpp>
#include <iostream>
namespace {
// https://www.epochconverter.com/
// epoc value for: Thu, 27 Apr 2017 06:22:49 GMT
time_t k2017_April_27th = 1493274147;
auto kTimePoint_2017_April_27th = std::chrono::system_clock::from_time_t(k2017_April_27th);
std::chrono::time_point<std::chrono::system_clock> k1970_January_1st = {};
const std::string kFile = __FILE__;
const int kLine = 123;
const std::string kFunction = "MyTest::Foo";
const LEVELS kLevel = INFO;
const std::string testdirectory = "./";
} // namespace
TEST(Message, DefaultLogDetals_toString) {
using namespace g3;
LogMessage msg{kFile, kLine, kFunction, kLevel};
auto details = LogMessage::DefaultLogDetailsToString(msg);
auto details2 = msg._logDetailsToStringFunc(msg);
EXPECT_EQ(details, details2);
}
TEST(Message, Default_toString) {
using namespace g3;
LogMessage msg{kFile, kLine, kFunction, kLevel};
auto details = LogMessage::DefaultLogDetailsToString(msg);
auto output = msg.toString();
testing_helpers::verifyContent(output, details);
}
TEST(Message, UseOverride_4_DetailsWithThreadID_toString) {
using namespace g3;
LogMessage msg{kFile, kLine, kFunction, kLevel};
msg.overrideLogDetailsFunc(&LogMessage::FullLogDetailsToString);
auto output = msg.toString();
std::ostringstream thread_id_oss;
thread_id_oss << std::this_thread::get_id();
testing_helpers::verifyContent(output, thread_id_oss.str());
testing_helpers::verifyContent(output, kFile);
testing_helpers::verifyContent(output, kLevel.text);
testing_helpers::verifyContent(output, kFunction);
testing_helpers::verifyContent(output, std::to_string(kLine));
std::cout << output << std::endl;
}
TEST(Message, UseLogCall_4_DetailsWithThreadID_toString) {
using namespace g3;
LogMessage msg{kFile, kLine, kFunction, kLevel};
auto output = msg.toString(&LogMessage::FullLogDetailsToString);
std::ostringstream thread_id_oss;
thread_id_oss << std::this_thread::get_id();
testing_helpers::verifyContent(output, thread_id_oss.str());
testing_helpers::verifyContent(output, kFile);
testing_helpers::verifyContent(output, kLevel.text);
testing_helpers::verifyContent(output, kFunction);
testing_helpers::verifyContent(output, std::to_string(kLine));
std::cout << output << std::endl;
}
TEST(Message, DefaultFormattingToLogFile) {
using namespace g3;
std::string file_content;
{
testing_helpers::RestoreFileLogger logger(testdirectory);
LOG(WARNING) << "testing";
logger.reset(); // force flush of logger (which will trigger a shutdown)
file_content = testing_helpers::readFileToText(logger.logFile()); // logger is already reset
}
std::ostringstream thread_id_oss;
thread_id_oss << " [" << std::this_thread::get_id() << " ";
EXPECT_FALSE(testing_helpers::verifyContent(file_content, thread_id_oss.str()));
}
TEST(Message, FullFormattingToLogFile) {
using namespace g3;
std::string file_content;
{
testing_helpers::RestoreFileLogger logger(testdirectory);
logger._handle->call(&FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString);
LOG(WARNING) << "testing";
logger.reset(); // force flush of logger (which will trigger a shutdown)
file_content = testing_helpers::readFileToText(logger.logFile()); // logger is already reset
}
std::ostringstream thread_id_oss;
thread_id_oss << " [" << std::this_thread::get_id() << " ";
EXPECT_TRUE(testing_helpers::verifyContent(file_content, thread_id_oss.str()));
}
TEST(Message, CppSupport) {
// ref: http://www.cplusplus.com/reference/clibrary/ctime/strftime/
// ref: http://en.cppreference.com/w/cpp/io/manip/put_time
// 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
// --- WARNING: The try/catch setup does NOT work,. but for fun and for fake-clarity I leave it
// --- For formatting options to std::put_time that are NOT YET implemented on Windows fatal errors/assert will occurr
// --- the last example is such an example.
try {
std::cout << g3::localtime_formatted(std::chrono::system_clock::now(), "%a %b %d %H:%M:%S %Y") << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << g3::localtime_formatted(std::chrono::system_clock::now(), "%%Y/%%m/%%d %%H:%%M:%%S = %Y/%m/%d %H:%M:%S") << std::endl;
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
std::cerr << "Formatting options skipped due to VS2012, C++11 non-conformance for" << std::endl;
std::cerr << " some formatting options. The skipped code was:\n\t\t %EX %Ec, \n(see http://en.cppreference.com/w/cpp/io/manip/put_time for details)" << std::endl;
#else
std::cout << "C++11 new formatting options:\n"
<< g3::localtime_formatted(std::chrono::system_clock::now(), "%%EX: %EX\n%%z: %z\n%%Ec: %Ec") << std::endl;
#endif
}
// This does not work. Other kinds of fatal exits (on Windows) seems to be used instead of exceptions
// Maybe a signal handler catch would be better? --- TODO: Make it better, both failing and correct
catch (...) {
ADD_FAILURE() << "On this platform the library does not support given (C++11?) specifiers";
return;
}
ASSERT_TRUE(true); // no exception. all good
}
TEST(Message, GetFractional_Empty_buffer_ExpectDefaults) {
auto fractional = g3::internal::getFractional("", 0);
const auto expected = g3::internal::Fractional::NanosecondDefault;
EXPECT_EQ(fractional, expected);
fractional = g3::internal::getFractional("", 100);
EXPECT_EQ(fractional, expected);
}
TEST(Message, GetFractional_MilliSeconds) {
auto fractional = g3::internal::getFractional("%f3", 0);
const auto expected = g3::internal::Fractional::Millisecond;
EXPECT_EQ(fractional, expected);
}
TEST(Message, GetFractional_Microsecond) {
auto fractional = g3::internal::getFractional("%f6", 0);
const auto expected = g3::internal::Fractional::Microsecond;
EXPECT_EQ(fractional, expected);
}
TEST(Message, GetFractional_Nanosecond) {
auto fractional = g3::internal::getFractional("%f9", 0);
const auto expected = g3::internal::Fractional::Nanosecond;
EXPECT_EQ(fractional, expected);
}
TEST(Message, GetFractional_NanosecondDefault) {
auto fractional = g3::internal::getFractional("%f", 0);
const auto expected = g3::internal::Fractional::NanosecondDefault;
EXPECT_EQ(fractional, expected);
}
TEST(Message, GetFractional_All) {
std::string formatted = "%f, %f9, %f6, %f3";
auto fractional = g3::internal::getFractional(formatted, 0);
auto expected = g3::internal::Fractional::NanosecondDefault;
EXPECT_EQ(fractional, expected);
// ns
fractional = g3::internal::getFractional(formatted, 4);
expected = g3::internal::Fractional::Nanosecond;
EXPECT_EQ(fractional, expected);
// us
fractional = g3::internal::getFractional(formatted, 9);
expected = g3::internal::Fractional::Microsecond;
EXPECT_EQ(fractional, expected);
// ms
fractional = g3::internal::getFractional(formatted, 14);
expected = g3::internal::Fractional::Millisecond;
EXPECT_EQ(fractional, expected);
}
TEST(Message, FractionalToString_SizeCheck) {
auto value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Nanosecond);
EXPECT_EQ("000000000", value);
value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::NanosecondDefault);
EXPECT_EQ("000000000", value);
// us
value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Microsecond);
EXPECT_EQ("000000", value);
// ms
value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Millisecond);
EXPECT_EQ("000", value);
}
TEST(Message, FractionalToStringNanoPadded) {
auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Nanosecond);
EXPECT_EQ("000000000", value);
// 0000000012
value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::NanosecondDefault);
EXPECT_EQ("000000000", value);
}
TEST(Message, FractionalToString12NanoPadded) {
auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Nanosecond);
EXPECT_EQ("000000000", value);
// 0000000012
value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::NanosecondDefault);
EXPECT_EQ("000000000", value);
}
TEST(Message, FractionalToStringMicroPadded) {
auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Microsecond);
EXPECT_EQ("000000", value);
value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Microsecond);
EXPECT_EQ("000000", value);
}
TEST(Message, FractionalToStringMilliPadded) {
auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Millisecond);
EXPECT_EQ("000", value);
value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Millisecond);
EXPECT_EQ("000", value);
}
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
TEST(Message, localtime_formatted) {
char* tz = nullptr;
std::shared_ptr<void> RaiiTimeZoneReset(nullptr, [&](void*) {
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
});
tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
auto time_point = std::chrono::system_clock::from_time_t(k2017_April_27th);
auto format = g3::localtime_formatted(time_point, "%Y-%m-%d %H:%M:%S"); // %Y/%m/%d
std::string expected = {"2017-04-27 06:22:27"};
EXPECT_EQ(expected, format);
auto us_format = g3::localtime_formatted(time_point, g3::internal::time_formatted); // "%H:%M:%S %f6";
EXPECT_EQ("06:22:27 000000", us_format);
auto ns_format = g3::localtime_formatted(time_point, "%H:%M:%S %f");
EXPECT_EQ("06:22:27 000000000", ns_format);
auto ms_format = g3::localtime_formatted(time_point, "%H:%M:%S %f3");
EXPECT_EQ("06:22:27 000", ms_format);
}
#endif // timezone
#if defined(CHANGE_G3LOG_DEBUG_TO_DBUG)
TEST(Level, G3LogDebug_is_DBUG) {
LOG(DBUG) << "DBUG equals G3LOG_DEBUG";
LOG(G3LOG_DEBUG) << "G3LOG_DEBUG equals DBUG";
}
#else
TEST(Level, G3LogDebug_is_DEBUG) {
LOG(DEBUG) << "DEBUG equals G3LOG_DEBUG";
LOG(G3LOG_DEBUG) << "G3LOG_DEBUG equals DEBUG";
}
#endif
#ifdef G3_DYNAMIC_LOGGING
namespace {
using LevelsContainer = std::map<int, g3::LoggingLevel>;
const LevelsContainer g_test_log_level_defaults = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG}},
{INFO.value, {INFO}},
{WARNING.value, {WARNING}},
{FATAL.value, {FATAL}}};
const LevelsContainer g_test_all_disabled = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, false}},
{INFO.value, {INFO, false}},
{WARNING.value, {WARNING, false}},
{FATAL.value, {FATAL, false}}};
bool mapCompare(LevelsContainer const& lhs, LevelsContainer const& rhs) {
auto pred = [](auto a, auto b) {
return (a.first == b.first) &&
(a.second == b.second);
};
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
}
} // namespace
TEST(Level, Default) {
g3::only_change_at_initialization::reset();
auto defaults = g3::log_levels::getAll();
EXPECT_EQ(defaults.size(), g_test_log_level_defaults.size());
EXPECT_TRUE(mapCompare(defaults, g_test_log_level_defaults));
}
TEST(Level, DefaultChanged_only_change_at_initialization) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
g3::only_change_at_initialization::addLogLevel(INFO, false);
auto defaults = g3::log_levels::getAll();
EXPECT_EQ(defaults.size(), g_test_log_level_defaults.size());
EXPECT_FALSE(mapCompare(defaults, g_test_log_level_defaults));
const LevelsContainer defaultsWithInfoChangged = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, false}},
{WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}}};
EXPECT_TRUE(mapCompare(defaults, defaultsWithInfoChangged));
}
TEST(Level, DefaultChanged_log_levels) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
g3::log_levels::disable(INFO);
auto defaults = g3::log_levels::getAll();
EXPECT_EQ(defaults.size(), g_test_log_level_defaults.size());
EXPECT_FALSE(mapCompare(defaults, g_test_log_level_defaults));
const LevelsContainer defaultsWithInfoChangged = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, false}},
{WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}}};
EXPECT_TRUE(mapCompare(defaults, defaultsWithInfoChangged));
}
TEST(Level, Reset) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
g3::log_levels::disableAll();
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, g_test_all_disabled));
g3::only_change_at_initialization::reset();
all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, g_test_log_level_defaults));
}
TEST(Level, AllDisabled) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
auto all_levels = g3::log_levels::getAll();
EXPECT_EQ(all_levels.size(), g_test_all_disabled.size());
EXPECT_FALSE(mapCompare(all_levels, g_test_all_disabled));
g3::log_levels::disableAll();
all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, g_test_all_disabled));
}
TEST(Level, setHighestLogLevel_high_end) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
g3::log_levels::enableAll();
g3::log_levels::disable(FATAL);
g3::log_levels::setHighest(FATAL);
LevelsContainer expected = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, false}},
{INFO.value, {INFO, false}},
{WARNING.value, {WARNING, false}},
{FATAL.value, {FATAL, true}}};
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, expected)) << g3::log_levels::to_string();
}
TEST(Level, setHighestLogLevel_low_end) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
g3::log_levels::disableAll();
g3::log_levels::setHighest(G3LOG_DEBUG);
LevelsContainer expected = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, true}},
{WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}}};
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, expected)) << g3::log_levels::to_string();
}
TEST(Level, setHighestLogLevel_middle) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
g3::log_levels::enableAll();
g3::log_levels::setHighest(WARNING);
LevelsContainer expected = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, false}},
{INFO.value, {INFO, false}},
{WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}}};
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, expected));
}
TEST(Level, setHighestLogLevel_StepWiseDisableAll) {
g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LevelsContainer changing_levels = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, true}},
{WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}}};
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, g_test_log_level_defaults));
size_t counter = 0;
for (auto& lvl : changing_levels) {
g3::log_levels::setHighest(lvl.second.level);
all_levels = g3::log_levels::getAll();
ASSERT_TRUE(mapCompare(all_levels, changing_levels)) << "counter: " << counter << "\nsystem:\n"
<< g3::log_levels::to_string(all_levels) << "\nexpected:\n"
<< g3::log_levels::to_string(changing_levels);
++counter;
if (counter != changing_levels.size()) {
// for next round this level will be disabled
lvl.second.status = false;
}
}
// in the end all except the last should be disabled
auto mostly_disabled = g_test_all_disabled;
mostly_disabled[FATAL.value].status = true;
EXPECT_TRUE(mapCompare(changing_levels, mostly_disabled));
all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, mostly_disabled)) << "\nsystem:\n"
<< g3::log_levels::to_string(all_levels) << "\nexpected:\n"
<< g3::log_levels::to_string(mostly_disabled);
}
TEST(Level, Print) {
g3::only_change_at_initialization::reset();
std::string expected = std::string{"name: DEBUG level: 100 status: 1\n"} + "name: INFO level: 300 status: 1\n" + "name: WARNING level: 500 status: 1\n" + "name: FATAL level: 1000 status: 1\n";
EXPECT_EQ(g3::log_levels::to_string(), expected);
}
TEST(Level, AddOneEnabled_option1) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
g3::only_change_at_initialization::addLogLevel(MYINFO, true);
auto modified = g_test_log_level_defaults;
modified[MYINFO.value] = MYINFO;
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(modified, all_levels)) << "\nsystem:\n"
<< g3::log_levels::to_string(all_levels) << "\nexpected:\n"
<< g3::log_levels::to_string(modified);
}
TEST(Level, AddOneEnabled_option2) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
g3::only_change_at_initialization::addLogLevel(MYINFO);
auto modified = g_test_log_level_defaults;
modified[MYINFO.value] = MYINFO;
auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(modified, all_levels)) << "\nsystem:\n"
<< g3::log_levels::to_string(all_levels) << "\nexpected:\n"
<< g3::log_levels::to_string(modified);
}
TEST(Level, Addlevel_using_addLevel) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::only_change_at_initialization::addLogLevel(MYINFO);
status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Enabled);
}
TEST(Level, Addlevel_using_addLogLevel_disabled) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::only_change_at_initialization::addLogLevel(MYINFO, false);
status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Disabled);
}
TEST(Level, Addlevel__disabled) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::log_levels::enable(MYINFO);
status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::log_levels::set(MYINFO, true);
status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::only_change_at_initialization::addLogLevel(MYINFO, false);
status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Disabled);
}
TEST(Level, Addlevel__enabled) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset();
});
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::only_change_at_initialization::addLogLevel(MYINFO);
status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Enabled);
}
#endif // G3_DYNAMIC_LOGGING

View File

@ -1,62 +0,0 @@
#include <gtest/gtest.h>
#include "g3log/crashhandler.hpp"
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#include <fcntl.h>
#include <unistd.h>
class SignalHandlingTest : public ::testing::Test {
protected:
int original_stderr;
int pipefd[2];
FILE* temp_stderr;
void SetUp() override {
// Redirect stderr to a pipe
ASSERT_EQ(pipe(pipefd), 0);
original_stderr = dup(STDERR_FILENO);
ASSERT_NE(original_stderr, -1);
ASSERT_NE(dup2(pipefd[1], STDERR_FILENO), -1);
temp_stderr = fdopen(pipefd[1], "w");
setvbuf(temp_stderr, NULL, _IONBF, 0); // Disable buffering
// Set the read end of the pipe to non-blocking mode
// so we can verify when buffer is empty
int flags = fcntl(pipefd[0], F_GETFL, 0);
fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK);
}
void TearDown() override {
// Restore the original stderr
fclose(temp_stderr);
close(pipefd[0]);
close(pipefd[1]);
dup2(original_stderr, STDERR_FILENO);
close(original_stderr);
}
std::string ReadStderr() {
char buffer[1024];
ssize_t bytes_read = read(pipefd[0], buffer, sizeof(buffer) - 1);
if (bytes_read >= 0) {
buffer[bytes_read] = '\0'; // Null-terminate the string
return std::string(buffer);
}
return "";
}
};
TEST_F(SignalHandlingTest, WriteErrorMessage_WritesToStderr) {
const char* test_message = "Test error message";
g3::internal::writeErrorMessage(test_message);
std::string output = ReadStderr();
ASSERT_EQ(output, test_message);
}
TEST_F(SignalHandlingTest, WriteErrorMessage_Nullptr_DoesNotWriteToStderr) {
g3::internal::writeErrorMessage(nullptr);
std::string output = ReadStderr();
ASSERT_TRUE(output.empty());
}
#endif // #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))

View File

@ -2,36 +2,37 @@
* 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <future>
#include <g3log/generated_definitions.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <atomic>
#include <vector>
#include <memory>
#include <thread>
#include <chrono>
#include <string>
#include <future>
#include "testing_helpers.h"
#include "g3log/logmessage.hpp"
#include "g3log/logworker.hpp"
#include "testing_helpers.h"
#include "g3log/std2_make_unique.hpp"
using namespace testing_helpers;
using namespace std;
TEST(Sink, OneSink) {
using namespace g3;
AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared<atomic<int>>(0);
using namespace g3;
AtomicBoolPtr flag = make_shared < atomic<bool >> (false);
AtomicIntPtr count = make_shared < atomic<int >> (0);
{
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
auto handle = worker->addSink(std2::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
EXPECT_FALSE(flag->load());
EXPECT_TRUE(0 == count->load());
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
LogMessagePtr message{std2::make_unique<LogMessage>("test", 0, "test", DEBUG)};
message.get()->write().append("this message should trigger an atomic increment at the sink");
worker->save(message);
}
@ -39,201 +40,67 @@ TEST(Sink, OneSink) {
EXPECT_TRUE(1 == count->load());
}
TEST(Sink, OneSinkRemove) {
using namespace g3;
AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared<atomic<int>>(0);
{
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
EXPECT_FALSE(flag->load());
EXPECT_TRUE(0 == count->load());
LogMessagePtr message1{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
message1.get()->write().append("this message should trigger an atomic increment at the sink");
worker->save(message1);
worker->removeSink(std::move(handle));
EXPECT_TRUE(flag->load());
EXPECT_TRUE(1 == count->load());
LogMessagePtr message2{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
message2.get()->write().append("this message is issued after all sinks are removed");
worker->save(message2);
}
EXPECT_TRUE(1 == count->load());
}
// just compile test
TEST(Sink, DefaultSinkRemove) {
using namespace g3;
AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared<atomic<int>>(0);
{
auto worker = g3::LogWorker::createLogWorker();
auto handle1 = worker->addDefaultLogger("test1", "./");
auto handle2 = worker->addDefaultLogger("test2", "./");
worker->removeSink(std::move(handle1));
worker->removeAllSinks();
}
}
TEST(Sink, NullSinkRemove) {
using namespace g3;
AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared<atomic<int>>(0);
{
auto worker = g3::LogWorker::createLogWorker();
std::unique_ptr<g3::SinkHandle<ScopedSetTrue>> nullsink;
worker->removeSink(std::move(nullsink));
}
}
namespace {
using AtomicBoolPtr = std::shared_ptr<std::atomic<bool>>;
using AtomicIntPtr = std::shared_ptr<std::atomic<int>>;
using BoolList = vector<AtomicBoolPtr>;
using IntVector = vector<AtomicIntPtr>;
size_t countDestroyedFlags(BoolList& flags) {
size_t destroyed_count = 0;
for (auto& flag : flags) {
if (flag->load()) {
++destroyed_count;
}
}
return destroyed_count;
}
bool expectedMessagesPerSink(const size_t expected, IntVector& messages) {
bool result = true;
for (auto& count : messages) {
result = result && (count->load() == expected);
}
return result;
}
size_t countTotalMessages(IntVector& messages) {
size_t total_count = 0;
for (auto& count : messages) {
total_count += count->load();
}
return total_count;
}
} // namespace
typedef std::shared_ptr<std::atomic<bool >> AtomicBoolPtr;
typedef std::shared_ptr<std::atomic<int >> AtomicIntPtr;
typedef vector<AtomicBoolPtr> BoolList;
typedef vector<AtomicIntPtr> IntVector;
}
TEST(ConceptSink, OneHundredSinks) {
using namespace g3;
BoolList flags;
IntVector counts;
size_t kNumberOfItems = 100;
for (size_t index = 0; index < kNumberOfItems; ++index) {
flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared<atomic<int>>(0));
size_t NumberOfItems = 100;
for (size_t index = 0; index < NumberOfItems; ++index) {
flags.push_back(make_shared < atomic<bool >> (false));
counts.push_back(make_shared < atomic<int >> (0));
}
{
RestoreFileLogger logger{"./"};
g3::LogWorker* worker = logger._scope->get(); //g3LogWorker::createLogWorker();
g3::LogWorker* worker = logger._scope->get(); //g3LogWorker::createLogWorker();
size_t index = 0;
for (auto& flag : flags) {
auto& count = counts[index++];
// ignore the handle
worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
worker->addSink(std2::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
}
LogMessagePtr message1{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
LogMessagePtr message2{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
LOG(DEBUG) << "start message";
LogMessagePtr message1{std2::make_unique<LogMessage>("test", 0, "test", DEBUG)};
LogMessagePtr message2{std2::make_unique<LogMessage>("test", 0, "test", DEBUG)};
auto& write1 = message1.get()->write();
write1.append("Hello to 100 receivers :)");
worker->save(message1);
auto& write2 = message2.get()->write();
write2.append("Hello to 100 receivers :)");
worker->save(message2);
LOG(INFO) << "end message";
logger.reset();
}
// at the curly brace above the ScopedLogger will go out of scope and all the
// at the curly brace above the ScopedLogger will go out of scope and all the
// 100 logging receivers will get their message to exit after all messages are
// are processed
auto destroyed = countDestroyedFlags(flags);
auto messages = countTotalMessages(counts);
ASSERT_TRUE(destroyed == kNumberOfItems);
EXPECT_TRUE(expectedMessagesPerSink(2, counts));
EXPECT_EQ(2 * kNumberOfItems, messages);
}
using SinkHandleT = std::unique_ptr<g3::SinkHandle<ScopedSetTrue>>;
void AddManySinks(size_t kNumberOfSinks, BoolList& flags, IntVector& counts,
std::vector<SinkHandleT>& sink_handles, g3::LogWorker* worker) {
flags.clear();
counts.clear();
sink_handles.clear();
sink_handles.reserve(kNumberOfSinks);
for (size_t idx = 0; idx < kNumberOfSinks; ++idx) {
flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared<atomic<int>>(0));
sink_handles.push_back(worker->addSink(std::make_unique<ScopedSetTrue>(flags[idx], counts[idx]), &ScopedSetTrue::ReceiveMsg));
size_t index = 0;
for (auto& flag : flags) {
auto& count = counts[index++];
ASSERT_TRUE(flag->load()) << ", count : " << (index - 1);
ASSERT_TRUE(4 == count->load()) << ", count : " << (index - 1);
}
}
TEST(ConceptSink, OneHundredSinksRemoved) {
using namespace g3;
BoolList flags;
IntVector counts;
size_t kNumberOfItems = 100;
std::vector<SinkHandleT> sink_handles;
{
RestoreFileLogger logger{"./"};
g3::LogWorker* worker = logger._scope->get(); //think: g3LogWorker::createLogWorker();
AddManySinks(kNumberOfItems, flags, counts, sink_handles, worker);
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
auto& write = message.get()->write();
write.append("Hello to 100 receivers :)");
worker->save(message);
for (auto& x : sink_handles) {
worker->removeSink(std::move(x));
}
EXPECT_EQ(kNumberOfItems, countDestroyedFlags(flags));
EXPECT_EQ(kNumberOfItems, countTotalMessages(counts));
// at the curly brace above the ScopedLogger will go out of scope. The logging sink removal
// is synchronous and all the sinks are guaranteed to have received the message before the sink is removed.
}
}
TEST(ConceptSink, OneHundredRemoveAllSinks) {
using namespace g3;
BoolList flags;
IntVector counts;
size_t kNumberOfItems = 100;
std::vector<SinkHandleT> sink_handles;
{
RestoreFileLogger logger{"./"};
g3::LogWorker* worker = logger._scope->get(); //think: g3LogWorker::createLogWorker();
AddManySinks(kNumberOfItems, flags, counts, sink_handles, worker);
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
auto& write = message.get()->write();
write.append("Hello to 100 receivers :)");
worker->save(message);
worker->removeAllSinks();
EXPECT_EQ(kNumberOfItems, countDestroyedFlags(flags));
EXPECT_EQ(kNumberOfItems, countTotalMessages(counts));
// at the curly brace above the ScopedLogger will go out of scope. The logging sink removal
// is synchronous and all the sinks are guaranteed to have received the message before the sink is removed.
}
cout << "test one hundred sinks is finished finished\n";
}
struct VoidReceiver {
std::atomic<int>* _atomicCounter;
explicit VoidReceiver(std::atomic<int>* counter) :
_atomicCounter(counter) {}
void receiveMsg(std::string msg) { /*ignored*/
}
void incrementAtomic() {
(*_atomicCounter)++;
explicit VoidReceiver(std::atomic<int>* counter) : _atomicCounter(counter){}
void receiveMsg(std::string msg){ /*ignored*/}
void incrementAtomic(){
(*_atomicCounter)++;
}
};
@ -241,8 +108,8 @@ TEST(ConceptSink, VoidCall__NoCall_ExpectingNoAdd) {
std::atomic<int> counter{0};
{
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
auto handle = worker->addSink(std::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
}
auto handle = worker->addSink(std2::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
}
EXPECT_EQ(counter, 0);
}
@ -250,9 +117,9 @@ TEST(ConceptSink, VoidCall__OneCall_ExpectingOneAdd) {
std::atomic<int> counter{0};
{
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
auto handle = worker->addSink(std::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
auto handle = worker->addSink(std2::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
std::future<void> ignored = handle->call(&VoidReceiver::incrementAtomic);
}
}
EXPECT_EQ(counter, 1);
}
@ -260,27 +127,26 @@ TEST(ConceptSink, VoidCall__TwoCalls_ExpectingTwoAdd) {
std::atomic<int> counter{0};
{
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
auto handle = worker->addSink(std::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
auto voidFuture1 = handle->call(&VoidReceiver::incrementAtomic);
auto voidFuture2 = handle->call(&VoidReceiver::incrementAtomic);
auto handle = worker->addSink(std2::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
auto voidFuture1 = handle->call(&VoidReceiver::incrementAtomic);
auto voidFuture2 = handle->call(&VoidReceiver::incrementAtomic);
voidFuture1.wait();
EXPECT_TRUE(counter >= 1);
}
}
EXPECT_EQ(counter, 2);
}
struct IntReceiver {
std::atomic<int>* _atomicCounter;
explicit IntReceiver(std::atomic<int>* counter) :
_atomicCounter(counter) {}
void receiveMsgDoNothing(std::string msg) { /*ignored*/
}
void receiveMsgIncrementAtomic(std::string msg) { incrementAtomic(); }
int incrementAtomic() {
(*_atomicCounter)++;
int value = *_atomicCounter;
return value;
explicit IntReceiver(std::atomic<int>* counter) : _atomicCounter(counter){}
void receiveMsgDoNothing(std::string msg){ /*ignored*/}
void receiveMsgIncrementAtomic(std::string msg){ incrementAtomic(); }
int incrementAtomic(){
(*_atomicCounter)++;
int value = *_atomicCounter;
return value;
}
};
@ -288,147 +154,81 @@ TEST(ConceptSink, IntCall__TwoCalls_ExpectingTwoAdd) {
std::atomic<int> counter{0};
{
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
auto handle = worker->addSink(std::make_unique<IntReceiver>(&counter), &IntReceiver::receiveMsgDoNothing);
auto handle = worker->addSink(std2::make_unique<IntReceiver>(&counter), &IntReceiver::receiveMsgDoNothing);
std::future<int> intFuture1 = handle->call(&IntReceiver::incrementAtomic);
EXPECT_EQ(intFuture1.get(), 1);
EXPECT_EQ(counter, 1);
auto intFuture2 = handle->call(&IntReceiver::incrementAtomic);
EXPECT_EQ(intFuture2.get(), 2);
}
auto intFuture2 = handle->call(&IntReceiver::incrementAtomic);
EXPECT_EQ(intFuture2.get(), 2);
}
EXPECT_EQ(counter, 2);
}
void DoLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
while (doWhileTrue->load()) {
void DoLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
while(doWhileTrue->load()) {
LOG(INFO) << "Calling from #" << counter;
std::cout << "-";
std::this_thread::yield();
}
}
}
void DoSlowLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
size_t messages = 0;
while (doWhileTrue->load()) {
LOG(INFO) << "Calling from #" << counter;
++messages;
int random = rand() % 10 + 1; // Range 1-10
std::this_thread::sleep_for(std::chrono::microseconds(random));
}
std::string out = "#" + std::to_string(counter) + " number of messages sent: " + std::to_string(messages) + "\n";
std::cout << out;
}
TEST(ConceptSink, CannotCallSpawnTaskOnNullptrWorker) {
auto FailedHelloWorld = [] {
std::cout << "Hello World" << std::endl;
};
kjellkod::Active* active = nullptr;
auto failed = g3::spawn_task(FailedHelloWorld, active);
EXPECT_ANY_THROW(failed.get());
auto FailedHelloWorld = []{ std::cout << "Hello World" << std::endl; };
kjellkod::Active* active = nullptr;
auto failed = g3::spawn_task(FailedHelloWorld, active);
EXPECT_ANY_THROW(failed.get());
}
TEST(ConceptSink, AggressiveThreadCallsDuringAddAndRemoveSink) {
TEST(ConceptSink, AggressiveThreadCallsDuringShutdown) {
std::atomic<bool> keepRunning{true};
size_t numberOfCycles = 10;
std::vector<std::thread> threads;
const size_t numberOfThreads = std::thread::hardware_concurrency() / 2;
const size_t numberOfThreads = 100;
threads.reserve(numberOfThreads);
g3::internal::shutDownLogging();
// Avoid annoying printouts at log shutdown
stringstream cerr_buffer;
stringstream cerr_buffer;
testing_helpers::ScopedOut guard1(std::cerr, &cerr_buffer);
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
g3::initializeLogging(worker.get());
using SinkHandleT = std::unique_ptr<g3::SinkHandle<IntReceiver>>;
std::vector<SinkHandleT> sink_handles;
sink_handles.reserve(numberOfCycles);
// these threads will continue to write to a logger
// while the receiving logger is instantiated, and destroyed repeatedly
for (size_t caller = 0; caller <= numberOfThreads; ++caller) {
threads.push_back(std::thread(DoSlowLogCalls, &keepRunning, caller));
for (size_t caller = 0; caller < numberOfThreads; ++ caller) {
threads.push_back(std::thread(DoLogCalls, &keepRunning, caller));
}
std::atomic<int> atomicCounter{0};
std::cout << "Add sinks, remove sinks, " << numberOfCycles << " times\n\tWhile " << numberOfThreads << " threads are continously doing LOG calls" << std::endl;
size_t numberOfCycles = 25;
std::cout << "Create logger, delete active logger, " << numberOfCycles << " times\n\tWhile " << numberOfThreads << " threads are continously doing LOG calls" << std::endl;
std::cout << "Create/Destroy Times #";
for (size_t create = 0; create < numberOfCycles; ++create) {
worker->removeAllSinks();
sink_handles.clear();
sink_handles.reserve(numberOfCycles);
std::cout << create << " ";
std::cout << ".";
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
auto handle = worker->addSink(std2::make_unique<IntReceiver>(&atomicCounter), &IntReceiver::receiveMsgIncrementAtomic);
g3::initializeLogging(worker.get());
// wait till some LOGS streaming in
atomicCounter = 0;
for (size_t sinkIdx = 0; sinkIdx < 2; ++sinkIdx) {
sink_handles.push_back(worker->addSink(std::make_unique<IntReceiver>(&atomicCounter), &IntReceiver::receiveMsgIncrementAtomic));
}
// wait till some LOGS streaming in
while (atomicCounter.load() < 10) {
std::this_thread::yield();
while(atomicCounter.load() < 10) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
} // g3log worker exists: 1) shutdownlogging 2) flush of queues and shutdown of sinks
} // g3log worker exists: 1) shutdownlogging 2) flush of queues and shutdown of sinks
worker.reset();
// exit the threads
keepRunning = false;
for (auto& t : threads) {
t.join();
}
std::cout << "\nAll threads are joined " << std::endl;
// exit the threads
keepRunning = false;
for (auto& t : threads) {
t.join();
}
std::cout << "\nAll threads are joined " << std::endl;
}
// This test is commented out but kept here for documentation purposes.
// Actually shutting down and re-initializing the logger is not the intention of g3log.
// the are several initial setups that happen ONCE and the logger relies on the client
// to properly own the logworker that is the key object that receives and direct LOG calls.
// Making LOG calls thread safe through repeated initialization/shutdowns would come at a
// high expense of logworker existance synchronization checks.
//
// TEST(ConceptSink, DISABLED_AggressiveThreadCallsDuringShutdown) {
// std::atomic<bool> keepRunning{true};
// std::vector<std::thread> threads;
// const size_t numberOfThreads = std::thread::hardware_concurrency();
// threads.reserve(numberOfThreads);
// g3::internal::shutDownLogging();
// // Avoid annoying printouts at log shutdown
// stringstream cerr_buffer;
// testing_helpers::ScopedOut guard1(std::cerr, &cerr_buffer);
// // these threads will continue to write to a logger
// // while the receiving logger is instantiated, and destroyed repeatedly
// for (size_t caller = 0; caller < numberOfThreads; ++ caller) {
// threads.push_back(std::thread(DoLogCalls, &keepRunning, caller));
// }
// std::atomic<int> atomicCounter{0};
// size_t numberOfCycles = 2500;
// std::cout << "Create logger, delete active logger, " << numberOfCycles << " times\n\tWhile " << numberOfThreads << " threads are continously doing LOG calls" << std::endl;
// std::cout << "Initialize logger / Shutdown logging #";
// for (size_t create = 0; create < numberOfCycles; ++create) {
// std::cout << ".";
// std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
// auto handle = worker->addSink(std::make_unique<IntReceiver>(&atomicCounter), &IntReceiver::receiveMsgIncrementAtomic);
// g3::initializeLogging(worker.get());
// // wait till some LOGS streaming in
// atomicCounter = 0;
// while (atomicCounter.load() < 10) {
// std::this_thread::sleep_for(std::chrono::microseconds(1));
// }
// } // g3log worker exists: 1) shutdownlogging 2) flush of queues and shutdown of sinks
// // exit the threads
// keepRunning = false;
// for (auto& t : threads) {
// t.join();
// }
// std::cout << "\nAll threads are joined " << std::endl;
// }

Some files were not shown because too many files have changed in this diff Show More