├── .hgtags ├── .gitignore ├── .hgrc_copy ├── CMakeLists.txt.in ├── .travis.yml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── sublime.formatting ├── scripts ├── .travis-bootstrap-ubuntu.sh └── buildAndRunTests.sh ├── test_main └── test_main.cpp ├── PULL_REQUEST_TEMPLATE.md ├── test_unit ├── tester_sharedlib.h ├── tester_sharedlib.cpp ├── test_linux_dynamic_loaded_sharedlib.cpp ├── test_crashhandler_windows.cpp ├── testing_helpers.h ├── test_cpp_future_concepts.cpp ├── Test.cmake ├── testing_helpers.cpp ├── test_filechange.cpp └── test_concept_sink.cpp ├── src ├── g3log │ ├── sinkwrapper.hpp │ ├── filesink.hpp │ ├── atomicbool.hpp │ ├── moveoncopy.hpp │ ├── stacktrace_windows.hpp │ ├── active.hpp │ ├── sinkhandle.hpp │ ├── stlpatch_future.hpp │ ├── shared_queue.hpp │ ├── future.hpp │ ├── logcapture.hpp │ ├── sink.hpp │ ├── crashhandler.hpp │ ├── time.hpp │ ├── logmessage.hpp │ ├── logworker.hpp │ └── loglevels.hpp ├── g2log.hpp ├── loglevels.cpp ├── filesink.cpp ├── logcapture.cpp ├── filesinkhelper.ipp ├── logworker.cpp ├── time.cpp ├── logmessage.cpp └── stacktrace_windows.cpp ├── CleanAll.cmake ├── appveyor.yml ├── LICENSE ├── example ├── Example.cmake ├── main_contract.cpp └── main_sigsegv.cpp ├── cmake └── g3logConfig.cmake ├── test_performance ├── Performance.cmake ├── performance.h ├── main_threaded_mean.cpp └── main_threaded_worst.cpp ├── GenerateMacroDefinitionsFile.cmake ├── CPackLists.txt ├── iOSBuild.cmake ├── Build.cmake ├── Options.cmake └── CMakeLists.txt /.hgtags: -------------------------------------------------------------------------------- 1 | d9a55a4a615449b8eb50ed6208e3dcaf1c678d7e version-1.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | sinks/* 3 | build/* 4 | nbproject/* 5 | build_clang 6 | build_travis 7 | gtest-1.7.0 8 | *~ 9 | -------------------------------------------------------------------------------- /.hgrc_copy: -------------------------------------------------------------------------------- 1 | [paths] 2 | default = https://bitbucket.org/KjellKod/g3log 3 | 4 | [extensions] 5 | hgext.bookmarks = 6 | hggit = 7 | 8 | [paths] 9 | github = git+ssh://git@github.com/KjellKod/g3log.git 10 | 11 | [hooks] 12 | outgoing = hg bookmark -r default master || true && hg push github || true 13 | -------------------------------------------------------------------------------- /CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | URL https://github.com/google/googletest/archive/master.zip 8 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 9 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 10 | CONFIGURE_COMMAND "" 11 | BUILD_COMMAND "" 12 | INSTALL_COMMAND "" 13 | TEST_COMMAND "" 14 | ) 15 | 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: cpp 3 | 4 | git: 5 | quiet: true 6 | 7 | matrix: 8 | include: 9 | - os: linux 10 | dist: xenial 11 | - os: osx 12 | osx_image: xcode10.1 13 | 14 | 15 | compiler: 16 | - gcc 17 | # - clang 18 | 19 | before_install: 20 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then uname -a; fi 21 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo uname -a; sudo sh scripts/.travis-bootstrap-ubuntu.sh; fi 22 | 23 | script: "./scripts/buildAndRunTests.sh" 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | What system, compiler etc was used to run this. 15 | Is something done differently on your setup than what the project specifies? 16 | Have you provided a code snippet that can be tested? 17 | 18 | **Additional context** 19 | Add any other context about the problem here. 20 | -------------------------------------------------------------------------------- /sublime.formatting: -------------------------------------------------------------------------------- 1 | { 2 | "AStyleFormatter": 3 | { 4 | "options_default": 5 | { 6 | "indent": "spaces", 7 | "indent-modifiers": false, 8 | "indent-namespaces": true, 9 | "indent-preproc-block": true, 10 | "indent-spaces": 3, 11 | "style": "googles" 12 | } 13 | }, 14 | "color_scheme": "Packages/Color Scheme - Default/Twilight.tmTheme", 15 | "font_size": 11, 16 | "highlight_modified_tabs": true, 17 | "ignored_packages": 18 | [ 19 | "Vintage" 20 | ], 21 | "tab_size": 3, 22 | "translate_tabs_to_spaces": true, 23 | "word_wrap": true 24 | } 25 | -------------------------------------------------------------------------------- /scripts/.travis-bootstrap-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ev 4 | set -x 5 | 6 | apt-get update -y 7 | apt-get install -y apt-utils | true 8 | apt-get install -y software-properties-common | true 9 | apt-get install -y python-software-properties 10 | apt-get update -y 11 | add-apt-repository -y ppa:jonathonf/gcc 12 | apt-get update -y 13 | apt-get install -y cmake software-properties-common git make 14 | apt-get install -y gcc-7 g++-7 15 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 90 16 | update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90 17 | apt-get install -y unzip zlib1g-dev 18 | apt-get install -y libboost-all-dev -------------------------------------------------------------------------------- /test_main/test_main.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * ============================================================================*/ 6 | 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | testing::InitGoogleTest(&argc, argv); 13 | int return_value = RUN_ALL_TESTS(); 14 | std::cout << "FINISHED WITH THE TESTING" << std::endl; 15 | return return_value; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /scripts/buildAndRunTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ev 4 | set -x 5 | 6 | 7 | mkdir -p build_travis 8 | cd build_travis 9 | cmake -DADD_G3LOG_BENCH_PERFORMANCE=ON -DADD_G3LOG_UNIT_TEST=ON -DCMAKE_INSTALL_PREFIX=./install -DCPACK_PACKAGING_INSTALL_PREFIX=/opt/g3log .. 10 | cmake --build . --target install 11 | 12 | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 13 | cpack -G "ZIP" 14 | unzip g3log-*-Darwin.zip 15 | fi 16 | 17 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 18 | cpack -G "DEB;TGZ" 19 | tar zxvf g3log-*-Linux.tar.gz 20 | fi 21 | 22 | # LINUX OR OSX 23 | makeArg=`grep -c ^processor /proc/cpuinfo || sysctl -n hw.ncpu` 24 | 25 | make -j$makeArg 26 | ctest -V 27 | 28 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] **TDD** 2 | 3 | New/modified code must be backed down with unit test - preferably TDD style development) 4 | 5 | 6 | - [ ] **Documentation** 7 | 8 | All new/modified functionality should be backed up with API documentation (API.markdown or README.markdown) 9 | 10 | 11 | **Cross-Platform Testing** 12 | - [ ] Travis-CI (Linux, OSX) + AppVeyor-CI (Windows)\ 13 | - [ ] *Optional:* Local/VM testing: Windows 14 | - [ ] *Optional:* Local/VM testing: OSX 15 | - [ ] *Optional:* Local/VM testing: Linux 16 | 17 | 18 | **Testing Advice** 19 | ```bash 20 | mkdir build; cd build; cmake -DADD_G3LOG_UNIT_TEST=ON .. 21 | ``` 22 | 23 | **Run Test Alternatives:** 24 | - Cross-Platform: `ctest` 25 | - or `ctest -V` for verbose output 26 | - Linux: `make test` 27 | -------------------------------------------------------------------------------- /test_unit/tester_sharedlib.h: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #pragma once 11 | 12 | struct SomeLibrary { 13 | 14 | SomeLibrary() {}; 15 | 16 | virtual ~SomeLibrary() {}; 17 | virtual void action() = 0; 18 | }; 19 | 20 | class LibraryFactory { 21 | public: 22 | virtual SomeLibrary* CreateLibrary() = 0; 23 | }; 24 | -------------------------------------------------------------------------------- /src/g3log/sinkwrapper.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/logmessage.hpp" 12 | 13 | namespace g3 { 14 | namespace internal { 15 | 16 | struct SinkWrapper { 17 | virtual ~SinkWrapper() { } 18 | virtual void send(LogMessageMover msg) = 0; 19 | }; 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /CleanAll.cmake: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================*/ 8 | 9 | 10 | set(cmake_generated ${CMAKE_BINARY_DIR}/CMakeCache.txt 11 | ${CMAKE_BINARY_DIR}/cmake_install.cmake 12 | ${CMAKE_BINARY_DIR}/Makefile 13 | ${CMAKE_BINARY_DIR}/CMakeFiles 14 | ) 15 | 16 | foreach(file ${cmake_generated}) 17 | if (EXISTS ${file}) 18 | message( STATUS "Removing: ${file}" ) 19 | file(REMOVE_RECURSE ${file}) 20 | endif() 21 | endforeach(file) -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | P: "c:/projects/libs" 3 | 4 | # Operating system (build VM template) 5 | os: Visual Studio 2017 6 | 7 | # scripts that are called at very beginning, before repo cloning 8 | #init: 9 | 10 | 11 | # clone directory 12 | clone_folder: c:\projects\g3log 13 | 14 | platform: x64 15 | configuration: Release 16 | 17 | install: 18 | # by default, all script lines are interpreted as batch 19 | 20 | # scripts to run before build 21 | before_build: 22 | - cd c:\projects\g3log\ 23 | - mkdir build 24 | - cd build 25 | 26 | build_script: 27 | - cmake -G "Visual Studio 15 2017 Win64" -DADD_G3LOG_UNIT_TEST=ON -DUSE_DYNAMIC_LOGGING_LEVELS=ON -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON -DCMAKE_INSTALL_PREFIX=c:\g3log .. 28 | - cmake --build . --config Release --target install 29 | 30 | # scripts to run after build 31 | after_build: 32 | - cmd /c Release\g3log-FATAL-contract.exe || exit /B 0 33 | - tree /A /F c:\g3log 34 | - cpack -G "NSIS;ZIP" 35 | - cpack -C Release 36 | - ctest -C Release --verbose 37 | 38 | -------------------------------------------------------------------------------- /src/g2log.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | // For convenience: If you don't want to do a recursive search and replace in your source code 12 | // for replacing g2log.hpp for g3log/g3log.hpp then you can choose to add this header file to your 13 | // code. It will get the necessary includes 14 | // 15 | // 16 | // Btw: replacing g2log for g3log include is easy on Linux 17 | // find . -name "*.cpp*" -print | xargs sed -i -e 's/\g2log\.hpp/\g3log\/g3log\.hpp/g' 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | -------------------------------------------------------------------------------- /test_unit/tester_sharedlib.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #include 11 | #include 12 | #include "tester_sharedlib.h" 13 | 14 | struct RuntimeLoadedLib : public SomeLibrary { 15 | 16 | RuntimeLoadedLib() { 17 | LOG(INFO) << "Library was created"; 18 | LOGF(INFO, "Ready for testing"); 19 | } 20 | 21 | ~RuntimeLoadedLib() { 22 | LOG(G3LOG_DEBUG) << "Library destroyed"; 23 | } 24 | 25 | void action() { 26 | LOG(WARNING) << "Action, action, action. Safe for LOG calls by runtime dynamically loaded libraries"; 27 | } 28 | }; 29 | 30 | struct RealLibraryFactory : public LibraryFactory { 31 | SomeLibrary* CreateLibrary() { 32 | return new RuntimeLoadedLib; 33 | } 34 | }; 35 | 36 | RealLibraryFactory testRealFactory; 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /src/g3log/filesink.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | #include "g3log/logmessage.hpp" 14 | namespace g3 { 15 | 16 | class FileSink { 17 | public: 18 | FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string &logger_id="g3log"); 19 | virtual ~FileSink(); 20 | 21 | void fileWrite(LogMessageMover message); 22 | std::string changeLogFile(const std::string &directory, const std::string &logger_id); 23 | std::string fileName(); 24 | void overrideLogDetails(LogMessage::LogDetailsFunc func); 25 | void overrideLogHeader(const std::string& change); 26 | 27 | 28 | private: 29 | LogMessage::LogDetailsFunc _log_details_func; 30 | 31 | std::string _log_file_with_path; 32 | std::string _log_prefix_backup; // needed in case of future log file changes of directory 33 | std::unique_ptr _outptr; 34 | std::string _header; 35 | bool _firstEntry; 36 | 37 | void addLogFileHeader(); 38 | std::ofstream &filestream() { 39 | return *(_outptr.get()); 40 | } 41 | 42 | 43 | FileSink &operator=(const FileSink &) = delete; 44 | FileSink(const FileSink &other) = delete; 45 | 46 | }; 47 | } // g3 48 | 49 | -------------------------------------------------------------------------------- /src/g3log/atomicbool.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | namespace g3 { 15 | /// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c 16 | struct atomicbool { 17 | private: 18 | std::atomic value_; 19 | public: 20 | atomicbool(): value_ {false} {} 21 | atomicbool(const bool& value): value_ {value} {} 22 | atomicbool(const std::atomic& value) : value_ {value.load(std::memory_order_acquire)} {} 23 | atomicbool(const atomicbool& other): value_ {other.value_.load(std::memory_order_acquire)} {} 24 | 25 | atomicbool& operator=(const atomicbool& other) { 26 | value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release); 27 | return *this; 28 | } 29 | 30 | atomicbool& operator=(const bool other) { 31 | value_.store(other, std::memory_order_release); 32 | return *this; 33 | } 34 | 35 | bool operator==(const atomicbool& rhs) const { 36 | return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire)); 37 | } 38 | 39 | bool value() {return value_.load(std::memory_order_acquire);} 40 | std::atomic& get() {return value_;} 41 | }; 42 | } // g3 43 | // explicit whitespace/EOF for VS15 -------------------------------------------------------------------------------- /src/g3log/moveoncopy.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | namespace g3 { 11 | 12 | // A straightforward technique to move around packaged_tasks. 13 | // Instances of std::packaged_task are MoveConstructible and MoveAssignable, but 14 | // not CopyConstructible or CopyAssignable. To put them in a std container they need 15 | // to be wrapped and their internals "moved" when tried to be copied. 16 | 17 | template 18 | struct MoveOnCopy { 19 | mutable Moveable _move_only; 20 | 21 | explicit MoveOnCopy(Moveable &&m) : _move_only(std::move(m)) {} 22 | MoveOnCopy(MoveOnCopy const &t) : _move_only(std::move(t._move_only)) {} 23 | MoveOnCopy(MoveOnCopy &&t) : _move_only(std::move(t._move_only)) {} 24 | 25 | MoveOnCopy &operator=(MoveOnCopy const &other) { 26 | _move_only = std::move(other._move_only); 27 | return *this; 28 | } 29 | 30 | MoveOnCopy &operator=(MoveOnCopy && other) { 31 | _move_only = std::move(other._move_only); 32 | return *this; 33 | } 34 | 35 | void operator()() { 36 | _move_only(); 37 | } 38 | 39 | Moveable &get() { 40 | return _move_only; 41 | } 42 | 43 | Moveable release() { 44 | return std::move(_move_only); 45 | } 46 | }; 47 | 48 | } // g3 49 | -------------------------------------------------------------------------------- /src/g3log/stacktrace_windows.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc AND Robert Engeln. 3 | * The stacktrace code was given as a public domain dedication by Robert Engeln 4 | * It was originally published at: http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html 5 | * It was (here) modified for g3log purposes. 6 | * 7 | * This is PUBLIC DOMAIN to use at your own risk and comes 8 | * with no warranties. This code is yours to share, use and modify with no 9 | * strings attached and no restrictions or obligations. 10 | * 11 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 12 | * ============================================================================*/ 13 | 14 | 15 | #pragma once 16 | #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 17 | #error "stacktrace_win.cpp used but not on a windows system" 18 | #endif 19 | 20 | #include "g3log/crashhandler.hpp" 21 | 22 | #include 23 | #include 24 | 25 | namespace stacktrace { 26 | /// return the text description of a Windows exception code 27 | std::string exceptionIdToText(g3::SignalType id); 28 | 29 | /// return whether or not the exception is a known exception, i.e. 30 | /// an exception that we should treat as a fatal event 31 | bool isKnownException(g3::SignalType id); 32 | 33 | /// helper function: retrieve stackdump from no excisting exception pointer 34 | std::string stackdump(); 35 | 36 | /// helper function: retrieve stackdump, starting from an exception pointer 37 | std::string stackdump(EXCEPTION_POINTERS *info); 38 | 39 | /// main stackdump function. retrieve stackdump, from the given context 40 | std::string stackdump(CONTEXT *context); 41 | 42 | } // stacktrace 43 | -------------------------------------------------------------------------------- /example/Example.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | 13 | 14 | 15 | # ============================================================== 16 | # -DUSE_SIMPLE_EXAMPLE=OFF : to turn off the fatal examples 17 | # 18 | # 19 | # Leaving it to ON will create 20 | # g3log-FATAL-sigsegv 21 | # g3log-FATAL-contract 22 | # 23 | # ============================================================== 24 | 25 | IF (MSVC OR MINGW) 26 | set(EXAMPLE_PLATFORM_LINK_LIBRIES dbghelp) 27 | ENDIF() 28 | 29 | set(DIR_EXAMPLE ${g3log_SOURCE_DIR}/example) 30 | option (ADD_FATAL_EXAMPLE "Fatal (fatal-crashes/contract) examples " ON) 31 | 32 | 33 | IF (ADD_FATAL_EXAMPLE) 34 | message( STATUS "-DADD_FATAL_EXAMPLE=ON" ) 35 | message( STATUS "\t\t[contract][sigsegv][fatal choice] are examples of when g3log comes in handy\n" ) 36 | include_directories (${DIR_EXAMPLE}) 37 | add_executable(g3log-FATAL-contract ${DIR_EXAMPLE}/main_contract.cpp) 38 | add_executable(g3log-FATAL-sigsegv ${DIR_EXAMPLE}/main_sigsegv.cpp) 39 | add_executable(g3log-FATAL-choice ${DIR_EXAMPLE}/main_fatal_choice.cpp) 40 | 41 | target_link_libraries(g3log-FATAL-contract ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES}) 42 | target_link_libraries(g3log-FATAL-sigsegv ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES}) 43 | target_link_libraries(g3log-FATAL-choice ${G3LOG_LIBRARY} ${EXAMPLE_PLATFORM_LINK_LIBRIES}) 44 | ELSE() 45 | message( STATUS "-DADD_SIMPLE_EXAMPLE=OFF" ) 46 | ENDIF (ADD_FATAL_EXAMPLE) 47 | -------------------------------------------------------------------------------- /cmake/g3logConfig.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindG3log 3 | # ------- 4 | # 5 | # Find libg3log, G3log is an asynchronous, "crash safe", logger that is easy to use with default logging sinks or you can add your own. 6 | # 7 | # This defines the cmake import target "g3log" you can use like this 8 | #``` 9 | # target_link_libraries(YourTarget PUBLIC g3log) 10 | #``` 11 | # Variables and features 12 | # ---------------------- 13 | # * ``G3LOG`` -- if this environment variable is set, it'll be used as a hint as to where the g3log files are. 14 | # * ``G3LOG_INCLUDE_DIRS`` -- raw cmake variable with include path 15 | # * ``G3LOG_LIBRARIES`` -- raw cmake variable with library link line 16 | # * ``G3LOG_FOUND`` -- check if the lib was found without using the newer ``if(TARGET g3log)...`` 17 | 18 | include(FindPackageHandleStandardArgs) 19 | include(SelectLibraryConfigurations) 20 | 21 | @PACKAGE_INIT@ 22 | 23 | find_package(Threads REQUIRED) 24 | 25 | if (NOT TARGET g3log) 26 | include("${CMAKE_CURRENT_LIST_DIR}/g3logTargets.cmake") 27 | 28 | get_target_property(G3LOG_INCLUDE_DIR g3log INTERFACE_INCLUDE_DIRECTORIES) 29 | get_target_property(G3LOG_LIBRARY_DEBUG g3log IMPORTED_IMPLIB_DEBUG) 30 | 31 | if (G3LOG_LIBRARY_DEBUG MATCHES ".*-NOTFOUND") 32 | get_target_property(G3LOG_LIBRARY_DEBUG g3log IMPORTED_LOCATION_DEBUG) 33 | endif () 34 | 35 | get_target_property(G3LOG_LIBRARY_RELEASE g3log IMPORTED_IMPLIB_RELEASE) 36 | if (G3LOG_LIBRARY_RELEASE MATCHES ".*-NOTFOUND") 37 | get_target_property(G3LOG_LIBRARY_RELEASE g3log IMPORTED_LOCATION_RELEASE) 38 | endif () 39 | 40 | select_library_configurations(G3LOG) 41 | 42 | if (G3LOG_LIBRARY) 43 | list(APPEND G3LOG_LIBRARY Threads::Threads) 44 | if (WIN32) 45 | list(APPEND G3LOG_LIBRARY DbgHelp.lib) 46 | endif () 47 | endif () 48 | endif () 49 | 50 | find_package_handle_standard_args(G3LOG REQUIRED_VARS G3LOG_INCLUDE_DIR G3LOG_LIBRARY) 51 | mark_as_advanced(G3LOG_INCLUDE_DIR G3LOG_LIBRARY) 52 | set(G3LOG_INCLUDE_DIRS ${G3LOG_INCLUDE_DIR}) 53 | set(G3LOG_LIBRARIES ${G3LOG_LIBRARY}) 54 | -------------------------------------------------------------------------------- /test_performance/Performance.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | 13 | 14 | # . performance test (average + worst case) for KjellKod's g3log 15 | # Do 'cmake -DADD_G3LOG_BENCH_PERFORMANCE=ON' to enable this 16 | option (ADD_G3LOG_BENCH_PERFORMANCE "g3log performance test" OFF) 17 | 18 | 19 | 20 | 21 | # create the g3log's performance tests 22 | # ========================= 23 | IF (ADD_G3LOG_BENCH_PERFORMANCE) 24 | set(DIR_PERFORMANCE ${g3log_SOURCE_DIR}/test_performance) 25 | 26 | message( STATUS "-DADD_G3LOG_BENCH_PERFORMANCE=ON" ) 27 | include_directories (${DIR_PERFORMANCE}) 28 | 29 | # MEAN PERFORMANCE TEST 30 | add_executable(g3log-performance-threaded_mean 31 | ${DIR_PERFORMANCE}/main_threaded_mean.cpp 32 | ${DIR_PERFORMANCE}/performance.h) 33 | # Turn on G3LOG performance flag 34 | set_target_properties(g3log-performance-threaded_mean PROPERTIES 35 | COMPILE_DEFINITIONS "G3LOG_PERFORMANCE=1") 36 | target_link_libraries(g3log-performance-threaded_mean 37 | ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES}) 38 | 39 | # WORST CASE PERFORMANCE TEST 40 | add_executable(g3log-performance-threaded_worst 41 | ${DIR_PERFORMANCE}/main_threaded_worst.cpp ${DIR_PERFORMANCE}/performance.h) 42 | # Turn on G3LOG performance flag 43 | set_target_properties(g3log-performance-threaded_worst PROPERTIES 44 | COMPILE_DEFINITIONS "G3LOG_PERFORMANCE=1") 45 | target_link_libraries(g3log-performance-threaded_worst 46 | ${G3LOG_LIBRARY} ${PLATFORM_LINK_LIBRIES}) 47 | 48 | ELSE() 49 | message( STATUS "-DADD_G3LOG_BENCH_PERFORMANCE=OFF" ) 50 | ENDIF(ADD_G3LOG_BENCH_PERFORMANCE) 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/g3log/active.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================ 8 | * 9 | * Example of a Active Object, using C++11 std::thread mechanisms to make it 10 | * safe for thread communication. 11 | * 12 | * This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x 13 | * and inspired from Herb Sutter's C++11 Active Object 14 | * http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads 15 | * 16 | * Last update 2013-12-19 by Kjell Hedstrom, 17 | * e-mail: hedstrom at kjellkod dot cc 18 | * linkedin: http://linkedin.com/se/kjellkod */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include "g3log/shared_queue.hpp" 26 | 27 | namespace kjellkod { 28 | typedef std::function Callback; 29 | 30 | class Active { 31 | private: 32 | Active() : done_(false) {} // Construction ONLY through factory createActive(); 33 | Active(const Active &) = delete; 34 | Active &operator=(const Active &) = delete; 35 | 36 | void run() { 37 | while (!done_) { 38 | Callback func; 39 | mq_.wait_and_pop(func); 40 | func(); 41 | } 42 | } 43 | 44 | shared_queue mq_; 45 | std::thread thd_; 46 | bool done_; 47 | 48 | 49 | public: 50 | virtual ~Active() { 51 | send([this] { done_ = true;}); 52 | thd_.join(); 53 | } 54 | 55 | void send(Callback msg_) { 56 | mq_.push(msg_); 57 | } 58 | 59 | /// Factory: safe construction of object before thread start 60 | static std::unique_ptr createActive() { 61 | std::unique_ptr aPtr(new Active()); 62 | aPtr->thd_ = std::thread(&Active::run, aPtr.get()); 63 | return aPtr; 64 | } 65 | }; 66 | 67 | 68 | 69 | } // kjellkod 70 | -------------------------------------------------------------------------------- /GenerateMacroDefinitionsFile.cmake: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================*/ 8 | 9 | # Prerequisite : Options.cmake should run first 10 | 11 | SET(HEADER "/** ========================================================================== 12 | * 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 13 | * with no warranties. This code is yours to share, use and modify with no 14 | * strings attached and no restrictions or obligations. 15 | * 16 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 17 | * ============================================================================*/") 18 | 19 | 20 | 21 | message( STATUS "" ) 22 | message( STATUS "COMPILE_DEFINITIONS:\n\t[${G3_DEFINITIONS}]" ) 23 | message( STATUS "" ) 24 | 25 | SET(GENERATED_G3_DEFINITIONS "${CMAKE_CURRENT_BINARY_DIR}/include/g3log/generated_definitions.hpp") 26 | 27 | # If it exists, read existing file 28 | set(current_content "") 29 | if(EXISTS ${GENERATED_G3_DEFINITIONS}) 30 | file(READ ${GENERATED_G3_DEFINITIONS} current_content) 31 | endif() 32 | 33 | set(generated_content "// AUTO GENERATED MACRO DEFINITIONS FOR G3LOG\n\n") 34 | set(generated_content "${generated_content}\n${HEADER}\n") 35 | set(generated_content "${generated_content}\n#pragma once\n\n") 36 | set(generated_content "${generated_content}\n// CMake induced definitions below. See g3log/Options.cmake for details.\n\n") 37 | 38 | FOREACH(definition ${G3_DEFINITIONS} ) 39 | set(generated_content "${generated_content}\n#define ${definition}\n") 40 | ENDFOREACH(definition) 41 | 42 | if(NOT "${current_content}" STREQUAL "${generated_content}") 43 | 44 | message( STATUS "Generated ${GENERATED_G3_DEFINITIONS}" ) 45 | 46 | message( STATUS "******************** START *************************" ) 47 | message(${generated_content}) 48 | message( STATUS "******************** END *************************" ) 49 | 50 | file(WRITE ${GENERATED_G3_DEFINITIONS} ${generated_content}) 51 | 52 | endif() 53 | 54 | -------------------------------------------------------------------------------- /test_unit/test_linux_dynamic_loaded_sharedlib.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "tester_sharedlib.h" 20 | #include 21 | 22 | struct LogMessageCounter { 23 | std::vector& bank; 24 | LogMessageCounter(std::vector& storeMessages) : bank(storeMessages) { 25 | } 26 | 27 | void countMessages(std::string msg) { 28 | bank.push_back(msg); 29 | } 30 | }; 31 | 32 | TEST(DynamicLoadOfLibrary, JustLoadAndExit) { 33 | std::vector receiver; 34 | 35 | { // scope to flush logs at logworker exit 36 | auto worker = g3::LogWorker::createLogWorker(); 37 | auto handle = worker->addSink(std::make_unique(std::ref(receiver)), &LogMessageCounter::countMessages); 38 | 39 | // add another sink just for more throughput of data 40 | auto fileHandle = worker->addSink(std::make_unique("runtimeLoadOfDynamiclibs", "/tmp"), &g3::FileSink::fileWrite); 41 | g3::initializeLogging(worker.get()); 42 | 43 | void* libHandle = dlopen("libtester_sharedlib.so", RTLD_LAZY | RTLD_GLOBAL); 44 | EXPECT_FALSE(nullptr == libHandle); 45 | LibraryFactory* factory = reinterpret_cast ((dlsym(libHandle, "testRealFactory"))); 46 | EXPECT_FALSE(nullptr == factory); 47 | SomeLibrary* loadedLibrary = factory->CreateLibrary(); 48 | 49 | for (auto i = 0; i < 300; ++i) { 50 | loadedLibrary->action(); 51 | } 52 | 53 | delete loadedLibrary; 54 | dlclose(libHandle); 55 | } // scope exit. All log entries must be flushed now 56 | const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library 57 | EXPECT_EQ(receiver.size(), numberOfMessages); 58 | } 59 | -------------------------------------------------------------------------------- /src/g3log/sinkhandle.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/sink.hpp" 12 | 13 | #include 14 | #include 15 | 16 | namespace g3 { 17 | 18 | // The Sinkhandle is the client's access point to the specific sink instance. 19 | // Only through the Sinkhandle can, and should, the real sink's specific API 20 | // be called. 21 | // 22 | // The real sink will be owned by g3log. If the real sink is deleted 23 | // calls to sink's API through the SinkHandle will return an exception embedded 24 | // in the resulting future. Ref: SinkHandle::call 25 | template 26 | class SinkHandle { 27 | std::weak_ptr> _sink; 28 | 29 | public: 30 | SinkHandle(std::shared_ptr> sink) 31 | : _sink(sink) {} 32 | 33 | ~SinkHandle() {} 34 | 35 | 36 | // Asynchronous call to the real sink. If the real sink is already deleted 37 | // the returned future will contain a bad_weak_ptr exception instead of the 38 | // call result. 39 | template 40 | auto call(AsyncCall func , Args&& ... args) -> std::future> { 41 | try { 42 | std::shared_ptr> sink(_sink); 43 | return sink->async(func, std::forward(args)...); 44 | } catch (const std::bad_weak_ptr& e) { 45 | typedef std::invoke_result_t PromiseType; 46 | std::promise promise; 47 | promise.set_exception(std::make_exception_ptr(e)); 48 | return std::move(promise.get_future()); 49 | } 50 | } 51 | 52 | /// Get weak_ptr access to the sink(). Make sure to check that the returned pointer is valid, 53 | /// auto p = sink(); auto ptr = p.lock(); if (ptr) { .... } 54 | /// ref: https://en.cppreference.com/w/cpp/memory/weak_ptr/lock 55 | std::weak_ptr> sink() { 56 | return _sink.lock(); 57 | } 58 | }; 59 | 60 | } // g3 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/g3log/stlpatch_future.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * 8 | * 9 | * 2013/12/28 Bugfix for Visual Studio 2013 which does not handle well 10 | * std::packaged_task. Thanks to Michael Rasmussen (lap777) 11 | * Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable 12 | * ============================================================================*/ 13 | 14 | 15 | 16 | #pragma once 17 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800) 18 | namespace std { 19 | 20 | template 21 | class packaged_task 22 | { 23 | promise _my_promise; 24 | function _my_func; 25 | 26 | public: 27 | packaged_task() { 28 | } 29 | 30 | template 31 | explicit packaged_task(_Fty2 &&_Fnarg) 32 | : _my_func(_Fnarg) { 33 | } 34 | 35 | packaged_task(packaged_task &&_Other) 36 | : _my_promise(move(_Other._my_promise)), 37 | _my_func(move(_Other._my_func)) { 38 | } 39 | 40 | packaged_task &operator=(packaged_task && _Other) { 41 | _my_promise = move(_Other._my_promise); 42 | _my_func = move(_Other._my_func); 43 | return (*this); 44 | } 45 | 46 | packaged_task(const packaged_task &) = delete; 47 | packaged_task &operator=(const packaged_task &) = delete; 48 | 49 | ~packaged_task() { 50 | } 51 | 52 | void swap(packaged_task &_Other) { 53 | swap(_my_promise, _Other._my_promise); 54 | swap(_my_func, _Other._my_func); 55 | } 56 | 57 | explicit operator bool() const { 58 | return _my_func != false; 59 | } 60 | 61 | bool valid() const { 62 | return _my_func != false; 63 | } 64 | 65 | future get_future() { 66 | return _my_promise.get_future(); 67 | } 68 | 69 | void operator()(_ArgTypes... _Args) { 70 | _my_func(forward<_ArgTypes>(_Args)...); 71 | _my_promise.set_value(); 72 | } 73 | 74 | void reset() { 75 | _my_promise.swap(promise()); 76 | _my_func.swap(function()); 77 | } 78 | }; 79 | 80 | }; // namespace std 81 | #endif // defined(WIN32) ... 82 | -------------------------------------------------------------------------------- /src/g3log/shared_queue.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================ 8 | * 9 | * Example of a normal std::queue protected by a mutex for operations, 10 | * making it safe for thread communication, using std::mutex from C++0x with 11 | * the help from the std::thread library from JustSoftwareSolutions 12 | * ref: http://www.stdthread.co.uk/doc/headers/mutex.html 13 | * 14 | * This example was totally inspired by Anthony Williams lock-based data structures in 15 | * Ref: "C++ Concurrency In Action" http://www.manning.com/williams */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | /** Multiple producer, multiple consumer thread safe queue 25 | * Since 'return by reference' is used this queue won't throw */ 26 | template 27 | class shared_queue 28 | { 29 | std::queue queue_; 30 | mutable std::mutex m_; 31 | std::condition_variable data_cond_; 32 | 33 | shared_queue &operator=(const shared_queue &) = delete; 34 | shared_queue(const shared_queue &other) = delete; 35 | 36 | public: 37 | shared_queue() {} 38 | 39 | void push(T item) { 40 | { 41 | std::lock_guard lock(m_); 42 | queue_.push(std::move(item)); 43 | } 44 | data_cond_.notify_one(); 45 | } 46 | 47 | /// \return immediately, with true if successful retrieval 48 | bool try_and_pop(T &popped_item) { 49 | std::lock_guard lock(m_); 50 | if (queue_.empty()) { 51 | return false; 52 | } 53 | popped_item = std::move(queue_.front()); 54 | queue_.pop(); 55 | return true; 56 | } 57 | 58 | /// Try to retrieve, if no items, wait till an item is available and try again 59 | void wait_and_pop(T &popped_item) { 60 | std::unique_lock lock(m_); 61 | while (queue_.empty()) 62 | { 63 | data_cond_.wait(lock); 64 | // This 'while' loop is equal to 65 | // data_cond_.wait(lock, [](bool result){return !queue_.empty();}); 66 | } 67 | popped_item = std::move(queue_.front()); 68 | queue_.pop(); 69 | } 70 | 71 | bool empty() const { 72 | std::lock_guard lock(m_); 73 | return queue_.empty(); 74 | } 75 | 76 | unsigned size() const { 77 | std::lock_guard lock(m_); 78 | return queue_.size(); 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /src/g3log/future.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** ========================================================================== 3 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 4 | * with no warranties. This code is yours to share, use and modify with no 5 | * strings attached and no restrictions or obligations. 6 | * 7 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 8 | * ============================================================================ 9 | * Filename:g3future.hpp 10 | * Helper functionality to put packaged_tasks in standard container. This 11 | * is especially helpful for background thread processing a la async but through 12 | * an actor pattern (active object), thread pool or similar. 13 | * Created: 2012 by Kjell Hedström 14 | * 15 | * COMMUNITY THANKS: 16 | * The code below is in large thanks to exemplifying code snippets from StackOverflow 17 | * question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm 18 | * and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html 19 | * 20 | * Both are highly recommended reads if you are interested in c++ concurrency library 21 | * - Kjell, 2012 22 | * 23 | * PUBLIC DOMAIN and NOT under copywrite protection. 24 | * ********************************************* */ 25 | 26 | 27 | 28 | #include 29 | #include "g3log/active.hpp" 30 | #include "g3log/moveoncopy.hpp" 31 | #include "g3log/stlpatch_future.hpp" 32 | 33 | namespace g3 { 34 | // Generic helper function to avoid repeating the steps for managing 35 | // asynchronous task job (by active object) that returns a future results 36 | // could of course be made even more generic if done more in the way of 37 | // std::async, ref: http://en.cppreference.com/w/cpp/thread/async 38 | // 39 | // Example usage: 40 | // std::unique_ptr bgWorker{Active::createActive()}; 41 | // ... 42 | // auto msg_call=[=](){return ("Hello from the Background");}; 43 | // auto future_msg = g3::spawn_task(msg_lambda, bgWorker.get()); 44 | template 45 | std::future> spawn_task(Func func, BgWorker *worker) 46 | { 47 | typedef std::invoke_result_t result_type; 48 | typedef std::packaged_task task_type; 49 | 50 | if (nullptr == worker) { 51 | auto p = std::make_shared>(); 52 | std::future future_result = p->get_future(); 53 | p->set_exception(std::make_exception_ptr(std::runtime_error("nullptr instantiated worker"))); 54 | return future_result; 55 | } 56 | 57 | task_type task(std::move(func)); 58 | 59 | std::future result = task.get_future(); 60 | worker->send(MoveOnCopy(std::move(task))); 61 | return result; 62 | } 63 | } // end namespace g3 64 | -------------------------------------------------------------------------------- /src/g3log/logcapture.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include "g3log/loglevels.hpp" 12 | #include "g3log/crashhandler.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef _MSC_VER 19 | # include 20 | #endif 21 | 22 | /** 23 | * Simple struct for capturing log/fatal entries. At destruction the captured message is 24 | * forwarded to background worker. 25 | * As a safety precaution: No memory allocated here will be moved into the background 26 | * worker in case of dynamic loaded library reasons 27 | */ 28 | struct LogCapture { 29 | /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) 30 | LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump = nullptr); 31 | 32 | 33 | /** 34 | * @file, line, function are given in g3log.hpp from macros 35 | * @level INFO/DEBUG/WARNING/FATAL 36 | * @expression for CHECK calls 37 | * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler 38 | */ 39 | 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); 40 | 41 | 42 | // At destruction the message will be forwarded to the g3log worker. 43 | // In the case of dynamically (at runtime) loaded libraries, the important thing to know is that 44 | // all strings are copied, so the original are not destroyed at the receiving end, only the copy 45 | virtual ~LogCapture() noexcept (false); 46 | 47 | 48 | 49 | 50 | #ifdef _MSC_VER 51 | # if _MSC_VER >= 1400 52 | # define G3LOG_FORMAT_STRING _Printf_format_string_ 53 | # else 54 | # define G3LOG_FORMAT_STRING __format_string 55 | # endif 56 | 57 | void capturef(G3LOG_FORMAT_STRING const char *printf_like_message, ...); 58 | #else 59 | # define G3LOG_FORMAT_STRING 60 | 61 | // Use "-Wall" to generate warnings in case of illegal printf format. 62 | // Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html 63 | [[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char *printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18 64 | #endif 65 | 66 | /// prettifying API for this completely open struct 67 | std::ostringstream &stream() { 68 | return _stream; 69 | } 70 | 71 | 72 | 73 | std::ostringstream _stream; 74 | std::string _stack_trace; 75 | const char* _file; 76 | const int _line; 77 | const char* _function; 78 | const LEVELS &_level; 79 | const char* _expression; 80 | const g3::SignalType _fatal_signal; 81 | 82 | }; 83 | //} // g3 84 | -------------------------------------------------------------------------------- /test_unit/test_crashhandler_windows.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2014 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #include 11 | 12 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 13 | #include "g3log/stacktrace_windows.hpp" 14 | #include 15 | 16 | 17 | TEST(CrashHandler_Windows, ExceptionType) { 18 | EXPECT_EQ(stacktrace::exceptionIdToText(123), "UNKNOWN EXCEPTION:123"); 19 | EXPECT_EQ(stacktrace::exceptionIdToText(1), "UNKNOWN EXCEPTION:1"); 20 | 21 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ACCESS_VIOLATION), "EXCEPTION_ACCESS_VIOLATION"); 22 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); 23 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_BREAKPOINT), "EXCEPTION_BREAKPOINT"); 24 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_DATATYPE_MISALIGNMENT), "EXCEPTION_DATATYPE_MISALIGNMENT"); 25 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DENORMAL_OPERAND), "EXCEPTION_FLT_DENORMAL_OPERAND"); 26 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_DIVIDE_BY_ZERO), "EXCEPTION_FLT_DIVIDE_BY_ZERO"); 27 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT), "EXCEPTION_FLT_INEXACT_RESULT"); 28 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INEXACT_RESULT), "EXCEPTION_FLT_INEXACT_RESULT"); 29 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_INVALID_OPERATION), "EXCEPTION_FLT_INVALID_OPERATION"); 30 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_OVERFLOW), "EXCEPTION_FLT_OVERFLOW"); 31 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_STACK_CHECK), "EXCEPTION_FLT_STACK_CHECK"); 32 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_FLT_UNDERFLOW), "EXCEPTION_FLT_UNDERFLOW"); 33 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_ILLEGAL_INSTRUCTION), "EXCEPTION_ILLEGAL_INSTRUCTION"); 34 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_IN_PAGE_ERROR), "EXCEPTION_IN_PAGE_ERROR"); 35 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_DIVIDE_BY_ZERO), "EXCEPTION_INT_DIVIDE_BY_ZERO"); 36 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INT_OVERFLOW), "EXCEPTION_INT_OVERFLOW"); 37 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_INVALID_DISPOSITION), "EXCEPTION_INVALID_DISPOSITION"); 38 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_NONCONTINUABLE_EXCEPTION), "EXCEPTION_NONCONTINUABLE_EXCEPTION"); 39 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_PRIV_INSTRUCTION), "EXCEPTION_PRIV_INSTRUCTION"); 40 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_SINGLE_STEP), "EXCEPTION_SINGLE_STEP"); 41 | EXPECT_EQ(stacktrace::exceptionIdToText(EXCEPTION_STACK_OVERFLOW), "EXCEPTION_STACK_OVERFLOW"); 42 | } 43 | 44 | #endif // defined WIN32 -------------------------------------------------------------------------------- /src/g3log/sink.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | 12 | #include "g3log/sinkwrapper.hpp" 13 | #include "g3log/active.hpp" 14 | #include "g3log/future.hpp" 15 | #include "g3log/logmessage.hpp" 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace g3 { 22 | namespace internal { 23 | typedef std::function AsyncMessageCall; 24 | 25 | /// The asynchronous Sink has an active object, incoming requests for actions 26 | // will be processed in the background by the specific object the Sink represents. 27 | // 28 | // The Sink will wrap either 29 | // a Sink with Message object receiving call 30 | // or a Sink with a LogEntry (string) receiving call 31 | // 32 | // The Sink can also be used through the SinkHandler to call Sink specific function calls 33 | // Ref: send(Message) deals with incoming log entries (converted if necessary to string) 34 | // Ref: send(Call call, Args... args) deals with calls 35 | // to the real sink's API 36 | 37 | template 38 | struct Sink : public SinkWrapper { 39 | std::unique_ptr _real_sink; 40 | std::unique_ptr _bg; 41 | AsyncMessageCall _default_log_call; 42 | 43 | template 44 | Sink(std::unique_ptr sink, DefaultLogCall call) 45 | : SinkWrapper(), 46 | _real_sink {std::move(sink)}, 47 | _bg(kjellkod::Active::createActive()), 48 | _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { 49 | } 50 | 51 | 52 | Sink(std::unique_ptr sink, void(T::*Call)(std::string) ) 53 | : SinkWrapper(), 54 | _real_sink {std::move(sink)}, 55 | _bg(kjellkod::Active::createActive()) { 56 | std::function adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); 57 | _default_log_call = [ = ](LogMessageMover m) { 58 | adapter(m.get().toString()); 59 | }; 60 | } 61 | 62 | virtual ~Sink() { 63 | _bg.reset(); // TODO: to remove 64 | } 65 | 66 | void send(LogMessageMover msg) override { 67 | _bg->send([this, msg] { 68 | _default_log_call(msg); 69 | }); 70 | } 71 | 72 | template 73 | auto async(Call call, Args &&... args)-> std::future> { 74 | return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward(args)...), _bg.get()); 75 | } 76 | }; 77 | } // internal 78 | } // g3 79 | -------------------------------------------------------------------------------- /example/main_contract.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace 17 | { 18 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 19 | const std::string path_to_log_file = "./"; 20 | #else 21 | const std::string path_to_log_file = "/tmp/"; 22 | #endif 23 | } 24 | 25 | namespace example_fatal 26 | { 27 | void killWithContractIfNonEqual(int first, int second) 28 | { 29 | 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"; 30 | } 31 | } // example fatal 32 | 33 | int main(int argc, char **argv) 34 | { 35 | double pi_d = 3.1415926535897932384626433832795; 36 | float pi_f = 3.1415926535897932384626433832795f; 37 | 38 | auto worker = g3::LogWorker::createLogWorker(); 39 | auto handle= worker->addDefaultLogger(argv[0], path_to_log_file); 40 | g3::initializeLogging(worker.get()); 41 | std::future log_file_name = handle->call(&g3::FileSink::fileName); 42 | 43 | // Exmple of overriding the default formatting of log entry 44 | auto changeFormatting = handle->call(&g3::FileSink::overrideLogDetails, g3::LogMessage::FullLogDetailsToString); 45 | 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"; 46 | // example of ovrriding the default formatting of header 47 | auto changeHeader = handle->call(&g3::FileSink::overrideLogHeader, newHeader); 48 | 49 | changeFormatting.wait(); 50 | changeHeader.wait(); 51 | 52 | 53 | std::cout << "* This is an example of g3log. It WILL exit by a failed CHECK(...)" << std::endl; 54 | std::cout << "* that acts as a FATAL trigger. Please see the generated log and " << std::endl; 55 | std::cout << "* compare to the code at:\n* \t g3log/test_example/main_contract.cpp" << std::endl; 56 | std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl; 57 | 58 | LOGF(INFO, "Hi log %d", 123); 59 | LOG(INFO) << "Test SLOG INFO"; 60 | LOG(G3LOG_DEBUG) << "Test SLOG DEBUG"; 61 | LOG(INFO) << "one: " << 1; 62 | LOG(INFO) << "two: " << 2; 63 | LOG(INFO) << "one and two: " << 1 << " and " << 2; 64 | LOG(G3LOG_DEBUG) << "float 2.14: " << 1000 / 2.14f; 65 | LOG(G3LOG_DEBUG) << "pi double: " << pi_d; 66 | LOG(G3LOG_DEBUG) << "pi float: " << pi_f; 67 | LOG(G3LOG_DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f; 68 | LOGF(INFO, "pi float printf:%f", pi_f); 69 | 70 | // FATAL SECTION 71 | int smaller = 1; 72 | int larger = 2; 73 | example_fatal::killWithContractIfNonEqual(smaller, larger); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/g3log/crashhandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** ========================================================================== 4 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 5 | * with no warranties. This code is yours to share, use and modify with no 6 | * strings attached and no restrictions or obligations. 7 | * 8 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 9 | * ============================================================================*/ 10 | #include 11 | #include 12 | #include 13 | #include "g3log/loglevels.hpp" 14 | #include "g3log/generated_definitions.hpp" 15 | 16 | // kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp 17 | // implementationsfilen kan vara den samma 18 | namespace g3 { 19 | 20 | // PUBLIC API: 21 | /** Install signal handler that catches FATAL C-runtime or OS signals 22 | See the wikipedia site for details http://en.wikipedia.org/wiki/SIGFPE 23 | See the this site for example usage: http://www.tutorialspoint.com/cplusplus/cpp_signal_handling 24 | SIGABRT ABORT (ANSI), abnormal termination 25 | SIGFPE Floating point exception (ANSI) 26 | SIGILL ILlegal instruction (ANSI) 27 | SIGSEGV Segmentation violation i.e. illegal memory reference 28 | SIGTERM TERMINATION (ANSI) */ 29 | void installCrashHandler(); 30 | 31 | 32 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 33 | typedef unsigned long SignalType; 34 | /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread 35 | /// on Windows. This is automatically done if you do at least one LOG(...) call 36 | /// you can also use this function call, per thread so make sure these three 37 | /// fatal signals are covered in your thread (even if you don't do a LOG(...) call 38 | void installSignalHandlerForThread(); 39 | #else 40 | typedef int SignalType; 41 | std::string signalToStr(int signal_number); 42 | 43 | // restore to whatever signal handler was used before signal handler installation 44 | void restoreSignalHandler(int signal_number); 45 | 46 | 47 | /// Overrides the existing signal handling for custom signals 48 | /// For example: usage of zcmq relies on its own signal handler for SIGTERM 49 | /// so users of g3log with zcmq should then use the @ref overrideSetupSignals 50 | /// , likely with the original set of signals but with SIGTERM removed 51 | /// 52 | /// call example: 53 | /// g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"},{SIGILL, "SIGILL"}, 54 | // {SIGSEGV, "SIGSEGV"},}); 55 | void overrideSetupSignals(const std::map overrideSignals); 56 | #endif 57 | 58 | 59 | namespace internal { 60 | /// Resets the fatal signal/exception handling back to default 61 | /// which might be needed in case it was previously overridden 62 | /// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM 63 | void restoreFatalHandlingToDefault(); 64 | 65 | 66 | /** return whether or any fatal handling is still ongoing 67 | * this is used by g3log::fatalCallToLogger 68 | * only in the case of Windows exceptions (not fatal signals) 69 | * are we interested in changing this from false to true to 70 | * help any other exceptions handler work with 'EXCEPTION_CONTINUE_SEARCH'*/ 71 | bool shouldBlockForFatalHandling(); 72 | 73 | /** \return signal_name Ref: signum.hpp and \ref installSignalHandler 74 | * or for Windows exception name */ 75 | std::string exitReasonName(const LEVELS& level, g3::SignalType signal_number); 76 | 77 | /** return calling thread's stackdump*/ 78 | std::string stackdump(const char* dump = nullptr); 79 | 80 | /** Re-"throw" a fatal signal, previously caught. This will exit the application 81 | * This is an internal only function. Do not use it elsewhere. It is triggered 82 | * from g3log, g3LogWorker after flushing messages to file */ 83 | void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number); 84 | } // end g3::internal 85 | } // g3 86 | -------------------------------------------------------------------------------- /test_unit/testing_helpers.h: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "g3log/logworker.hpp" 18 | #include "g3log/logmessage.hpp" 19 | #include "g3log/filesink.hpp" 20 | 21 | namespace testing_helpers { 22 | 23 | std::string mockFatalMessage(); 24 | int mockFatalSignal(); 25 | bool mockFatalWasCalled(); 26 | void mockFatalCall(g3::FatalMessagePtr fatal_message); 27 | void clearMockFatal(); 28 | 29 | bool removeFile(std::string path_to_file); 30 | bool verifyContent(const std::string& total_text, std::string msg_to_find); 31 | std::string readFileToText(std::string filename); 32 | 33 | 34 | 35 | /** After initializing ScopedCout all std::couts is redirected to the buffer 36 | @verbatim 37 | Example: 38 | stringstream buffer; 39 | ScopedCout guard(std::cout, &buffer); // std::cerr is also fine 40 | cout << "Hello World"; 41 | ASSERT_STREQ(buffer.str().c_str(), "Hello World"); */ 42 | class ScopedOut { 43 | std::ostream& _out_type; 44 | std::streambuf* _old_cout; 45 | public: 46 | explicit ScopedOut(std::ostream& out_type, std::stringstream* buffer) 47 | : _out_type(out_type) 48 | , _old_cout(_out_type.rdbuf()) { 49 | _out_type.rdbuf(buffer->rdbuf()); 50 | } 51 | 52 | virtual ~ScopedOut() { 53 | _out_type.rdbuf(_old_cout); 54 | } 55 | }; 56 | 57 | /// RAII cluttering files cleanup 58 | class LogFileCleaner { 59 | private: 60 | std::vector logs_to_clean_; 61 | std::mutex g_mutex; 62 | public: 63 | size_t size(); 64 | LogFileCleaner() {} 65 | virtual ~LogFileCleaner(); 66 | void addLogToClean(std::string path_to_log); 67 | }; 68 | 69 | 70 | struct ScopedLogger { 71 | ScopedLogger(); 72 | virtual ~ScopedLogger(); 73 | 74 | g3::LogWorker* get(); 75 | std::unique_ptr _currentWorker; 76 | }; 77 | 78 | /** RAII temporarily replace of logger 79 | * and restoration of original logger at scope end*/ 80 | struct RestoreFileLogger { 81 | explicit RestoreFileLogger(std::string directory); 82 | ~RestoreFileLogger(); 83 | 84 | std::unique_ptr _scope; 85 | void reset() { _scope.reset();} 86 | 87 | 88 | template 89 | std::invoke_result_t callToLogger(Call call, Args&& ... args) { 90 | auto func = std::bind(call, _scope->get(), std::forward(args)...); 91 | return func(); 92 | } 93 | 94 | std::string logFile(); 95 | std::string resetAndRetrieveContent(); 96 | std::unique_ptr> _handle; 97 | std::string _log_file; 98 | }; 99 | 100 | typedef std::shared_ptr> AtomicBoolPtr; 101 | typedef std::shared_ptr> AtomicIntPtr; 102 | struct ScopedSetTrue { 103 | AtomicBoolPtr _flag; 104 | AtomicIntPtr _count; 105 | 106 | explicit ScopedSetTrue(AtomicBoolPtr flag, AtomicIntPtr count) 107 | : _flag(flag), _count(count) { 108 | } 109 | 110 | void ReceiveMsg(std::string message) { 111 | std::chrono::milliseconds wait{100}; 112 | std::this_thread::sleep_for(wait); 113 | ++(*_count); 114 | } 115 | 116 | ~ScopedSetTrue() { 117 | (*_flag) = true; 118 | } 119 | }; 120 | } // testing_helpers 121 | 122 | #ifdef CHANGE_G3LOG_DEBUG_TO_DBUG 123 | #undef DEBUG 124 | #define DEBUG DBUG 125 | #endif 126 | 127 | 128 | -------------------------------------------------------------------------------- /src/g3log/time.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** ========================================================================== 3 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 4 | * with no warranties. This code is yours to share, use and modify with no 5 | * strings attached and no restrictions or obligations. 6 | * 7 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 8 | * ============================================================================ 9 | * Filename:g3time.h cross-platform, thread-safe replacement for C++11 non-thread-safe 10 | * localtime (and similar) 11 | * Created: 2012 by Kjell Hedström 12 | * 13 | * PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc 14 | * ********************************************* */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | // FYI: 21 | // namespace g3::internal ONLY in g3time.cpp 22 | // std::string put_time(const struct tm* tmb, const char* c_time_format) 23 | 24 | namespace g3 { 25 | typedef std::chrono::time_point system_time_point; 26 | typedef std::chrono::time_point high_resolution_time_point; 27 | typedef std::chrono::milliseconds milliseconds; 28 | typedef std::chrono::microseconds microseconds; 29 | 30 | namespace internal { 31 | enum class Fractional {Millisecond, Microsecond, Nanosecond, NanosecondDefault}; 32 | Fractional getFractional(const std::string& format_buffer, size_t pos); 33 | std::string to_string(const g3::system_time_point& ts, Fractional fractional); 34 | std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer); 35 | static const std::string date_formatted = "%Y/%m/%d"; 36 | // %f: fractions of seconds (%f is nanoseconds) 37 | // %f3: milliseconds, 3 digits: 001 38 | // %6: microseconds: 6 digits: 000001 --- default for the time_format 39 | // %f9, %f: nanoseconds, 9 digits: 000000001 40 | static const std::string time_formatted = "%H:%M:%S %f6"; 41 | } // internal 42 | 43 | 44 | // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" 45 | // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. 46 | // return value is SIMPLIFIED to only return a std::string 47 | std::string put_time(const struct tm* tmb, const char* c_time_format); 48 | 49 | /** return time representing POD struct (ref ctime + wchar) that is normally 50 | * retrieved with std::localtime. g3::localtime is threadsafe which std::localtime is not. 51 | * g3::localtime is probably used together with @ref g3::systemtime_now */ 52 | tm localtime(const std::time_t& time); 53 | 54 | /** format string must conform to std::put_time's demands. 55 | * WARNING: At time of writing there is only so-so compiler support for 56 | * std::put_time. A possible fix if your c++11 library is not updated is to 57 | * modify this to use std::strftime instead */ 58 | std::string localtime_formatted(const system_time_point& ts, const std::string& time_format) ; 59 | 60 | inline system_time_point to_system_time(const high_resolution_time_point& ts) 61 | { 62 | // On some (windows) systems, the system_clock does not provide the highest possible time 63 | // resolution. Thus g3log uses high_resolution_clock for message time stamps. However, 64 | // unlike system_clock, high_resolution_clock cannot be converted to a time and date as 65 | // it usually measures reflects the time since power-up. 66 | // Thus, hrs_now and sys_now are recorded once when the program starts to be able to convert 67 | // timestamps to dime and date using to_system_time(). The precision of the absolute time is 68 | // of course that of system_clock() with some error added due to the non-simultaneous initialization 69 | // of the two static variables but relative times within one log will be as precise as 70 | // high_resolution_clock. 71 | using namespace std::chrono; 72 | static const auto hrs_now = high_resolution_clock::now(); 73 | static const auto sys_now = system_clock::now(); 74 | 75 | return time_point_cast(sys_now + (ts - hrs_now)); 76 | } 77 | } 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/loglevels.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/loglevels.hpp" 10 | #include 11 | 12 | #include 13 | 14 | namespace g3 { 15 | namespace internal { 16 | bool wasFatal(const LEVELS& level) { 17 | return level.value >= FATAL.value; 18 | } 19 | 20 | #ifdef G3_DYNAMIC_LOGGING 21 | const std::map g_log_level_defaults = { 22 | {G3LOG_DEBUG.value,{G3LOG_DEBUG}}, 23 | {INFO.value, {INFO}}, 24 | {WARNING.value, {WARNING}}, 25 | {FATAL.value, {FATAL}} 26 | }; 27 | 28 | std::map g_log_levels = g_log_level_defaults; 29 | #endif 30 | } // internal 31 | 32 | #ifdef G3_DYNAMIC_LOGGING 33 | namespace only_change_at_initialization { 34 | 35 | void addLogLevel(LEVELS lvl, bool enabled) { 36 | int value = lvl.value; 37 | internal::g_log_levels[value] = {lvl, enabled}; 38 | } 39 | 40 | 41 | void addLogLevel(LEVELS level) { 42 | addLogLevel(level, true); 43 | } 44 | 45 | void reset() { 46 | g3::internal::g_log_levels = g3::internal::g_log_level_defaults; 47 | } 48 | } // only_change_at_initialization 49 | 50 | 51 | namespace log_levels { 52 | 53 | void setHighest(LEVELS enabledFrom) { 54 | auto it = internal::g_log_levels.find(enabledFrom.value); 55 | if (it != internal::g_log_levels.end()) { 56 | for (auto& v : internal::g_log_levels) { 57 | if (v.first < enabledFrom.value) { 58 | disable(v.second.level); 59 | } else { 60 | enable(v.second.level); 61 | } 62 | 63 | } 64 | } 65 | } 66 | 67 | 68 | void set(LEVELS level, bool enabled) { 69 | auto it = internal::g_log_levels.find(level.value); 70 | if (it != internal::g_log_levels.end()) { 71 | internal::g_log_levels[level.value] = {level, enabled}; 72 | } 73 | } 74 | 75 | 76 | void disable(LEVELS level) { 77 | set(level, false); 78 | } 79 | 80 | void enable(LEVELS level) { 81 | set(level, true); 82 | } 83 | 84 | 85 | void disableAll() { 86 | for (auto& v : internal::g_log_levels) { 87 | v.second.status = false; 88 | } 89 | } 90 | 91 | void enableAll() { 92 | for (auto& v : internal::g_log_levels) { 93 | v.second.status = true; 94 | } 95 | } 96 | 97 | 98 | std::string to_string(std::map levelsToPrint) { 99 | std::string levels; 100 | for (auto& v : levelsToPrint) { 101 | levels += "name: " + v.second.level.text + " level: " + std::to_string(v.first) + " status: " + std::to_string(v.second.status.value()) + "\n"; 102 | } 103 | return levels; 104 | } 105 | 106 | std::string to_string() { 107 | return to_string(internal::g_log_levels); 108 | } 109 | 110 | 111 | std::map getAll() { 112 | return internal::g_log_levels; 113 | } 114 | 115 | // status : {Absent, Enabled, Disabled}; 116 | status getStatus(LEVELS level) { 117 | const auto it = internal::g_log_levels.find(level.value); 118 | if (internal::g_log_levels.end() == it) { 119 | return status::Absent; 120 | } 121 | 122 | return (it->second.status.get().load() ? status::Enabled : status::Disabled); 123 | 124 | } 125 | } // log_levels 126 | 127 | #endif 128 | 129 | 130 | bool logLevel(const LEVELS& log_level) { 131 | #ifdef G3_DYNAMIC_LOGGING 132 | int level = log_level.value; 133 | bool status = internal::g_log_levels[level].status.value(); 134 | return status; 135 | #else 136 | return true; 137 | #endif 138 | } 139 | } // g3 140 | -------------------------------------------------------------------------------- /CPackLists.txt: -------------------------------------------------------------------------------- 1 | # ========================================================================== 2 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================*/ 8 | 9 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 10 | IF(NOT CPACK_PACKAGING_INSTALL_PREFIX) 11 | IF(NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 12 | SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) 13 | # set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}) 14 | ELSE() 15 | SET(CPACK_PACKAGING_INSTALL_PREFIX /usr/local) 16 | ENDIF() 17 | ENDIF() 18 | # message("Install rpath location: ${CMAKE_INSTALL_RPATH}") 19 | ENDIF() 20 | 21 | INCLUDE(CMakePackageConfigHelpers) 22 | INCLUDE(GNUInstallDirs) 23 | 24 | SET(CPACK_PACKAGE_NAME g3log) 25 | SET(CPACK_PACKAGE_VERSION_MAJOR ${MAJOR_VERSION}) 26 | SET(CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION}) 27 | SET(CPACK_PACKAGE_VERSION_PATCH ${BUILD_NUMBER}) 28 | SET(CPACK_PACKAGE_DESCRIPTION "Asynchronous 'crash safe' logger 29 | License: http://unlicense.org 30 | Repository: https://github.com/KjellKod/g3log") 31 | SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CPACK_PACKAGE_DESCRIPTION}) 32 | SET(CPACK_PACKAGE_CONTACT "Kjell Hedstrom hedstrom@kjellkoc.cc") 33 | SET(CPACK_RESOURCE_FILE_LICENSE ${g3log_SOURCE_DIR}/LICENSE) 34 | SET(CPACK_PACKAGE_VENDOR "KjellKod") 35 | 36 | IF(INSTALL_G3LOG) 37 | INSTALL( TARGETS g3log 38 | EXPORT g3log-targets 39 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries 40 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries 41 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries 42 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 43 | ) 44 | 45 | INSTALL( FILES ${HEADER_FILES} 46 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/g3log 47 | COMPONENT headers) 48 | 49 | INSTALL( 50 | EXPORT g3log-targets 51 | FILE g3logTargets.cmake 52 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log 53 | ) 54 | 55 | CONFIGURE_PACKAGE_CONFIG_FILE( 56 | ${PROJECT_SOURCE_DIR}/cmake/g3logConfig.cmake 57 | ${CMAKE_CURRENT_BINARY_DIR}/g3logConfig.cmake 58 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log 59 | ) 60 | 61 | install( 62 | FILES ${CMAKE_CURRENT_BINARY_DIR}/g3logConfig.cmake 63 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/g3log 64 | ) 65 | ENDIF() 66 | 67 | 68 | SET(CPACK_COMPONENTS_ALL libraries headers) 69 | SET(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "g3log libraries") 70 | SET(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "g3log C++ headers") 71 | 72 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 73 | SET(CPACK_GENERATOR "DEB") 74 | SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "KjellKod - Kjell Hedstrom") 75 | ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") 76 | SET(CPACK_GENERATOR "ZIP") # Otherwise, NSIS is needed. 77 | ENDIF() 78 | 79 | message( STATUS "\nTo create installation package: " ) 80 | message( STATUS "make package" ) 81 | 82 | message( STATUS "\nOption to install using 'make install'" ) 83 | message( STATUS "Installation locations: " ) 84 | message( STATUS "====================" ) 85 | message( STATUS "Headers: ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/g3log" ) 86 | message( STATUS "Library installation directory: ${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" ) 87 | 88 | message( STATUS "For more information please see g3log/CPackLists.txt\n\n" ) 89 | IF(NOT MINGW) 90 | message( STATUS "To install: sudo dpkg -i g3log-***Linux.deb" ) 91 | message( STATUS "To list package contents: sudo dpkg --contents g3log-***Linux.deb" ) 92 | 93 | message( STATUS "List content of the installed package: sudo dpkg -L g3log" ) 94 | message( STATUS "To remove: sudo dpkg -r g3log" ) 95 | ENDIF() 96 | # NOTE: to change installation locations you can use the settings below 97 | # examples: 98 | # CPACK_PACKAGING_INSTALL_PREFIX 99 | # CPACK_OUTPUT_FILE_PREFIX 100 | # CMAKE_INSTALL_PREFIX 101 | 102 | INCLUDE(CPack) 103 | 104 | 105 | -------------------------------------------------------------------------------- /iOSBuild.cmake: -------------------------------------------------------------------------------- 1 | if(IOS_PLATFORM) 2 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) 3 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) 4 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/Binaries) 5 | endif(IOS_PLATFORM) 6 | 7 | 8 | if(G3_IOS_LIB) 9 | if (CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET) 10 | set (ENV{CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET} ${CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET}) 11 | endif(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET) 12 | 13 | set(TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/iOS.cmake") 14 | 15 | set(SIM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.i386" CACHE INTERNAL "") 16 | set(SIM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") 17 | 18 | set(SIM64_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.x86_64" CACHE INTERNAL "") 19 | set(SIM64_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") 20 | 21 | set(ARM_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build.arm" CACHE INTERNAL "") 22 | set(ARM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "") 23 | 24 | file(MAKE_DIRECTORY ${SIM_BINARY_DIR}) 25 | execute_process(WORKING_DIRECTORY ${SIM_BINARY_DIR} 26 | COMMAND ${CMAKE_COMMAND} 27 | -GXcode 28 | -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} 29 | -DIOS_PLATFORM=SIMULATOR 30 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 31 | -DADD_FATAL_EXAMPLE=OFF 32 | -DADD_G3LOG_BENCH_PERFORMANCE=OFF 33 | -DADD_G3LOG_UNIT_TEST=OFF 34 | -DG3_SHARED_LIB=OFF 35 | -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON 36 | -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE} 37 | -DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING} 38 | "${SIM_SOURCE_DIR}" 39 | ) 40 | 41 | file(MAKE_DIRECTORY ${SIM64_BINARY_DIR}) 42 | execute_process(WORKING_DIRECTORY ${SIM64_BINARY_DIR} 43 | COMMAND ${CMAKE_COMMAND} 44 | -GXcode 45 | -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} 46 | -DIOS_PLATFORM=SIMULATOR64 47 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 48 | -DADD_FATAL_EXAMPLE=OFF 49 | -DG3_SHARED_LIB=OFF 50 | -DADD_G3LOG_BENCH_PERFORMANCE=OFF 51 | -DADD_G3LOG_UNIT_TEST=OFF 52 | -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON 53 | -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE} 54 | -DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING} 55 | "${SIM64_SOURCE_DIR}" 56 | ) 57 | 58 | file(MAKE_DIRECTORY ${ARM_BINARY_DIR}) 59 | execute_process(WORKING_DIRECTORY ${ARM_BINARY_DIR} 60 | COMMAND ${CMAKE_COMMAND} 61 | -GXcode 62 | -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FILE} 63 | -DIOS_PLATFORM=OS 64 | -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} 65 | -DADD_FATAL_EXAMPLE=OFF 66 | -DG3_SHARED_LIB=OFF 67 | -DADD_G3LOG_BENCH_PERFORMANCE=OFF 68 | -DADD_G3LOG_UNIT_TEST=OFF 69 | -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON 70 | -DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=${USE_G3_DYNAMIC_MAX_MESSAGE_SIZE} 71 | -DENABLE_FATAL_SIGNALHANDLING=${ENABLE_FATAL_SIGNALHANDLING} 72 | "${ARM_SOURCE_DIR}" 73 | ) 74 | 75 | ## Simulator i386 version 76 | add_custom_target(sim 77 | COMMAND ${CMAKE_COMMAND} 78 | --build ${SIM_BINARY_DIR} 79 | --config ${CMAKE_BUILD_TYPE} 80 | COMMENT "Building for i386 (simulator)" 81 | VERBATIM 82 | ) 83 | 84 | ## Simulator x86_64 version 85 | add_custom_target(sim64 86 | COMMAND ${CMAKE_COMMAND} 87 | --build ${SIM64_BINARY_DIR} 88 | --config ${CMAKE_BUILD_TYPE} 89 | COMMENT "Building for x86_64 (simulator)" 90 | VERBATIM 91 | ) 92 | 93 | ## ARM version 94 | add_custom_target(arm 95 | COMMAND ${CMAKE_COMMAND} 96 | --build ${ARM_BINARY_DIR} 97 | --config ${CMAKE_BUILD_TYPE} 98 | COMMENT "Building for armv7, armv7s, arm64, arm64e" 99 | VERBATIM 100 | ) 101 | 102 | set(LIB_G3 libg3log.a) 103 | add_custom_command( 104 | OUTPUT ${LIB_G3} 105 | COMMAND lipo -create 106 | -output "${CMAKE_CURRENT_BINARY_DIR}/${LIB_G3}" 107 | ${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3} 108 | ${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3} 109 | ${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3} 110 | DEPENDS 111 | sim 112 | sim64 113 | arm 114 | "${SIM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}" 115 | "${SIM64_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}" 116 | "${ARM_BINARY_DIR}/Binaries/${CMAKE_BUILD_TYPE}/${LIB_G3}" 117 | VERBATIM 118 | ) 119 | add_custom_target(g3log ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${LIB_G3}) 120 | endif(G3_IOS_LIB) 121 | 122 | -------------------------------------------------------------------------------- /test_performance/performance.h: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #if defined(G3LOG_PERFORMANCE) 23 | #include 24 | #include 25 | using namespace g3::internal; 26 | 27 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 28 | #include 29 | #else 30 | #error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined 31 | #endif 32 | 33 | typedef std::chrono::high_resolution_clock::time_point time_point; 34 | typedef std::chrono::duration > millisecond; 35 | typedef std::chrono::duration > microsecond; 36 | 37 | namespace g3_test 38 | { 39 | enum WriteMode 40 | { 41 | kAppend = 0, 42 | kTruncate = 1 43 | }; 44 | 45 | const uint64_t g_loop{1}; 46 | const uint64_t g_iterations{1000000}; 47 | const char* charptrmsg = "\tmessage by char*"; 48 | const std::string strmsg{"\tmessage by string"}; 49 | float pi_f{3.1415926535897932384626433832795f}; 50 | 51 | 52 | bool writeTextToFile(const std::string& filename, const std::string& msg, const WriteMode write_mode, bool push_out = true) 53 | { 54 | if(push_out) 55 | { 56 | std::cout << msg << std::flush; 57 | } 58 | 59 | std::ofstream out; 60 | std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream 61 | (kTruncate == write_mode) ? mode |= std::ios_base::trunc : mode |= std::ios_base::app; 62 | out.open(filename.c_str(), mode); 63 | if (!out.is_open()) 64 | { 65 | std::ostringstream ss_error; 66 | ss_error << "Fatal error could not open log file:[" << filename << "]"; 67 | ss_error << "\n\t\t std::ios_base state = " << out.rdstate(); 68 | std::cerr << ss_error.str().c_str() << std::endl << std::flush; 69 | return false; 70 | } 71 | 72 | out << msg; 73 | return true; 74 | } 75 | 76 | uint64_t mean(const std::vector &v) 77 | { 78 | uint64_t total = std::accumulate(v.begin(), v.end(), uint64_t(0) ); // '0' is the initial value 79 | return total/v.size(); 80 | } 81 | 82 | 83 | 84 | 85 | void measurePeakDuringLogWrites(const std::string& title, std::vector& result); 86 | inline void measurePeakDuringLogWrites(const std::string& title, std::vector& result) 87 | { 88 | 89 | 90 | #if defined(G3LOG_PERFORMANCE) 91 | std::cout << "G3LOG (" << title << ") WORST_PEAK PERFORMANCE TEST" << std::endl; 92 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 93 | std::cout << "GOOGLE_GLOG (" << title << ") WORST_PEAK PERFORMANCE TEST" << std::endl; 94 | #else 95 | std::cout << "ERROR no performance type chosen" << std::endl; 96 | assert(false); 97 | #endif 98 | for(uint64_t count = 0; count < g_iterations; ++count) 99 | { 100 | auto start_time = std::chrono::high_resolution_clock::now(); 101 | LOG(INFO) << title << " iteration #" << count << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f; 102 | auto stop_time = std::chrono::high_resolution_clock::now(); 103 | uint64_t time_us = std::chrono::duration_cast(stop_time - start_time).count(); 104 | result.push_back(time_us); 105 | } 106 | } 107 | 108 | 109 | void doLogWrites(const std::string& title); 110 | inline void doLogWrites(const std::string& title) 111 | { 112 | #if defined(G3LOG_PERFORMANCE) 113 | std::cout << "G3LOG (" << title << ") PERFORMANCE TEST" << std::endl; 114 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 115 | std::cout << "GOOGLE_GLOG (" << title << ") PERFORMANCE TEST" << std::endl; 116 | #else 117 | std::cout << "ERROR no performance type chosen" << std::endl; 118 | assert(false); 119 | #endif 120 | for(uint64_t count = 0; count < g_iterations; ++count) 121 | { 122 | LOG(INFO) << title << " iteration #" << count << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f; 123 | } 124 | } 125 | 126 | 127 | } // end namespace 128 | -------------------------------------------------------------------------------- /src/filesink.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/filesink.hpp" 10 | #include "filesinkhelper.ipp" 11 | #include 12 | #include 13 | 14 | namespace g3 { 15 | using namespace internal; 16 | 17 | FileSink::FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string& logger_id) 18 | : _log_details_func(&LogMessage::DefaultLogDetailsToString) 19 | ,_log_file_with_path(log_directory) 20 | , _log_prefix_backup(log_prefix) 21 | , _outptr(new std::ofstream) 22 | , _header("\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE->FUNCTION:LINE] message\n\n\t\t(uuu*: microseconds fractions of the seconds value)\n\n") 23 | , _firstEntry(true) 24 | { 25 | _log_prefix_backup = prefixSanityFix(log_prefix); 26 | if (!isValidFilename(_log_prefix_backup)) { 27 | std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; 28 | abort(); 29 | } 30 | 31 | std::string file_name = createLogFileName(_log_prefix_backup, logger_id); 32 | _log_file_with_path = pathSanityFix(_log_file_with_path, file_name); 33 | _outptr = createLogFile(_log_file_with_path); 34 | 35 | if (!_outptr) { 36 | std::cerr << "Cannot write log file to location, attempting current directory" << std::endl; 37 | _log_file_with_path = "./" + file_name; 38 | _outptr = createLogFile(_log_file_with_path); 39 | } 40 | assert(_outptr && "cannot open log file at startup"); 41 | } 42 | 43 | 44 | FileSink::~FileSink() { 45 | std::string exit_msg {"g3log g3FileSink shutdown at: "}; 46 | auto now = std::chrono::system_clock::now(); 47 | exit_msg.append(localtime_formatted(now, internal::time_formatted)).append("\n"); 48 | filestream() << exit_msg << std::flush; 49 | 50 | exit_msg.append("Log file at: [").append(_log_file_with_path).append("]\n"); 51 | std::cerr << exit_msg << std::flush; 52 | } 53 | 54 | // The actual log receiving function 55 | void FileSink::fileWrite(LogMessageMover message) { 56 | if (_firstEntry ) { 57 | addLogFileHeader(); 58 | _firstEntry = false; 59 | } 60 | 61 | std::ofstream &out(filestream()); 62 | out << message.get().toString(_log_details_func) << std::flush; 63 | } 64 | 65 | std::string FileSink::changeLogFile(const std::string &directory, const std::string &logger_id) { 66 | 67 | auto now = std::chrono::system_clock::now(); 68 | auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted}); 69 | 70 | std::string file_name = createLogFileName(_log_prefix_backup, logger_id); 71 | std::string prospect_log = directory + file_name; 72 | std::unique_ptr log_stream = createLogFile(prospect_log); 73 | if (nullptr == log_stream) { 74 | filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; 75 | return {}; // no success 76 | } 77 | 78 | addLogFileHeader(); 79 | std::ostringstream ss_change; 80 | ss_change << "\n\tChanging log file from : " << _log_file_with_path; 81 | ss_change << "\n\tto new location: " << prospect_log << "\n"; 82 | filestream() << now_formatted << ss_change.str(); 83 | ss_change.str(""); 84 | 85 | std::string old_log = _log_file_with_path; 86 | _log_file_with_path = prospect_log; 87 | _outptr = std::move(log_stream); 88 | ss_change << "\n\tNew log file. The previous log file was at: "; 89 | ss_change << old_log << "\n"; 90 | filestream() << now_formatted << ss_change.str(); 91 | return _log_file_with_path; 92 | } 93 | 94 | std::string FileSink::fileName() { 95 | return _log_file_with_path; 96 | } 97 | 98 | void FileSink::overrideLogDetails(LogMessage::LogDetailsFunc func) { 99 | _log_details_func = func; 100 | } 101 | 102 | void FileSink::overrideLogHeader(const std::string& change) { 103 | _header = change; 104 | } 105 | 106 | void FileSink::addLogFileHeader() { 107 | filestream() << header(_header); 108 | } 109 | } // g3 110 | -------------------------------------------------------------------------------- /src/logcapture.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/logcapture.hpp" 10 | #include "g3log/g3log.hpp" 11 | #include "g3log/crashhandler.hpp" 12 | 13 | #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE 14 | #include 15 | #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ 16 | 17 | // For Windows we need force a thread_local install per thread of three 18 | // signals that must have a signal handler installed per thread-basis 19 | // It is really a royal pain. Seriously Microsoft? Seriously? 20 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 21 | #define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread() 22 | #else 23 | // Does nothing --- enforces that semicolon must be written 24 | #define SIGNAL_HANDLER_VERIFY() do {} while(0) 25 | #endif 26 | 27 | #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE 28 | // MaxMessageSize is message limit used with vsnprintf/vsnprintf_s 29 | static int MaxMessageSize = 2048; 30 | 31 | void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) { 32 | MaxMessageSize = max_size; 33 | } 34 | #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ 35 | 36 | /** logCapture is a simple struct for capturing log/fatal entries. At destruction the 37 | * captured message is forwarded to background worker. 38 | * As a safety precaution: No memory allocated here will be moved into the background 39 | * worker in case of dynamic loaded library reasons instead the arguments are copied 40 | * inside of g3log.cpp::saveMessage*/ 41 | LogCapture::~LogCapture() noexcept (false) { 42 | using namespace g3::internal; 43 | SIGNAL_HANDLER_VERIFY(); 44 | saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str()); 45 | } 46 | 47 | 48 | /// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) 49 | LogCapture::LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const char *dump) : LogCapture("", 0, "", level, "", fatal_signal, dump) { 50 | } 51 | 52 | /** 53 | * @file, line, function are given in g3log.hpp from macros 54 | * @level INFO/DEBUG/WARNING/FATAL 55 | * @expression for CHECK calls 56 | * @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler 57 | */ 58 | LogCapture::LogCapture(const char *file, const int line, const char *function, const LEVELS &level, 59 | const char *expression, g3::SignalType fatal_signal, const char *dump) 60 | : _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) { 61 | 62 | if (g3::internal::wasFatal(level)) { 63 | _stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"}; 64 | _stack_trace.append(g3::internal::stackdump(dump)); 65 | } 66 | } 67 | 68 | 69 | 70 | /** 71 | * capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF 72 | * See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18 73 | */ 74 | void LogCapture::capturef(const char *printf_like_message, ...) { 75 | static const std::string kTruncatedWarningText = "[...truncated...]"; 76 | #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE 77 | std::vector finished_message_backing(MaxMessageSize); 78 | char *finished_message = finished_message_backing.data(); 79 | auto finished_message_len = MaxMessageSize; 80 | #else 81 | static const int kMaxMessageSize = 2048; 82 | char finished_message[kMaxMessageSize]; 83 | #if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__)) 84 | auto finished_message_len = _countof(finished_message); 85 | #else 86 | int finished_message_len = sizeof(finished_message); 87 | #endif 88 | #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE*/ 89 | 90 | va_list arglist; 91 | va_start(arglist, printf_like_message); 92 | 93 | #if ((defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__GNUC__)) 94 | const int nbrcharacters = vsnprintf_s(finished_message, finished_message_len, _TRUNCATE, printf_like_message, arglist); 95 | #else 96 | const int nbrcharacters = vsnprintf(finished_message, finished_message_len, printf_like_message, arglist); 97 | #endif 98 | va_end(arglist); 99 | 100 | if (nbrcharacters < 0) { 101 | stream() << "\n\tERROR LOG MSG NOTIFICATION: Failure to successfully parse the message"; 102 | stream() << '"' << printf_like_message << '"' << std::endl; 103 | } else if (nbrcharacters > finished_message_len) { 104 | stream() << finished_message << kTruncatedWarningText; 105 | } else { 106 | stream() << finished_message; 107 | } 108 | } 109 | 110 | 111 | -------------------------------------------------------------------------------- /test_unit/test_cpp_future_concepts.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "g3log/time.hpp" 19 | #include "g3log/future.hpp" 20 | 21 | 22 | std::future sillyFutureReturn() 23 | { 24 | std::packaged_task task([](){return std::string("Hello Future");}); // wrap the function 25 | std::future result = task.get_future(); // get a future 26 | std::thread(std::move(task)).detach(); // launch on a thread 27 | std::cout << "Waiting..."; 28 | result.wait(); 29 | return result; // already wasted 30 | } 31 | 32 | 33 | TEST(Configuration, FutureSilly) 34 | { 35 | std::string hello = sillyFutureReturn().get(); 36 | ASSERT_STREQ(hello.c_str(), "Hello Future"); 37 | } 38 | 39 | struct MsgType 40 | { 41 | std::string msg_; 42 | MsgType(std::string m): msg_(m){}; 43 | std::string msg(){return msg_;} 44 | }; 45 | 46 | 47 | TEST(TestOf_CopyableCall, Expecting_SmoothSailing) 48 | { 49 | using namespace kjellkod; 50 | const std::string str("Hello from struct"); 51 | MsgType type(str); 52 | std::unique_ptr bgWorker(Active::createActive()); 53 | std::future fstring = 54 | g3::spawn_task(std::bind(&MsgType::msg, type), bgWorker.get()); 55 | ASSERT_STREQ(str.c_str(), fstring.get().c_str()); 56 | } 57 | 58 | 59 | 60 | TEST(TestOf_CopyableLambdaCall, Expecting_AllFine) 61 | { 62 | using namespace kjellkod; 63 | std::unique_ptr bgWorker(Active::createActive()); 64 | 65 | // lambda task 66 | const std::string str_standalone("Hello from standalone"); 67 | auto msg_lambda=[=](){return (str_standalone+str_standalone);}; 68 | std::string expected(str_standalone+str_standalone); 69 | 70 | auto fstring_standalone = g3::spawn_task(msg_lambda, bgWorker.get()); 71 | ASSERT_STREQ(expected.c_str(), fstring_standalone.get().c_str()); 72 | } 73 | 74 | 75 | 76 | 77 | template 78 | std::future> ObsoleteSpawnTask(F f) 79 | { 80 | typedef std::invoke_result_t result_type; 81 | typedef std::packaged_task task_type; 82 | 83 | task_type task(std::move(f)); 84 | std::future result = task.get_future(); 85 | 86 | std::vector> vec; 87 | vec.push_back(g3::MoveOnCopy(std::move(task))); 88 | std::thread(std::move(vec.back())).detach(); 89 | result.wait(); 90 | return std::move(result); 91 | } 92 | 93 | TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString) 94 | { 95 | std::string str("Hello"); 96 | std::string expected(str+str); 97 | auto msg_lambda=[=](){return (str+str);}; 98 | auto future_string = ObsoleteSpawnTask(msg_lambda); 99 | 100 | ASSERT_STREQ(expected.c_str(), future_string.get().c_str()); 101 | } 102 | // gcc thread example below 103 | // tests code below copied from mail-list conversion between 104 | // Lars Gullik Bjønnes and Jonathan Wakely 105 | // http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html 106 | 107 | // -------------------------------------------------------------- 108 | namespace WORKING 109 | { 110 | using namespace g3; 111 | 112 | #include 113 | 114 | #include 115 | #include 116 | #include 117 | #include 118 | 119 | std::vector> vec; 120 | 121 | template 122 | std::future> spawn_task(F f) 123 | { 124 | typedef std::invoke_result_t result_type; 125 | typedef std::packaged_task task_type; 126 | 127 | task_type task(std::move(f)); 128 | std::future res = task.get_future(); 129 | 130 | vec.push_back( 131 | MoveOnCopy( 132 | std::move(task))); 133 | 134 | std::thread([]() 135 | { 136 | auto task = std::move(vec.back()); 137 | vec.pop_back(); 138 | task(); 139 | } 140 | ).detach(); 141 | 142 | return std::move(res); 143 | } 144 | 145 | 146 | 147 | double get_res() 148 | { 149 | return 42.2; 150 | } 151 | 152 | std::string msg3(){return "msg3";} 153 | } // WORKING 154 | 155 | TEST(Yalla, Testar) 156 | { 157 | using namespace WORKING; 158 | auto f = spawn_task(get_res); 159 | ASSERT_EQ(42.2, f.get()); 160 | 161 | auto f2 = spawn_task(msg3); 162 | ASSERT_EQ("msg3", f2.get()); 163 | 164 | 165 | ASSERT_TRUE(true); 166 | } 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /test_performance/main_threaded_mean.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | // through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE 10 | #include "performance.h" 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(G3LOG_PERFORMANCE) 16 | const std::string title = "G3LOG"; 17 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 18 | const std::string title = "GOOGLE__GLOG"; 19 | #else 20 | #error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined 21 | #endif 22 | 23 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 24 | const std::string g_path = "./"; 25 | #else 26 | const std::string g_path = "/tmp/"; 27 | #endif 28 | using namespace g3_test; 29 | 30 | int main(int argc, char **argv) 31 | { 32 | #ifdef G3_DYNAMIC_LOGGING 33 | std::cerr << "G3_DYNAMIC_LOGGING is enabled" << std::endl; 34 | #else 35 | std::cerr << "G3_DYNAMIC_LOGGING is DISABLED" << std::endl; 36 | #endif 37 | 38 | size_t number_of_threads = 0; 39 | if (argc == 2) 40 | { 41 | number_of_threads = atoi(argv[1]); 42 | } 43 | if (argc != 2 || number_of_threads == 0) 44 | { 45 | std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl; 46 | return 1; 47 | } 48 | 49 | std::ostringstream thread_count_oss; 50 | thread_count_oss << number_of_threads; 51 | const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-MEAN_LOG"; 52 | const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt"; 53 | 54 | std::ostringstream oss; 55 | const uint64_t us_to_s = 1000000; 56 | oss << "\n\n" << title << " performance " << number_of_threads << " threads MEAN times\n"; 57 | oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry 58 | const uint64_t xtra_margin = 2; 59 | 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; 60 | writeTextToFile(g_measurement_dump, oss.str(), kAppend); 61 | oss.str(""); // clear the stream 62 | 63 | #if defined(G3LOG_PERFORMANCE) 64 | auto worker = g3::LogWorker::createLogWorker(); 65 | auto handle= worker->addDefaultLogger(g_prefix_log_name, g_path); 66 | g3::initializeLogging(worker.get()); 67 | 68 | 69 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 70 | google::InitGoogleLogging(argv[0]); 71 | #endif 72 | auto start_time = std::chrono::high_resolution_clock::now(); 73 | 74 | std::thread *threads = new std::thread[number_of_threads]; 75 | // kiss: just loop, create threads, store them then join 76 | // could probably do this more elegant with lambdas 77 | for (size_t idx = 0; idx < number_of_threads; ++idx) 78 | { 79 | std::ostringstream count; 80 | count << idx + 1; 81 | std::string thread_name = title + "_T" + count.str(); 82 | std::cout << "Creating thread: " << thread_name << std::endl; 83 | threads[idx] = std::thread(doLogWrites, thread_name); 84 | } 85 | for (size_t idx = 0; idx < number_of_threads; ++idx) 86 | { 87 | threads[idx].join(); 88 | } 89 | auto application_end_time = std::chrono::high_resolution_clock::now(); 90 | delete [] threads; 91 | 92 | #if defined(G3LOG_PERFORMANCE) 93 | worker.reset(); // will flush anything in the queue to file 94 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 95 | google::ShutdownGoogleLogging(); 96 | #endif 97 | 98 | auto worker_end_time = std::chrono::high_resolution_clock::now(); 99 | uint64_t application_time_us = std::chrono::duration_cast(application_end_time - start_time).count(); 100 | uint64_t total_time_us = std::chrono::duration_cast(worker_end_time - start_time).count(); 101 | 102 | oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk" << std::endl; 103 | oss << "[Application(" << number_of_threads << "):\t\t:" << application_time_us / 1000 << " ms]" << std::endl; 104 | oss << "[Background thread to finish\t:" << total_time_us / uint64_t(1000 ) << " ms]" << std::endl; 105 | oss << "\nAverage time per log entry:" << std::endl; 106 | oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; 107 | oss << "[Background+Application: " << total_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; 108 | writeTextToFile(g_measurement_dump, oss.str(), kAppend); 109 | std::cout << "Result can be found at:" << g_measurement_dump << std::endl; 110 | 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /example/main_sigsegv.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | namespace 17 | { 18 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 19 | const std::string path_to_log_file = "./"; 20 | #else 21 | const std::string path_to_log_file = "/tmp/"; 22 | #endif 23 | } 24 | 25 | namespace example_fatal 26 | { 27 | // on Ubunti this caused get a compiler warning with gcc4.6 28 | // from gcc 4.7.2 (at least) it causes a crash (as expected) 29 | // On windows it'll probably crash too. 30 | void tryToKillWithIllegalPrintout() 31 | { 32 | std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush; 33 | std::cout << "************************************************************\n\n" << std::endl << std::flush; 34 | std::this_thread::sleep_for(std::chrono::seconds(1)); 35 | const std::string logging = "logging"; 36 | LOGF(G3LOG_DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str()); 37 | } 38 | 39 | 40 | // The function above 'tryToKillWithIllegalPrintout' IS system / compiler dependent. Older compilers sometimes did NOT generate a SIGSEGV 41 | // fault as expected by the illegal printf-format usage. just in case we exit by zero division" 42 | void killByZeroDivision(int value) 43 | { 44 | int zero = 0; // trying to fool the compiler to automatically warn 45 | LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero; 46 | } 47 | 48 | 49 | void tryToKillWithAccessingIllegalPointer(std::unique_ptr badStringPtr) { 50 | auto badPtr = std::move(badStringPtr); 51 | LOG(INFO) << "Function calls through a nullptr object will trigger SIGSEGV"; 52 | badStringPtr->append("crashing"); 53 | } 54 | 55 | 56 | } // example fatal 57 | 58 | 59 | 60 | int main(int argc, char **argv) 61 | { 62 | double pi_d = 3.1415926535897932384626433832795; 63 | float pi_f = 3.1415926535897932384626433832795f; 64 | 65 | using namespace g3; 66 | 67 | 68 | std::unique_ptr logworker {LogWorker::createLogWorker()}; 69 | auto sinkHandle = logworker->addSink(std::make_unique(argv[0], path_to_log_file), 70 | &FileSink::fileWrite); 71 | 72 | initializeLogging(logworker.get()); 73 | std::future log_file_name = sinkHandle->call(&FileSink::fileName); 74 | std::cout << "* This is an example of g3log. It WILL exit by a FATAL trigger" << std::endl; 75 | std::cout << "* Please see the generated log and compare to the code at" << std::endl; 76 | std::cout << "* g3log/test_example/main.cpp" << std::endl; 77 | std::cout << "*\n* Log file: [" << log_file_name.get() << "]\n\n" << std::endl; 78 | 79 | 80 | LOGF(INFO, "Hi log %d", 123); 81 | LOG(INFO) << "Test SLOG INFO"; 82 | LOG(G3LOG_DEBUG) << "Test SLOG DEBUG"; 83 | LOG(INFO) << "one: " << 1; 84 | LOG(INFO) << "two: " << 2; 85 | LOG(INFO) << "one and two: " << 1 << " and " << 2; 86 | LOG(G3LOG_DEBUG) << "float 2.14: " << 1000 / 2.14f; 87 | LOG(G3LOG_DEBUG) << "pi double: " << pi_d; 88 | LOG(G3LOG_DEBUG) << "pi float: " << pi_f; 89 | LOG(G3LOG_DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f; 90 | LOGF(INFO, "pi float printf:%f", pi_f); 91 | 92 | // 93 | // START: LOG Entris that were in the CodeProject article 94 | // 95 | //LOG(UNKNOWN_LEVEL) << "This log attempt will cause a compiler error"; 96 | 97 | LOG(INFO) << "Simple to use with streaming syntax, easy as abc or " << 123; 98 | LOGF(WARNING, "Printf-style syntax is also %s", "available"); 99 | LOG_IF(INFO, (1 < 2)) << "If true this text will be logged"; 100 | LOGF_IF(INFO, (1 < 2), "if %d<%d : then this text will be logged", 1, 2); 101 | LOG_IF(FATAL, (2 > 3)) << "This message should NOT throw"; 102 | LOGF(G3LOG_DEBUG, "This API is popular with some %s", "programmers"); 103 | LOGF_IF(G3LOG_DEBUG, (1 < 2), "If true, then this %s will be logged", "message"); 104 | 105 | // OK --- on Ubunti this caused get a compiler warning with gcc4.6 106 | // from gcc 4.7.2 (at least) it causes a crash (as expected) 107 | // On windows itll probably crash 108 | example_fatal::tryToKillWithIllegalPrintout(); 109 | 110 | // try 2 111 | std::unique_ptr badStringPtr; 112 | example_fatal::tryToKillWithAccessingIllegalPointer(std::move(badStringPtr)); 113 | 114 | // what happened? OK. let us just exit with SIGFPE 115 | int value = 1; // system dependent but it SHOULD never reach this line 116 | example_fatal::killByZeroDivision(value); 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /test_unit/Test.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | # ============================================================================ 13 | # TEST OPTIONS: Turn OFF the ones that is of no interest to you 14 | # ---- by default all is OFF: except 'g3log-FATAL-example ----- 15 | # ---- the reason for this is that 16 | # ----- 1) the performance tests were only thoroughly tested on Ubuntu, not windows- 17 | # (g3log windows/linux, but Google's glog only on linux) 18 | # 19 | # 2) The unit test were tested windows/linux 20 | # ============================================================================ 21 | 22 | 23 | # Unit test for g3log (cmake -DUSE_G3LOG_UNIT_TEST=ON ..) 24 | option (ADD_G3LOG_UNIT_TEST "g3log unit tests" OFF) 25 | 26 | 27 | # 4. create the unit tests for g3log --- ONLY TESTED THE UNIT TEST ON LINUX 28 | # ========================= 29 | IF (ADD_G3LOG_UNIT_TEST) 30 | # Download and unpack googletest at configure time 31 | configure_file(CMakeLists.txt.in 32 | googletest-download/CMakeLists.txt) 33 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 34 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 35 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 36 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download ) 37 | 38 | # Prevent GoogleTest from overriding our compiler/linker options 39 | # when building with Visual Studio 40 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 41 | 42 | # Add googletest directly to our build. This adds 43 | # the following targets: gtest, gtest_main, gmock 44 | # and gmock_main 45 | add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src 46 | ${CMAKE_BINARY_DIR}/googletest-build) 47 | 48 | # The gtest/gmock targets carry header search path 49 | # dependencies automatically when using CMake 2.8.11 or 50 | # later. Otherwise we have to add them here ourselves. 51 | if (CMAKE_VERSION VERSION_LESS 2.8.11) 52 | include_directories("${gtest_SOURCE_DIR}/include" 53 | "${gmock_SOURCE_DIR}/include") 54 | endif() 55 | 56 | enable_testing() 57 | 58 | set(DIR_UNIT_TEST ${g3log_SOURCE_DIR}/test_unit) 59 | message( STATUS "-DADD_G3LOG_UNIT_TEST=ON" ) 60 | 61 | # obs see this: http://stackoverflow.com/questions/9589192/how-do-i-change-the-number-of-template-arguments-supported-by-msvcs-stdtupl 62 | # and this: http://stackoverflow.com/questions/2257464/google-test-and-visual-studio-2010-rc 63 | 64 | 65 | IF (MSVC OR MINGW) 66 | SET(OS_SPECIFIC_TEST test_crashhandler_windows) 67 | ENDIF(MSVC OR MINGW) 68 | 69 | SET(tests_to_run test_message test_filechange test_io test_cpp_future_concepts test_concept_sink test_sink ${OS_SPECIFIC_TEST}) 70 | SET(helper ${DIR_UNIT_TEST}/testing_helpers.h ${DIR_UNIT_TEST}/testing_helpers.cpp) 71 | include_directories(${DIR_UNIT_TEST}) 72 | 73 | FOREACH(test ${tests_to_run} ) 74 | SET(all_tests ${all_tests} ${DIR_UNIT_TEST}/${test}.cpp ) 75 | IF(${test} STREQUAL "test_filechange") 76 | add_executable(test_filechange ${DIR_UNIT_TEST}/${test}.cpp ${helper}) 77 | ELSE() 78 | add_executable(${test} ${g3log_SOURCE_DIR}/test_main/test_main.cpp ${DIR_UNIT_TEST}/${test}.cpp ${helper}) 79 | ENDIF(${test} STREQUAL "test_filechange") 80 | 81 | set_target_properties(${test} PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0") 82 | set_target_properties(${test} PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0") 83 | IF( NOT(MSVC)) 84 | set_target_properties(${test} PROPERTIES COMPILE_FLAGS "-isystem -pthread ") 85 | ENDIF( NOT(MSVC)) 86 | target_link_libraries(${test} g3log gtest_main) 87 | add_test( ${test} ${test} ) 88 | ENDFOREACH(test) 89 | 90 | # 91 | # Test for Linux, runtime loading of dynamic libraries 92 | # 93 | IF (NOT WIN32 AND NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang") AND G3_SHARED_LIB) 94 | add_library(tester_sharedlib SHARED ${DIR_UNIT_TEST}/tester_sharedlib.h ${DIR_UNIT_TEST}/tester_sharedlib.cpp) 95 | target_link_libraries(tester_sharedlib ${G3LOG_LIBRARY}) 96 | 97 | add_executable(test_dynamic_loaded_shared_lib ${g3log_SOURCE_DIR}/test_main/test_main.cpp ${DIR_UNIT_TEST}/test_linux_dynamic_loaded_sharedlib.cpp) 98 | set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_TR1_TUPLE=0") 99 | set_target_properties(test_dynamic_loaded_shared_lib PROPERTIES COMPILE_DEFINITIONS "GTEST_HAS_RTTI=0") 100 | target_link_libraries(test_dynamic_loaded_shared_lib ${G3LOG_LIBRARY} -ldl gtest_main) 101 | ENDIF() 102 | ELSE() 103 | message( STATUS "-DADD_G3LOG_UNIT_TEST=OFF" ) 104 | ENDIF (ADD_G3LOG_UNIT_TEST) 105 | -------------------------------------------------------------------------------- /src/filesinkhelper.ipp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | namespace g3 { 22 | namespace internal { 23 | static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S"; 24 | 25 | // check for filename validity - filename should not be part of PATH 26 | bool isValidFilename(const std::string &prefix_filename) { 27 | std::string illegal_characters("/,|<>:#$%{}[]\'\"^!?+* "); 28 | size_t pos = prefix_filename.find_first_of(illegal_characters, 0); 29 | if (pos != std::string::npos) { 30 | std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl; 31 | return false; 32 | } else if (prefix_filename.empty()) { 33 | std::cerr << "Empty filename prefix is not allowed" << std::endl; 34 | return false; 35 | } 36 | 37 | return true; 38 | } 39 | 40 | std::string prefixSanityFix(std::string prefix) { 41 | prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end()); 42 | prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); 43 | prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); 44 | prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); 45 | prefix.erase(std::remove(prefix.begin(), prefix.end(), ':'), prefix.end()); 46 | if (!isValidFilename(prefix)) { 47 | return 48 | { 49 | }; 50 | } 51 | return prefix; 52 | } 53 | 54 | std::string pathSanityFix(std::string path, std::string file_name) { 55 | // Unify the delimeters,. maybe sketchy solution but it seems to work 56 | // on at least win7 + ubuntu. All bets are off for older windows 57 | std::replace(path.begin(), path.end(), '\\', '/'); 58 | 59 | // clean up in case of multiples 60 | auto contains_end = [&](std::string & in) -> bool { 61 | size_t size = in.size(); 62 | if (!size) return false; 63 | char end = in[size - 1]; 64 | return (end == '/' || end == ' '); 65 | }; 66 | 67 | while (contains_end(path)) { 68 | path.erase(path.size() - 1); 69 | } 70 | 71 | if (!path.empty()) { 72 | path.insert(path.end(), '/'); 73 | } 74 | 75 | path.insert(path.size(), file_name); 76 | return path; 77 | } 78 | 79 | std::string header(const std::string& headerFormat) { 80 | std::ostringstream ss_entry; 81 | // Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012 82 | auto now = std::chrono::system_clock::now(); 83 | ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n"; 84 | ss_entry << headerFormat; 85 | return ss_entry.str(); 86 | } 87 | 88 | std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) { 89 | std::stringstream oss_name; 90 | oss_name << verified_prefix << "."; 91 | if( logger_id != "" ) { 92 | oss_name << logger_id << "."; 93 | } 94 | auto now = std::chrono::system_clock::now(); 95 | oss_name << g3::localtime_formatted(now, file_name_time_formatted); 96 | oss_name << ".log"; 97 | return oss_name.str(); 98 | } 99 | 100 | bool openLogFile(const std::string &complete_file_with_path, std::ofstream &outstream) { 101 | std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream 102 | mode |= std::ios_base::trunc; 103 | outstream.open(complete_file_with_path, mode); 104 | if (!outstream.is_open()) { 105 | std::ostringstream ss_error; 106 | ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]"; 107 | ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate(); 108 | std::cerr << ss_error.str().c_str() << std::endl; 109 | outstream.close(); 110 | return false; 111 | } 112 | return true; 113 | } 114 | 115 | std::unique_ptr createLogFile(const std::string &file_with_full_path) { 116 | std::unique_ptr out(new std::ofstream); 117 | std::ofstream &stream(*(out.get())); 118 | bool success_with_open_file = openLogFile(file_with_full_path, stream); 119 | if (false == success_with_open_file) { 120 | out.release(); 121 | } 122 | return out; 123 | } 124 | 125 | 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/logworker.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/logworker.hpp" 10 | #include "g3log/logmessage.hpp" 11 | #include "g3log/active.hpp" 12 | #include "g3log/g3log.hpp" 13 | #include "g3log/future.hpp" 14 | #include "g3log/crashhandler.hpp" 15 | 16 | #include 17 | 18 | namespace g3 { 19 | 20 | LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { } 21 | 22 | void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) { 23 | std::unique_ptr uniqueMsg(std::move(msgPtr.get())); 24 | 25 | for (auto& sink : _sinks) { 26 | LogMessage msg(*(uniqueMsg)); 27 | sink->send(LogMessageMover(std::move(msg))); 28 | } 29 | 30 | if (_sinks.empty()) { 31 | std::string err_msg {"g3logworker has no sinks. Message: ["}; 32 | err_msg.append(uniqueMsg.get()->toString()).append("]\n"); 33 | std::cerr << err_msg; 34 | } 35 | } 36 | 37 | void LogWorkerImpl::bgFatal(FatalMessagePtr msgPtr) { 38 | // this will be the last message. Only the active logworker can receive a FATAL call so it's 39 | // safe to shutdown logging now 40 | g3::internal::shutDownLogging(); 41 | 42 | std::string reason = msgPtr.get()->reason(); 43 | const auto level = msgPtr.get()->_level; 44 | const auto fatal_id = msgPtr.get()->_signal_id; 45 | 46 | 47 | std::unique_ptr uniqueMsg(std::move(msgPtr.get())); 48 | uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); 49 | 50 | 51 | // Change output in case of a fatal signal (or windows exception) 52 | std::string exiting = {"Fatal type: "}; 53 | 54 | uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason) 55 | .append("\nLog content flushed successfully to sink\n\n"); 56 | 57 | std::cerr << uniqueMsg->toString() << std::flush; 58 | for (auto& sink : _sinks) { 59 | LogMessage msg(*(uniqueMsg)); 60 | sink->send(LogMessageMover(std::move(msg))); 61 | } 62 | 63 | 64 | // This clear is absolutely necessary 65 | // All sinks are forced to receive the fatal message above before we continue 66 | _sinks.clear(); // flush all queues 67 | internal::exitWithDefaultSignalHandler(level, fatal_id); 68 | 69 | // should never reach this point 70 | perror("g3log exited after receiving FATAL trigger. Flush message status: "); 71 | } 72 | 73 | LogWorker::~LogWorker() { 74 | g3::internal::shutDownLoggingForActiveOnly(this); 75 | 76 | // The sinks WILL automatically be cleared at exit of this destructor 77 | // The waiting inside removeAllSinks ensures that all messages until this point are 78 | // taken care of before any internals/LogWorkerImpl of LogWorker starts to be destroyed. 79 | // i.e. this avoids a race with another thread slipping through the "shutdownLogging" and 80 | // calling ::save or ::fatal through LOG/CHECK with lambda messages and "partly 81 | // deconstructed LogWorkerImpl" 82 | // 83 | // Any messages put into the queue will be OK due to: 84 | // *) If it is before the wait below then they will be executed 85 | // *) If it is AFTER the wait below then they will be ignored and NEVER executed 86 | removeAllSinks(); 87 | 88 | // The background worker WILL be automatically cleared at the exit of the destructor 89 | // However, the explicitly clearing of the background worker (below) makes sure that there can 90 | // be no thread that manages to add another sink after the call to clear the sinks above. 91 | // i.e. this manages the extremely unlikely case of another thread calling 92 | // addWrappedSink after the sink clear above. Normally adding of sinks should be done in main.cpp 93 | // and be closely coupled with the existence of the LogWorker. Sharing this adding of sinks to 94 | // other threads that do not know the state of LogWorker is considered a bug but it is dealt with 95 | // nonetheless below. 96 | // 97 | // If sinks would already have been added after the sink clear above then this reset will deal with it 98 | // without risking lambda execution with a partially deconstructed LogWorkerImpl 99 | // Calling g3::spawn_task on a nullptr Active object will not crash but return 100 | // a future containing an appropriate exception. 101 | _impl._bg.reset(nullptr); 102 | } 103 | 104 | void LogWorker::save(LogMessagePtr msg) { 105 | _impl._bg->send([this, msg] {_impl.bgSave(msg); }); 106 | } 107 | 108 | void LogWorker::fatal(FatalMessagePtr fatal_message) { 109 | _impl._bg->send([this, fatal_message] {_impl.bgFatal(fatal_message); }); 110 | } 111 | 112 | void LogWorker::addWrappedSink(std::shared_ptr sink) { 113 | auto bg_addsink_call = [this, sink] {_impl._sinks.push_back(sink);}; 114 | auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get()); 115 | token_done.wait(); 116 | } 117 | 118 | std::unique_ptr LogWorker::createLogWorker() { 119 | return std::unique_ptr(new LogWorker); 120 | } 121 | 122 | std::unique_ptrLogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) { 123 | return addSink(std::make_unique(log_prefix, log_directory, default_id), &FileSink::fileWrite); 124 | } 125 | 126 | } // g3 127 | -------------------------------------------------------------------------------- /test_unit/testing_helpers.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "testing_helpers.h" 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace std; 22 | using namespace g3; 23 | 24 | namespace testing_helpers { 25 | 26 | std::string g_mockFatal_message = {}; 27 | int g_mockFatal_signal = -1; 28 | bool g_mockFatalWasCalled = false; 29 | 30 | std::string mockFatalMessage() { 31 | return g_mockFatal_message; 32 | } 33 | 34 | int mockFatalSignal() { 35 | return g_mockFatal_signal; 36 | } 37 | 38 | bool mockFatalWasCalled() { 39 | return g_mockFatalWasCalled; 40 | } 41 | 42 | void mockFatalCall(FatalMessagePtr fatal_message) { 43 | g_mockFatal_message = fatal_message.get()->toString(); 44 | g_mockFatal_signal = fatal_message.get()->_signal_id; 45 | g_mockFatalWasCalled = true; 46 | LogMessagePtr message{fatal_message.release()}; 47 | g3::internal::pushMessageToLogger(message); //fatal_message.copyToLogMessage()); 48 | } 49 | 50 | void clearMockFatal() { 51 | g_mockFatal_message.clear(); 52 | g_mockFatal_signal = -1; 53 | g_mockFatalWasCalled = false; 54 | } 55 | 56 | bool removeFile(std::string path_to_file) { 57 | return (0 == std::remove(path_to_file.c_str())); 58 | } 59 | 60 | bool verifyContent(const std::string &total_text, std::string msg_to_find) { 61 | std::string content(total_text); 62 | size_t location = content.find(msg_to_find); 63 | return (location != std::string::npos); 64 | } 65 | 66 | std::string readFileToText(std::string filename) { 67 | std::ifstream in; 68 | in.open(filename.c_str(), std::ios_base::in); 69 | if (!in.is_open()) { 70 | return 71 | { 72 | }; // error just return empty string - test will 'fault' 73 | } 74 | std::ostringstream oss; 75 | oss << in.rdbuf(); 76 | return oss.str(); 77 | // RAII of std::ifstream will automatically close the file 78 | } 79 | 80 | size_t LogFileCleaner::size() { 81 | return logs_to_clean_.size(); 82 | } 83 | 84 | 85 | LogFileCleaner::~LogFileCleaner() { 86 | std::lock_guard lock(g_mutex); 87 | { 88 | for (const auto& file : logs_to_clean_) { 89 | if (!removeFile(file)) { 90 | ADD_FAILURE() << "UNABLE to remove: " << file << std::endl; 91 | } 92 | } 93 | logs_to_clean_.clear(); 94 | } // mutex 95 | } 96 | 97 | void LogFileCleaner::addLogToClean(std::string path_to_log) { 98 | std::lock_guard lock(g_mutex); 99 | { 100 | if (std::find(logs_to_clean_.begin(), logs_to_clean_.end(), path_to_log.c_str()) == logs_to_clean_.end()) 101 | logs_to_clean_.push_back(path_to_log); 102 | } 103 | } 104 | 105 | ScopedLogger::ScopedLogger() : _currentWorker(g3::LogWorker::createLogWorker()) {} 106 | ScopedLogger::~ScopedLogger() {} 107 | 108 | g3::LogWorker* ScopedLogger::get() { 109 | return _currentWorker.get(); 110 | } 111 | 112 | RestoreFileLogger::RestoreFileLogger(std::string directory) 113 | : _scope(new ScopedLogger), _handle(_scope->get()->addSink(std::make_unique("UNIT_TEST_LOGGER", directory), &g3::FileSink::fileWrite)) { 114 | using namespace g3; 115 | g3::initializeLogging(_scope->_currentWorker.get()); 116 | clearMockFatal(); 117 | setFatalExitHandler(&mockFatalCall); 118 | 119 | auto filename = _handle->call(&FileSink::fileName); 120 | if (!filename.valid()) ADD_FAILURE(); 121 | _log_file = filename.get(); 122 | 123 | #ifdef G3_DYNAMIC_LOGGING 124 | g3::only_change_at_initialization::addLogLevel(INFO, true); 125 | g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, true); 126 | g3::only_change_at_initialization::addLogLevel(WARNING, true); 127 | g3::only_change_at_initialization::addLogLevel(FATAL, true); 128 | #endif 129 | } 130 | 131 | RestoreFileLogger::~RestoreFileLogger() { 132 | g3::internal::shutDownLogging(); // is done at reset. Added for test clarity 133 | reset(); 134 | 135 | if (!removeFile(_log_file)) 136 | ADD_FAILURE(); 137 | } 138 | 139 | std::string RestoreFileLogger::logFile() { 140 | if (_scope) { 141 | // beware for race condition 142 | // example: 143 | // LOG(INFO) << ... 144 | // auto file = logger.logFile() 145 | // auto content = ReadContentFromFile(file) 146 | // ... it is not guaranteed that the content will contain (yet) the LOG(INFO) 147 | std::future filename = _handle->call(&g3::FileSink::fileName); 148 | _log_file = filename.get(); 149 | } 150 | return _log_file; 151 | } 152 | 153 | // Beware of race between LOG(...) and this function. 154 | // since LOG(...) passes two queues but the handle::call only passes one queue 155 | // the handle::call can happen faster 156 | std::string RestoreFileLogger::resetAndRetrieveContent() { 157 | std::future filename = _handle->call(&g3::FileSink::fileName); 158 | reset(); // flush all queues to sinks 159 | EXPECT_TRUE(filename.valid()); 160 | auto file = filename.get(); 161 | return readFileToText(file); 162 | } 163 | } // testing_helpers 164 | -------------------------------------------------------------------------------- /src/g3log/logmessage.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | 11 | 12 | 13 | #include "g3log/loglevels.hpp" 14 | #include "g3log/time.hpp" 15 | #include "g3log/moveoncopy.hpp" 16 | #include "g3log/crashhandler.hpp" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace g3 { 24 | 25 | /** LogMessage contains all the data collected from the LOG(...) call. 26 | * If the sink receives a std::string it will be the std::string toString()... function 27 | * that will format the data into a string 28 | * 29 | * For sinks that receive a LogMessage they can either use the toString() function, or use 30 | * the helper functions or even the public raw data to format the saved log message any 31 | * desired way. 32 | */ 33 | struct LogMessage { 34 | std::string file_path() const { 35 | return _file_path; 36 | } 37 | std::string file() const { 38 | return _file; 39 | } 40 | std::string line() const { 41 | return std::to_string(_line); 42 | } 43 | std::string function() const { 44 | return _function; 45 | } 46 | std::string level() const { 47 | return _level.text; 48 | } 49 | 50 | /// use a different format string to get a different look on the time. 51 | // default look is Y/M/D H:M:S 52 | std::string timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const; 53 | 54 | std::string message() const { 55 | return _message; 56 | } 57 | std::string& write() const { 58 | return _message; 59 | } 60 | 61 | std::string expression() const { 62 | return _expression; 63 | } 64 | bool wasFatal() const { 65 | return internal::wasFatal(_level); 66 | } 67 | 68 | std::string threadID() const; 69 | 70 | void setExpression(const std::string expression) { 71 | _expression = expression; 72 | } 73 | 74 | 75 | LogMessage& operator=(LogMessage other); 76 | 77 | 78 | LogMessage(std::string file, const int line, std::string function, const LEVELS level); 79 | 80 | explicit LogMessage(const std::string& fatalOsSignalCrashMessage); 81 | LogMessage(const LogMessage& other); 82 | LogMessage(LogMessage&& other); 83 | virtual ~LogMessage() {} 84 | 85 | 86 | // helper log printing functions used by "toString()" 87 | static std::string splitFileName(const std::string& str); 88 | static std::string fatalSignalToString(const LogMessage& msg); 89 | // windows only: fatalExceptionToString 90 | static std::string fatalExceptionToString(const LogMessage& msg); 91 | static std::string fatalLogToString(const LogMessage& msg); 92 | static std::string fatalCheckToString(const LogMessage& msg); 93 | static std::string normalToString(const LogMessage& msg); 94 | 95 | 96 | 97 | // the default formatting option 98 | static std::string DefaultLogDetailsToString(const LogMessage& msg); 99 | 100 | // this function can be used by the logging sink to add thread ID 101 | // see this concept and it is easy to make your own custom formatting 102 | static std::string FullLogDetailsToString(const LogMessage& msg); 103 | 104 | using LogDetailsFunc = std::string (*) (const LogMessage&); 105 | std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const; 106 | 107 | 108 | void overrideLogDetailsFunc(LogDetailsFunc func) const; 109 | 110 | 111 | 112 | // 113 | // Complete access to the raw data in case the helper functions above 114 | // are not enough. 115 | // 116 | mutable LogDetailsFunc _logDetailsToStringFunc; 117 | g3::high_resolution_time_point _timestamp; 118 | std::thread::id _call_thread_id; 119 | std::string _file; 120 | std::string _file_path; 121 | int _line; 122 | std::string _function; 123 | LEVELS _level; 124 | std::string _expression; // only with content for CHECK(...) calls 125 | mutable std::string _message; 126 | 127 | 128 | 129 | friend void swap(LogMessage& first, LogMessage& second) { 130 | using std::swap; 131 | swap(first._timestamp, second._timestamp); 132 | swap(first._call_thread_id, second._call_thread_id); 133 | swap(first._file, second._file); 134 | swap(first._line, second._line); 135 | swap(first._function, second._function); 136 | swap(first._level, second._level); 137 | swap(first._expression, second._expression); 138 | swap(first._message, second._message); 139 | } 140 | 141 | }; 142 | 143 | 144 | 145 | 146 | /** Trigger for flushing the message queue and exiting the application 147 | * A thread that causes a FatalMessage will sleep forever until the 148 | * application has exited (after message flush) */ 149 | struct FatalMessage : public LogMessage { 150 | FatalMessage(const LogMessage& details, g3::SignalType signal_id); 151 | FatalMessage(const FatalMessage&); 152 | virtual ~FatalMessage() {} 153 | 154 | LogMessage copyToLogMessage() const; 155 | std::string reason() const; 156 | 157 | const SignalType _signal_id; 158 | }; 159 | 160 | 161 | typedef MoveOnCopy> FatalMessagePtr; 162 | typedef MoveOnCopy> LogMessagePtr; 163 | typedef MoveOnCopy LogMessageMover; 164 | } // g3 165 | -------------------------------------------------------------------------------- /src/time.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/time.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef __MACH__ 19 | #include 20 | #endif 21 | 22 | namespace g3 { 23 | namespace internal { 24 | const std::string kFractionalIdentier = "%f"; 25 | const size_t kFractionalIdentierSize = 2; 26 | 27 | Fractional getFractional(const std::string& format_buffer, size_t pos) { 28 | char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0'); 29 | Fractional type = Fractional::NanosecondDefault; 30 | switch (ch) { 31 | case '3': type = Fractional::Millisecond; break; 32 | case '6': type = Fractional::Microsecond; break; 33 | case '9': type = Fractional::Nanosecond; break; 34 | default: type = Fractional::NanosecondDefault; break; 35 | } 36 | return type; 37 | } 38 | 39 | // Returns the fractional as a string with padded zeroes 40 | // 1 ms --> 001 41 | // 1 us --> 000001 42 | // 1 ns --> 000000001 43 | std::string to_string(const g3::system_time_point& ts, Fractional fractional) { 44 | auto duration = ts.time_since_epoch(); 45 | auto sec_duration = std::chrono::duration_cast(duration); 46 | duration -= sec_duration; 47 | auto ns = std::chrono::duration_cast(duration).count(); 48 | 49 | auto zeroes = 9; // default ns 50 | auto digitsToCut = 1; // default ns, divide by 1 makes no change 51 | switch (fractional) { 52 | case Fractional::Millisecond : { 53 | zeroes = 3; 54 | digitsToCut = 1000000; 55 | break; 56 | } 57 | case Fractional::Microsecond : { 58 | zeroes = 6; 59 | digitsToCut = 1000; 60 | break; 61 | } 62 | case Fractional::Nanosecond : 63 | case Fractional::NanosecondDefault: 64 | default: 65 | zeroes = 9; 66 | digitsToCut = 1; 67 | 68 | } 69 | 70 | ns /= digitsToCut; 71 | auto value = std::string(std::to_string(ns)); 72 | return std::string(zeroes - value.size(), '0') + value; 73 | } 74 | 75 | std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer) { 76 | // iterating through every "%f" instance in the format string 77 | auto identifierExtraSize = 0; 78 | for (size_t pos = 0; 79 | (pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos; 80 | pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) { 81 | // figuring out whether this is nano, micro or milli identifier 82 | auto type = g3::internal::getFractional(format_buffer, pos); 83 | auto value = g3::internal::to_string(ts, type); 84 | auto padding = 0; 85 | if (type != g3::internal::Fractional::NanosecondDefault) { 86 | padding = 1; 87 | } 88 | 89 | // replacing "%f[3|6|9]" with sec fractional part value 90 | format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value); 91 | } 92 | return format_buffer; 93 | } 94 | 95 | } // internal 96 | } // g3 97 | 98 | 99 | 100 | namespace g3 { 101 | // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" 102 | // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet. 103 | // return value is SIMPLIFIED to only return a std::string 104 | std::string put_time(const struct tm* tmb, const char* c_time_format) { 105 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) 106 | std::ostringstream oss; 107 | oss.fill('0'); 108 | // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* " 109 | oss << std::put_time(const_cast (tmb), c_time_format); 110 | return oss.str(); 111 | #else // LINUX 112 | const size_t size = 1024; 113 | char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time. 114 | // ... also ... This is way more buffer space then we need 115 | 116 | auto success = std::strftime(buffer, size, c_time_format, tmb); 117 | // In DEBUG the assert will trigger a process exit. Once inside the if-statement 118 | // the 'always true' expression will be displayed as reason for the exit 119 | // 120 | // In Production mode 121 | // the assert will do nothing but the format string will instead be returned 122 | if (0 == success) { 123 | assert((0 != success) && "strftime fails with illegal formatting"); 124 | return c_time_format; 125 | } 126 | return buffer; 127 | #endif 128 | } 129 | 130 | 131 | 132 | tm localtime(const std::time_t& ts) { 133 | struct tm tm_snapshot; 134 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 135 | localtime_s(&tm_snapshot, &ts); // windsows 136 | #else 137 | localtime_r(&ts, &tm_snapshot); // POSIX 138 | #endif 139 | return tm_snapshot; 140 | } 141 | 142 | 143 | std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) { 144 | auto format_buffer = internal::localtime_formatted_fractions(ts, time_format); 145 | auto time_point = std::chrono::system_clock::to_time_t(ts); 146 | std::tm t = localtime(time_point); 147 | return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S"); 148 | } 149 | } // g3 150 | -------------------------------------------------------------------------------- /src/g3log/logworker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** ========================================================================== 3 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 4 | * with no warranties. This code is yours to share, use and modify with no 5 | * strings attached and no restrictions or obligations. 6 | * 7 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 8 | * ============================================================================ 9 | * Filename:g3logworker.h Framework for Logging and Design By Contract 10 | * Created: 2011 by Kjell Hedström 11 | * 12 | * PUBLIC DOMAIN and Not copyrighted. First published at KjellKod.cc 13 | * ********************************************* */ 14 | #include "g3log/g3log.hpp" 15 | #include "g3log/sinkwrapper.hpp" 16 | #include "g3log/sinkhandle.hpp" 17 | #include "g3log/filesink.hpp" 18 | #include "g3log/logmessage.hpp" 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | namespace g3 { 27 | class LogWorker; 28 | struct LogWorkerImpl; 29 | using FileSinkHandle = g3::SinkHandle; 30 | 31 | /// Background side of the LogWorker. Internal use only 32 | struct LogWorkerImpl final { 33 | typedef std::shared_ptr SinkWrapperPtr; 34 | std::vector _sinks; 35 | std::unique_ptr _bg; // do not change declaration order. _bg must be destroyed before sinks 36 | 37 | LogWorkerImpl(); 38 | ~LogWorkerImpl() = default; 39 | 40 | void bgSave(g3::LogMessagePtr msgPtr); 41 | void bgFatal(FatalMessagePtr msgPtr); 42 | 43 | LogWorkerImpl(const LogWorkerImpl&) = delete; 44 | LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; 45 | }; 46 | 47 | 48 | /// Front end of the LogWorker. API that is useful is 49 | /// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example 50 | /// save( msg ) : internal use 51 | /// fatal ( fatal_msg ) : internal use 52 | class LogWorker final { 53 | LogWorker() = default; 54 | void addWrappedSink(std::shared_ptr wrapper); 55 | 56 | LogWorkerImpl _impl; 57 | LogWorker(const LogWorker&) = delete; 58 | LogWorker& operator=(const LogWorker&) = delete; 59 | 60 | 61 | public: 62 | ~LogWorker(); 63 | 64 | /// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it 65 | /// if you want to use the default file logger then see below for @ref addDefaultLogger 66 | static std::unique_ptr createLogWorker(); 67 | 68 | 69 | /** 70 | A convenience function to add the default g3::FileSink to the log worker 71 | @param log_prefix that you want 72 | @param log_directory where the log is to be stored. 73 | @return a handle for API access to the sink. See the README for example usage 74 | 75 | @verbatim 76 | Example: 77 | using namespace g3; 78 | std::unique_ptr logworker {LogWorker::createLogWorker()}; 79 | auto handle = addDefaultLogger("my_test_log", "/tmp"); 80 | initializeLogging(logworker.get()); // ref. g3log.hpp 81 | 82 | std::future log_file_name = sinkHandle->call(&FileSink::fileName); 83 | std::cout << "The filename is: " << log_file_name.get() << std::endl; 84 | // something like: /tmp/my_test_log.g3log.20150819-100300.log 85 | */ 86 | std::unique_ptr addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id = "g3log"); 87 | 88 | /// Adds a sink and returns the handle for access to the sink 89 | /// @param real_sink unique_ptr ownership is passed to the log worker 90 | /// @param call the default call that should receive either a std::string or a LogMessageMover message 91 | /// @return handle to the sink for API access. See usage example below at @ref addDefaultLogger 92 | template 93 | std::unique_ptr> addSink(std::unique_ptr real_sink, DefaultLogCall call) { 94 | using namespace g3; 95 | using namespace g3::internal; 96 | auto sink = std::make_shared> (std::move(real_sink), call); 97 | addWrappedSink(sink); 98 | return std::make_unique> (sink); 99 | } 100 | 101 | 102 | /// Removes a sink. This is a synchronous call. 103 | /// You are guaranteed that the sink is removed by the time the call returns 104 | /// @param sink_handle the ownership of the sink handle is given 105 | template 106 | void removeSink(std::unique_ptr> sink_handle) { 107 | if (sink_handle) { 108 | // sink_handle->sink().use_count() is 1 at this point 109 | // i.e. this would be safe as long as no other weak_ptr to shared_ptr conversion 110 | // was made by the client: assert(sink_handle->sink().use_count() == 0); 111 | auto weak_ptr_sink = sink_handle->sink(); { 112 | auto bg_removesink_call = [this, weak_ptr_sink] { 113 | auto shared_sink = weak_ptr_sink.lock(); 114 | if (shared_sink) { 115 | _impl._sinks.erase(std::remove(_impl._sinks.begin(), _impl._sinks.end(), shared_sink), _impl._sinks.end()); 116 | } 117 | }; 118 | auto token_done = g3::spawn_task(bg_removesink_call, _impl._bg.get()); 119 | token_done.wait(); 120 | } 121 | // sink_handle->sink().use_count() is 1 at this point. 122 | // i.e. this would be safe: assert(sink_handle->sink().use_count() == 0); 123 | // as long as the client has not converted more instances from the weak_ptr 124 | } 125 | } 126 | 127 | /// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink 128 | /// handle then the sink will be removed internally but will live on in the client's instance 129 | void removeAllSinks() { 130 | auto bg_clear_sink_call = [this] { _impl._sinks.clear(); }; 131 | auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get()); 132 | token_cleared.wait(); 133 | } 134 | 135 | 136 | 137 | /// internal: 138 | /// pushes in background thread (asynchronously) input messages to log file 139 | void save(LogMessagePtr entry); 140 | 141 | /// internal: 142 | // pushes a fatal message on the queue, this is the last message to be processed 143 | /// this way it's ensured that all existing entries were flushed before 'fatal' 144 | /// Will abort the application! 145 | void fatal(FatalMessagePtr fatal_message); 146 | 147 | 148 | }; 149 | } // g3 150 | -------------------------------------------------------------------------------- /Build.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | # GENERIC STEPS 12 | SET(LOG_SRC ${g3log_SOURCE_DIR}/src) 13 | 14 | file(GLOB SRC_FILES ${LOG_SRC}/*.cpp ${LOG_SRC}/*.ipp) 15 | file(GLOB HEADER_FILES ${LOG_SRC}/g3log/*.hpp) 16 | 17 | list( APPEND HEADER_FILES ${GENERATED_G3_DEFINITIONS} ) 18 | list( APPEND SRC_FILES ${GENERATED_G3_DEFINITIONS} ) 19 | 20 | IF (MSVC OR MINGW) 21 | list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_unix.cpp) 22 | ELSE() 23 | list(REMOVE_ITEM SRC_FILES ${LOG_SRC}/crashhandler_windows.cpp ${LOG_SRC}/g3log/stacktrace_windows.hpp ${LOG_SRC}/stacktrace_windows.cpp) 24 | ENDIF (MSVC OR MINGW) 25 | 26 | set(SRC_FILES ${SRC_FILES} ${SRC_PLATFORM_SPECIFIC}) 27 | 28 | # Create the g3log library 29 | SET(G3LOG_LIBRARY g3log) 30 | 31 | 32 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") 33 | message("CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") 34 | IF( NOT CMAKE_INSTALL_PREFIX) 35 | SET(CMAKE_INSTALL_PREFIX /usr/local) 36 | ENDIF() 37 | 38 | set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}) 39 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 40 | message("Install rpath location: ${CMAKE_INSTALL_RPATH}") 41 | ENDIF() 42 | 43 | IF( G3_SHARED_LIB ) 44 | IF( WIN32 ) 45 | IF(NOT(${CMAKE_VERSION} VERSION_LESS "3.4")) 46 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 47 | ELSE() 48 | message( FATAL_ERROR "Need CMake version >=3.4 to build shared windows library!" ) 49 | ENDIF() 50 | ENDIF() 51 | ADD_LIBRARY(${G3LOG_LIBRARY} SHARED ${SRC_FILES}) 52 | ELSE() 53 | IF(MSVC) 54 | IF(NOT G3_SHARED_RUNTIME) 55 | SET(CompilerFlags 56 | CMAKE_CXX_FLAGS 57 | CMAKE_CXX_FLAGS_DEBUG 58 | CMAKE_CXX_FLAGS_RELEASE 59 | CMAKE_C_FLAGS 60 | CMAKE_C_FLAGS_DEBUG 61 | CMAKE_C_FLAGS_RELEASE 62 | ) 63 | foreach(CompilerFlag ${CompilerFlags}) 64 | string(REPLACE "/MDd" "/MTd" ${CompilerFlag} "${${CompilerFlag}}") 65 | string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") 66 | endforeach() 67 | ENDIF() 68 | ENDIF() 69 | ADD_LIBRARY(${G3LOG_LIBRARY} STATIC ${SRC_FILES}) 70 | ENDIF() 71 | 72 | SET(${G3LOG_LIBRARY}_VERSION_STRING ${VERSION}) 73 | MESSAGE( STATUS "Creating ${G3LOG_LIBRARY} VERSION: ${VERSION}" ) 74 | 75 | SET_TARGET_PROPERTIES(${G3LOG_LIBRARY} PROPERTIES 76 | LINKER_LANGUAGE CXX 77 | OUTPUT_NAME g3log 78 | CLEAN_DIRECT_OUTPUT 1 79 | SOVERSION ${VERSION} 80 | ) 81 | 82 | IF(APPLE) 83 | SET_TARGET_PROPERTIES(${G3LOG_LIBRARY} PROPERTIES MACOSX_RPATH TRUE) 84 | ENDIF() 85 | 86 | # require here some proxy for c++14 standard to avoid problems TARGET_PROPERTY CXX_STANDARD 87 | TARGET_COMPILE_FEATURES(${G3LOG_LIBRARY} PUBLIC cxx_variable_templates) 88 | 89 | TARGET_INCLUDE_DIRECTORIES(${G3LOG_LIBRARY} 90 | PUBLIC 91 | $ 92 | $ 93 | ) 94 | 95 | SET(ACTIVE_CPP0xx_DIR "Release") 96 | 97 | # find corresponding thread lib (e.g. whether -lpthread is needed or not) 98 | FIND_PACKAGE(Threads REQUIRED) 99 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} Threads::Threads ) 100 | 101 | # check for backtrace and cxa_demangle only in non-Windows dev environments 102 | IF(NOT(MSVC OR MINGW)) 103 | # the backtrace module does not provide a modern cmake target 104 | FIND_PACKAGE(Backtrace REQUIRED) 105 | if(Backtrace_FOUND) 106 | TARGET_INCLUDE_DIRECTORIES(${G3LOG_LIBRARY} PRIVATE ${Backtrace_INCLUDE_DIRS}) 107 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} ${Backtrace_LIBRARIES}) 108 | else() 109 | message( FATAL_ERROR "Could not find Library to create backtraces") 110 | endif() 111 | 112 | 113 | INCLUDE(CheckLibraryExists) 114 | INCLUDE(CheckCXXSymbolExists) 115 | 116 | #if demangle is in c++ runtime lib 117 | CHECK_CXX_SYMBOL_EXISTS(abi::__cxa_demangle "cxxabi.h" DEMANGLE_EXISTS) 118 | IF( NOT (DEMANGLE_EXISTS)) 119 | #try to link against c++abi to get demangle 120 | CHECK_LIBRARY_EXISTS(c++abi abi::__cxa_demangle "cxxabi.h" NEED_C++ABI) 121 | IF( NEED_C++ABI) 122 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} c++abi) 123 | ELSE() 124 | message( FATAL_ERROR "Could not find function abi::__cxa_demangle") 125 | ENDIF() 126 | endif() 127 | ENDIF() 128 | # add Warnings 129 | target_compile_options(${G3LOG_LIBRARY} PRIVATE 130 | # clang/GCC warnings 131 | $<$,$>:-Wall -Wunused> 132 | # MSVC warnings 133 | $<$:/W4>) 134 | # add GCC specific stuff 135 | target_compile_options(${G3LOG_LIBRARY} PRIVATE 136 | # clang/GCC warnings 137 | $<$,$>>:-rdynamic> 138 | ) 139 | 140 | #cmake -DCMAKE_CXX_COMPILER=clang++ .. 141 | # WARNING: If Clang for Linux does not work with full c++14 support it might be your 142 | # installation that is faulty. When I tested Clang on Ubuntu I followed the following 143 | # description 144 | # 1) http://kjellkod.wordpress.com/2013/09/23/experimental-g3log-with-clang/ 145 | # 2) https://github.com/maidsafe/MaidSafe/wiki/Hacking-with-Clang-llvm-abi-and-llvm-libc 146 | 147 | # Windows Stuff 148 | IF(MSVC OR MINGW) 149 | TARGET_COMPILE_DEFINITIONS(${G3LOG_LIBRARY} PRIVATE NOGDI) 150 | TARGET_LINK_LIBRARIES(${G3LOG_LIBRARY} dbghelp) 151 | # VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408 152 | # add_definition(-D_VARIADIC_MAX=10) 153 | # https://github.com/anhstudios/swganh/pull/186/files 154 | TARGET_COMPILE_DEFINITIONS(${G3LOG_LIBRARY} PRIVATE _VARIADIC_MAX=10) 155 | MESSAGE(STATUS "- MSVC: Set variadic max to 10 for MSVC compatibility") 156 | # Remember to set set target properties if using GTEST similar to done below on target "unit_test" 157 | # "set_target_properties(unit_test PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0") 158 | message( STATUS "" ) 159 | message( STATUS "Windows: Run cmake with the appropriate Visual Studio generator" ) 160 | message( STATUS "The generator is one number below the official version number. I.e. VS2013 -> Generator 'Visual Studio 12'" ) 161 | MESSAGE( STATUS "I.e. if VS2013: Please run the command [cmake -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 12\" ..]") 162 | message( STATUS "if cmake finishes OK, do 'msbuild g3log.sln /p:Configuration=Release'" ) 163 | message( STATUS "then run 'Release\\g3log-FATAL-*' examples" ) 164 | message( STATUS "" ) 165 | ENDIF() 166 | 167 | TARGET_COMPILE_OPTIONS(${G3LOG_LIBRARY} PRIVATE 168 | $<$:/utf-8> # source code already in utf-8, force it for compilers in non-utf8_windows_locale 169 | $<$:$<$:/arch:IA32>> 170 | ) 171 | -------------------------------------------------------------------------------- /Options.cmake: -------------------------------------------------------------------------------- 1 | # g3log is a KjellKod Logger 2 | # 2015 @author Kjell Hedström, hedstrom@kjellkod.cc 3 | # ================================================================== 4 | # 2015 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own 5 | # risk and comes with no warranties. 6 | # 7 | # This code is yours to share, use and modify with no strings attached 8 | # and no restrictions or obligations. 9 | # =================================================================== 10 | 11 | 12 | # PLEASE NOTE THAT: 13 | # the following definitions can through options be added 14 | # to the auto generated file src/g3log/generated_definitions.hpp 15 | # add_definitions(-DG3_DYNAMIC_LOGGING) 16 | # add_definitions(-DCHANGE_G3LOG_DEBUG_TO_DBUG) 17 | # add_definitions(-DDISABLE_FATAL_SIGNALHANDLING) 18 | # add_definitions(-DDISABLE_VECTORED_EXCEPTIONHANDLING) 19 | # add_definitions(-DDEBUG_BREAK_AT_FATAL_SIGNAL) 20 | # add_definitions(-DG3_DYNAMIC_MAX_MESSAGE_SIZE) 21 | 22 | 23 | 24 | # Used for generating a macro definitions file that is to be included 25 | # that way you do not have to re-state the Options.cmake definitions when 26 | # compiling your binary (if done in a separate build step from the g3log library) 27 | SET(G3_DEFINITIONS "") 28 | 29 | # -DG3_IOS_LIB=ON : iOS version of library 30 | option(G3_IOS_LIB 31 | "iOS version of library." OFF) 32 | IF(G3_IOS_LIB) 33 | MESSAGE("-DG3_IOS_LIB=ON\t\t\t\tBuilding iOS version") 34 | ENDIF(G3_IOS_LIB) 35 | 36 | # -DUSE_DYNAMIC_LOGGING_LEVELS=ON : run-type turn on/off levels 37 | option (USE_DYNAMIC_LOGGING_LEVELS 38 | "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) 39 | IF(USE_DYNAMIC_LOGGING_LEVELS) 40 | LIST(APPEND G3_DEFINITIONS G3_DYNAMIC_LOGGING) 41 | message( STATUS "-DUSE_DYNAMIC_LOGGING_LEVELS=ON" ) 42 | message( STATUS "\tDynamic logging levels is used" ) 43 | message( STATUS "\tUse [g3::addLogLevel(LEVEL boolean)] to enable/disable logging on specified levels\n\n" ) 44 | ELSE() 45 | message( STATUS "-DUSE_DYNAMIC_LOGGING_LEVELS=OFF" ) 46 | ENDIF(USE_DYNAMIC_LOGGING_LEVELS) 47 | 48 | 49 | 50 | # -DCHANGE_G3LOG_DEBUG_TO_DBUG=ON : change the DEBUG logging level to be DBUG to avoid clash with other libraries that might have 51 | # predefined DEBUG for their own purposes 52 | option (CHANGE_G3LOG_DEBUG_TO_DBUG 53 | "Use DBUG logging level instead of DEBUG. By default DEBUG is the debugging level" OFF) 54 | IF(CHANGE_G3LOG_DEBUG_TO_DBUG) 55 | LIST(APPEND G3_DEFINITIONS CHANGE_G3LOG_DEBUG_TO_DBUG) 56 | LIST(APPEND G3_DEFINITIONS "G3LOG_DEBUG DBUG") 57 | message( STATUS "-DCHANGE_G3LOG_DEBUG_TO_DBUG=ON DBUG instead of DEBUG logging level is used" ) 58 | ELSE() 59 | LIST(APPEND G3_DEFINITIONS "G3LOG_DEBUG DEBUG") 60 | message( STATUS "-DCHANGE_G3LOG_DEBUG_TO_DBUG=OFF \t(Debuggin logging level is 'DEBUG')" ) 61 | ENDIF(CHANGE_G3LOG_DEBUG_TO_DBUG) 62 | 63 | 64 | # -DG3_DYNAMIC_MAX_MESSAGE_SIZE : use dynamic memory for final_message in logcapture.cpp 65 | option (USE_G3_DYNAMIC_MAX_MESSAGE_SIZE 66 | "Use dynamic memory for message buffer during log capturing" OFF) 67 | IF(USE_G3_DYNAMIC_MAX_MESSAGE_SIZE) 68 | LIST(APPEND G3_DEFINITIONS G3_DYNAMIC_MAX_MESSAGE_SIZE) 69 | message( STATUS "-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=ON\t\tDynamic memory used during log capture" ) 70 | ELSE() 71 | message( STATUS "-DUSE_G3_DYNAMIC_MAX_MESSAGE_SIZE=OFF" ) 72 | ENDIF(USE_G3_DYNAMIC_MAX_MESSAGE_SIZE) 73 | 74 | 75 | # G3LOG_FULL_FILENAME logs full file name instead of short filename. This makes it 76 | # easier to copy filenames to open them without needing to search. 77 | option (G3_LOG_FULL_FILENAME "Log full filename" OFF) 78 | IF(G3_LOG_FULL_FILENAME) 79 | LIST(APPEND G3_DEFINITIONS G3_LOG_FULL_FILENAME) 80 | message( STATUS "-DG3_LOG_FULL_FILENAME=ON\t\tShowing full filenames with logs") 81 | ELSE() 82 | message( STATUS "-DG3_LOG_FULL_FILENAME=OFF") 83 | ENDIF(G3_LOG_FULL_FILENAME) 84 | 85 | 86 | # -DENABLE_FATAL_SIGNALHANDLING=ON : default change the 87 | # By default fatal signal handling is enabled. You can disable it with this option 88 | # enumerated in src/stacktrace_windows.cpp 89 | option (ENABLE_FATAL_SIGNALHANDLING 90 | "Vectored exception / crash handling with improved stack trace" ON) 91 | 92 | IF(NOT ENABLE_FATAL_SIGNALHANDLING) 93 | LIST(APPEND G3_DEFINITIONS DISABLE_FATAL_SIGNALHANDLING) 94 | 95 | message( STATUS "-DENABLE_FATAL_SIGNALHANDLING=OFF Fatal signal handler is disabled" ) 96 | ELSE() 97 | message( STATUS "-DENABLE_FATAL_SIGNALHANDLING=ON\tFatal signal handler is enabled" ) 98 | ENDIF(NOT ENABLE_FATAL_SIGNALHANDLING) 99 | 100 | # Option for building as a static or shared library in all platforms 101 | option (G3_SHARED_LIB "Build shared library" ON) 102 | IF(G3_SHARED_LIB) 103 | message( STATUS "-DG3_SHARED_LIB=ON\tBuild shared library" ) 104 | ELSE() 105 | MESSAGE( STATUS "-DG3_SHARED_LIB=OFF\tBuild static library") 106 | ENDIF() 107 | 108 | # Option for building as a static or shared runtime library in MS VC++ 109 | option (G3_SHARED_RUNTIME "Build shared runtime library MS VC" ON) 110 | IF(G3_SHARED_RUNTIME) 111 | message( STATUS "-DG3_SHARED_RUNTIME=ON\tBuild shared runtime library" ) 112 | ELSE() 113 | message( STATUS "-DG3_SHARED_RUNTIME=OFF\tBuild static runtime library") 114 | ENDIF() 115 | 116 | # WINDOWS OPTIONS 117 | IF (MSVC OR MINGW) 118 | # -DENABLE_VECTORED_EXCEPTIONHANDLING=ON : defualt change the 119 | # By default vectored exception handling is enabled, you can disable it with this option. 120 | # Please know that only known fatal exceptions will be caught, these exceptions are the ones 121 | # enumerated in src/stacktrace_windows.cpp 122 | option (ENABLE_VECTORED_EXCEPTIONHANDLING 123 | "Vectored exception / crash handling with improved stack trace" ON) 124 | 125 | IF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING) 126 | LIST(APPEND G3_DEFINITIONS DISABLE_VECTORED_EXCEPTIONHANDLING) 127 | message( STATUS "-DENABLE_VECTORED_EXCEPTIONHANDLING=OFF Vectored exception handling is disabled" ) 128 | ELSE() 129 | message( STATUS "-DENABLE_VECTORED_EXCEPTIONHANDLING=ON\t\t\tVectored exception handling is enabled" ) 130 | ENDIF(NOT ENABLE_VECTORED_EXCEPTIONHANDLING) 131 | 132 | 133 | 134 | 135 | # Default ON. Will trigger a break point in DEBUG builds if the signal handler 136 | # receives a fatal signal. 137 | # 138 | option (DEBUG_BREAK_AT_FATAL_SIGNAL 139 | "Enable Visual Studio break point when receiving a fatal exception. In __DEBUG mode only" OFF) 140 | IF(DEBUG_BREAK_AT_FATAL_SIGNAL) 141 | LIST(APPEND G3_DEFINITIONS DEBUG_BREAK_AT_FATAL_SIGNAL) 142 | message( STATUS "-DDEBUG_BREAK_AT_FATAL_SIGNAL=ON Break point for fatal signal is enabled for __DEBUG." ) 143 | ELSE() 144 | message( STATUS "-DDEBUG_BREAK_AT_FATAL_SIGNAL=OFF\t\t\tBreak point for fatal signal is disabled" ) 145 | ENDIF(DEBUG_BREAK_AT_FATAL_SIGNAL) 146 | 147 | ENDIF (MSVC OR MINGW) 148 | message( STATUS "\n\n\n" ) 149 | 150 | option(INSTALL_G3LOG "Enable installation of g3log. (Projects embedding g3log may want to turn this OFF.)" ON) 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/g3log/loglevels.hpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #pragma once 10 | #include "g3log/generated_definitions.hpp" 11 | 12 | // Users of Juce or other libraries might have a define DEBUG which clashes with 13 | // the DEBUG logging level for G3log. In that case they can instead use the define 14 | // "CHANGE_G3LOG_DEBUG_TO_DBUG" and G3log's logging level DEBUG is changed to be DBUG 15 | #if (defined(CHANGE_G3LOG_DEBUG_TO_DBUG)) 16 | #if (defined(DBUG)) 17 | #error "DBUG is already defined elsewhere which clashes with G3Log's log level DBUG" 18 | #endif 19 | #else 20 | #if (defined(DEBUG)) 21 | #error "DEBUG is already defined elsewhere which clashes with G3Log's log level DEBUG" 22 | #endif 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod 32 | struct LEVELS { 33 | // force internal copy of the const char*. This is a simple safeguard for when g3log is used in a 34 | // "dynamic, runtime loading of shared libraries" 35 | 36 | LEVELS(const LEVELS& other): value(other.value), text(other.text.c_str()) {} 37 | LEVELS(int id, const std::string& idtext) : value(id), text(idtext) {} 38 | 39 | bool operator==(const LEVELS& rhs) const { 40 | return (value == rhs.value && text == rhs.text); 41 | } 42 | 43 | bool operator!=(const LEVELS& rhs) const { 44 | return (value != rhs.value || text != rhs.text); 45 | } 46 | 47 | friend void swap(LEVELS& first, LEVELS& second) { 48 | using std::swap; 49 | swap(first.value, second.value); 50 | swap(first.text, second.text); 51 | } 52 | 53 | 54 | LEVELS& operator=(LEVELS other) { 55 | swap(*this, other); 56 | return *this; 57 | } 58 | 59 | 60 | int value; 61 | std::string text; 62 | }; 63 | 64 | // If you want to add any extra logging level then please add to your own source file the logging level you need 65 | // 1. If the cmake option G3_DYNAMIC_LOGGING is enabled then you must use g3::only_change_at_initialization::addLogLevel(...). 66 | // to give g3log a record of your logging level and if it is an enabled or disbled logging level. 67 | // 68 | // 2. If the cmake dynamic logging option is turned OFF 69 | // then giving g3log a record of your logging level with 'addLogLevel(...) is NOT needed since no "disbled/enabled" 70 | // check will happen - all logging levels will be considered enabled. 71 | // 3. See also the [g3log/API.markdown](https://github.com/KjellKod/g3log/blob/master/API.markdown) for for information. 72 | // 73 | // example: MyLoggingLevel.h 74 | // #pragma once 75 | // const LEVELS MYINFO {WARNING.value +1, "MyInfoLevel"}; 76 | // const LEVELS MYFATAL {FATAL.value +1, "MyFatalLevel"}; 77 | // 78 | // ... somewhere else when G3_DYNAMIC_LOGGING is enabled 79 | // addLogLevel(MYINFO, true); 80 | // LOG(MYINFO) << "some text"; 81 | // 82 | // ... another example, when G3_DYNAMIC_LOGGING is enabled 83 | // 'addLogLevel' is NOT required 84 | // LOG(MYFATAL) << "this will just work, and it will be counted as a FATAL event"; 85 | namespace g3 { 86 | static const int kDebugValue = 100; 87 | static const int kInfoValue = 300; 88 | static const int kWarningValue = 500; 89 | static const int kFatalValue = 1000; 90 | static const int kInternalFatalValue = 2000; 91 | } // g3 92 | 93 | 94 | const LEVELS G3LOG_DEBUG{g3::kDebugValue, {"DEBUG"}}, 95 | INFO {g3::kInfoValue, {"INFO"}}, 96 | WARNING {g3::kWarningValue, {"WARNING"}}, 97 | FATAL {g3::kFatalValue, {"FATAL"}}; 98 | 99 | 100 | 101 | namespace g3 { 102 | // Logging level and atomic status collection struct 103 | struct LoggingLevel { 104 | atomicbool status; 105 | LEVELS level; 106 | 107 | // default operator needed for std::map compliance 108 | LoggingLevel(): status(false), level(INFO) {}; 109 | LoggingLevel(const LoggingLevel& lvl) : status(lvl.status), level(lvl.level) {} 110 | LoggingLevel(const LEVELS& lvl): status(true), level(lvl) {}; 111 | LoggingLevel(const LEVELS& lvl, bool enabled): status(enabled), level(lvl) {}; 112 | ~LoggingLevel() = default; 113 | 114 | LoggingLevel& operator=(const LoggingLevel& other) { 115 | status = other.status; 116 | level = other.level; 117 | return *this; 118 | } 119 | 120 | bool operator==(const LoggingLevel& rhs) const { 121 | return (status == rhs.status && level == rhs.level); 122 | } 123 | 124 | }; 125 | } // g3 126 | 127 | 128 | 129 | 130 | namespace g3 { 131 | namespace internal { 132 | const LEVELS CONTRACT {g3::kInternalFatalValue, {"CONTRACT"}}, 133 | FATAL_SIGNAL {g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}}, 134 | FATAL_EXCEPTION {kInternalFatalValue + 2, {"FATAL_EXCEPTION"}}; 135 | 136 | /// helper function to tell the logger if a log message was fatal. If it is it will force 137 | /// a shutdown after all log entries are saved to the sinks 138 | bool wasFatal(const LEVELS& level); 139 | } 140 | 141 | #ifdef G3_DYNAMIC_LOGGING 142 | // Only safe if done at initialization in a single-thread context 143 | namespace only_change_at_initialization { 144 | 145 | /// add a custom level - enabled or disabled 146 | void addLogLevel(LEVELS level, bool enabled); 147 | 148 | /// add a custom level - enabled 149 | void addLogLevel(LEVELS level); 150 | 151 | /// reset all default logging levels to enabled 152 | /// remove any added logging levels so that the only ones left are 153 | /// {DEBUG,INFO,WARNING,FATAL} 154 | void reset(); 155 | } // only_change_at_initialization 156 | 157 | 158 | namespace log_levels { 159 | /// Enable log level >= log_level. 160 | /// log levels below will be disabled 161 | /// log levels equal or higher will be enabled. 162 | void setHighest(LEVELS level); 163 | 164 | void set(LEVELS level, bool enabled); 165 | void disable(LEVELS level); 166 | void enable(LEVELS level); 167 | 168 | /// WARNING: This will also disable FATAL events from being logged 169 | void disableAll(); 170 | void enableAll(); 171 | 172 | 173 | /// print all levels with their disabled or enabled status 174 | std::string to_string(std::map levelsToPrint); 175 | 176 | /// print snapshot of system levels with their 177 | /// disabled or enabled status 178 | std::string to_string(); 179 | 180 | 181 | /// Snapshot view of the current logging levels' status 182 | std::map getAll(); 183 | 184 | enum class status {Absent, Enabled, Disabled}; 185 | status getStatus(LEVELS level); 186 | } // log_levels 187 | 188 | #endif 189 | /// Enabled status for the given logging level 190 | bool logLevel(const LEVELS& level); 191 | 192 | } // g3 193 | -------------------------------------------------------------------------------- /test_unit/test_filechange.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include 20 | #include "g3log/g3log.hpp" 21 | #include "g3log/logworker.hpp" 22 | #include "testing_helpers.h" 23 | 24 | using namespace testing_helpers; 25 | 26 | 27 | namespace { // anonymous 28 | const char* name_path_1 = "./(some_fake_DirectoryOrName_1_)"; 29 | const std::string kReplaceFileName = "(ReplaceLogFile)"; 30 | g3::LogWorker* g_logger_ptr = nullptr; 31 | g3::SinkHandle* g_filesink_handler = nullptr; 32 | LogFileCleaner* g_cleaner_ptr = nullptr; 33 | 34 | std::string setLogNameAndAddCount(std::string new_file_to_create, std::string logger_id = "g3log") { 35 | static std::mutex m; 36 | static int count; 37 | std::string add_count; 38 | std::lock_guard lock(m); 39 | { 40 | add_count = std::to_string(++count) + "_"; 41 | auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create + add_count, logger_id); 42 | auto new_log = future_new_log.get(); 43 | if (!new_log.empty()) { 44 | g_cleaner_ptr->addLogToClean(new_log); 45 | } else { 46 | std::cout << "\nFailed to set filename: " << new_file_to_create << std::endl; 47 | } 48 | return new_log; 49 | } 50 | return add_count; 51 | } 52 | 53 | std::string setLogName(std::string new_file_to_create, std::string logger_id = "g3log") { 54 | auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create, logger_id); 55 | auto new_log = future_new_log.get(); 56 | if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log); 57 | return new_log; 58 | } 59 | 60 | std::string getLogName() { 61 | return g_filesink_handler->call(&g3::FileSink::fileName).get(); 62 | } 63 | 64 | } // anonymous 65 | 66 | TEST(TestOf_GetFileName, Expecting_ValidLogFile) { 67 | 68 | LOG(INFO) << "test_filechange, Retrieving file name: "; 69 | ASSERT_NE(g_logger_ptr, nullptr); 70 | ASSERT_FALSE(getLogName().empty()); 71 | } 72 | 73 | TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed) { 74 | auto old_log = getLogName(); 75 | std::string name = setLogNameAndAddCount(name_path_1); 76 | auto new_log = setLogName(name); 77 | ASSERT_NE(old_log, new_log); 78 | } 79 | 80 | TEST(TestOf_ChangingLogFile_Id, Expecting_NewLogFileUsed1) { 81 | auto old_log = getLogName(); 82 | setLogNameAndAddCount(name_path_1); 83 | auto new_log = setLogName("foo", "new_logger_id"); 84 | ASSERT_NE(old_log, new_log); 85 | std::string new_name = getLogName(); 86 | auto expected_part_of__new_name = std::string("foo") + kReplaceFileName + ".new_logger_id"; 87 | auto extracted_name = new_name.substr(0, expected_part_of__new_name.size()); 88 | ASSERT_EQ(extracted_name.c_str(), expected_part_of__new_name); 89 | } 90 | 91 | TEST(TestOf_ChangingLogFile_NoId, Expecting_NewLogFileUsed2) { 92 | auto old_log = getLogName(); 93 | setLogNameAndAddCount(name_path_1); 94 | auto new_log = setLogName("foo", ""); 95 | ASSERT_NE(old_log, new_log); 96 | std::string new_name = getLogName(); 97 | auto expected_part_of__new_name = std::string("foo") + kReplaceFileName; 98 | auto extracted_name = new_name.substr(0, expected_part_of__new_name.size()); 99 | ASSERT_EQ(extracted_name.c_str(), expected_part_of__new_name); 100 | } 101 | 102 | TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) { 103 | auto old_log = g_filesink_handler->call(&g3::FileSink::fileName).get(); 104 | if (!old_log.empty()) g_cleaner_ptr->addLogToClean(old_log); 105 | 106 | LOG(INFO) << "SoManyThreadsAllDoingChangeFileName"; 107 | std::vector threads; 108 | auto max = 2; 109 | auto size = g_cleaner_ptr->size(); 110 | for (auto count = 0; count < max; ++count) { 111 | std::string drive = ((count % 2) == 0) ? "./_threadEven_" : "./_threaOdd_"; 112 | std::string logger_id = std::to_string(count); 113 | threads.push_back(std::thread(setLogNameAndAddCount, drive, logger_id)); 114 | } 115 | for (auto& thread : threads) 116 | thread.join(); 117 | 118 | // check that all logs were created 119 | ASSERT_EQ(size + max, g_cleaner_ptr->size()); 120 | } 121 | 122 | TEST(TestOf_IllegalLogFileName, Expecting_NoChangeToOriginalFileName) { 123 | std::string original = getLogName(); 124 | auto perhaps_a_name = setLogName("XY:/"); // does not exist 125 | ASSERT_TRUE(perhaps_a_name.empty()); 126 | std::string post_illegal = getLogName(); 127 | ASSERT_STREQ(original.c_str(), post_illegal.c_str()); 128 | } 129 | 130 | TEST(TestOf_SinkHandleDifferentId, Expecting_DifferentId) { 131 | auto sink = std::make_unique("AnotherLogFile", name_path_1, "logger_id"); 132 | auto name = sink->fileName(); 133 | ASSERT_STREQ( name.substr(0, 26).c_str(), "./AnotherLogFile.logger_id"); 134 | g_cleaner_ptr->addLogToClean(name); 135 | } 136 | 137 | TEST(TestOf_LegalLogFileNam, With_parenthesis) { 138 | std::string original = getLogName(); 139 | auto perhaps_a_name = setLogName("(test)"); // does not exist 140 | EXPECT_NE(original, perhaps_a_name); 141 | std::string post_legal = getLogName(); 142 | EXPECT_TRUE(std::string::npos != post_legal.find("(test)")) << "filename was: " << post_legal; 143 | } 144 | 145 | 146 | int main(int argc, char* argv[]) { 147 | LogFileCleaner cleaner; 148 | g_cleaner_ptr = &cleaner; 149 | int return_value = 1; 150 | std::stringstream cerrDump; 151 | 152 | std::string last_log_file; 153 | { 154 | 155 | testing_helpers::ScopedOut scopedCerr(std::cerr, &cerrDump); 156 | 157 | auto worker = g3::LogWorker::createLogWorker(); 158 | auto handle = worker->addDefaultLogger(kReplaceFileName, name_path_1); 159 | g_logger_ptr = worker.get(); 160 | g_filesink_handler = handle.get(); 161 | last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get(); 162 | std::cout << "log file at: " << last_log_file << std::endl; 163 | cleaner.addLogToClean(last_log_file); 164 | 165 | 166 | g3::initializeLogging(g_logger_ptr); 167 | LOG(INFO) << "test_filechange demo*" << std::endl; 168 | 169 | testing::InitGoogleTest(&argc, argv); 170 | return_value = RUN_ALL_TESTS(); 171 | 172 | last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get(); 173 | std::cout << "log file at: " << last_log_file << std::endl; 174 | //g3::internal::shutDownLogging(); 175 | } 176 | std::cout << "FINISHED WITH THE TESTING" << std::endl; 177 | // cleaning up 178 | cleaner.addLogToClean(last_log_file); 179 | return return_value; 180 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | # with no warranties. This code is yours to share, use and modify with no 4 | # strings attached and no restrictions or obligations. 5 | # 6 | # For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | # ============================================================================== 8 | 9 | # Below are details for compiling on Windows and Linux by default only an 10 | # example g3log binary is created the performance and unit tests creation can be 11 | # enabled by switching their OPTIONs from OFF to ON --- See below at around line 12 | # 110 13 | 14 | # === WINDOWS === 15 | # Example for: Visual Studio 2013 (earlier should work too) 1. please use the 16 | # "Visual Studio Command Prompt 12 (2013)" 2. from the g3log folder mkdir build 17 | # cd build; 3. cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio XXX" .. (cmake 18 | # -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 12") MAKE SURE you check the 19 | # CMake documentation so you are using the correct bit flags(64 bit etc). The 20 | # "XXX" needs tto be replaced for your specific build system, ref: cmake docs. 21 | # 22 | # (Example from Appveyor Ci: 23 | # https://github.com/KjellKod/g3log/blob/master/appveyor.yml cmake -G "Visual 24 | # Studio 14 2015 Win64" -DADD_G3LOG_UNIT_TEST=ON ..) 25 | # 26 | # 1. msbuild g3log.sln /p:Configuration=Release 27 | # 28 | # Try to run an example, such as: 5. Release\g3log-FATAL-contract.exe 29 | # 30 | 31 | # === LINUX: === To try this out from folder g3log: mkdir build cd build >> 32 | # create makefiles in g3log/build directory cmake -DCMAKE_BUILD_TYPE=Release .. 33 | # make -jN (where N stands for number of cores you want to utilize) 34 | # 35 | # === Clang on Linux === 36 | # From g3log mkdir build && cd build cmake -DCMAKE_CXX_COMPILER=clang++ .. if 37 | # you want to double-check settings: "VERBOSE=1 make" otherwise just run: 38 | # "make -j" 39 | # 40 | # ============================================================================ 41 | 42 | cmake_minimum_required(VERSION 3.2) 43 | 44 | project(g3log CXX) 45 | 46 | set(CMAKE_CXX_STANDARD 17) 47 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 48 | 49 | if(NOT CMAKE_BUILD_TYPE AND NOT (MSVC_IDE OR XCODE)) 50 | set(CMAKE_BUILD_TYPE 51 | Release 52 | CACHE STRING "Build type, one of: Release, Debug" FORCE) 53 | endif() 54 | 55 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 56 | message(STATUS "Configuration types: ${CMAKE_CONFIGURATION_TYPES}") 57 | 58 | # Detect 64 or 32 bit 59 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 60 | # 64-bit project 61 | set(64_BIT_OS TRUE) 62 | message(STATUS "A 64-bit OS detected") 63 | else() 64 | set(64_BIT_OS FALSE) 65 | message(STATUS "A 32-bit OS detected") 66 | endif() 67 | 68 | 69 | # Calculate the version number 70 | SET(MAJOR_VERSION 2) 71 | SET(MINOR_VERSION 1) 72 | 73 | IF ( NOT VERSION ) 74 | IF ( "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows" ) 75 | message("windows: Extracting git software version") 76 | execute_process(COMMAND cmd /c "git rev-list --branches HEAD | find /v " " /c" OUTPUT_VARIABLE GIT_VERSION WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 77 | ELSE() 78 | IF(UNIX OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 79 | message( STATUS "nix: Extracting git software version" ) 80 | ELSE() 81 | message( STATUS "unknown platform: extracting git software version" ) 82 | ENDIF() 83 | execute_process(COMMAND bash "-c" "git rev-list --branches HEAD | wc -l | tr -d ' ' | tr -d '\n'" OUTPUT_VARIABLE GIT_VERSION WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 84 | ENDIF() 85 | 86 | math(EXPR VERSION-BASE ${GIT_VERSION}/255) 87 | math(EXPR VERSION-REMAINDER ${GIT_VERSION}%255) 88 | message( STATUS "git build version: ${GIT_VERSION}" ) 89 | message( STATUS "version base: ${VERSION-BASE}" ) 90 | message( STATUS "version remainder: ${VERSION-REMAINDER}" ) 91 | SET(BUILD_NUMBER ${VERSION-BASE}) 92 | SET(VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${BUILD_NUMBER}-${VERSION-REMAINDER}) 93 | ENDIF() 94 | message( STATUS "Software Version: ${VERSION}" ) 95 | 96 | # ============================================================================ 97 | # G3LOG OPTIONAL FEATURES 98 | # ============================================================================ 99 | include(${g3log_SOURCE_DIR}/Options.cmake) 100 | 101 | # ============================================================================ 102 | # G3LOG iOS BUILD SUPPORT 103 | # ============================================================================ 104 | include(${g3log_SOURCE_DIR}/iOSBuild.cmake) 105 | 106 | if(G3_IOS_LIB) 107 | # G3_IOS_LIB is the pass used to generate all the other cmakefiles for the 108 | # different architectures needed for the universal library. So we're done at 109 | # here. 110 | return() 111 | endif() 112 | 113 | # ========================================================================= 114 | # G3 Macro definitions in Options.cmake are written to file this avoids having 115 | # to re-state your definitions in your source code or compile options 116 | # ========================================================================== 117 | include(${g3log_SOURCE_DIR}/GenerateMacroDefinitionsFile.cmake) 118 | 119 | # ========================================================================= 120 | # G3LOG BUILD 121 | # ========================================================================== 122 | include(${g3log_SOURCE_DIR}/Build.cmake) 123 | 124 | # ============================================================================ 125 | # EXAMPLE OPTIONS: By defauls is ON. This will create 'g3log-FATAL-* examples' 126 | # ============================================================================ 127 | # DISABLE WITH: -DADD_FATAL_EXAMPLE=OFF 128 | include(${g3log_SOURCE_DIR}/example/Example.cmake) 129 | 130 | # ============================================================================ 131 | # PERFORMANCE TEST OPTIONS: Performance operations for g3log 132 | # ============================================================================ 133 | # ENABLE WITH: -DADD_G3LOG_PERFORMANCE=ON 134 | include(${g3log_SOURCE_DIR}/test_performance/Performance.cmake) 135 | 136 | # ========================================================================== 137 | # UNIT TEST OPTIONS: 138 | # ============================================================================ 139 | # ENABLE WITH: -DADD_G3LOG_UNIT_TEST=ON 140 | include(${g3log_SOURCE_DIR}/test_unit/Test.cmake) 141 | 142 | # ========================================================================== 143 | # CMAKE INSTALL AND CPACK OPTIONS: 144 | # ========================================================================== 145 | # 146 | # Alternative 1: Package handling is done AFTER all other CMake setup usage: 147 | # make package Check the output result and install accordingly. 148 | # 149 | # Alternative 2: usage: make; sudo make install 150 | # 151 | # For OSX you can also install an older version using 'brew install' 152 | # 153 | # ========================================================================== 154 | include(${g3log_SOURCE_DIR}/CPackLists.txt) 155 | 156 | if(MINGW) 157 | # this enables strerror_s 158 | add_definitions(-DMINGW_HAS_SECURE_API) 159 | endif() 160 | 161 | if(NOT MSVC) 162 | message( 163 | STATUS 164 | "\n\n 165 | ******************************************************************* 166 | Please do 'make clean-cmake' before next cmake generation. 167 | It is a good idea to purge your build directory of CMake 168 | generated cache files 169 | ******************************************************************* 170 | ") 171 | add_custom_target(clean-cmake COMMAND ${CMAKE_COMMAND} -P 172 | ${g3log_SOURCE_DIR}/CleanAll.cmake) 173 | endif() 174 | -------------------------------------------------------------------------------- /src/logmessage.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include "g3log/logmessage.hpp" 10 | #include "g3log/crashhandler.hpp" 11 | #include "g3log/time.hpp" 12 | #include 13 | 14 | 15 | 16 | 17 | namespace g3 { 18 | 19 | std::string LogMessage::splitFileName(const std::string& str) { 20 | size_t found; 21 | found = str.find_last_of("(/\\"); 22 | return str.substr(found + 1); 23 | } 24 | 25 | 26 | // helper for fatal signal 27 | std::string LogMessage::fatalSignalToString(const LogMessage& msg) { 28 | std::string out; // clear any previous text and formatting 29 | out.append(msg.timestamp() 30 | + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" 31 | + msg.message() + '\n'); 32 | return out; 33 | } 34 | 35 | 36 | // helper for fatal exception (windows only) 37 | std::string LogMessage::fatalExceptionToString(const LogMessage& msg) { 38 | std::string out; // clear any previous text and formatting 39 | out.append(msg.timestamp() 40 | + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" 41 | + msg.message() + '\n'); 42 | return out; 43 | } 44 | 45 | 46 | // helper for fatal LOG 47 | std::string LogMessage::fatalLogToString(const LogMessage& msg) { 48 | auto out = msg._logDetailsToStringFunc(msg); 49 | static const std::string fatalExitReason = {"EXIT trigger caused by LOG(FATAL) entry: "}; 50 | out.append("\n\t*******\t " + fatalExitReason + "\n\t" + '"' + msg.message() + '"'); 51 | return out; 52 | } 53 | 54 | // helper for fatal CHECK 55 | std::string LogMessage::fatalCheckToString(const LogMessage& msg) { 56 | auto out = msg._logDetailsToStringFunc(msg); 57 | static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"}; 58 | out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" 59 | + '"' + msg. message() + '"'); 60 | return out; 61 | } 62 | 63 | // helper for setting the normal log details in an entry 64 | std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) { 65 | std::string out; 66 | out.append(msg.timestamp() + "\t" 67 | + msg.level() 68 | + " [" 69 | + msg.file() 70 | + "->" 71 | + msg.function() 72 | + ":" + msg.line() + "]\t"); 73 | return out; 74 | } 75 | 76 | 77 | std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) { 78 | std::string out; 79 | out.append(msg.timestamp() + "\t" 80 | + msg.level() 81 | + " [" 82 | + msg.threadID() 83 | + " " 84 | + msg.file() 85 | + "->"+ msg.function() 86 | + ":" + msg.line() + "]\t"); 87 | return out; 88 | } 89 | 90 | 91 | // helper for normal 92 | std::string LogMessage::normalToString(const LogMessage& msg) { 93 | auto out = msg._logDetailsToStringFunc(msg); 94 | out.append(msg.message() + '\n'); 95 | return out; 96 | } 97 | 98 | 99 | 100 | // end static functions section 101 | 102 | void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const{ 103 | _logDetailsToStringFunc = func; 104 | } 105 | 106 | 107 | 108 | // Format the log message according to it's type 109 | std::string LogMessage::toString(LogDetailsFunc formattingFunc) const { 110 | overrideLogDetailsFunc(formattingFunc); 111 | 112 | if (false == wasFatal()) { 113 | return LogMessage::normalToString(*this); 114 | } 115 | 116 | const auto level_value = _level.value; 117 | if (internal::FATAL_SIGNAL.value == _level.value) { 118 | return LogMessage::fatalSignalToString(*this); 119 | } 120 | 121 | if (internal::FATAL_EXCEPTION.value == _level.value) { 122 | return LogMessage::fatalExceptionToString(*this); 123 | } 124 | 125 | if (FATAL.value == _level.value) { 126 | return LogMessage::fatalLogToString(*this); 127 | } 128 | 129 | if (internal::CONTRACT.value == level_value) { 130 | return LogMessage::fatalCheckToString(*this); 131 | } 132 | 133 | // What? Did we hit a custom made level? 134 | auto out = _logDetailsToStringFunc(*this); 135 | static const std::string errorUnknown = {"UNKNOWN or Custom made Log Message Type"}; 136 | out.append("\t*******" + errorUnknown + "\n\t" + message() + '\n'); 137 | return out; 138 | } 139 | 140 | 141 | 142 | std::string LogMessage::timestamp(const std::string& time_look) const { 143 | return g3::localtime_formatted(to_system_time(_timestamp), time_look); 144 | } 145 | 146 | 147 | // By copy, not by reference. See this explanation for details: 148 | // http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom 149 | LogMessage& LogMessage::operator=(LogMessage other) { 150 | swap(*this, other); 151 | return *this; 152 | } 153 | 154 | 155 | LogMessage::LogMessage(std::string file, const int line, 156 | std::string function, const LEVELS level) 157 | : _logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString) 158 | , _timestamp(std::chrono::high_resolution_clock::now()) 159 | , _call_thread_id(std::this_thread::get_id()) 160 | #if defined(G3_LOG_FULL_FILENAME) 161 | , _file(file) 162 | #else 163 | , _file(LogMessage::splitFileName(file)) 164 | #endif 165 | , _file_path(file) 166 | , _line(line) 167 | , _function(function) 168 | , _level(level) { 169 | } 170 | 171 | 172 | LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) 173 | : LogMessage( {""}, 0, {""}, internal::FATAL_SIGNAL) { 174 | _message.append(fatalOsSignalCrashMessage); 175 | } 176 | 177 | LogMessage::LogMessage(const LogMessage& other) 178 | : _logDetailsToStringFunc(other._logDetailsToStringFunc) 179 | , _timestamp(other._timestamp) 180 | , _call_thread_id(other._call_thread_id) 181 | , _file(other._file) 182 | , _file_path(other._file_path) 183 | , _line(other._line) 184 | , _function(other._function) 185 | , _level(other._level) 186 | , _expression(other._expression) 187 | , _message(other._message) { 188 | } 189 | 190 | LogMessage::LogMessage(LogMessage&& other) 191 | : _logDetailsToStringFunc(other._logDetailsToStringFunc) 192 | , _timestamp(other._timestamp) 193 | , _call_thread_id(other._call_thread_id) 194 | , _file(std::move(other._file)) 195 | , _file_path(std::move(other._file_path)) 196 | , _line(other._line) 197 | , _function(std::move(other._function)) 198 | , _level(other._level) 199 | , _expression(std::move(other._expression)) 200 | , _message(std::move(other._message)) { 201 | } 202 | 203 | 204 | 205 | std::string LogMessage::threadID() const { 206 | std::ostringstream oss; 207 | oss << _call_thread_id; 208 | return oss.str(); 209 | } 210 | 211 | 212 | 213 | FatalMessage::FatalMessage(const LogMessage& details, g3::SignalType signal_id) 214 | : LogMessage(details), _signal_id(signal_id) { } 215 | 216 | 217 | 218 | FatalMessage::FatalMessage(const FatalMessage& other) 219 | : LogMessage(other), _signal_id(other._signal_id) {} 220 | 221 | 222 | LogMessage FatalMessage::copyToLogMessage() const { 223 | return LogMessage(*this); 224 | } 225 | 226 | std::string FatalMessage::reason() const { 227 | return internal::exitReasonName(_level, _signal_id); 228 | } 229 | 230 | 231 | } // g3 232 | -------------------------------------------------------------------------------- /test_performance/main_threaded_worst.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | // through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE 10 | #include "performance.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if defined(G3LOG_PERFORMANCE) 19 | const std::string title { 20 | "G3LOG" 21 | }; 22 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 23 | const std::string title { 24 | "GOOGLE__GLOG" 25 | }; 26 | #else 27 | #error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined 28 | #endif 29 | 30 | 31 | #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 32 | const std::string g_path { 33 | "./" 34 | }; 35 | #else 36 | const std::string g_path { 37 | "/tmp/" 38 | }; 39 | #endif 40 | 41 | 42 | 43 | using namespace g3_test; 44 | 45 | 46 | // 47 | // OK: The code below isn't pretty but it works. Lots and lots of log entries 48 | // to keep track of! 49 | // 50 | int main(int argc, char** argv) 51 | { 52 | size_t number_of_threads {0}; 53 | if (argc == 2) 54 | { 55 | number_of_threads = atoi(argv[1]); 56 | } 57 | if (argc != 2 || number_of_threads == 0) 58 | { 59 | std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl; 60 | return 1; 61 | } 62 | std::ostringstream thread_count_oss; 63 | thread_count_oss << number_of_threads; 64 | 65 | const std::string g_prefix_log_name = title + "-performance-" + thread_count_oss.str() + "threads-WORST_LOG"; 66 | const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt"; 67 | const std::string g_measurement_bucket_dump = g_path + g_prefix_log_name + "_RESULT_buckets.txt"; 68 | const uint64_t us_to_ms { 69 | 1000 70 | }; 71 | const uint64_t us_to_s { 72 | 1000000 73 | }; 74 | 75 | 76 | std::ostringstream oss; 77 | oss << "\n\n" << title << " performance " << number_of_threads << " threads WORST (PEAK) times\n"; 78 | oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry 79 | const uint64_t xtra_margin { 80 | 2 81 | }; 82 | 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; 83 | writeTextToFile(g_measurement_dump, oss.str(), kAppend); 84 | oss.str(""); // clear the stream 85 | 86 | #if defined(G3LOG_PERFORMANCE) 87 | auto worker = g3::LogWorker::createLogWorker(); 88 | auto handle= worker->addDefaultLogger(g_prefix_log_name, g_path); 89 | g3::initializeLogging(worker.get()); 90 | 91 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 92 | google::InitGoogleLogging(argv[0]); 93 | #endif 94 | 95 | std::thread* threads = new std::thread[number_of_threads]; 96 | std::vector* threads_result = new std::vector[number_of_threads]; 97 | 98 | // kiss: just loop, create threads, store them then join 99 | // could probably do this more elegant with lambdas 100 | for (uint64_t idx = 0; idx < number_of_threads; ++idx) 101 | { 102 | threads_result[idx].reserve(g_iterations); 103 | } 104 | 105 | auto start_time = std::chrono::high_resolution_clock::now(); 106 | for (uint64_t idx = 0; idx < number_of_threads; ++idx) 107 | { 108 | std::ostringstream count; 109 | count << idx + 1; 110 | std::string thread_name = title + "_T" + count.str(); 111 | std::cout << "Creating thread: " << thread_name << std::endl; 112 | threads[idx] = std::thread(measurePeakDuringLogWrites, thread_name, std::ref(threads_result[idx])); 113 | } 114 | // wait for thread finishing 115 | for (uint64_t idx = 0; idx < number_of_threads; ++idx) 116 | { 117 | threads[idx].join(); 118 | } 119 | auto application_end_time = std::chrono::high_resolution_clock::now(); 120 | delete [] threads; 121 | 122 | 123 | #if defined(G3LOG_PERFORMANCE) 124 | worker.reset(); // will flush anything in the queue to file 125 | #elif defined(GOOGLE_GLOG_PERFORMANCE) 126 | google::ShutdownGoogleLogging(); 127 | #endif 128 | 129 | auto worker_end_time = std::chrono::high_resolution_clock::now(); 130 | uint64_t application_time_us = std::chrono::duration_cast(application_end_time - start_time).count(); 131 | uint64_t total_time_us = std::chrono::duration_cast(worker_end_time - start_time).count(); 132 | 133 | oss << "\n" << number_of_threads << "*" << g_iterations << " log entries took: [" << total_time_us / us_to_s << " s] to write to disk" << std::endl; 134 | oss << "[Application(" << number_of_threads << "_threads+overhead time for measurement):\t" << application_time_us / us_to_ms << " ms]" << std::endl; 135 | oss << "[Background thread to finish:\t\t\t\t" << total_time_us / us_to_ms << " ms]" << std::endl; 136 | oss << "\nAverage time per log entry:" << std::endl; 137 | oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << std::endl; 138 | 139 | for (uint64_t idx = 0; idx < number_of_threads; ++idx) 140 | { 141 | std::vector &t_result = threads_result[idx]; 142 | uint64_t worstUs = (*std::max_element(t_result.begin(), t_result.end())); 143 | oss << "[Application t" << idx + 1 << " worst took: " << worstUs / uint64_t(1000) << " ms (" << worstUs << " us)] " << std::endl; 144 | } 145 | writeTextToFile(g_measurement_dump, oss.str(), kAppend); 146 | std::cout << "Result can be found at:" << g_measurement_dump << std::endl; 147 | 148 | // now split the result in buckets of 10ms each so that it's obvious how the peaks go 149 | std::vector all_measurements; 150 | all_measurements.reserve(g_iterations * number_of_threads); 151 | for (uint64_t idx = 0; idx < number_of_threads; ++idx) 152 | { 153 | std::vector &t_result = threads_result[idx]; 154 | all_measurements.insert(all_measurements.end(), t_result.begin(), t_result.end()); 155 | } 156 | delete [] threads_result; // finally get rid of them 157 | 158 | std::sort (all_measurements.begin(), all_measurements.end()); 159 | std::map value_amounts; 160 | std::map value_amounts_for_0ms_bucket; 161 | 162 | for (auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter) 163 | { 164 | uint64_t value = (*iter) / us_to_ms; // convert to ms 165 | ++value_amounts[value]; // asuming uint64_t is default 0 when initialized 166 | 167 | if (0 == value) { 168 | ++value_amounts_for_0ms_bucket[*iter]; 169 | } 170 | } 171 | 172 | oss.str(""); 173 | oss << "Number of values rounded to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl; 174 | if (1 == value_amounts.size()) { 175 | oss << "Format: bucket of us inside bucket0 for ms\nFormat:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl; 176 | oss << "\n"; 177 | } 178 | else { 179 | oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl; 180 | } 181 | std::cout << oss.str() << std::endl; 182 | 183 | // 184 | // If all values are for the 0ms bucket then instead show us buckets 185 | // 186 | if (1 == value_amounts.size()) { 187 | oss << "\n\n***** Microsecond bucket measurement for all measurements that went inside the '0 millisecond bucket' ****\n"; 188 | for (auto us_bucket : value_amounts_for_0ms_bucket) { 189 | oss << us_bucket.first << "\t" << us_bucket.second << std::endl; 190 | } 191 | oss << "\n\n***** Millisecond bucket measurement ****\n"; 192 | } 193 | 194 | for (auto ms_bucket : value_amounts) 195 | { 196 | oss << ms_bucket.first << "\t, " << ms_bucket.second << std::endl; 197 | } 198 | writeTextToFile(g_measurement_bucket_dump, oss.str(), kAppend, false); 199 | 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /test_unit/test_concept_sink.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * 2013 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes 3 | * with no warranties. This code is yours to share, use and modify with no 4 | * strings attached and no restrictions or obligations. 5 | * 6 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 7 | * ============================================================================*/ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "testing_helpers.h" 19 | #include "g3log/sink.hpp" 20 | #include "g3log/sinkwrapper.hpp" 21 | #include "g3log/sinkhandle.hpp" 22 | #include "g3log/logmessage.hpp" 23 | #include "g3log/generated_definitions.hpp" 24 | 25 | using namespace std; 26 | using namespace testing_helpers; 27 | 28 | class CoutSink { 29 | stringstream buffer; 30 | unique_ptr scope_ptr; 31 | 32 | CoutSink() : scope_ptr(std::make_unique(std::cout, &buffer)) {} 33 | public: 34 | void clear() { buffer.str(""); } 35 | std::string string() { return buffer.str(); } 36 | void save(g3::LogMessageMover msg) { std::cout << msg.get().message(); } 37 | virtual ~CoutSink() final {} 38 | static std::unique_ptr createSink() { return std::unique_ptr(new CoutSink);} 39 | }; 40 | 41 | struct StringSink { 42 | std::string raw; 43 | void append(g3::LogMessageMover entry) { raw.append(entry.get().message());} 44 | std::string string() { 45 | return raw; 46 | } 47 | }; 48 | 49 | 50 | namespace { 51 | typedef std::shared_ptr SinkWrapperPtr; 52 | } 53 | 54 | namespace g3 { 55 | 56 | class Worker { 57 | std::vector _container; // should be hidden in a pimple with a bg active object 58 | std::unique_ptr _bg; 59 | 60 | void bgSave(std::string msg) { 61 | for (auto& sink : _container) { 62 | g3::LogMessage message("test", 0, "test", DEBUG); 63 | message.write().append(msg); 64 | sink->send(LogMessageMover(std::move(message))); 65 | } 66 | } 67 | 68 | public: 69 | 70 | Worker() : _bg { 71 | kjellkod::Active::createActive() 72 | } { 73 | } 74 | 75 | ~Worker() { 76 | _bg->send([this] { 77 | _container.clear(); 78 | }); 79 | } 80 | 81 | void save(std::string msg) { 82 | _bg->send([this, msg] { bgSave(msg); }); 83 | } 84 | 85 | 86 | template 87 | std::unique_ptr< SinkHandle > addSink(std::unique_ptr unique, DefaultLogCall call) { 88 | auto sink = std::make_shared < internal::Sink > (std::move(unique), call); 89 | auto add_sink_call = [this, sink] { _container.push_back(sink); }; 90 | auto wait_result = g3::spawn_task(add_sink_call, _bg.get()); 91 | wait_result.wait(); 92 | 93 | auto handle = std::make_unique< SinkHandle >(sink); 94 | return handle; 95 | } 96 | }; 97 | 98 | } // g3 99 | 100 | 101 | 102 | 103 | using namespace g3; 104 | using namespace g3::internal; 105 | 106 | 107 | TEST(ConceptSink, CreateHandle) { 108 | Worker worker; 109 | auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save); 110 | ASSERT_NE(nullptr, handle.get()); 111 | } 112 | 113 | 114 | TEST(ConceptSink, OneSink__VerifyMsgIn) { 115 | Worker worker; 116 | auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save); 117 | worker.save("Hello World!"); 118 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 119 | auto output = handle->call(&CoutSink::string); 120 | auto content = output.get(); 121 | auto pos = content.find("Hello World!"); 122 | ASSERT_NE(pos, std::string::npos); 123 | } 124 | 125 | 126 | TEST(ConceptSink, DualSink__VerifyMsgIn) { 127 | Worker worker; 128 | auto h1 = worker.addSink(CoutSink::createSink(), &CoutSink::save); 129 | auto h2 = worker.addSink(std::make_unique(), &StringSink::append); 130 | worker.save("Hello World!"); 131 | 132 | 133 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 134 | auto first = h1->call(&CoutSink::string); 135 | auto second = h2->call(&StringSink::string); 136 | 137 | 138 | ASSERT_EQ("Hello World!", first.get()); 139 | ASSERT_EQ("Hello World!", second.get()); 140 | } 141 | 142 | TEST(ConceptSink, DeletedSink__Exptect_badweak_ptr___exception) { 143 | auto worker = std::make_unique(); 144 | auto h1 = worker->addSink(CoutSink::createSink(), &CoutSink::save); 145 | worker->save("Hello World!"); 146 | worker.reset(); 147 | 148 | auto first = h1->call(&CoutSink::string); 149 | EXPECT_THROW(first.get(), std::bad_weak_ptr); 150 | } 151 | 152 | namespace { 153 | using AtomicBooleanPtr = std::shared_ptr>; 154 | using AtomicIntegerPtr = std::shared_ptr> ; 155 | using BoolList = std::vector ; 156 | using IntVector = std::vector; 157 | } 158 | 159 | TEST(ConceptSink, OneHundredSinks_part1) { 160 | BoolList flags; 161 | IntVector counts; 162 | 163 | size_t NumberOfItems = 100; 164 | for (size_t index = 0; index < NumberOfItems; ++index) { 165 | flags.push_back(make_shared < atomic> (false)); 166 | counts.push_back(make_shared < atomic> (0)); 167 | } 168 | 169 | { 170 | auto worker = std::unique_ptr(new Worker); 171 | size_t index = 0; 172 | for (auto& flag : flags) { 173 | auto& count = counts[index++]; 174 | // ignore the handle 175 | worker->addSink(std::make_unique(flag, count), &ScopedSetTrue::ReceiveMsg); 176 | } 177 | worker->save("Hello to 100 receivers :)"); 178 | worker->save("Hello to 100 receivers :)"); 179 | } 180 | // at the curly brace above the ScopedLogger will go out of scope and all the 181 | // 100 logging receivers will get their message to exit after all messages are 182 | // are processed 183 | size_t index = 0; 184 | for (auto& flag : flags) { 185 | auto& count = counts[index++]; 186 | ASSERT_TRUE(flag->load()) << ", count : " << (index - 1); 187 | ASSERT_TRUE(2 == count->load()) << ", count : " << (index - 1); 188 | } 189 | 190 | cout << "test one hundred sinks is finished\n"; 191 | } 192 | 193 | 194 | TEST(ConceptSink, OneHundredSinks_part2) { 195 | using BoolPtrVector = std::vector ; 196 | using IntPtrVector = vector ; 197 | BoolPtrVector flags; 198 | IntPtrVector counts; 199 | 200 | int NumberOfItems = 100; 201 | for (int index = 0; index < NumberOfItems; ++index) { 202 | flags.push_back(make_shared>(false)); 203 | counts.push_back(make_shared>(0)); 204 | } 205 | 206 | { 207 | auto worker = g3::LogWorker::createLogWorker(); 208 | size_t index = 0; 209 | for (auto& flag : flags) { 210 | auto& count = counts[index++]; 211 | // ignore the handle 212 | worker->addSink(std::make_unique(flag, count), &ScopedSetTrue::ReceiveMsg); 213 | } 214 | 215 | // 100 logs 216 | for (int index = 0; index < NumberOfItems; ++index) { 217 | LogMessagePtr message{std::make_unique("test", 0, "test", DEBUG)}; 218 | message.get()->write().append("Hello to 100 receivers :)"); 219 | worker->save(message); 220 | } 221 | } // RAII exit 222 | 223 | // at the curly brace above the ScopedLogger will go out of scope and all the 224 | // 100 logging receivers will get their message to exit after all messages are 225 | // are processed at the curly brace above the ScopedLogger will go out of scope and all the 226 | // 100 logging receivers will get their message to exit after all messages are 227 | // are processed 228 | size_t index = 0; 229 | for (auto& flag : flags) { 230 | auto& count = counts[index++]; 231 | EXPECT_TRUE(flag->load()); 232 | EXPECT_EQ(NumberOfItems, count->load()); 233 | } 234 | } 235 | 236 | 237 | TEST(ConceptSink, OneSinkWithHandleOutOfScope) { 238 | AtomicBooleanPtr flag = make_shared>(false); 239 | AtomicIntegerPtr count = make_shared>(0); 240 | { 241 | auto worker = g3::LogWorker::createLogWorker(); 242 | { 243 | auto handle = worker->addSink(std::make_unique(flag, count), &ScopedSetTrue::ReceiveMsg); 244 | } 245 | EXPECT_FALSE(flag->load()); 246 | EXPECT_TRUE(0 == count->load()); 247 | 248 | LogMessagePtr message{std::make_unique("test", 0, "test", DEBUG)}; 249 | message.get()->write().append("this message should trigger an atomic increment at the sink"); 250 | worker->save(message); 251 | } 252 | EXPECT_TRUE(flag->load()); 253 | EXPECT_TRUE(1 == count->load()); 254 | } 255 | -------------------------------------------------------------------------------- /src/stacktrace_windows.cpp: -------------------------------------------------------------------------------- 1 | /** ========================================================================== 2 | * Original code made by Robert Engeln. Given as a PUBLIC DOMAIN dedication for 3 | * the benefit of g3log. It was originally published at: 4 | * http://code-freeze.blogspot.com/2012/01/generating-stack-traces-from-c.html 5 | 6 | * 2014-2015: adapted for g3log by Kjell Hedstrom (KjellKod). 7 | * 8 | * This is PUBLIC DOMAIN to use at your own risk and comes 9 | * with no warranties. This code is yours to share, use and modify with no 10 | * strings attached and no restrictions or obligations. 11 | * 12 | * For more information see g3log/LICENSE or refer refer to http://unlicense.org 13 | * ============================================================================*/ 14 | 15 | #include "g3log/stacktrace_windows.hpp" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #pragma comment(lib, "dbghelp.lib") 27 | 28 | 29 | #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) 30 | #error "stacktrace_win.cpp used but not on a windows system" 31 | #endif 32 | 33 | 34 | 35 | #define g3_MAP_PAIR_STRINGIFY(x) {x, #x} 36 | 37 | namespace { 38 | thread_local size_t g_thread_local_recursive_crash_check = 0; 39 | 40 | const std::map kExceptionsAsText = { 41 | g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION) 42 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED) 43 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_DATATYPE_MISALIGNMENT) 44 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DENORMAL_OPERAND) 45 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_DIVIDE_BY_ZERO) 46 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT) 47 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INEXACT_RESULT) 48 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_INVALID_OPERATION) 49 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_OVERFLOW) 50 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_STACK_CHECK) 51 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_FLT_UNDERFLOW) 52 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_ILLEGAL_INSTRUCTION) 53 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_IN_PAGE_ERROR) 54 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_DIVIDE_BY_ZERO) 55 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_INT_OVERFLOW) 56 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_INVALID_DISPOSITION) 57 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_NONCONTINUABLE_EXCEPTION) 58 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_PRIV_INSTRUCTION) 59 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_STACK_OVERFLOW) 60 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_BREAKPOINT) 61 | , g3_MAP_PAIR_STRINGIFY(EXCEPTION_SINGLE_STEP) 62 | 63 | }; 64 | 65 | 66 | // Using the given context, fill in all the stack frames. 67 | // Which then later can be interpreted to human readable text 68 | void captureStackTrace(CONTEXT *context, std::vector &frame_pointers) { 69 | DWORD machine_type = 0; 70 | STACKFRAME64 frame = {}; // force zeroing 71 | frame.AddrPC.Mode = AddrModeFlat; 72 | frame.AddrFrame.Mode = AddrModeFlat; 73 | frame.AddrStack.Mode = AddrModeFlat; 74 | #ifdef _M_X64 75 | frame.AddrPC.Offset = context->Rip; 76 | frame.AddrFrame.Offset = context->Rbp; 77 | frame.AddrStack.Offset = context->Rsp; 78 | machine_type = IMAGE_FILE_MACHINE_AMD64; 79 | #else 80 | frame.AddrPC.Offset = context->Eip; 81 | frame.AddrPC.Offset = context->Ebp; 82 | frame.AddrPC.Offset = context->Esp; 83 | machine_type = IMAGE_FILE_MACHINE_I386; 84 | #endif 85 | for (size_t index = 0; index < frame_pointers.size(); ++index) 86 | { 87 | if (StackWalk64(machine_type, 88 | GetCurrentProcess(), 89 | GetCurrentThread(), 90 | &frame, 91 | context, 92 | NULL, 93 | SymFunctionTableAccess64, 94 | SymGetModuleBase64, 95 | NULL)) { 96 | frame_pointers[index] = frame.AddrPC.Offset; 97 | } else { 98 | break; 99 | } 100 | } 101 | } 102 | 103 | 104 | 105 | // extract readable text from a given stack frame. All thanks to 106 | // using SymFromAddr and SymGetLineFromAddr64 with the stack pointer 107 | std::string getSymbolInformation(const size_t index, const std::vector &frame_pointers) { 108 | auto addr = frame_pointers[index]; 109 | std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t"; 110 | 111 | DWORD64 displacement64; 112 | DWORD displacement; 113 | char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; 114 | SYMBOL_INFO *symbol = reinterpret_cast(symbol_buffer); 115 | symbol->SizeOfStruct = sizeof(SYMBOL_INFO); 116 | symbol->MaxNameLen = MAX_SYM_NAME; 117 | 118 | IMAGEHLP_LINE64 line; 119 | line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); 120 | std::string lineInformation; 121 | std::string callInformation; 122 | if (SymFromAddr(GetCurrentProcess(), addr, &displacement64, symbol)) { 123 | callInformation.append(" ").append(std::string(symbol->Name, symbol->NameLen)); 124 | if (SymGetLineFromAddr64(GetCurrentProcess(), addr, &displacement, &line)) { 125 | lineInformation.append("\t").append(line.FileName).append(" L: "); 126 | lineInformation.append(std::to_string(line.LineNumber)); 127 | } 128 | } 129 | frame_dump.append(lineInformation).append(callInformation); 130 | return frame_dump; 131 | } 132 | 133 | 134 | // Retrieves all the symbols for the stack frames, fills them within a text representation and returns it 135 | std::string convertFramesToText(std::vector &frame_pointers) { 136 | std::string dump; // slightly more efficient than ostringstream 137 | const size_t kSize = frame_pointers.size(); 138 | for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) { 139 | dump += getSymbolInformation(index, frame_pointers); 140 | dump += "\n"; 141 | } 142 | return dump; 143 | } 144 | } // anonymous 145 | 146 | 147 | 148 | 149 | namespace stacktrace { 150 | const std::string kUnknown = {"UNKNOWN EXCEPTION"}; 151 | /// return the text description of a Windows exception code 152 | /// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx 153 | std::string exceptionIdToText(g3::SignalType id) { 154 | const auto iter = kExceptionsAsText.find(id); 155 | if ( iter == kExceptionsAsText.end()) { 156 | std::string unknown = {kUnknown + ":" + std::to_string(id)}; 157 | return unknown; 158 | } 159 | return iter->second; 160 | } 161 | 162 | /// Yes a double lookup: first for isKnownException and then exceptionIdToText 163 | /// for vectored exceptions we only deal with known exceptions so this tiny 164 | /// overhead we can live with 165 | bool isKnownException(g3::SignalType id) { 166 | return (kExceptionsAsText.end() != kExceptionsAsText.find(id)); 167 | } 168 | 169 | /// helper function: retrieve stackdump from no excisting exception pointer 170 | std::string stackdump() { 171 | CONTEXT current_context; 172 | memset(¤t_context, 0, sizeof(CONTEXT)); 173 | RtlCaptureContext(¤t_context); 174 | return stackdump(¤t_context); 175 | } 176 | 177 | /// helper function: retrieve stackdump, starting from an exception pointer 178 | std::string stackdump(EXCEPTION_POINTERS *info) { 179 | auto context = info->ContextRecord; 180 | return stackdump(context); 181 | 182 | } 183 | 184 | 185 | /// main stackdump function. retrieve stackdump, from the given context 186 | std::string stackdump(CONTEXT *context) { 187 | 188 | if (g_thread_local_recursive_crash_check >= 2) { // In Debug scenarios we allow one extra pass 189 | std::string recursive_crash = {"\n\n\n***** Recursive crash detected"}; 190 | recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n"); 191 | return recursive_crash; 192 | } 193 | ++g_thread_local_recursive_crash_check; 194 | 195 | static std::mutex m; 196 | std::lock_guard lock(m); 197 | { 198 | const BOOL kLoadSymModules = TRUE; 199 | const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules); 200 | if (TRUE != initialized) { 201 | return { "Error: Cannot call SymInitialize(...) for retrieving symbols in stack" }; 202 | } 203 | 204 | std::shared_ptr RaiiSymCleaner(nullptr, [&](void *) { 205 | SymCleanup(GetCurrentProcess()); 206 | }); // Raii sym cleanup 207 | 208 | 209 | const size_t kmax_frame_dump_size = 64; 210 | std::vector frame_pointers(kmax_frame_dump_size); 211 | // C++11: size set and values are zeroed 212 | 213 | assert(frame_pointers.size() == kmax_frame_dump_size); 214 | captureStackTrace(context, frame_pointers); 215 | return convertFramesToText(frame_pointers); 216 | } 217 | } 218 | 219 | } // stacktrace 220 | --------------------------------------------------------------------------------