Compare commits

...

408 Commits
v1.1 ... master

Author SHA1 Message Date
Kjell Hedström
38dbddc707
Botantony/cmake fix (#548)
* Fix build for CMake >= 4.0.0

Signed-off-by: botantony <antonsm21@gmail.com>

* updating minor version for hotfix
* removed accidental inclusion from way back

---------

Signed-off-by: botantony <antonsm21@gmail.com>
Co-authored-by: botantony <antonsm21@gmail.com>
2025-06-10 08:30:15 -06:00
ellipsis-dev[bot]
14db492d9d
[Ellipsis] chore: update CMake minimum version to 3.5 (#546)
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-06-09 09:43:07 -06:00
Kjell Hedström
cec84ba592
Update README.md (#543) with code scanning insights. 2025-03-03 17:10:46 -07:00
ablangy
96023dbffd
Issue #541 Linux: access to siginfo_t (#542)
By using some informations located in siginfo_t for tracing,
it remain accessible to be inspected by debugger even if the
code compiled with optimisation flags (typically -O2).
2025-01-17 14:43:47 -07:00
Kjell Hedström
1dc74351ff
dummy test to validate updated ci (#539)
disable ci/action ubuntu as it's redundant with the matrix runs
2024-08-27 22:42:30 -06:00
Kjell Hedström
5a4a1094d8
Remove redundant CI tests (appveyor, circleci) and rely only on github actions matrix windows/macos/ubuntu (#538)
* Update README.md
* Delete appveyor.yml
* Delete .circleci/config.yml
2024-08-27 21:52:55 -06:00
Kjell Hedström
37cfffce91
Update Build.cmake (#536) 2024-08-27 21:42:27 -06:00
Kjell Hedström
1a1d2a12ea
Update ctest.yml 2024-08-27 21:23:51 -06:00
Kjell Hedström
943487f29a
Update ctest.yml (#537) 2024-08-27 21:15:19 -06:00
Kjell Hedström
c1dd801e42
Update mkdocs.yml - removed test trigger (#530) 2024-05-23 19:05:35 -06:00
Kjell
e3d85a58c2 squashed many small commit messages for documentation and matrix/ci improvements 2024-05-23 13:18:22 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
095ed1cdf6
Update README.md 2024-03-22 06:18:55 -06:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
850eb0dd07
Update README.md 2024-03-22 06:18:34 -06:00
Kjell Hedstrom
ff8e284326 fixed visual studio debug 2024-02-19 11:49:34 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
326fef95ac
dummy pr to check CI (#517)
Updated checkout version to v3 to move to node20 (node16 end of life)
* updated to v3
2024-02-05 10:32:06 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
45ca1f6b6e
Update API.md 2024-02-03 15:39:46 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
9a8c0fc854
Windows path for gitactions? (#516)
* Update ctest.yml and add Windows to CI pipeline gitactions.
2024-02-03 15:37:06 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
f2b9f9b62c
Update README.md 2024-02-03 15:29:23 -07:00
Kjell Hedstrom
540f2711af .DS store gone 2023-12-07 11:54:20 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
4c1194720a
adding default container extensions (#514) 2023-12-06 20:23:58 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
5980182db0
Versioning (#515)
* making sure that git checkout of tags, doesn't get incrementing numbers when master gets more commits
2023-12-06 20:14:22 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
626191a62d
Mostly a refactor of tests (#513) 2023-12-05 23:33:03 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
055f5e4d21
updated docs (#512)
* updated docs for API and added sequence diagrams.
2023-12-05 21:57:13 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
bba825815a
refactor fatal call (#511)
* refactor fatal handling
2023-12-05 21:04:01 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
d70ae50ee3
gitignore (#510)
* gitingore
2023-12-05 20:02:08 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
f26b058d16
Update PULL_REQUEST_TEMPLATE.md 2023-11-30 16:22:17 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
cf91227966
adding consistent and easy formatting (#508)
*  clang format configuration file replaces sublime Astyleformatter
* instructions added to pull request template
* code base re-formatted to be consistent throughout.
2023-11-30 16:17:45 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
0708f5aeb4
Update README.md 2023-11-28 08:37:15 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
59fdb4e2c8
Update README.md 2023-11-27 21:08:21 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
c2661853d0
Codespaces tryout (#507)
* adding example vs code launch file
2023-11-27 21:07:23 -07:00
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities
c3a46e6043
trying out codespaces (#506)
* trying out codespaces
2023-11-27 19:13:07 -07:00
Kjell Hedström - looking for a challenge contract or full-time
f1036e62ac
Update README.md (#505) 2023-11-25 14:48:09 -07:00
Kjell Hedström - looking for a challenge contract or full-time
c41c82e032
Update README.md (#504) 2023-11-25 11:12:58 -07:00
Sean -Kjell- Hedström
63f3272703
clarify c++ version support (#502) 2023-11-12 14:42:52 -07:00
Kjell Hedström
b24d4a4523
update doc for default flush (#499) 2023-08-17 16:55:24 -06:00
CryptoManiac
7817fd3a94
Update loglevels.hpp (#496) 2023-05-20 07:16:32 -06:00
Dmitry Tsarevich
bad9c58e60
Fix found by PVS-Studio issues (#490)
Small changes, essentially cleanup, with no actual code logic change. 
* Ensure symbol_buffer aligned same as SYMBOL_INFO
* Use constexpr for compile-time constant
* Use = default for ctor bodies to allow compiler optimize them
* Pass string by reference to prevent copying, other smaller types we can  avoid passing by reference as the type is cheaper to copy.
2023-05-15 07:59:44 -06:00
SzGaa
cc0fb7c1ea
constexpr LEVELS for easier custom level usage (#483)
Usage of C++20's new constexpr functionalities for easier usage of custom LEVELS!

Modified:
* With C++20 LEVELS's constructor can have a constexpr specifier
* const LEVELS can be replaced to be constexpr's instead
2023-05-03 21:50:48 -06:00
Kjell Hedström
b249fb6c6c
Update appveyor.yml to use Visual Studio 2022 (17) (#487) 2023-04-19 22:39:32 -06:00
GergoTot
5323480780
Avoid pending of containerized applications in case of aborting (#481)
Improvement for Docker run C++ applications with g3log 

* This addresses the case when a PID1 process crashes and the signal handling, goes into multiple or even infinite loops due to subsequent crashes. The PR makes sure to restore all signal handlers to the original signal handling after the first crash is detected. For
2023-03-07 17:30:58 -07:00
Kjell Hedström
4f1224b9d5
Fix filesink drop messages improved (#479)
* Fix FileSink dropping messages introduced by 'optionable buffer to x messages' feature
Commit 6c6122fafc79e92fc94a851b3ff83f87e8b80398 introduced a bug where 99 out of 100 logs were actually dumped

* Updated unit tests to trigger sink flush bug
---------

Co-authored-by: Ryan Ammoury <ryan.ammoury@squadrone-system.com>
2023-02-20 06:16:34 -07:00
Kjell Hedström
43f5eddcdd
updated version to 2.2 (#476) 2023-02-04 04:45:29 -07:00
Grzegorz Głowacki
09317e3573
Parse OSX stack dump format in order to demangle it correctly (#473)
* Parse OSX format  mangled stack trace correctly
Co-authored-by: Grzegorz Glowacki <grzegorz.glowacki@avid.com>
2023-02-04 04:40:56 -07:00
Kjell Hedström
16bb6f7e04
force check of stackdump (#475) 2023-02-04 04:37:13 -07:00
Kjell Hedström
6c6122fafc
optionable to buffer x messages before writing to file, default to 100 which will really boost performance (#471) 2022-12-08 22:08:28 -07:00
Kjell Hedström
dbd3d74a39
__SIGFUNC__ no longer default for Windows. __PRETTY__FUNCTION no longer default for gcc/clang (#470)
* __SIGFUNC__ no longer default for Windows, It has to be explicitly picked through CMAKE option
* __PRETTY_FUNCTION__ no longer default for gcc/clang, It has to be explicitly picked through CMAKE option
2022-11-29 22:16:35 -07:00
Kjell Hedström
881e6da439
__func__ doesn't make sense (#469)
__func__ doesn't make sense since we have left c++11 in the dust
2022-11-29 09:00:46 -07:00
Kjell Hedström
6f6da0ed2a
Update CMakeLists.txt 2022-11-10 09:34:52 -07:00
ablangy
0c09462e4d
exitWithDefaultSignalHandler() should block until signal handler returns (#464)
raise() system call does the same as kill() system call in a
single-threaded program.
In a multithreaded program, it does the same as pthread_kill() which
ensures that if the signal causes a handler to be called, raise() will
return only after the signal handler has returned.
2022-11-10 09:34:07 -07:00
Kjell Hedström
21195751be
corrected version 2022-11-08 09:31:37 -07:00
bmagistro
ed91b899f0
Resolve noexcept warnings on lambdas (#463)
When running with additional warnings enabled, a couple lambdas produce
a warning that they should be declared with noexcept. This addresses the
lambdas that had been identified while building.

Signed-off-by: Ben Magistro <koncept1@gmail.com>
2022-09-14 14:04:34 -06:00
神楽坂帕琪
5adecb5ad9
Fix typo in README (#460) 2022-08-17 01:04:44 -06:00
Reece Watson
a0c7e5e0e1
Fix typo in README.md (#458) 2022-07-29 06:38:21 -06:00
zjeffer
1ee2cd0de6
fix(cmake): fixes #413 (#457) 2022-06-11 06:17:32 -06:00
Kjell Hedström
7758aa7913
Update README.md (#456)
* Update README.md and PULL_REQUEST_TEMPLATE.md
2022-05-29 04:24:55 -06:00
Kjell Hedström. We are hiring @ Ganaz
ccf6b97054
Mkdocs (#455)
* adding header/footer navigation links
2022-05-28 23:37:48 -06:00
Kjell Hedström. We are hiring @ Ganaz
429047e815
Mkdocs (#453)
* adding back README
2022-05-28 23:29:03 -06:00
Kjell Hedström. We are hiring @ Ganaz
eaf3f7b312
Create .ciignore (#454) 2022-05-28 23:24:45 -06:00
Kjell Hedström. We are hiring @ Ganaz
359ca69774
Set theme jekyll-theme-cayman 2022-05-28 22:27:35 -06:00
Kjell Hedström. We are hiring @ Ganaz
f370e32346
Update mkdocs.yml 2022-05-28 22:11:11 -06:00
Kjell Hedström. We are hiring @ Ganaz
4ade209945
Update mkdocs.yml 2022-05-28 22:02:25 -06:00
Kjell Hedström. We are hiring @ Ganaz
eb82e25958
Rename publish_docs.yml to docs.yml 2022-05-28 22:00:40 -06:00
Kjell Hedström. We are hiring @ Ganaz
f4fa1cad94
Update index.md 2022-05-28 21:50:00 -06:00
Kjell Hedström. We are hiring @ Ganaz
808b787b60
Mkdocs (#452)
* removed obseolted README.mkd
2022-05-28 21:45:59 -06:00
Kjell Hedström. We are hiring @ Ganaz
fec09b2bba
Mkdocs (#451)
* updating for mkdocs generation
* updated look
2022-05-28 21:38:45 -06:00
Kjell Hedström. We are hiring @ Ganaz
0e3fef50bb
Update publish_docs.yml 2022-05-28 21:36:58 -06:00
Kjell Hedström. We are hiring @ Ganaz
f72f47e533
Update publish_docs.yml 2022-05-28 21:23:51 -06:00
Kjell Hedstrom
fc2af1ddc2 updating for mkdocs generation 2022-05-28 21:13:54 -06:00
Kjell Hedstrom
c52de9e1cf updating for mkdocs generation 2022-05-28 21:13:54 -06:00
Kjell Hedström. We are hiring @ Ganaz
83a1ce24d9 Update index.md 2022-05-24 17:52:03 -06:00
Kjell Hedström. We are hiring @ Ganaz
6c956e5fc8
Update publish_docs.yml 2022-05-24 17:51:24 -06:00
Kjell Hedström. We are hiring @ Ganaz
d057e0a9d5 Update mkdocs.yml 2022-05-24 17:43:32 -06:00
Kjell Hedström. We are hiring @ Ganaz
a1efe3b4a4 Update index.md 2022-05-24 17:39:35 -06:00
Kjell Hedström. We are hiring @ Ganaz
9190b37706 Update API.md 2022-05-24 17:34:39 -06:00
Kjell Hedström. We are hiring @ Ganaz
f9a19c5fe7
Update buildAndRunTests.yml 2022-05-24 17:29:47 -06:00
Kjell Hedström. We are hiring @ Ganaz
d51fe70f96
Update codeql-analysis.yml 2022-05-24 17:29:28 -06:00
Kjell Hedström. We are hiring @ Ganaz
20100be78e
Update ctest.yml 2022-05-24 17:29:00 -06:00
Kjell Hedström. We are hiring @ Ganaz
4584d1ed10
Create .ciignore 2022-05-24 17:15:18 -06:00
Kjell Hedström. We are hiring @ Ganaz
4de1e9f5cf
Create API.md 2022-05-24 17:12:08 -06:00
Kjell Hedström. We are hiring @ Ganaz
9bdd710907
Update mkdocs.yml 2022-05-24 17:11:20 -06:00
Kjell Hedström. We are hiring @ Ganaz
cf8ef87c95
Update publish_docs.yml 2022-05-24 17:03:53 -06:00
Kjell Hedström. We are hiring @ Ganaz
9119a63062
test file to be removed 2022-05-24 14:22:54 -06:00
Kjell Hedström. We are hiring @ Ganaz
e48d46332d
Update publish_docs.yml 2022-05-24 14:21:05 -06:00
Kjell Hedström. We are hiring @ Ganaz
5b114c4f39
Update ctest.yml 2022-05-24 14:19:27 -06:00
Kjell Hedström. We are hiring @ Ganaz
8afd63dd86
Update buildAndRunTests.yml 2022-05-24 14:18:11 -06:00
Kjell Hedström. We are hiring @ Ganaz
9c71dfd783
Update ctest.yml 2022-05-24 14:16:31 -06:00
Kjell Hedström. We are hiring @ Ganaz
0bfff72ea2
Update ctest.yml 2022-05-23 17:44:56 -06:00
Kjell Hedström. We are hiring @ Ganaz
b3b75bfc43
Update buildAndRunTests.yml 2022-05-23 17:44:39 -06:00
Kjell Hedström. We are hiring @ Ganaz
4c3b65e631
Update ctest.yml 2022-05-23 17:43:29 -06:00
Kjell Hedström. We are hiring @ Ganaz
4175efea31
Update buildAndRunTests.yml 2022-05-23 17:42:39 -06:00
Kjell Hedström. We are hiring @ Ganaz
5cde8ed2ce
Update buildAndRunTests.yml 2022-05-23 17:41:51 -06:00
Kjell Hedström. We are hiring @ Ganaz
512be84bcb
Update publish_docs.yml 2022-05-23 17:30:25 -06:00
Kjell Hedström. We are hiring @ Ganaz
e79979767d
Rename docs/mkdocs.yml to mkdocs.yml 2022-05-23 17:29:41 -06:00
Kjell Hedström. We are hiring @ Ganaz
c5052998e3 Create index.md 2022-05-23 17:23:42 -06:00
Kjell Hedström. We are hiring @ Ganaz
fd47f6d731 Update mkdocs.yml 2022-05-23 17:23:42 -06:00
Kjell Hedström. We are hiring @ Ganaz
79ac87a7ec Create mkdocs.yml 2022-05-23 17:23:42 -06:00
Kjell Hedström. We are hiring @ Ganaz
f6394711e4 Update publish_docs.yml 2022-05-23 17:23:42 -06:00
Kjell Hedström. We are hiring @ Ganaz
1e82c5c957 Create publish_docs.yml
Example from: https://github.com/mhausenblas/mkdocs-template and https://github.com/marketplace/actions/deploy-mkdocs
2022-05-23 17:23:42 -06:00
Kjell Hedström. We are hiring @ Ganaz
485bf57fa8 Readme improvement 2022-04-28 23:14:19 -06:00
Kjell Hedström. We are hiring @ Ganaz
52af38396f
Update PULL_REQUEST_TEMPLATE.md (#440)
* Update PULL_REQUEST_TEMPLATE.md so it's easier to understand what a decent PR should look like
2022-03-19 00:43:35 -06:00
Kjell Hedström. We are hiring @ Ganaz
234b4ed70f
Update cmake.yml (#437)
* Renamed ci action files 
* added matrix setup for runs with both macos-latest and ubuntu-latest.
2022-03-08 20:42:02 -07:00
Kjell Hedström. We are hiring @ Ganaz
4fa7a72953
Updated CI configurations and README that explained them (#436)
* Update config.yml
* Update README.markdown
2022-03-07 21:44:22 -07:00
Kjell Hedström. We are hiring @ Ganaz
cebe73492f
Update cpp.yml 2022-03-07 20:47:15 -07:00
Kjell Hedström. We are hiring @ Ganaz
54c5c0110a
Update cpp.yml 2022-03-07 20:46:22 -07:00
Kjell Hedström. We are hiring @ Ganaz
de706a905b
Update cmake.yml 2022-03-07 20:44:29 -07:00
Kjell Hedström. We are hiring @ Ganaz
e29226bd58
Delete .travis.yml (#435) 2022-03-07 19:51:10 -07:00
Kjell Hedström. We are hiring @ Ganaz
a248f6a930
dummy circle-ci (#434)
* Create config.yml

* Updated config.yml
2022-03-07 19:20:19 -07:00
Kjell Hedström. We are hiring @ Ganaz
22f8d8cb99
Create cpp.yml 2022-03-07 18:07:42 -07:00
DlubalSmidJaroslav
a7192f2b1c
Fixes #428: memory leak in g3::internal::createLogFile (#430)
Co-authored-by: Jaroslav Smid <smidjar2.reg@email.cz>
2022-01-10 20:49:59 -07:00
landwehrj
9bb36eb7e5
Fix pretty function magic constant overwriting (#424)
* Fix bug with __PRETTY_FUNCTION__ being always overwritten.

* Fix unit test to reflect new pretty function.
2021-12-19 20:45:12 -07:00
Szekely Gyorgy
c51128f051
Unblock faulting thread signal handler after log flush is complete (#419) 2021-11-22 08:39:53 -07:00
Kjell Hedström : Engineering Leadership
9009e109e9
update gtest zip URL (#417) 2021-11-16 10:11:37 -07:00
Kjell Hedström : Engineering Leadership
df1fd3a5c2 Update issue templates 2021-07-07 16:11:36 -06:00
Kjell Hedström : Engineering Leadership
68aff4dabd Update issue templates 2021-07-07 16:10:21 -06:00
Kjell Hedström : Engineering Leadership
8dcda0ed69
Create CODE_OF_CONDUCT.md (#411) 2021-07-07 15:55:50 -06:00
Kjell Hedström : Engineering Leadership
53cb214a6d
Update README.markdown 2021-07-07 15:55:28 -06:00
Kjell Hedström : Engineering Leadership
d56589e649 Update issue templates 2021-07-07 15:34:59 -06:00
Kjell Hedström : Engineering Leadership
3d08371fc6
Create CONTRIBUTING.md 2021-07-07 15:30:43 -06:00
Kjell Hedström : Engineering Leadership
b5d37b2953
Update cmake.yml 2021-04-08 22:21:02 -06:00
Kjell Hedström : Engineering Leadership
cc6582ab99
Update cmake.yml 2021-04-08 22:17:37 -06:00
Kjell Hedström : Engineering Leadership
afaec74e4a
Update cmake.yml 2021-04-08 22:09:00 -06:00
Kjell Hedström : Engineering Leadership
f75c65af2b
Create cmake.yml 2021-04-08 21:53:46 -06:00
Kjell Hedström : Engineering Leadership
95291b5190
fix version (#404) 2021-04-08 21:44:33 -06:00
Shachar Shemesh
c769b76715
Make SONAME less volatile (#402) 2021-04-08 21:29:40 -06:00
Kjell Hedström : Engineering Leadership
0a5a962679
Create codeql-analysis.yml 2021-04-08 21:28:14 -06:00
Kjell Hedström : Engineering Leadership
663050dc52
release version and cmake coded version sync 2021-04-08 21:13:18 -06:00
Chad Engler
d3f2d11ae6
Fix ARM compilation for windows. (#401) 2021-04-07 21:33:40 -06:00
lukeocamden
61f3f6e3db
Avoid copying LOGLEVELS for g3::logLevel call (#399)
* Avoid copy-constructing LEVELS for logLevel call
2021-01-26 12:49:10 -07:00
FriendlyFire
e28f559d32
C++20 compatability -- std::result_of with std::invoke_result (#392)
* Replaced result_of with invoke_result_t.
* Fixed a few compile errors and updated CMakeLists.
* Updated Cloud CI
2020-12-16 06:51:26 -07:00
Alvin
2fca06ff6d
Fix compiler warnings and condition check improvements (#390)
* fix compiler warnings

- loglevels.cpp - warns about double return if dynamic logging is on

- crash handler_unix.cpp - warns about unused variable
2020-11-23 21:23:13 -07:00
Alvin
c0ae589024
Alvin/minor fixes and improvements (#389)
* Fix typos
* Fix build for MinGW
- MinGW g++ doesn't recoganize `-rdynamic`
2020-11-17 20:40:26 -07:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
f1eff42b91
Update README.markdown 2020-08-08 08:40:17 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
9c6879226b
Update README.markdown 2020-08-07 23:10:58 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
3e1534eb81
Update API.markdown 2020-08-07 10:48:51 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
e32cee6c01
Update API.markdown 2020-08-07 10:48:07 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
dd4bb0f62c
Update API.markdown 2020-08-07 10:45:29 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
a4abb8b521
Update loglevels.hpp 2020-08-07 10:22:18 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
c2b1ff6475
Update loglevels.hpp 2020-08-05 12:30:09 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
e55c270753
Update loglevels.hpp 2020-08-05 12:28:17 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
b93e96ebf7
Update loglevels.hpp 2020-08-05 12:27:05 -06:00
Tian Xiao
5dd714b626
Fix some minor errors in API doc (#378)
* Typo in API doc

`SIGILL` and `SIGSEGV` were duplicated

* Fix the link to logrotate

Co-authored-by: Tian Xiao <tian.xiao@antelopetechnology.com>
2020-08-05 04:59:39 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
83737bbb8f
cpack version number (#376)
* cpack version number. Build number must be set since it's used by CPACK
2020-07-09 07:33:25 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
69a0be4c9c
play/tryout: It is confusing to name the target g3logger. KISS --> g3log (#372)
* It is confusing to name the target g3logger. KISS --> g3log. This MIGHT break installations for some but it's a needed correction. 

* Updated version number to 2.1.x
2020-07-08 22:42:55 -06:00
shiyuge
8e79dd6554
bug fix: vsnprintf_s return value 0 isn't error (#373)
Conforming with vsnprintf_s
2020-07-06 21:21:28 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
cdceb28deb
Update logworker.hpp (#369) 2020-06-29 11:58:01 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
a5f0158abd
speed up build (#367) by downloading gtest zip and avoiding cloning 2020-06-28 07:54:53 -06:00
shiyuge
e639f6d800
fix typo in logworker.hpp (#365) 2020-06-24 07:31:26 -06:00
shiyuge
2206cea309
fix typo in shared_queue.hpp exampel -> example (#363) 2020-06-23 06:53:33 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
5ef196eed5
Manual addition of sink removal functionality from C++11 branch (#361)
* Added sink removal
* improved build script
2020-06-19 13:49:30 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
e0e03ed105
Update PULL_REQUEST_TEMPLATE.md 2020-06-19 13:48:32 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
a67711a2df
Update PULL_REQUEST_TEMPLATE.md 2020-06-19 13:45:39 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
f9005d13bf
Update PULL_REQUEST_TEMPLATE.md 2020-06-19 13:36:48 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
7f3159e17f
Pull request template is improved 2020-06-19 12:02:48 -06:00
xgdgsc
6d0eb32ddb
dead link ,header name, format (#359) 2020-06-19 07:53:24 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
e21f93944e
Update README.markdown 2020-06-06 16:40:36 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
f9ac8f5e08
improved documentation (#358) 2020-06-06 16:39:27 -06:00
Murat Kilivan
68f3b174d9
Use CMake to download GoogleTest as part of build (#355)
Having GoogleTest source in the project means we are likely using a stale version of test. This change is to use CMake to download GoogleTest as part of the build's configuration step.
2020-06-03 15:37:46 -06:00
Murat Kilivan
639bfd7452
Use the correct namespace to call shutDownLogging() (#353)
This commit is to align the example source code in README with the source code.
2020-05-31 14:50:15 -06:00
Roman Popov
751330bb41
Allow throwing from exit handler (#349)
For example for unit testing purposes we may want to replace SIGABRT with
throwing an exception, so that unit tests for CHECK()s can be written.
2020-05-08 20:13:25 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
aab25a4091
one more comment fix 2020-05-08 14:33:50 -06:00
Kjell Hedström : Available for new Engineering Leadership and Engineering Opportunities
62162e8613
updated code comments since SIGABRT is used with default fatal handler instead of a throw 2020-05-08 14:30:52 -06:00
Kjell Hedström
6810f060f6
update travis, don't bog down the log file (#340) 2020-04-11 15:07:40 -06:00
Kjell Hedström
8dbd784922 Update issue templates 2020-04-02 21:32:39 -06:00
Kjell Hedström
960d48d726 Update issue templates 2020-04-02 21:31:28 -06:00
JoelStienlet
4000c5c899
remove std::move in return : std::future can only be moved anyway (#336)
Co-authored-by: Joel Stienlet <stienlet@localhost.localdomain>
2020-03-06 07:55:36 -07:00
Nicholas Lederer
9fb3e61e89
fix issue with stacktrace_windows not allocating sufficient memory for SYMBOL_INFO struct (#334) 2020-02-25 07:15:09 -07:00
bmagistro
3ffc36a3a2
add option to disable installing g3log for project embedding (#333) 2020-02-14 15:24:48 -07:00
Kjell Hedström
3a905ed83d
Update README.markdown 2019-12-16 15:43:32 -07:00
myd7349
29fb209b08 Update README.markdown (#329) 2019-12-14 06:51:10 -07:00
Jean-Charles Quillet
9b5527d773 Set link to local files as relative (#328) 2019-11-20 10:30:02 -07:00
Paul Smith
f149179178 Ensure LOG* and CHECK* macros are statement-safe (#320)
The LOG and LOGF macros had been modified to be statement-safe, but the
LOG_IF, CHECK, LOGF_IF, CHECKF, and CHECK_F macros were all still
unsafe in face of code where single-statement blocks were not enclosed
in {}.

For example code like this:

  if (!foobar)
      CHECKF(goodness, "badness detected!");
  else
      handle_foobar(foobar);

would fail in subtle and possibly dangerous ways.

Fix this by combining multiple if-statements into a single conditional
and inverting the conditions, then adding an empty then-block and moving
the log statement to the else-block.
2019-07-28 16:07:16 -06:00
myd7349
5cb5371e0e Fix CMake interface include directories (#321) 2019-07-28 15:55:57 -06:00
myd7349
376c417ad1 Improve CMake module file (#312)
* Improve CMake module file
* Fix packaging on Win32
* appveyor: Test install and package target
* Travis-CI: Test install and package target
* Update documentation for working with CMake
* Simplify g3loggerConfig.cmake
* Add Doc Prerequisites section
2019-05-13 22:42:15 -06:00
Kjell Hedström
9aecd55051
Update README.markdown (#316) 2019-05-02 15:32:13 -06:00
mobileben
9c5e7f3bcb #309 properly pass through ENABLE_FATAL_SIGNALHANDLING for the various iOS slices (#310) 2019-03-22 17:12:23 -06:00
mobileben
bc358e7436 Add arm64e support (#307) 2019-03-14 22:10:58 -06:00
Kjell Hedström
cb4a94da7d
Cloud CI: updated for travis OSX + Ubuntu. Simplified setup (#301)
* updated for travis OSX + Ubuntu. Simplified setup

* more robust script for test
2019-02-18 21:01:30 -07:00
Benjamin Beichler
e8a07f25b5 Fix several CMake Issues (#294)
* Restructure Build.cmake to use "modern" Cmake

Since it is deprecated to modify the global compiler flags and similar options, the Build.cmake is converted to use only per target operations.

Additionally, the checks for backtrace and Pthread lib is converted to use the in cmake included functions. Also the check for the cxa_demangle function should be more robust now.

* fixed option for performance test

* use CMAKE_CURRENT_SOURCE_DIR in git cmd-lines to get current version, for better integration as cmake subdirectory

* bump required cmake version to 3.2 and use target_compile_feature to fix OSX compiler recognition and c++14 compiler flags
2019-02-18 12:43:04 -07:00
Michael Gratton
e08e933f3e Create an IMPORT target in g3loggerConfig.cmake (#299) 2019-02-18 10:52:02 -07:00
Kjell Hedström
1eb0408347
Simplified travis setup with ubuntu xenail (#300)
* test in progress

* improved PR instructions

* revert back
2019-02-18 10:50:37 -07:00
Kjell Hedström
e7b70f3138
Update README.markdown 2019-02-16 16:02:12 -07:00
Aleksey Dobrunov
1a010de2f6 Mingw parse error (#293)
* fix 'Failure to successfully parse the message' on mingw

* removed redundant checks
2019-01-15 12:27:53 -07:00
Aleksey Dobrunov
502bf318a4 add option G3_SHARED_RUNTIME (#292) 2019-01-15 12:25:51 -07:00
Christos
cf36852238 Wrap gnu::format around #if to avoid msvc warnings (#291)
* Wrap gnu::format around #if to avoid msvc warnings

* Fix compilation issue
2018-12-17 20:08:02 -07:00
Nils Gladitz
048b23d38c Use C++11 generic attribute syntax to avoid issue #282 (#283) 2018-11-19 15:25:14 -07:00
Kjell Hedström
de870694d5
Revert "add /MT for MSVC static lib (#278)" (#279)
This reverts commit ab76fc32c02f063f9f8d0daaefc41c28f5a1e69a.
2018-07-15 14:01:56 -06:00
AlexP11223
ab76fc32c0 add /MT for MSVC static lib (#278) 2018-07-14 08:59:36 -06:00
ccvca
39526ce9d2 Fix: VS 2015 logmessage.cpp(167): error C2437: "_file" already initialized (#277) 2018-07-13 05:59:11 -06:00
Kjell Hedström
3c9a590a76
Improved readme for Windows 64 bit (#276)
* Improved readme for Windows 64 bit 

This clarifies that Windows users have to check the CMake documentation

* Spell check
2018-07-12 21:40:03 -06:00
outkontroll
a6788f89be VS2013 missing include (#273) 2018-06-21 15:39:26 -06:00
Max Savenkov
5ffb360e2f export generated defines directory so that the library can be used via add_subdirectory from another CMake-based project (#268) 2018-06-12 14:59:15 -06:00
mobileben
86c04cf729 Support defining the minimum deployment since newer Xcode defines minimum target of 10.0 for 32-bit devices (#272) 2018-06-06 07:21:23 -06:00
Kjell Hedström
b1beccc3a7
Update API.markdown 2018-05-24 17:12:47 -06:00
Kjell Hedström
f2c331f14c
Added description for FATAL hook function 2018-05-24 17:10:53 -06:00
Kjell Hedström
90065889b6
Added description for overriding the default signal handling 2018-05-24 17:06:28 -06:00
Kjell Hedström
01be7d4a0e
Added information for PID1 fatal signal handling 2018-05-24 16:57:44 -06:00
Kjell Hedström
2a21914c07
Update API.markdown
Added description: disable fatal signal handling
2018-05-24 16:50:55 -06:00
Julien Lecomte
e58c8d1ddf Add missing STATUS keyword following commit 82df2168aaa for #190 (#267) 2018-05-15 05:49:41 -06:00
Andreas Schönle
7b0414f76f Use defective stlpatch_future.hpp only for VS2013 (#266) 2018-05-14 06:43:05 -06:00
Eli (Codekrafter)
e8fe9d8b38 fixed warning about whitespace (#265) 2018-05-09 10:54:48 -06:00
John Farrier
69f21e3513 Fixed ambiguous constructor error. (#262) 2018-04-26 20:30:26 -06:00
Thomas Khyn
b6220b9174 MinGW / Windows fixes (#260)
* Add MINGW_HAS_SECURE_API flag for mingw compiler

* Use cmd rather than powershell to get GIT_VERSION on windows

powershell causes cmake to hang
2018-04-22 10:26:28 -06:00
DerekJuba-NIST
7f25b004cf Update loglevels.hpp (#259)
Fixed typo DEBUG -> DBUG
2018-04-12 09:55:42 -06:00
Kjell Hedström
ee742752e1
Update PULL_REQUEST_TEMPLATE.md 2018-03-30 21:20:39 -06:00
Kjell Hedström
9f4023c0cb
Create PULL_REQUEST_TEMPLATE.md 2018-03-30 21:20:12 -06:00
Kjell Hedström
8112f75dcf
improved runalltests (#255) 2018-03-25 23:35:52 -06:00
Jeff Ebert
408061280f restore original sigaction in restoreSignalHandler (#254)
* restore original sigaction in restoreSignalHandler

- Save original sigactions in a map called gSavedSigActions
- In restoreSignalHandler, do nothing if there is no saved sigaction.
  If there is a saved sigaction, then re-install it.
- Fixes issue #253

* fix bug found in code review of PR #254

* add functions for reporting sigaction errors

- Factor out reportSigactionError functions from functions that
  call sigaction to allow unit testing.
- Use strsignal libc function to convert from signal number to name
  to eliminate chance of not finding the name in {g,k}Signals maps.

* cleanup. perror doesn't need a wrapper. put code under test
2018-03-25 21:43:49 -06:00
Kjell Hedström
14db37ad23
G3log placeholder thread ID formatting (#248)
* Added thread ID configurable through API calls. Left to do g3sinks, documentation and some corner test cases for CHECK, LOG(FATAL) and FATAL signal

* improve test script, break if error

* easier to use API. Api docs added

* Update API.markdown

* Update API.markdown

* Update API.markdown

* updated readme with details for overriding default for "full details"

* updated custom sink with custom formatting example
2018-03-08 09:16:12 -07:00
Kjell Hedström
f2b860a2b4
Addressing the std2 concern raised in https://github.com/KjellKod/g3log/issues/212 (#246) 2018-02-20 22:02:19 -07:00
Петр Шургалин
217f52fb12 CMake: CMAKE_INSTALL_PREFIX for Linux (#243)
* CMake: CMAKE_INSTALL_PREFIX for Linux

If on Linux system CMAKE_INSTALL_PREFIX is set it overrides
CPACK_PACKAGING_INSTALL_PREFIX key.

* Readme: readme for MinGW building and installation

* Update README.markdown
2018-01-08 21:22:08 -07:00
Петр Шургалин
11f9f06f5c CMake: add install rule (#241)
* CMake: add MinGW install rule

* CMake: relative packaging paths
2017-12-27 13:09:40 -07:00
Kjell Hedström
c79695c387
Update CMakeLists.txt 2017-12-20 12:24:00 -07:00
Kjell Hedström
287bfc11cb
Update CMakeLists.txt 2017-12-20 12:09:41 -07:00
Петр Шургалин
a2ed65600b CMake: fix CMake version check on windows (#240)
CMake was telling that my version 3.10.1 is lesser thar 3.4
2017-12-20 10:32:36 -07:00
maj-tom
0ddfd6dccc Fix dangling else in LOG and LOGF macros (#231)
* Fix dangling else in LOG and LOGF macros
Closes #224

* added unit test
2017-12-06 21:31:55 -07:00
PeasantCodeFarmer
e7d3b9d7b1 Comment change for issue #232 (#237)
* Fixing some English/typos and a cyclical header include.

* Comment fix for issue# 232
2017-12-04 18:24:55 -07:00
Jean-Christophe Fillion-Robin
0005e14a9c cmake: Avoid extra recompilation updating generated_definitions.hpp only if needed (#235)
This commit updates the build system to ensure the generated_definitions
header is updated only if its content changed. This will avoid recompilation
if the re-configured without changing any options.
2017-12-02 17:21:56 -07:00
PeasantCodeFarmer
b4be5a0f00 Fixing some English/typos and a cyclical header include. (#234) 2017-11-22 07:41:35 -07:00
Matt Patterson
b3cb1ba057 atomicbool.hpp needs EOF outside of comment to build on VS15 (#229) 2017-10-26 18:37:37 -06:00
spinorx
28105e08d7 Support full filenames in logs. (#226)
This is helpful for doing cut and paste of filenames when opening.
Also fixed couple of macro parentheses.
2017-10-11 12:59:46 -06:00
mobileben
833b22d23b iOS Support (#183) (#198) 2017-09-26 21:33:08 -06:00
Kjell Hedsröm
96d6cda239 fixed comparison warning 2017-09-23 19:16:51 -06:00
mobileben
93c05406d8 Add in support for configurable kMaxMessageValue #203 (#208)
* Add in support for configurable kMaxMessageValue #203

* Add in documentation for USE_G3_DYNAMIC_MAX_MESSAGE_SIZE
2017-09-08 22:51:46 -06:00
maj-tom
97c6cf45b3 Update filesink.cpp (#216) 2017-07-19 13:16:50 -06:00
Thomas ten Cate
2a8ebca2d7 Remove double flushed from example (#214)
It's gone since e31c204
2017-07-13 07:01:00 -06:00
Andreas Schönle
f42611d2a1 fixed windows timestamp (#200)
* fixed windows timestamp

* ficing linux time

* Fix to_system_time

* fix last review issue.

* fixed formatting.

* Comment added to to_system_time() functiuon.:wq
2017-06-04 07:47:29 -06:00
Andreas Schönle
82df2168aa CMake messages changed to message( STATUS "..." ) (#190) 2017-05-17 14:31:19 -06:00
Andreas Schönle
ac37076327 Cross-compiling: Version readout fixed (#189)
* Cross-comoiling: Version readout fixed
Test.cmake: Path handling consistent

* ".x" syntax in variable checking removed

* Use G3LOG_DEBUG also in linux-specific code

* Fixed error when linux and shared library is on

* Fixed linux/windows shared lib handling: FATAL_ERROR for wrong cmake
version and no message in linux
2017-05-17 14:24:11 -06:00
Kjell Hedström
769feca4d0 Corrected cmake log message (#197) 2017-05-17 08:32:10 -06:00
Kjell Hedström
c08fba999b Definitions made explicit (#196)
* force definition inclusion

* to make it clear to the user
2017-05-17 08:25:29 -06:00
Jakob Wanner
24dea1ceea Cast char array to pointer to char in INTERNAL_LOG_MESSAGE (#193) 2017-05-17 08:07:39 -06:00
Andreas Schönle
123977f106 unit tests running with windows shared dll (#178)
* unit tests running with windows shared dll

* CMake automatically uses DLL runtime - remove cmake entries modifying
compiler flags

* missing DBUG vs DEBUG issue in Linux

* generated header added

* correction in CMake: Set HEADER_FILES correctly

* added static library option

* switched to powershell and included WORKING_DIRECTORY

* powershell use in windows and WORKING_DIRECTORY instead of cd

* Fixed appveyor.yml to use Release configuration and run unit tests

* trying to make appveyor work again ...

* make sure ERRORLEVEL is 0 when unit tests run successfully

* Still trying to fix appveyor ...

* Defaulting to shared library in linux, too

* Removed runtime loading test when g3log is not a shared library in linux

* Run unit tests verbosly using ctest.exe

* Disabled AggressiveThreadCallsDuringShutdown test

* Revert "Run unit tests verbosly using ctest.exe" (accidental commit)

This reverts commit b30513450d02d0bcb032e9b3997cf3592f87597e.

* re-committing valid parts of reverted commit

* DBUG vs DEBUG fix moved to generated_definitions.hpp

* cleanup shared/static option

* clarify cmake build types regardless of type

* Added compile test for DEBUG and DBUG

* put back formatting

* Removed commented out /MD switches for MSVC
2017-05-09 10:26:48 -06:00
Andreas Schönle
a1b5d97689 powershell use in windows and WORKING_DIRECTORY instead of cd (#181) 2017-05-04 14:00:17 -06:00
Kjell Hedström
613db7e346 test appveyor (#180)
* test appveyor

* fixed line issue

* extracting software version for windows too

* removed printout
2017-05-03 18:49:11 -06:00
Kjell Hedström
887673f4d0 Time adjustments for VMs and 32-bit embedded (#177)
* in progress. now using std::chrono

* in progress, not working but defined how to fix it - broken unit tests

* working - verified on VM. Unit tests not yet updated accordintly

* fixed all tests

* fixed formatting

* adjusted timezone during testing

* adjusted test for timezone
2017-04-27 22:57:04 -06:00
Kjell Hedström
121bb9c790 fix OSX/Clang build warnings and 32-bit linking errors regarding build versin (#176) 2017-04-24 21:50:35 -06:00
Kjell Hedström
ac54350346 The definitions file has to be generated after the options but before the build step. This way it will be included in the install target (#173) 2017-04-20 00:53:52 -06:00
Kjell Hedström
e04681ac42 Levels api changes 2 (#170)
* in progress

* Update loglevels.hpp

* Update loglevels.hpp

* don't code when too tired

* revert back

* removed comment
2017-04-05 23:03:55 -06:00
lemonnguyen
9f9062f45f Fix typos (#166) 2017-04-02 16:22:35 -06:00
Kjell Hedström
eba984171a Correcting define checks (#164)
* IF (NOT DEFINED  does NOT work on some CMake versions. This is a work-around

* removed log output
2017-04-01 10:39:34 -06:00
Kjell Hedström
33ed100f91 Colorer build and loglevel improvements (#161)
* static build with MS libs; remove vc11 support from cmake
* support AMD processor on winXp
* mingw build
* support older versions
* removed cmake warnings

* fix cmake >= 3.1 warnings (CMP0054)
* add target_include_directories - in projects that depend on g3log, do not need to specify include_directories
* Improved CPackage installation
* cleanup of build and test scripts
* c++14 gives for cleaner syntax
* build cleanup libg3logger.so will be a link to the actual libg3logger.so.<major><minor><build>. Only shared libraries from now on



* disableAll for log levels
* new setLogLevel impl
* cleaned up levels, now it should be easy for the user to understand how to add custom levels
2017-03-30 22:52:09 -06:00
Kjell Hedström
71ad664646 Update CPackLists.txt 2017-03-30 01:48:35 -06:00
Kjell Hedström
66a3d5ec0f Git release versioning (#163)
* corrected versions to fit release versioning

* fixed comments
2017-03-29 11:25:23 -06:00
Kjell Hedström
6e77118706 Time correction (#159)
* Improved the cpackage build. Also removed test compilation warnings

* To correcte what was reported in issue 155 (https://github.com/KjellKod/g3log/issues/155)
2017-03-28 11:09:10 -06:00
Kjell Hedström
417ae2ca10 Added a g3sink example (#158)
* Added a g3sink example

* Update API.markdown

* Update API.markdown

* Update API.markdown
2017-03-28 10:22:19 -06:00
Kjell Hedström
9b17525b97 Improved the cpackage build. Also removed test compilation warnings (#156) 2017-03-26 01:11:18 -06:00
Kjell Hedström
afa9a45a86 Update README.markdown 2017-03-24 13:49:24 -06:00
Aleksey Dobrunov
a48a4860fd fix mingw build (#152) 2017-02-19 10:50:58 -07:00
Kjell Hedström
3305652de5 Merge pull request #150 from 0017031/change_by_0017031
force /utf-8 for MSVC compiler
2017-02-15 00:02:56 -07:00
baic
411b8e249c force /utf-8 for MSVC compiler 2017-02-13 16:25:30 +08:00
Kjell Hedström
5660732d82 Merge pull request #147 from KjellKod/OSX_std_get_timespec
Update time.cpp
2017-01-08 14:07:02 -07:00
Kjell Hedström
c71349ac3d Update time.cpp 2017-01-08 14:01:38 -07:00
Kjell Hedström
a04b17abb5 Merge pull request #144 from rickyzhang82/pr-fix-output-format
Fix message newline issue
2016-12-23 09:13:19 -07:00
Ricky Zhang
d747de2840
Add back ugly tab
Signed-off-by: Ricky Zhang <rickyzhang@gmail.com>
2016-12-22 19:33:29 -05:00
Kjell Hedström
5bc693e099 Merge pull request #145 from rickyzhang82/pr-add-cmake-package
Add CMake package support
2016-12-22 10:24:37 -07:00
Ricky Zhang
601f6af1fd
Add CMake package support
Signed-off-by: Ricky Zhang <rickyzhang@gmail.com>
2016-12-22 11:30:18 -05:00
Ricky Zhang
fc615b4ac0
Fix message newline issue
Signed-off-by: Ricky Zhang <rickyzhang@gmail.com>
2016-12-22 11:23:25 -05:00
Kjell Hedström
3ed2ed2674 Merge pull request #141 from KjellKod/Build-CMake-flags
Inherit C++ flags from build environment
2016-12-21 15:44:47 -07:00
Kjell Hedström
ab5b408f40 Merge pull request #137 from AbberiorInstruments/windows-time-fix
fix timing inaccuracy on windows
2016-12-21 15:38:44 -07:00
Andreas Schönle
2ebb9c2346 Merge branch 'master' into windows-time-fix 2016-12-19 17:03:23 +01:00
Andreas Schönle
294a015175 Merge pull request #1 from AbberiorInstruments/KjellKod-clean-up-timing
merged pull request #1
2016-12-19 16:43:45 +01:00
Kjell Hedström
6cdefee897 Inherit C++ flags from build environment
As suggested in : http://stackoverflow.com/questions/36765217/how-to-create-cmake-recipes-in-yocto
2016-12-14 21:36:52 -07:00
Kjell Hedström
34aa4ff8d6 Update time.cpp
removed ifdef's and using Andreas' implementation of  get_time
2016-12-09 23:26:20 -07:00
Kjell Hedström
6bf8afddfa Merge pull request #133 from AbberiorInstruments/arm-fix
compiles on arm
2016-12-09 23:02:21 -07:00
Kjell Hedström
b7adc0e77d fix formatting to get the branch merged 2016-12-09 23:02:04 -07:00
Andreas Schönle
03734dd29c alternative, better fix as proposed by Kjell 2016-12-06 17:36:17 +01:00
Andreas Schönle
cc2bd60ecb Merge branch 'master' into windows-time-fix 2016-12-06 17:15:37 +01:00
Kjell Hedström
e6f9178d42 Merge pull request #138 from AbberiorInstruments/include-file-path-only
Include file path only
2016-12-06 09:15:05 -07:00
Andreas Schönle
8d01c2224d reverted file parameter to std::string reference, adjusted formatting 2016-12-06 17:13:27 +01:00
Kjell Hedström
dc06cfb683 Merge pull request #139 from AbberiorInstruments/add-levels-only
Add levels only
2016-12-06 08:49:21 -07:00
Andreas Schönle
f018f3e92f Fixed formatting 2016-12-06 14:34:23 +01:00
Andreas Schönle
793ddbd42e changed to copying the file path upon message creation 2016-12-06 14:07:01 +01:00
Andreas Schönle
4bd1870a76 effectively remove feature - it is almost useless. Keep the change to the unit test avoiding premature exit when fatal signal handling is not enabled in the library 2016-12-06 13:55:47 +01:00
Andreas Schönle
c8e9dc367c Merge branch 'master' into arm-fix 2016-12-06 12:01:16 +01:00
Andreas Schönle
7ece784109 Merge branch 'master' into windows-time-fix 2016-12-06 12:00:30 +01:00
Andreas Schönle
cb3e2b1336 Merge branch 'master' into include-file-path-only 2016-12-06 12:00:01 +01:00
Andreas Schönle
b43848c527 Merge branch 'master' into add-levels-only 2016-12-06 11:59:21 +01:00
Kjell Hedström
d9413181d3 Merge pull request #136 from KjellKod/KjellKod-Travis-test
Update .travis.yml
2016-12-05 23:55:17 -07:00
Kjell Hedström
1a27d6379f thanks to: https://github.com/PacificBiosciences/pbbam/blob/master/.travis.yml 2016-12-05 23:49:32 -07:00
Kjell Hedström
8cc59bece2 more sudo 2016-12-05 23:31:46 -07:00
Kjell Hedström
fc1f531d1d sudo change reverted 2016-12-05 23:29:43 -07:00
Kjell Hedström
c70fd4f978 space removed 2016-12-05 23:26:14 -07:00
Kjell Hedström
10150e7633 key-space issues, fixed1 2016-12-05 23:23:16 -07:00
Kjell Hedström
aed15716ad test3 2016-12-05 23:16:48 -07:00
Kjell Hedström
296b91d857 test2 2016-12-05 23:10:28 -07:00
Andreas Schönle
f1f2167864 Update API.markdown
Added description of addLogLevel function
2016-12-05 11:21:40 +01:00
SchoenleAndi
7d1481724c restore needs a flag to remove custom levels (at least the tests need to be able to do it)
addLogLevel again resets the log level to the passed enable_state as even requesting the level before adding it would otherwise lead to unexpectedly not setting a "new" level to the passed state. We now assume setLogLevel is not called before addLogLevel without considering this behavior.
2016-12-05 11:12:28 +01:00
SchoenleAndi
a98b91fc5c ensure that raise(SIGTERM) is not called in test if g3log was compiled without fatal signal handler 2016-12-05 10:48:52 +01:00
SchoenleAndi
a1748ce66b addLogLevel now plays nice with having called setLogLevel before. 2016-12-05 10:17:38 +01:00
SchoenleAndi
bb163ea376 Merge branch 'master' into windows-time-fix 2016-12-04 19:20:22 +01:00
SchoenleAndi
9436660109 Merge branch 'master' into arm-fix 2016-12-04 19:18:50 +01:00
SchoenleAndi
b1088d06ab Merge branch 'master' into include-file-path-only 2016-12-04 19:17:39 +01:00
SchoenleAndi
323ca8e7ce Merge branch 'master' into add-levels-only 2016-12-04 19:15:51 +01:00
Kjell Hedström
0624e8c655 Update .travis.yml
Ref: discussion at https://github.com/travis-ci/travis-ci/issues/4631
2016-12-04 08:32:45 -07:00
SchoenleAndi
069085c451 fix timing inaccuracy on windows 2016-12-04 16:09:23 +01:00
Kjell Hedström
d15fcb3776 Update .travis.yml
Dummy trigger to travis
2016-12-04 07:17:21 -07:00
Kjell Hedström
426e77943e Merge pull request #126 from dasmysh/master
Updated cmake version to correct string compare behaviour.
2016-12-04 07:15:54 -07:00
Kjell Hedström
1ba64d5e09 Merge pull request #134 from KjellKod/KjellKod-trigger-travis-1
trigger travis
2016-12-02 10:22:25 -07:00
Kjell Hedström
5a0e730d0c assume yes for interactive check 2016-12-02 10:16:46 -07:00
Kjell Hedström
0fd9cb8408 trigger travis
test
2016-12-02 10:13:02 -07:00
Kjell Hedström
0fdccaf823 Update .travis.yml
Upgrade travis cmake version
2016-12-02 10:10:01 -07:00
SchoenleAndi
e93031c6d0 compiles on arm 2016-12-02 17:01:16 +01:00
Kjell Hedström
813e3da1f9 Merge pull request #129 from AbberiorInstruments/msvc-format-specifier
MSVC printf format specifier
2016-10-20 13:37:16 -06:00
Kjell Hedström
570a85b997 Merge pull request #128 from AbberiorInstruments/DBUG-vs-DEBUG
Fix compilation errors when CHANGE_G3LOG_DEBUG_TO_DBUG is enabled-
2016-10-20 13:35:17 -06:00
SchoenleAndi
1dafdaf2a4 msfc fromat specifiers added 2016-10-19 13:41:48 +02:00
SchoenleAndi
c39e77c27e DBUG-vs-DEBUG in main_ programs fixed 2016-10-19 13:39:24 +02:00
SchoenleAndi
940d11a61d DBUG vs DEBUG fixed in unit test, too 2016-10-19 13:36:19 +02:00
SchoenleAndi
c467851423 save include fil full path as const char * in LogMessage 2016-10-19 13:21:52 +02:00
SchoenleAndi
09ede1bf1f add loglevels to upstream master only 2016-10-19 13:17:46 +02:00
Sebastian Maisch
e7af386de2 Updated cmake version to correct string compare behaviour. 2016-10-17 19:49:56 +02:00
Kjell Hedström
ba81287ca5 bugfix time formatting (#119)
* bugfix time formatting

* fixit
2016-08-30 18:34:13 -06:00
Lynn
1bae2e6a68 Typo fix (Vaulue → Value) (#120)
* typo fix (Vaulue → Value)

* typo fix (Vaulue → Value)
2016-08-30 11:10:58 -06:00
Kjell Hedstrom
e0fb4fd24e fixit 2016-08-25 00:51:21 -06:00
Kjell Hedstrom
26f76cd103 bugfix time formatting 2016-08-25 00:49:17 -06:00
Kjell Hedström
1496d7aa40 Update API.markdown (#115) 2016-08-18 00:49:49 -06:00
Kjell Hedström
7d6bf2fac5 Update README.markdown (#114)
* Update README.markdown

* Update README.markdown

* Update README.markdown

* Update README.markdown

* Update README.markdown
2016-08-18 00:37:28 -06:00
Kjell Hedström
9b8e13bcfe Update crashhandler.hpp (#110)
Thanks to @graugans
2016-08-11 07:08:41 -06:00
Kjell Hedström
86473c60e0 Gimesketvirtadien's time requested changes (#104)
* Added "removeSink" method and related functionality to LogWorker API

* Added a new API for plugging in custom Timestamp generator.

* Revert "Added "removeSink" method and related functionality to LogWorker API"

This reverts commit c9cee5d9a077ce1e7e68f07cc30d03e4bed19c7b.

* Dropping shared_ptr<Timestamp> in log messages and using regular vars

* Moving to standard timespec struct from custom Timestamp

* Wiring timespec timestamps with formating routine

* Falling back to clock_gettime

* Reverting g3 API changes

* Optimizing format string generation

* Removed _microseconds from LogMessage

* Implemented sec fractional format key

* Optimization of format string generation

* Adjusting comments

* Refining localtime_formatted by introducing two helper functions

* refactored and simplified code

* fixed up some commented away unit tests

* refactoring message specifics tests to it's own test

* Use gcc 4.9

* C++14 for Linux

* Update .travis.yml

* Update buildAndRunTests.sh

* lower case in `-std=c++14`

* -lrt flag for gcc

* Added support for high precision clock on Linux/gcc (already there now for OSX). Windows is still missing

* intermediate comments

* Clarified for some code readers the mysterious use of assert in an expression that is always true

* refactored + renamed functions and constants. Added unit test for retrieving fractional type

* committing changes previously fixed - finished unit testing for g3::internal::time::GetFractional(..)

* added unit test for fractional to string

* added missing unit tests for localtime_formatted

* fixed? nano / microsec functionality to timer

* test
2016-08-11 00:27:52 -06:00
jkhoogland
ff722164d4 Warning braces loglevels clang 3.7 (#108)
* Fix gcc compiler warning for braces around scalar

* use 'toString' to get the formatted output in the fatal cerr message when a LOG fatal or CONTRACT happens. This was already OK on the sink side

* Avoid initialization/shutdown deadlock that could occur due to wrong use of the API

* Workaround for windows testing (#100)

* added automatic linking pragma for `StackWalk64` (#96)

* fix to implicit type casting (#98)

* create a Xcode project

* fixed to implicit type casting

* Revert "create a Xcode project"

This reverts commit e3216391f536fa41c47067fdb0296c56bafa11fa.

* Fixed a bug causing cross-compilers to fail (#101)

(The edited line eliminated the original CXXFLAGS variable, removing the sysroots parameter, which is needed for cross compiling)

* Update logworker.cpp (#103)

* Fix gcc compiler warning for braces around scalar

* Add back braces
2016-07-31 17:01:54 -06:00
Kjell Hedström
e0d4434428 Update logworker.cpp (#103) 2016-06-23 21:35:59 -06:00
Nitaym
56b3f20517 Fixed a bug causing cross-compilers to fail (#101)
(The edited line eliminated the original CXXFLAGS variable, removing the sysroots parameter, which is needed for cross compiling)
2016-06-22 14:29:45 -04:00
Kjell Hedström
04817b6e85 Workaround for windows testing (#100) 2016-06-12 16:58:14 -06:00
Dmitry Ledentsov
f10fbe1d9c added automatic linking pragma for StackWalk64 (#96) 2016-06-12 16:57:55 -06:00
JiHyung Lee
736d6437b8 fix to implicit type casting (#98)
* create a Xcode project

* fixed to implicit type casting

* Revert "create a Xcode project"

This reverts commit e3216391f536fa41c47067fdb0296c56bafa11fa.
2016-06-12 14:09:55 -06:00
Kjell Hedström
e58cc942dd Merge pull request #94 from KjellKod/Fatal_exit_For_Erronous_Initialization
Avoid initialization/shutdown deadlock
2016-05-23 22:34:41 -06:00
Kjell Hedstrom
70b37d66a0 Avoid initialization/shutdown deadlock that could occur due to wrong use of the API 2016-05-23 22:25:32 -06:00
Kjell Hedström
6c1698c4f7 Merge pull request #86 from KjellKod/cerr_when_fatal_exit
use 'toString' to get the formatted output in the fatal cerr message …
2016-03-30 16:42:43 -06:00
Kjell Hedstrom
bfe1e8ad19 use 'toString' to get the formatted output in the fatal cerr message when a LOG fatal or CONTRACT happens. This was already OK on the sink side 2016-03-30 16:35:38 -06:00
Kjell Hedström
7d0779d92d Merge pull request #84 from KjellKod/checkmarx-update
removed false positives as detected by Checkmarx code analysis tool. …
2016-03-24 11:21:43 -06:00
Kjell Hedström
4472ba3cbf fix the tool, not the code 2016-03-24 10:43:09 -06:00
Kjell Hedström
d01dfbf992 revert "fix" for issue with checkmarx. The tool should be fixed, not the code 2016-03-24 10:41:23 -06:00
Kjell Hedstrom
8df4eadd92 removed false positives as detected by Checkmarx code analysis tool. Not impressed so far though with the tool 2016-03-18 10:50:59 -06:00
Kjell Hedström
db23383aea Merge pull request #83 from KjellKod/duedal-install-target
Duedal install target
2016-03-06 23:04:00 -07:00
Kjell Hedsröm
33003af08b fixed user com 2016-03-06 23:02:04 -07:00
Kjell Hedsröm
0bd43a9ef2 start of replacement for: https://github.com/KjellKod/g3log/pull/74/files 2016-03-06 15:55:35 -07:00
Kjell Hedström
5790c3723f Merge pull request #80 from KjellKod/Thread_local
Update stacktrace_windows.cpp
2016-02-24 19:16:09 -07:00
Kjell Hedström
56cef7959c Merge pull request #82 from KjellKod/repace-g3log-in-path
Repace g3log in path
2016-02-17 00:31:15 -07:00
Kjell Hedsröm
fbddb5a5ca added new API for AddDefaultLogger to API 2016-02-17 00:25:55 -07:00
Kjell Hedsröm
b7704b1ed7 fixed merge conflict 2016-02-17 00:06:45 -07:00
Kjell Hedsröm
1ebaf0e7e5 fixed conflict 2016-02-16 23:16:22 -07:00
Kjell Hedström
af505d572a Merge pull request #81 from KjellKod/path_parenthesis_fix
Allow parenthesis in path and filename
2016-02-16 23:00:23 -07:00
Kjell Hedsröm
072a7bef93 put back commented out code 2016-02-16 22:57:02 -07:00
Kjell Hedsröm
fb034e1000 fixed commented out code, left it but corrected 2016-02-16 22:56:21 -07:00
Kjell Hedsröm
a0b961ee04 Allow parenthesis in path and filename 2016-02-16 22:52:38 -07:00
Kjell Hedström
279587c567 Update stacktrace_windows.cpp
For access to thread_local definition
2016-02-16 20:55:28 -07:00
Jiri Hoogland
e981b08c81 Put include headers back per request of Kjell 2016-02-03 18:19:36 -05:00
Jiri Hoogland
b8f28d3a49 Fix missing include files 2016-02-02 19:05:29 -05:00
Kjell Hedström
579579962c Merge pull request #77 from sradigan/FixLinuxClangSupport
Fixed build options to allow compilation on linux using clang
2016-02-02 16:02:35 -07:00
Sean Radigan
a65b99087b Fixed build options to allow compilation on linux using clang 2016-02-02 16:30:44 -05:00
Jiri Hoogland
3aab24d8c8 Handle empty logger_id 2016-01-28 10:06:40 -05:00
Jiri Hoogland
31c01b168c Add ability for user to override 'g3log' monniker in log file 2016-01-28 10:02:22 -05:00
Jiri Hoogland
d9bc515ce8 Add ability for user to override 'g3log' monniker in log file 2016-01-27 18:51:02 -05:00
Aleksey Dobrunov
69e0cafee8 fix unit test 2016-01-27 13:56:14 -05:00
Aleksey Dobrunov
0384eb1638 fix build lib 2016-01-27 13:56:14 -05:00
Aleksey Dobrunov
c9e93c8d14 remove unused include 2016-01-27 13:56:14 -05:00
Hans Duedal
1847312bac Enable an install target
Should work fine on Unix systems, and support install prefixes.
Installs both a static and a shared library to the same location, so we
get an .a and a .so/.dylib file, which can be linked to in the ordinary
way, ie. `-lg3logger`.
2016-01-24 13:27:52 +01:00
Kjell Hedström
1c6ede6db4 Merge pull request #72 from colorer/unused_includes
remove unused include
2016-01-11 12:38:26 -07:00
Aleksey Dobrunov
444642f47b fix unit test 2016-01-11 23:48:56 +05:00
Aleksey Dobrunov
1f4ffbeb6f fix build lib 2016-01-11 23:47:00 +05:00
Aleksey Dobrunov
12beaf89de remove unused include 2016-01-11 00:41:05 +05:00
Kjell Hedström
31d12dc090 Merge pull request #66 from guanqun/patch-1
trivial fix: add double quote around "some text"
2015-12-01 09:53:12 -07:00
Lu Guanqun
901b0d74e6 trivial fix: add double quote around "some text" 2015-12-02 00:10:48 +08:00
Kjell Hedström
501b16ffbd Merge pull request #65 from cstamatopoulos/missing_import_lib_msvc
#13 generate import lib in msvc using cmake >= 3.4
2015-12-01 05:51:48 -07:00
Kjell Hedström
e97656b902 Merge pull request #56 from turenar/thread_local
suppress 'thread attribute directive ignored' warning on mingw
2015-11-26 19:31:34 -07:00
cstamatopoulos
d125990007 #13 generate import lib in msvc using cmake >= 3.4 2015-11-26 19:13:45 -05:00
Kjell Hedström
77b1d1c0d5 Merge pull request #63 from cstamatopoulos/fix-msvc2013-compilation
fix msvc2013 compilation issue of internal::g_log_level_status on res…
2015-11-24 00:22:18 -07:00
Kjell Hedström
2c7faa0ec1 Merge pull request #64 from KjellKod/API_Documentation
Fixed dynamic logging unit test
2015-11-23 23:50:52 -07:00
Kjell Hedsröm
d4d60ca517 Fixed dynamic logging unit testw: 2015-11-23 23:49:27 -07:00
cstamatopoulos
499d1a51ef fix msvc2013 compilation issue of internal::g_log_level_status on reset() 2015-11-24 01:46:27 -05:00
Kjell Hedström
d361a4bbd9 Merge pull request #54 from KjellKod/API_Documentation
Api documentation
2015-11-23 23:20:06 -07:00
Kjell Hedsröm
9a5508b327 fixed typ 2015-11-23 23:18:27 -07:00
Kjell Hedsröm
01666ab80c Added link to the API docmentation 2015-11-23 23:17:47 -07:00
Kjell Hedsröm
2cef7cb174 Merge branch 'master' of github.com:KjellKod/g3log into API_Documentation
Conflicts:
	test_unit/test_io.cpp
2015-11-23 23:12:30 -07:00
Kjell Hedsröm
1007ab1fd9 Added windows fatal handling. All except the stackdump... maybe the options need to be explained as well 2015-11-22 16:55:50 -07:00
Kjell Hedsröm
2b7331c892 TODO --> TOWRITE 2015-11-22 16:43:08 -07:00
Kjell Hedsröm
73cda4bd3e Marking all non-yet made entries with strikethrough and 'TODO' 2015-11-22 16:41:19 -07:00
Kjell Hedsröm
773000caf9 Added fatal handling: Linux 2015-11-22 16:36:30 -07:00
Kjell Hedsröm
11e22dd40d The sigsegv example exited by sigfpe. Fixed! 2015-11-22 16:35:36 -07:00
Kjell Hedsröm
df093e3563 Documentation done till fatal handling.
Improved logging levels
2015-11-22 16:01:53 -07:00
Kjell Hedström
0f790a4a11 Merge pull request #58 from KjellKod/Flush_On_Every_Write__DefaultLogger
flush after every write: https://github.com/KjellKod/g3log/issues/57
2015-11-03 13:54:50 -07:00
Kjell Hedström
4b3c4d7ff7 flush after every write: https://github.com/KjellKod/g3log/issues/57
https://github.com/KjellKod/g3log/issues/57
2015-11-03 13:35:47 -07:00
Turenar
fa15177d58 suppress 'thread attribute directive ignored' warning on mingw
make thread_local workaround on only <=VS2013
VS2015 has thread_local specifier
2015-10-30 16:09:39 +09:00
Kjell Hedsröm
c73c90f93c custom logging levels in progress 2015-10-10 15:25:35 -06:00
Kjell Hedsröm
91542319ee enable-disable logging levels at runtime 2015-10-10 15:14:47 -06:00
Kjell Hedsröm
8b1517896f API for logging and contract 2015-10-10 14:50:24 -06:00
Kjell Hedström
17756a2657 Merge pull request #55 from KjellKod/test_warnings
removed gcc5.2 warnings
2015-10-03 16:15:26 -06:00
Kjell Hedsröm
c1f5b20c6d removed gcc5.2 warnings 2015-10-03 16:13:21 -06:00
Kjell Hedstrom
bb1c31577d lists try 2 2015-09-21 16:23:27 -06:00
Kjell Hedstrom
4349797485 lists try 1 2015-09-21 16:20:32 -06:00
Kjell Hedström
077ede2bfb Merge pull request #53 from rayrapetyan/patch-1
Update Build.cmake (support for FreeBSD 10+)
2015-09-21 16:17:28 -06:00
Kjell Hedstrom
ed51ab8dbb ADDED vanilla API readme. Improved the README.markdown by elaborating on how to use a custom sink 2015-09-21 15:51:23 -06:00
Kjell Hedstrom
09a9bcc95e compile warnings, gtest comparisons 2015-09-21 15:33:34 -06:00
Robert Ayrapetyan
8f109ec131 Update Build.cmake (support for FreeBSD 10+)
Proposed fix for building on FreeBSD 10+ systems:
- c++abi and rt libs are not required
- pthread flag and execinfo lib is required
2015-09-20 19:23:38 -07:00
Kjell Hedström
35cdf42b05 Merge pull request #52 from craig-cogdill/SaveG3Log
Adding the function name and saving the day
2015-09-17 00:40:00 +02:00
Craig Cogdill
051fb50c98 Adding the function name and saving the day 2015-09-16 16:29:48 -06:00
Kjell Hedstrom
40fbc7b4b3 Merge pull request #50 from KjellKod/improve-default-formatting
Improve default formatting
2015-09-14 21:56:51 -06:00
Kjell Hedstrom
d936bc2763 Fixed formatting unit tests 2015-09-14 21:52:42 -06:00
Kjell Hedstrom
403f3cb94f Improve default formatting
Instead of 
<date and time> <file>:L<line> .... 

have 
<date and time>**<file>:<line>**
2015-09-14 19:03:32 -06:00
Kjell Hedstrom
4974cf1da6 Merge pull request #48 from KjellKod/disable_single_signals
Custom signal handler
2015-09-11 07:03:38 -06:00
Kjell Hedstrom
a1a3672d71 Missing include 2015-09-11 03:23:24 -06:00
Kjell Hedstrom
a5e21e5d52 testing ssh 2015-09-11 03:05:34 -06:00
Kjell Hedstrom
b9fc844bb4 testing it again 2015-09-11 03:02:35 -06:00
Kjell Hedstrom
dcdf41b3ee tesing ssh keys again 2015-09-11 02:57:38 -06:00
Kjell Hedstrom
980b863ec7 test ssh key 2015-09-11 02:54:27 -06:00
Kjell Hedstrom
fae01d5e92 Added missing test for custom signal handling for SIGTERM 2015-09-11 02:49:17 -06:00
Kjell Hedstrom
602e135f84 Added resotre and override signal handler 2015-09-10 06:20:26 -06:00
Kjell Hedstrom
6432487adc Merge branch 'master' of https://github.com/KjellKod/g3log into disable_single_signals 2015-09-10 04:18:58 -06:00
Kjell Hedstrom
3bcf360815 vanilla example. dynamic signal turning on/off 2015-09-01 00:35:47 -06:00
103 changed files with 6489 additions and 2851 deletions

83
.clang-format Normal file
View File

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

19
.devcontainer/Dockerfile Normal file
View File

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

View File

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

View File

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

19
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

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

View File

@ -0,0 +1,21 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Keep in mind that g3log is community driven. Do you want to take on the feature work yourself, or do you want someone else to take it on? The latter will require a whole lot more "selling". If you want to drive it yourself you can likely get community feedback on your idea + definitely great code reviews.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

20
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

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

49
.github/workflows/buildAndRunTests.yml vendored Normal file
View File

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

76
.github/workflows/codeql-analysis.yml vendored Normal file
View File

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

87
.github/workflows/ctest.yml vendored Normal file
View File

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

31
.github/workflows/documentation.yml vendored Normal file
View File

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

2
.gitignore vendored
View File

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

View File

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

13
.vscode/easycode.ignore vendored Normal file
View File

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

23
.vscode/launch.json vendored Normal file
View File

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

7
.vscode/settings.json vendored Normal file
View File

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

Binary file not shown.

View File

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

View File

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

15
CMakeLists.txt.in Normal file
View File

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

95
CODE_OF_CONDUCT.md Normal file
View File

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

13
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,13 @@
# New Code
1. Please support all changed functionality with unit testing. A TDD approach usually leaves a cleaner end result than writing test afterwards
# Issues
1. Please explain your environment in the ticket. Frequently initialization issues due to not following best practices or the documentation are the causes.
Check the documentation and search previous issues before opening up a new one.
1. Don't be afraid of adding additional contexst of an old issue in case you think
this will improve things for the community going forward.
# Community Driven
G3log is community driven. Be respectful. G3log is developed and maintained without financial support, being helpful and polite will move your request and input along faster.

View File

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

View File

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

View File

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

View File

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

36
PULL_REQUEST_TEMPLATE.md Normal file
View File

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

View File

@ -1,206 +0,0 @@
# G3log : Asynchronous logger with Dynamic Sinks
## EXAMPLE USAGE
#### Optional to use either streaming or printf-like syntax
```
LOG(INFO) << "streaming API is as easy as ABC or " << 123;
LOGF(WARNING, "Printf-style syntax is also %s", "available");
```
#### Conditional logging
int less = 1; int more = 2
LOG_IF(INFO, (less<more)) <<"If [true], then this text will be logged";
// or with printf-like syntax
LOGF_IF(INFO, (less<more), "if %d<%d then this text will be logged", less,more);
#### Design-by-Contract
*CHECK(false)* will trigger a "fatal" message. It will be logged, and then the
application will exit.
```
CHECK(less != more); // not FATAL
CHECK(less > more) << "CHECK(false) triggers a FATAL message";
```
## What G3Log is:
* ***G3log*** is the acting name for the third version of g2log and it stands for **g3log with dynamic sinks**
* G3log is an asynchronous, "crash-safe" logger. You can read more about it here [[g2log version]](
http://www.codeproject.com/Articles/288827/g2log-An-efficient-asynchronous-logger-using-Cplus)
* You can choose to use the default log receiver which saves all LOG calls to file, **or** you can choose to use your own custom made log receiver(s), **or** both, **or** as many sinks as you need.
## Benefits you get when using G3log ##
1. Easy to use, clean syntax and a blazing fast logger.
2. All the slow log I/O disk access is done in a background thread. This ensures that the LOG caller can immediately continue with other tasks and do not have to wait for the LOG call to finish.
3. G3log provides logging, Design-by-Contract [#CHECK], and flush of log to file at
shutdown. Buffered logs will be written to the sink before the application shuts down.
4. It is thread safe, so using it from multiple threads is completely fine.
5. It is *CRASH SAFE*. It will save the made logs to the sink before it shuts down.
The logger will catch certain fatal events *(Linux/OSX: signals, Windows: fatal OS exceptions and signals)* , so if your application crashes due to, say a segmentation fault, *SIGSEGV*, it will log and save the crash and all previously buffered log entries before exiting.
6. It is cross platform. Tested and used by me or by clients on OSX, Windows, Ubuntu, CentOS
7. G3log and G2log is used world wide in commercial products as well as hobby projects. G2log is used since early 2011.
8. The code is given for free as public domain. This gives the option to change, use, and do whatever with it, no strings attached.
9. Two versions of g3log exist that are under active development.
* This version: *[g3log](https://github.com/KjellKod/g3log)* : which is made to facilitate easy adding of custom log receivers. Its tested on at least the following platforms with Linux(Clang/gcc), Windows (mingw, visual studio 2013). My recommendation is to go with g3log if you have full C++11 support.
* *[g2log](https://bitbucket.org/KjellKod/g2log)*: The original. Simple, easy to modify and with the most OS support. Clients use g2log on environments such as OSX/Clang, Ubuntu, CentOS, Windows/mingw, Windows/Visual Studio. The focus on g2log is "slow to change" and compiler support. Only well, time tested, features from g3log will make it into g2log.
# G3log with sinks
[Sinks](http://en.wikipedia.org/wiki/Sink_(computing)) are receivers of LOG calls. G3log comes with a default sink (*the same as G3log uses*) that can be used to save log to file. A sink can be of *any* class type without restrictions as long as it can either receive a LOG message as a *std::string* **or** as a *g3::LogMessageMover*.
The *std::string* comes pre-formatted. The *g3::LogMessageMover* is a wrapped struct that contains the raw data for custom handling in your own sink.
A sink is *owned* by the G3log and is added to the logger inside a ```std::unique_ptr```. The sink can be called though its public API through a *handler* which will asynchronously forward the call to the receiving sink.
```
auto sinkHandle = logworker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
```
#Code Examples
Example usage where a custom sink is added. A function is called though the sink handler to the actual sink object.
```
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3log/std2_make_unique.hpp>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
auto sinkHandle = logworker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
// initialize the logger before it can receive LOG calls
initializeLogging(logworker.get());
LOG(WARNING) << "This log call, may or may not happend before"
<< "the sinkHandle->call below";
// You can call in a thread safe manner public functions on your sink
// The call is asynchronously executed on your custom sink.
std::future<void> received = sinkHandle->call(&CustomSink::Foo,
param1, param2);
// If the LogWorker is initialized then at scope exit the g3::shutDownLogging() will be called.
// This is important since it protects from LOG calls from static or other entities that will go out of
// scope at a later time.
//
// It can also be called manually:
g3::shutDownLogging();
}
// some_file.cpp : To show how easy it is to get the logger to work
// in other parts of your software
#include <g3log/g3log.hpp>
void SomeFunction() {
...
LOG(INFO) << "Hello World";
}
```
Example usage where a the default file logger is used **and** a custom sink is added
```
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3log/std2_make_unique.hpp>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
auto worker = LogWorker::createLogWorker();
auto defaultHandler = worker->addDefaultLogger(argv[0],
path_to_log_file);
// logger is initialized
g3::initializeLogging(worker.get());
LOG(DEBUG) << "Make log call, then add another sink";
worker->addSink(std2::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
...
}
```
# BUILDING g3log:
-----------
The default is to build an example binary 'g3log-FATAL-contract' and 'g3log-FATAL-sigsegv'. I suggest you start with that, run it and view the created log also.
If you are interested in the performance or unit tests then you can
enable the creation of them in the g3log/CMakeLists.txt file. See that file for
more details
```
cd g3log
mkdir build
cd build
```
## Building on Linux
```
cmake -DCMAKE_BUILD_TYPE=Release ..
make
```
## Building on Windows
Please use the Visual Studio 12 (2013) command prompt "Developer command prompt"
```
cmake -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 12" ..
msbuild g3log.sln /p:Configuration=Release
```
## Building on *nix with Clang
```
cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release ..
make
```
#Performance
G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small "worst case latency" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my laptop the average call, when doing extreme performance testing, will be about ~2 us.
The worst case latency is kept stabile with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest.
You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
#Enjoy
If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and g2log and it is also nice to see if someone is using it.
If you have ANY questions or problems please do not hesitate in contacting me on my blog
http://kjellkod.wordpress.com/2011/11/17/kjellkods-g2log-vs-googles-glog-are-asynchronous-loggers-taking-over
or at ```Hedstrom at KjellKod dot cc```
Cheers
Kjell *(a.k.a. KjellKod)*

136
README.md Normal file
View File

@ -0,0 +1,136 @@
**Scanning Update**: [![ci/action CodeQL](https://github.com/KjellKod/g3log/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/KjellKod/g3log/actions/workflows/codeql-analysis.yml)
# Contents
[**introduction**](docs/index.md) | [detailed information](docs/g3log_usage.md) | [Configure & Build](docs/building.md) | [API description](docs/API.md) | [Custom log formatting](docs/API_custom_formatting.md)
# Welcome to g3log
### Use [kjellkod.github.io/g3log/](https://kjellkod.github.io/g3log/) for best reading / searching / navigating of g3log's documentation
G3log is an asynchronous logger with three main features:
1. Intuitive `LOG(...)` API
2. `Design-by-contract` `CHECK(...)` functionality
3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash.
The super quick introduction to g3log can be seen in the steps 1 - 9 below.
For more in-depth information please see the full usage description in [g3log_usage.md](docs/g3log_usage.md). If you want to understand better the internals of g3log, then plase look at the [API.md](docs/API.md) for both high-level and deep-dive insights.
## Experiment and try-out g3log in Github Codespaces
ref: [codespaces.md](docs/codespaces.md)
## 1. Easy usage in files
Avoid deep dependency injection complexity and instead get access to the logger as easy as:
```
#include <g3log/g3log.hpp>
```
## 2. Access to streaming and print_f log call syntax
Both streaming syntax `LOG` and print_f `LOGF` syntax are available:
```
LOGF(INFO, "Hi log %d", 123);
LOG(INF) << "Hi log " << 123;
```
## 3. Conditional logging
```
LOG_IF(INFO, (1 < 2)) << "If true this message will be logged";
LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message");
```
## 4. Design-by-contract framework
```
CHECK(less != more); // not fatal
CHECK_F(less > more, "CHECK(false) will trigger a fatal message")
```
## 5. Handling of fatal
By default g3log will capture fatal events such as `LOG(FATAL)`, `CHECK(false)` and otherwise fatal signals such as:
```
SIGABRT
SIGFPE
SIGILL
SIGSEGV
SIGTERM
```
When a fatal event happens the not-yet written log activity will be flushed to the logging sinks. Only when all logging activity up to the point of the fatal event has happend, will g3log allow the fatal event to proceed and exit the process.
If `object` symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink.
#### 5b. Overriding and customization of fatal event handling
For overriding fatal error handling to use your own, or to add code `hooks` that you want to execute please see the [API.md](docs/API.md) doc.
## 6. Default and Custom logging levels
The default logging levels are `DEBUG`, `INFO`, `WARNING` and `FATAL`. You can define your own logging levels or completely replace the logging levels. Ref: [API.md](docs/API.md)
### 7. Log filtering
Log filtering is handled in g3log if dynamic logging levels are enabled
in the configuration. See the [API.md](docs/API.md) for information. Log filtering can also be handled through the sink as can be seen in [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks)
## 8. 3rd party and custom logging sinks
The default logging sink has no external 3rd party dependencies. For more logging sinks please see [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks)
- log rotate
- log to syslog
- log to colored terminal output
- log rotate with filter
See the [API.md](docs/API.md) for more information about the simple steps to creating your own logging sink.
## 9. Log instantiation
With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this:
```cpp
const std::string directory = "./";
const std::string name = "TestLogFile";
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(name, directory);
```
The resulting filename would be something like:
```
./TestLogFile.g3log.20160217-001406.log
```
## <a name="performance">Performance</a>
G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small "worst case latency" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my 2010 laptop the average call, when doing extreme performance testing, will be about ~2 us.
The worst case latency is kept stable with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest.
You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
## <a name="continuous_integration">Continuous Integration</a>
The g3log repository is evaluating github actions for executing test coverage, installation and document generation. In case you want to look into change any of these setups the following files are the ones of interest.
See `Actions` for matrix (ubuntu, macos, windows) testing as well as other actions for doc publishing.
## <a name="feedback">Feedback</a>
If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and it is always nice to hear when and how someone is using it.
If you have ANY questions or problems please do not hesitate in contacting me at
`Hedstrom @ Kjellod. cc`
# <a name="say-thanks">Say Thanks</a>
This logger is available for free and all of its source code is public domain. A great way of saying thanks is to send a donation. It would go a long way not only to show your support but also to boost continued development.
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/g3log/25)
* $5 for a cup of coffee
* $25 for a late evening coding with takeout
Cheers
Kjell *(a.k.a. KjellKod)*
[**introduction**](docs/index.md) | [detailed information](docs/g3log_usage.md) | [Configure & Build](docs/building.md) | [API description](docs/API.md) | [Custom log formatting](docs/API_custom_formatting.md)

53
cmake/g3logConfig.cmake Normal file
View File

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

BIN
docs/.DS_Store vendored Normal file

Binary file not shown.

1
docs/.ciignore Normal file
View File

@ -0,0 +1 @@

307
docs/API.md Normal file
View File

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

View File

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

252
docs/building.md Normal file
View File

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

85
docs/codespaces.md Normal file
View File

@ -0,0 +1,85 @@
# Codespaces
You can experiment with codespaces and g3log.
## Learn about Github Codespaces
For an introduction to codespaces you can check out [example c++ codespace](https://github.com/microsoft/vscode-remote-try-cpp/tree/main) and [using-github-codespaces-with-github-cli](https://docs.github.com/en/codespaces/developing-in-a-codespace/using-github-codespaces-with-github-cli)
# Commandline codespaces Quick Reference
1. List all your codespaces `gh codespace list`
2. Create a new codespace `gh codespace create -r OWNER/REPO_NAME [-b BRANCH]`. Ref [docs/github: Creating a codespace for a repository](https://docs.github.com/en/codespaces/developing-in-a-codespace/creating-a-codespace-for-a-repository)
3. View codebase details `gh codespace view`
4. Stop `gh codespace stop -c CODESPACE-NAME`
5. Delete `gh codespace delete -c CODESPACE-NAME`
6. Rebuild `gh codespace rebuild`
7. Rename `gh codespace edit -c CODESPACE-NAME -d DISPLAY-NAME`
8. SSH into REMOTE codespace `gh codespace ssh -c CODESPACE-NAME`
9. Open a remote codespace in CVisual Studio `gh codespace code -c CODESPACE-NAME` (ref: [github:doc cs studio](https://docs.github.com/en/codespaces/developing-in-a-codespace/using-github-codespaces-in-visual-studio-code))
10. Copy local file to/from codespace `gh codespace cp [-r] SOURCE(S) DESTINATION`. Example: Copy a file from the local machine to the $HOME directory of a codespace: `gh codespace cp myfile.txt remote:`. Example Copy a file from a codespace to the current directory on the local machine: `gh codespace cp remote:myfile.txt .` (more information available [here](https://cli.github.com/manual/gh_codespace_cp))
# Try g3log in a local dev container.
Please note that this will build g3log as if it's on a Debian Linux platform.
1. Clone this repository to your local filesystem.
2. Start Visual Studio Code. Press F1 and select the `Dev Containers: Open Folder in Container...` command.
3. Select the cloned copy of this g3log folder, wait for the container to start, and try things out! You should have debian C++ environment at hand.
### Example cmake configuration and build
```
Open a terminal in Visual Studio Code
mkdir debianbuild
cd debianbuild
cmake -DADD_G3LOG_UNIT_TEST=ON -DADD_G3LOG_BENCH_PERFORMANCE=ON ..
make -j
```
### Example runs
1. performance test in the container `./g3log-performance-threaded_mean 4`
2. unit tests `ctest -v`
3. Try a fatal example with dumped stack trace `./g3log-FATAL-contract`
### Example with Debugging.
Without any need to set up environment on your local machine you can also use Codespaces to debug examples, unit tests etc of g3log.
The pesky thing with VSCode, especially with cmake is to set up the launh.json.
It's a little bit easier if you open a VSCode terminal and do the cmake configuration and build there. Then the `launch.json` only needs to
contain information about the pecific executable.
Here we try out the `g3log-FATAL-contract` after cmake configure with `-DCMAKE_BUILD_TYPE=Debug`
```
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// Remember to build the specific part of cmake with
// "cmake -DCMAKE_BUILD_TYPE=Debug" if you want to be able to debug it.
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Start",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/g3log-FATAL-contract",
"MIMode": "gdb",
"cwd": "${workspaceFolder}/build"
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
```

15
docs/contributing.md Normal file
View File

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

BIN
docs/event_sequence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
docs/fatal_log_sequence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

221
docs/g3log_usage.md Normal file
View File

@ -0,0 +1,221 @@
[introduction](index.md) | [**detailed information**](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)
# How to use g3log
G3log is an asynchronous logger with dynamic sinks
## Example USAGE
#### Optional to use either streaming or printf-like syntax
```
LOG(INFO) << "streaming API is as easy as ABC or " << 123;
LOGF(WARNING, "Printf-style syntax is also %s", "available");
```
## <a name="what-g3log-is">What g3Log is</a>
* ***G3log*** is the acting name for the third version of g2log and it stands for **g3log with dynamic sinks**
* G3log is an asynchronous, "crash-safe" logger. You can read more about it here [[g2log version]](
http://www.codeproject.com/Articles/288827/g2log-An-efficient-asynchronous-logger-using-Cplus)
* You can choose to use the default log receiver which saves all LOG calls to file, **or** you can choose to use your own custom made log receiver(s), **or** both, **or** as many sinks as you need.
#### <a name="#conditional-logging">Conditional logging</a>
int less = 1; int more = 2
LOG_IF(INFO, (less<more)) <<"If [true], then this text will be logged";
// or with printf-like syntax
LOGF_IF(INFO, (less<more), "if %d<%d then this text will be logged", less,more);
#### <a name="design-by-contract">Design-by-Contract</a>
*CHECK(false)* will trigger a "fatal" message. It will be logged, and then the
application will exit.
```
CHECK(less != more); // not FATAL
CHECK(less > more) << "CHECK(false) triggers a FATAL message";
```
### Detailed API documentation
Please look at [API.md](API.md) for detailed API documentation
## <a name="benefits-with-g3log">Benefits you get when using g3log</a>
1. Easy to use, clean syntax and a blazing fast logger.
2. All the slow log I/O disk access is done in a background thread. This ensures that the LOG caller can immediately continue with other tasks and do not have to wait for the LOG call to finish.
3. G3log provides logging, Design-by-Contract [#CHECK], and flush of log to file at
shutdown. Buffered logs will be written to the sink before the application shuts down.
4. It is thread safe, so using it from multiple threads is completely fine.
5. It is *CRASH SAFE*. It will save the made logs to the sink before it shuts down.
The logger will catch certain fatal events *(Linux/OSX: signals, Windows: fatal OS exceptions and signals)* , so if your application crashes due to, say a segmentation fault, *SIGSEGV*, it will log and save the crash and all previously buffered log entries before exiting.
6. It is cross platform. Tested and used by me or by clients on OSX, Windows, Ubuntu, CentOS
7. G3log and G2log are used worldwide in commercial products as well as hobby projects. G2log was introduced in early 2011 and is now retired.
8. The code is given for free as public domain. This gives the option to change, use, and do whatever with it, no strings attached.
9. *[g3log](https://github.com/KjellKod/g3log)* : is made to facilitate easy adding of custom log receivers. Its tested on at least the following platforms with **Linux**(Clang/gcc), **Windows** (mingw, visual studio) and **OSX**. My recommendation is to go with g3log if you have full C++17 support
C++11 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.1).
C++14 support up to version: https://github.com/KjellKod/g3log/releases/tag/1.3.4
## <a name="g3log-with-sinks">G3log with sinks</a>
[Sinks](http://en.wikipedia.org/wiki/Sink_(computing)) are receivers of LOG calls. G3log comes with a default sink (*the same as g3log uses*) that can be used to save log to file. A sink can be of *any* class type without restrictions as long as it can either receive a LOG message as a *std::string* **or** as a *g3::LogMessageMover*.
The *std::string* comes pre-formatted. The *g3::LogMessageMover* is a wrapped struct that contains the raw data for custom handling in your own sink.
A sink is *owned* by the g3log and is added to the logger inside a ```std::unique_ptr```. The sink can be called though its public API through a *handler* which will asynchronously forward the call to the receiving sink.
It is <a name="crazy-simple">crazy simple to create a custom sink</a>. This example show what is needed to make a custom sink that is using custom log formatting but only using that
for adding color to the default log formatting. The sink forwards the colored log to cout
```cpp
// in file Customsink.hpp
#pragma once
#include <string>
#include <iostream>
#include <g3log/logmessage.hpp>
struct CustomSink {
// Linux xterm color
// http://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal
enum FG_Color {YELLOW = 33, RED = 31, GREEN=32, WHITE = 97};
FG_Color GetColor(const LEVELS level) const {
if (level.value == WARNING.value) { return YELLOW; }
if (level.value == DEBUG.value) { return GREEN; }
if (g3::internal::wasFatal(level)) { return RED; }
return WHITE;
}
void ReceiveLogMessage(g3::LogMessageMover logEntry) {
auto level = logEntry.get()._level;
auto color = GetColor(level);
std::cout << "\033[" << color << "m"
<< logEntry.get().toString() << "\033[m" << std::endl;
}
};
// in main.cpp, main() function
auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
```
## Adding and Removing Sinks
You can safely remove and add sinks during the running of your program.
**Keep in mind**
- *Initialization of the logger should happen before you have started any other threads that may call the logger.*
- *Destruction of the logger (RAII concept) should happen AFTER shutdown of other threads that are calling the logger.*
**Adding Sinks**
```cpp
auto sinkHandle1 = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
auto sinkHandle2 = logworker->addDefaultLogger(argv[0],
path_to_log_file);
logworker->removeSink(std::move(sinkHandle1)); // this will in a thread-safe manner remove the sinkHandle1
logworker->removeAllSinks(); // this will in a thread-safe manner remove any sinks.
```
**More sinks** can be found in the repository **[github.com/KjellKod/g3sinks](https://github.com/KjellKod/g3sinks)**.
## <a name="code-examples">Code Examples</a>
Example usage where a <a name="custom-sink">custom sink</a> is added. A function is called though the sink handler to the actual sink object.
```cpp
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
std::unique_ptr<LogWorker> logworker{ LogWorker::createLogWorker() };
auto sinkHandle = logworker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
// initialize the logger before it can receive LOG calls
initializeLogging(logworker.get());
LOG(WARNING) << "This log call, may or may not happend before"
<< "the sinkHandle->call below";
// You can call in a thread safe manner public functions on your sink
// The call is asynchronously executed on your custom sink.
std::future<void> received = sinkHandle->call(&CustomSink::Foo,
param1, param2);
// If the LogWorker is initialized then at scope exit the g3::internal::shutDownLogging() will be called.
// This is important since it protects from LOG calls from static or other entities that will go out of
// scope at a later time.
//
// It can also be called manually:
g3::internal::shutDownLogging();
}
// some_file.cpp : To show how easy it is to get the logger to work
// in other parts of your software
#include <g3log/g3log.hpp>
void SomeFunction() {
...
LOG(INFO) << "Hello World";
}
```
Example usage where a the <a name="default-file-logger">default file logger</a> is used **and** a custom sink is added
```cpp
// main.cpp
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>
#include "CustomSink.h"
int main(int argc, char**argv) {
using namespace g3;
auto worker = LogWorker::createLogWorker();
auto defaultHandler = worker->addDefaultLogger(argv[0],
path_to_log_file);
// logger is initialized
g3::initializeLogging(worker.get());
LOG(DEBUG) << "Make log call, then add another sink";
worker->addSink(std::make_unique<CustomSink>(),
&CustomSink::ReceiveLogMessage);
...
}
```
[introduction](index.md) | [**detailed information**](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)

132
docs/index.md Normal file
View File

@ -0,0 +1,132 @@
[**introduction**](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)
# Welcome to g3log
G3log is an asynchronous logger with three main features:
1. Intuitive `LOG(...)` API
2. `Design-by-contract` `CHECK(...)` functionality
3. Fatal crash handling for graceful shutdown of the logged process without loosing any log details up to the point of the crash
The super quick introduction to g3log can be seen in the steps 1 - 9 below.
For more in-depth information please see the full usage description in [g3log_usage.md](g3log_usage.md). The internal API for more advanced integration with g3log can be accessed in [API.md](API.md)
## 1. Easy usage in files
Avoid deep dependency injection complexity and instead get access to the logger as easy as
```
#include <g3log/g3log.hpp>
```
## 2. Access to streaming and print_f log call syntax
Both streaming syntax `LOG` and print_f `LOGF` syntax are available.
```
LOGF(INFO, "Hi log %d", 123);
LOG(INF) << "Hi log " << 123;
```
## 3. Conditional logging
```
LOG_IF(INFO, (1 < 2)) << "If true this message will be logged";
LOGF_IF(INFO, (1 < 2), "If true, then this %s will be logged", "message");
```
## 4. Design-by-contract framework
```
CHECK(less != more); // not fatal
CHECK_F(less > more, "CHECK(false) will trigger a fatal message")
```
## 5. Handling of fatal
By default g3log will capture fatal events such as `LOG(FATAL)`, `CHECK(false)` and otherwise fatal signals such as:
```
SIGABRT
SIGFPE
SIGILL
SIGSEGV
SIGTERM
```
When a fatal event happens the not-yet written log activity will be flushed to the logging sinks. Only when all logging activity up to the point of the fatal event has happend, will g3log allow the fatal event to proceed and exit the process.
If `object` symbols are available the fatal handler will attempt to push the stacktrace up to the fatal reason to the logging sink.
#### 5b. Overriding and customization of fatal event handling
For overriding fatal error handling to use your own, or to add code `hooks` that you want to execute please see the [API.md](API.md) doc.
## 6. Default and Custom logging levels
The default logging levels are `DEBUG`, `INFO`, `WARNING` and `FATAL`. You can define your own logging levels or completely replace the logging levels. Ref: [API.md](API.md)
### 7. Log filtering
Log filtering is handled in g3log if dynamic logging levels are enabled
in the configuration. See the [API.md](API.md) for information. Log filtering can also be handled through the sink as can be seen in [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks)
## 8. 3rd party and custom logging sinks
The default logging sink has no external 3rd party dependencies. For more logging sinks please see [github/Kjellod/g3sinks](https://github.com/KjellKod/g3sinks)
- log rotate
- log to syslog
- log to colored terminal output
- log rotate with filter
See the [API.md](API.md) for more information about the simple steps to creating your own logging sink.
## 9. Log instantiation
With the default application name left as is (i.e. "g3log") a creation of the logger could look something like this:
```cpp
const std::string directory = "./";
const std::string name = "TestLogFile";
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(name, directory);
```
The resulting filename would be something like:
```
./TestLogFile.g3log.20160217-001406.log
```
## <a name="performance">Performance</a>
G3log aims to keep all background logging to sinks with as little log overhead as possible to the logging sink and with as small "worst case latency" as possible. For this reason g3log is a good logger for many systems that deal with critical tasks. Depending on platform the average logging overhead will differ. On my 2010 laptop the average call, when doing extreme performance testing, will be about ~2 us.
The worst case latency is kept stable with no extreme peaks, in spite of any sudden extreme pressure. I have a blog post regarding comparing worst case latency for g3log and other loggers which might be of interest.
You can find it here: https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
## <a name="continuos_integration">Continuos Integration</a>
The g3log repository is evaluating both github actions and CircleCI for executing test coverage, installation and document generation. For windows the repo is still relying on appveyor. In case you want to look into change any of these setups the following files are the ones of interest.
```
1. appveyor --> g3log/appveyor.yml
2. circleCI --> g3log/.circleci/config.yml
3. github actions --> g3log/.github/workflows/*.yml
```
## <a name="feedback">Feedback</a>
If you like this logger (or not) it would be nice with some feedback. That way I can improve g3log and it is always nice to hear when and how someone is using it.
If you have ANY questions or problems please do not hesitate in contacting me at
`Hedstrom @ Kjellod. cc`
# <a name="say-thanks">Say Thanks</a>
This logger is available for free and all of its source code is public domain. A great way of saying thanks is to send a donation. It would go a long way not only to show your support but also to boost continued development.
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/g3log/25)
* $5 for a cup of coffee
* $25 for a late evening coding with takeout
Cheers
Kjell *(a.k.a. KjellKod)*
[**introduction**](index.md) | [detailed information](g3log_usage.md) | [Configure & Build](building.md) | [API description](API.md) | [Custom log formatting](API_custom_formatting.md)

BIN
docs/log_sequence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

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

View File

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

View File

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

View File

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

219
iOS.cmake Normal file
View File

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

121
iOSBuild.cmake Normal file
View File

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

17
mkdocs.yml Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ namespace g3 {
// check for filename validity - filename should not be part of PATH
bool isValidFilename(const std::string &prefix_filename) {
std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* ");
std::string illegal_characters("/,|<>:#$%{}[]\'\"^!?+* ");
size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
if (pos != std::string::npos) {
std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
@ -51,7 +51,7 @@ namespace g3 {
return prefix;
}
std::string pathSanityFix(std::string path, std::string file_name) {
std::string pathSanityFix(std::string path, const std::string &file_name) {
// Unify the delimeters,. maybe sketchy solution but it seems to work
// on at least win7 + ubuntu. All bets are off for older windows
std::replace(path.begin(), path.end(), '\\', '/');
@ -76,19 +76,23 @@ namespace g3 {
return path;
}
std::string header() {
std::string header(const std::string& headerFormat) {
std::ostringstream ss_entry;
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(g3::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss uuu* LEVEL FILE:LINE] message";
ss_entry << "\t\t(uuu*: microsecond counter since initialization of log worker)\n\n";
auto now = std::chrono::system_clock::now();
ss_entry << "\t\tg3log created log at: " << g3::localtime_formatted(now, "%a %b %d %H:%M:%S %Y") << "\n";
ss_entry << headerFormat;
return ss_entry.str();
}
std::string createLogFileName(const std::string &verified_prefix) {
std::string createLogFileName(const std::string &verified_prefix, const std::string &logger_id) {
std::stringstream oss_name;
oss_name << verified_prefix << ".g3log.";
oss_name << g3::localtime_formatted(g3::systemtime_now(), file_name_time_formatted);
oss_name << verified_prefix << ".";
if( !logger_id.empty() ) {
oss_name << logger_id << ".";
}
auto now = std::chrono::system_clock::now();
oss_name << g3::localtime_formatted(now, file_name_time_formatted);
oss_name << ".log";
return oss_name.str();
}
@ -113,7 +117,7 @@ namespace g3 {
std::ofstream &stream(*(out.get()));
bool success_with_open_file = openLogFile(file_with_full_path, stream);
if (false == success_with_open_file) {
out.release();
out.reset();
}
return out;
}

View File

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

View File

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

View File

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

47
src/g3log/atomicbool.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,45 +11,42 @@
* Ref: workarounds at http://connect.microsoft.com/VisualStudio/feedback/details/791185/std-packaged-task-t-where-t-is-void-or-a-reference-class-are-not-movable
* ============================================================================*/
#pragma once
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__) && (_MSC_VER <= 1800)
namespace std {
template<class... _ArgTypes>
class packaged_task<void(_ArgTypes...)>
{
template <class... _ArgTypes>
class packaged_task<void(_ArgTypes...)> {
promise<void> _my_promise;
function<void(_ArgTypes...)> _my_func;
public:
public:
packaged_task() {
}
template<class _Fty2>
explicit packaged_task(_Fty2 &&_Fnarg)
: _my_func(_Fnarg) {
template <class _Fty2>
explicit packaged_task(_Fty2&& _Fnarg) :
_my_func(_Fnarg) {
}
packaged_task(packaged_task &&_Other)
: _my_promise(move(_Other._my_promise)),
_my_func(move(_Other._my_func)) {
packaged_task(packaged_task&& _Other) :
_my_promise(move(_Other._my_promise)),
_my_func(move(_Other._my_func)) {
}
packaged_task &operator=(packaged_task && _Other) {
packaged_task& operator=(packaged_task&& _Other) {
_my_promise = move(_Other._my_promise);
_my_func = move(_Other._my_func);
return (*this);
}
packaged_task(const packaged_task &) = delete;
packaged_task &operator=(const packaged_task &) = delete;
packaged_task(const packaged_task&) = delete;
packaged_task& operator=(const packaged_task&) = delete;
~packaged_task() {
}
void swap(packaged_task &_Other) {
void swap(packaged_task& _Other) {
swap(_my_promise, _Other._my_promise);
swap(_my_func, _Other._my_func);
}
@ -77,5 +74,5 @@ namespace std {
}
};
}; // namespace std
#endif // defined(WIN32) ...
}; // namespace std
#endif // defined(WIN32) ...

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

349
test_unit/test_fatal.cpp Normal file
View File

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

View File

@ -2,19 +2,16 @@
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
*
*
* For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/
#include <gtest/gtest.h>
#include <memory>
#include <fstream>
#include <string>
#include <memory>
#include <future>
#include <memory>
#include <queue>
#include <string>
#include <thread>
#include "g3log/g3log.hpp"
@ -23,114 +20,157 @@
using namespace testing_helpers;
namespace { // anonymous
const char* name_path_1 = "./(some_fake_DirectoryOrName_1_)";
const std::string kReplaceFileName = "(ReplaceLogFile)";
g3::LogWorker* g_logger_ptr = nullptr;
g3::SinkHandle<g3::FileSink>* g_filesink_handler = nullptr;
LogFileCleaner* g_cleaner_ptr = nullptr;
namespace { // anonymous
const char* name_path_1 = "./some_fake_DirectoryOrName_1_";
g3::LogWorker* g_logger_ptr = nullptr;
g3::SinkHandle<g3::FileSink>* g_filesink_handler = nullptr;
LogFileCleaner* g_cleaner_ptr = nullptr;
std::string setLogNameAndAddCount(std::string new_file_to_create, std::string logger_id = "g3log") {
static std::mutex m;
static int count;
std::string add_count;
std::lock_guard<std::mutex> lock(m);
{
add_count = std::to_string(++count) + "_";
auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create + add_count, logger_id);
auto new_log = future_new_log.get();
if (!new_log.empty()) {
g_cleaner_ptr->addLogToClean(new_log);
} else {
std::cout << "\nFailed to set filename: " << new_file_to_create << std::endl;
}
return new_log;
}
return add_count;
}
std::string setLogNameAndAddCount(std::string new_file_to_create) {
static std::mutex m;
static int count;
std::string add_count;
std::lock_guard<std::mutex> lock(m);
{
add_count = std::to_string(++count) + "_";
auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create + add_count);
std::string setLogName(std::string new_file_to_create, std::string logger_id = "g3log") {
auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create, logger_id);
auto new_log = future_new_log.get();
if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
if (!new_log.empty())
g_cleaner_ptr->addLogToClean(new_log);
return new_log;
}
return add_count;
}
}
std::string setLogName(std::string new_file_to_create) {
auto future_new_log = g_filesink_handler->call(&g3::FileSink::changeLogFile, new_file_to_create);
auto new_log = future_new_log.get();
if (!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
return new_log;
}
std::string getLogName() {
return g_filesink_handler->call(&g3::FileSink::fileName).get();
}
std::string getLogName() {
return g_filesink_handler->call(&g3::FileSink::fileName).get();
}
} // anonymous
} // namespace
TEST(TestOf_GetFileName, Expecting_ValidLogFile) {
LOG(INFO) << "test_filechange, Retrieving file name: ";
ASSERT_NE(g_logger_ptr, nullptr);
ASSERT_FALSE(getLogName().empty());
LOG(INFO) << "test_filechange, Retrieving file name: ";
ASSERT_NE(g_logger_ptr, nullptr);
ASSERT_FALSE(getLogName().empty());
}
TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed) {
auto old_log = getLogName();
std::string name = setLogNameAndAddCount(name_path_1);
auto new_log = setLogName(name);
ASSERT_NE(old_log, new_log);
auto old_log = getLogName();
std::string name = setLogNameAndAddCount(name_path_1);
auto new_log = setLogName(name);
ASSERT_NE(old_log, new_log);
}
TEST(TestOf_ChangingLogFile_Id, Expecting_NewLogFileUsed1) {
auto old_log = getLogName();
setLogNameAndAddCount(name_path_1);
auto new_log = setLogName("foo", "new_logger_id");
ASSERT_NE(old_log, new_log);
std::string new_name = getLogName();
auto expected_part_of__new_name = std::string("foo") + kReplaceFileName + ".new_logger_id";
auto extracted_name = new_name.substr(0, expected_part_of__new_name.size());
ASSERT_EQ(extracted_name.c_str(), expected_part_of__new_name);
}
TEST(TestOf_ChangingLogFile_NoId, Expecting_NewLogFileUsed2) {
auto old_log = getLogName();
setLogNameAndAddCount(name_path_1);
auto new_log = setLogName("foo", "");
ASSERT_NE(old_log, new_log);
std::string new_name = getLogName();
auto expected_part_of__new_name = std::string("foo") + kReplaceFileName;
auto extracted_name = new_name.substr(0, expected_part_of__new_name.size());
ASSERT_EQ(extracted_name.c_str(), expected_part_of__new_name);
}
TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated) {
auto old_log = g_filesink_handler->call(&g3::FileSink::fileName).get();
if (!old_log.empty()) g_cleaner_ptr->addLogToClean(old_log);
auto old_log = g_filesink_handler->call(&g3::FileSink::fileName).get();
if (!old_log.empty())
g_cleaner_ptr->addLogToClean(old_log);
LOG(INFO) << "SoManyThreadsAllDoingChangeFileName";
std::vector<std::thread> threads;
auto max = 2;
auto size = g_cleaner_ptr->size();
for (auto count = 0; count < max; ++count) {
std::string drive = ((count % 2) == 0) ? "./_threadEven_" : "./_threaOdd_";
threads.push_back(std::thread(setLogNameAndAddCount, drive));
}
for (auto& thread : threads)
thread.join();
LOG(INFO) << "SoManyThreadsAllDoingChangeFileName";
std::vector<std::thread> threads;
auto max = 2;
auto size = g_cleaner_ptr->size();
for (auto count = 0; count < max; ++count) {
std::string drive = ((count % 2) == 0) ? "./_threadEven_" : "./_threaOdd_";
std::string logger_id = std::to_string(count);
threads.push_back(std::thread(setLogNameAndAddCount, drive, logger_id));
}
for (auto& thread : threads)
thread.join();
// check that all logs were created
ASSERT_EQ(size + max, g_cleaner_ptr->size());
// check that all logs were created
ASSERT_EQ(size + max, g_cleaner_ptr->size());
}
TEST(TestOf_IllegalLogFileName, Expecting_NoChangeToOriginalFileName) {
std::string original = getLogName();
auto perhaps_a_name = setLogName("XY:/"); // does not exist
ASSERT_TRUE(perhaps_a_name.empty());
std::string post_illegal = getLogName();
ASSERT_STREQ(original.c_str(), post_illegal.c_str());
std::string original = getLogName();
auto perhaps_a_name = setLogName("XY:/"); // does not exist
ASSERT_TRUE(perhaps_a_name.empty());
std::string post_illegal = getLogName();
ASSERT_STREQ(original.c_str(), post_illegal.c_str());
}
int main(int argc, char *argv[]) {
LogFileCleaner cleaner;
g_cleaner_ptr = &cleaner;
int return_value = 1;
std::stringstream cerrDump;
std::string last_log_file;
{
testing_helpers::ScopedOut scopedCerr(std::cerr, &cerrDump);
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger("ReplaceLogFile", name_path_1);
g_logger_ptr = worker.get();
g_filesink_handler = handle.get();
last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get();
cleaner.addLogToClean(last_log_file);
g3::initializeLogging(g_logger_ptr);
LOG(INFO) << "test_filechange demo*" << std::endl;
testing::InitGoogleTest(&argc, argv);
return_value = RUN_ALL_TESTS();
last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get();
std::cout << "log file at: " << last_log_file << std::endl;
//g3::shutDownLogging();
}
std::cout << "FINISHED WITH THE TESTING" << std::endl;
// cleaning up
cleaner.addLogToClean(last_log_file);
return return_value;
TEST(TestOf_SinkHandleDifferentId, Expecting_DifferentId) {
auto sink = std::make_unique<g3::FileSink>("AnotherLogFile", name_path_1, "logger_id");
auto name = sink->fileName();
ASSERT_STREQ(name.substr(0, 26).c_str(), "./AnotherLogFile.logger_id");
g_cleaner_ptr->addLogToClean(name);
}
TEST(TestOf_LegalLogFileNam, With_parenthesis) {
std::string original = getLogName();
auto perhaps_a_name = setLogName("(test)"); // does not exist
EXPECT_NE(original, perhaps_a_name);
std::string post_legal = getLogName();
EXPECT_TRUE(std::string::npos != post_legal.find("(test)")) << "filename was: " << post_legal;
}
int main(int argc, char* argv[]) {
LogFileCleaner cleaner;
g_cleaner_ptr = &cleaner;
int return_value = 1;
std::stringstream cerrDump;
std::string last_log_file;
{
testing_helpers::ScopedOut scopedCerr(std::cerr, &cerrDump);
auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addDefaultLogger(kReplaceFileName, name_path_1);
g_logger_ptr = worker.get();
g_filesink_handler = handle.get();
last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get();
std::cout << "log file at: " << last_log_file << std::endl;
cleaner.addLogToClean(last_log_file);
g3::initializeLogging(g_logger_ptr);
LOG(INFO) << "test_filechange demo*" << std::endl;
testing::InitGoogleTest(&argc, argv);
return_value = RUN_ALL_TESTS();
last_log_file = g_filesink_handler->call(&g3::FileSink::fileName).get();
std::cout << "log file at: " << last_log_file << std::endl;
//g3::internal::shutDownLogging();
}
std::cout << "FINISHED WITH THE TESTING" << std::endl;
// cleaning up
cleaner.addLogToClean(last_log_file);
return return_value;
}

View File

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

View File

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

584
test_unit/test_message.cpp Normal file
View File

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

62
test_unit/test_signal.cpp Normal file
View File

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

View File

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

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