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.
This commit is contained in:
Kjell Hedström - seeking Senior Engineering roles as well as contract opportunities 2023-11-30 16:17:45 -07:00 committed by GitHub
parent 0708f5aeb4
commit cf91227966
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1245 additions and 1521 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

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

@ -0,0 +1,4 @@
{
"cmake.configureOnOpen": false,
"editor.formatOnSave": true
}

View File

@ -3,16 +3,18 @@
`ADD CONTENT HERE TO DESCRIBE THE PURPOSE OF THE PULL REQUEST` `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_
# Testing # Testing
- [ ] This new/modified code was covered by unit tests. - [ ] This new/modified code was covered by unit tests.
- [ ] (insight) Was all tests written using TDD (Test Driven Development) style? - [ ] (insight) Was all tests written using TDD (Test Driven Development) style?
- [ ] The CI (Windows, Linux, OSX) are working without issues. - [ ] The CI (Windows, Linux, OSX) are working without issues.
- [ ] Was new functionality documented? - [ ] Was new functionality documented?
- [ ] The testing steps 1 - 2 below were followed - [ ] The testing steps 1 - 2 below were followed
_step 1_ _step 1_

View File

@ -213,7 +213,7 @@ will install the g3log library to `CPACK_PACKAGING_INSTALL_PREFIX`.
## <a name="testing">Testing</a> ## <a name="testing">Testing</a>
By default, tests will not be built. To enable unit testing, you should turn on `ADD_G3LOG_UNIT_TEST`. 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: Suppose the build process has completed, then you can run the tests with:
``` ```

View File

@ -9,34 +9,29 @@
#include <g3log/g3log.hpp> #include <g3log/g3log.hpp>
#include <g3log/logworker.hpp> #include <g3log/logworker.hpp>
#include <iomanip> #include <iomanip>
#include <thread>
#include <iostream> #include <iostream>
#include <thread>
namespace {
namespace
{
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string path_to_log_file = "./"; const std::string path_to_log_file = "./";
#else #else
const std::string path_to_log_file = "/tmp/"; const std::string path_to_log_file = "/tmp/";
#endif #endif
} } // namespace
namespace example_fatal namespace example_fatal {
{ void killWithContractIfNonEqual(int first, int second) {
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"; 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; double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f; float pi_f = 3.1415926535897932384626433832795f;
auto worker = g3::LogWorker::createLogWorker(); 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::initializeLogging(worker.get());
std::future<std::string> log_file_name = handle->call(&g3::FileSink::fileName); std::future<std::string> log_file_name = handle->call(&g3::FileSink::fileName);
@ -49,11 +44,11 @@ int main(int argc, char **argv)
changeFormatting.wait(); changeFormatting.wait();
changeHeader.wait(); changeHeader.wait();
std::cout << "* This is an example of g3log. It WILL exit by a failed CHECK(...)" << std::endl; 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 << "* 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 << "* 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); LOGF(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO"; LOG(INFO) << "Test SLOG INFO";
@ -72,4 +67,3 @@ int main(int argc, char **argv)
int larger = 2; int larger = 2;
example_fatal::killWithContractIfNonEqual(smaller, larger); example_fatal::killWithContractIfNonEqual(smaller, larger);
} }

View File

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

View File

@ -10,62 +10,54 @@
#include <g3log/logworker.hpp> #include <g3log/logworker.hpp>
#include <iomanip> #include <iomanip>
#include <thread>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
namespace #include <thread>
{ namespace {
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string path_to_log_file = "./"; const std::string path_to_log_file = "./";
#else #else
const std::string path_to_log_file = "/tmp/"; const std::string path_to_log_file = "/tmp/";
#endif #endif
} } // namespace
namespace example_fatal namespace example_fatal {
{
// on Ubunti this caused get a compiler warning with gcc4.6 // on Ubunti this caused get a compiler warning with gcc4.6
// from gcc 4.7.2 (at least) it causes a crash (as expected) // from gcc 4.7.2 (at least) it causes a crash (as expected)
// On windows it'll probably crash too. // On windows it'll probably crash too.
void tryToKillWithIllegalPrintout() void tryToKillWithIllegalPrintout() {
{ std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush; << std::flush;
std::cout << "************************************************************\n\n" << std::endl << std::flush; std::cout << "************************************************************\n\n"
<< std::endl
<< std::flush;
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
const std::string logging = "logging"; const std::string logging = "logging";
LOGF(G3LOG_DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str()); LOGF(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 SIGSEGV
// fault as expected by the illegal printf-format usage. just in case we exit by zero division" // fault as expected by the illegal printf-format usage. just in case we exit by zero division"
void killByZeroDivision(int value) void killByZeroDivision(int value) {
{ int zero = 0; // trying to fool the compiler to automatically warn
int zero = 0; // trying to fool the compiler to automatically warn
LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero; LOG(INFO) << "This is a bad operation [value/zero] : " << value / zero;
} }
void tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::string> badStringPtr) { void tryToKillWithAccessingIllegalPointer(std::unique_ptr<std::string> badStringPtr) {
auto badPtr = std::move(badStringPtr); auto badPtr = std::move(badStringPtr);
LOG(INFO) << "Function calls through a nullptr object will trigger SIGSEGV"; LOG(INFO) << "Function calls through a nullptr object will trigger SIGSEGV";
badStringPtr->append("crashing"); badStringPtr->append("crashing");
} }
} // namespace example_fatal
} // example fatal int main(int argc, char** argv) {
int main(int argc, char **argv)
{
double pi_d = 3.1415926535897932384626433832795; double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f; float pi_f = 3.1415926535897932384626433832795f;
using namespace g3; using namespace g3;
std::unique_ptr<LogWorker> logworker{LogWorker::createLogWorker()};
std::unique_ptr<LogWorker> logworker {LogWorker::createLogWorker()};
auto sinkHandle = logworker->addSink(std::make_unique<FileSink>(argv[0], path_to_log_file), auto sinkHandle = logworker->addSink(std::make_unique<FileSink>(argv[0], path_to_log_file),
&FileSink::fileWrite); &FileSink::fileWrite);
@ -74,8 +66,8 @@ 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 << "* 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 << "* Please see the generated log and compare to the code at" << std::endl;
std::cout << "* g3log/test_example/main.cpp" << 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); LOGF(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO"; LOG(INFO) << "Test SLOG INFO";
@ -111,8 +103,8 @@ int main(int argc, char **argv)
std::unique_ptr<std::string> badStringPtr; std::unique_ptr<std::string> badStringPtr;
example_fatal::tryToKillWithAccessingIllegalPointer(std::move(badStringPtr)); example_fatal::tryToKillWithAccessingIllegalPointer(std::move(badStringPtr));
// what happened? OK. let us just exit with SIGFPE // what happened? OK. let us just exit with SIGFPE
int value = 1; // system dependent but it SHOULD never reach this line int value = 1; // system dependent but it SHOULD never reach this line
example_fatal::killByZeroDivision(value); example_fatal::killByZeroDivision(value);
return 0; return 0;
} }

View File

@ -7,27 +7,26 @@
* ============================================================================*/ * ============================================================================*/
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/logcapture.hpp" #include "g3log/logcapture.hpp"
#include "g3log/loglevels.hpp" #include "g3log/loglevels.hpp"
#include "g3log/logmessage.hpp"
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "crashhandler_unix.cpp used but it's a windows system" #error "crashhandler_unix.cpp used but it's a windows system"
#endif #endif
#include <csignal>
#include <cstring>
#include <unistd.h>
#include <execinfo.h>
#include <cxxabi.h> #include <cxxabi.h>
#include <cstdlib> #include <execinfo.h>
#include <sstream> #include <unistd.h>
#include <iostream>
#include <thread>
#include <atomic> #include <atomic>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <sstream>
#include <thread>
// Linux/Clang, OSX/Clang, OSX/gcc // Linux/Clang, OSX/Clang, OSX/gcc
#if (defined(__clang__) || defined(__APPLE__)) #if (defined(__clang__) || defined(__APPLE__))
@ -36,10 +35,9 @@
#include <ucontext.h> #include <ucontext.h>
#endif #endif
namespace { namespace {
std::atomic<bool> gBlockForFatal {true}; std::atomic<bool> gBlockForFatal{true};
const std::map<int, std::string> kSignals = { const std::map<int, std::string> kSignals = {
{SIGABRT, "SIGABRT"}, {SIGABRT, "SIGABRT"},
@ -62,9 +60,9 @@ namespace {
// ALL thanks to this thread at StackOverflow. Pretty much borrowed from: // 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 // 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; using namespace g3::internal;
// Only one signal will be allowed past this point // Only one signal will be allowed past this point
if (false == shouldDoExit()) { if (false == shouldDoExit()) {
while (shouldBlockForFatalHandling()) { while (shouldBlockForFatalHandling()) {
@ -81,7 +79,7 @@ namespace {
fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl; fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl;
LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str()); LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
trigger.stream() << fatal_stream.str(); trigger.stream() << fatal_stream.str();
} // message sent to g3LogWorker } // message sent to g3LogWorker
// wait to die // wait to die
} }
@ -92,17 +90,17 @@ namespace {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) #if !(defined(DISABLE_FATAL_SIGNALHANDLING))
struct sigaction action; struct sigaction action;
sigemptyset(&action.sa_mask); sigemptyset(&action.sa_mask);
action.sa_sigaction = &signalHandler; // callback to crashHandler for fatal signals action.sa_sigaction = &signalHandler; // callback to crashHandler for fatal signals
// sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction // sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction
action.sa_flags = SA_SIGINFO; action.sa_flags = SA_SIGINFO;
// do it verbose style - install all signal actions // do it verbose style - install all signal actions
for (const auto& sig_pair : gSignals) { for (const auto& sig_pair : gSignals) {
struct sigaction old_action; struct sigaction old_action;
memset(&old_action, 0, sizeof (old_action)); memset(&old_action, 0, sizeof(old_action));
if (sigaction(sig_pair.first, &action, &old_action) < 0) { if (sigaction(sig_pair.first, &action, &old_action) < 0) {
std::string signalerror = "sigaction - " + sig_pair.second; std::string signalerror = "sigaction - " + sig_pair.second;
perror(signalerror.c_str()); perror(signalerror.c_str());
} else { } else {
gSavedSigActions[sig_pair.first] = old_action; gSavedSigActions[sig_pair.first] = old_action;
@ -111,14 +109,7 @@ namespace {
#endif #endif
} }
} // end anonymous namespace
} // end anonymous namespace
// Redirecting and using signals. In case of fatal signals g3log should log the fatal signal // 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 // and flush the log queue and then "rethrow" the signal to exit
@ -149,7 +140,7 @@ namespace g3 {
const size_t max_dump_size = 50; const size_t max_dump_size = 50;
void* dump[max_dump_size]; void* dump[max_dump_size];
const size_t size = backtrace(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 char** messages = backtrace_symbols(dump, static_cast<int>(size)); // overwrite sigaction with caller's address
// dump stack: skip first frame, since that is here // dump stack: skip first frame, since that is here
std::ostringstream oss; std::ostringstream oss;
@ -160,26 +151,21 @@ namespace g3 {
/// first look for format that includes brackets "(mangled_name+offset)"" /// first look for format that includes brackets "(mangled_name+offset)""
const auto firstBracket = strMessage.find_last_of('('); const auto firstBracket = strMessage.find_last_of('(');
const auto secondBracket = strMessage.find_last_of(')'); const auto secondBracket = strMessage.find_last_of(')');
if (firstBracket != strMessage.npos && secondBracket != strMessage.npos) if (firstBracket != strMessage.npos && secondBracket != strMessage.npos) {
{ const auto betweenBrackets = strMessage.substr(firstBracket + 1, secondBracket - firstBracket - 1);
const auto betweenBrackets = strMessage.substr(firstBracket + 1, secondBracket - firstBracket - 1); const auto plusSign = betweenBrackets.find_first_of('+');
const auto plusSign = betweenBrackets.find_first_of('+'); if (plusSign != betweenBrackets.npos) {
if (plusSign != betweenBrackets.npos) mangled_name = betweenBrackets.substr(0, plusSign);
{ offset = betweenBrackets.substr(plusSign + 1, 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('+');
else const auto lastUnderscore = strMessage.rfind(" _");
{ if (plusSign != strMessage.npos && lastUnderscore != strMessage.npos) {
/// we did not found brackets, looking for "_mangled_name + offset" mangled_name = strMessage.substr(lastUnderscore + 1, plusSign - lastUnderscore - 2);
const auto plusSign = strMessage.find_first_of('+'); offset = strMessage.substr(plusSign + 2, strMessage.npos);
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 the line could be processed, attempt to demangle the symbol
@ -188,71 +174,72 @@ namespace g3 {
char* real_name = abi::__cxa_demangle(mangled_name.c_str(), 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 demangling is successful, output the demangled function name
if (status == 0) { if (status == 0) {
oss << "\tstack dump [" << idx << "] " << real_name << " + " << offset<< std::endl; oss << "\tstack dump [" << idx << "] " << real_name << " + " << offset << std::endl;
}// otherwise, output the mangled function name } // otherwise, output the mangled function name
else { else {
oss << "\tstack dump [" << idx << "] " << mangled_name << " + " << offset<< 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 { } else {
// no demangling done -- just dump the whole line // no demangling done -- just dump the whole line
oss << "\tstack dump [" << idx << "] " << strMessage << 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); free(messages);
return oss.str(); return oss.str();
} }
/// string representation of signal ID /// 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); int signal_number = static_cast<int>(fatal_id);
switch (signal_number) { switch (signal_number) {
case SIGABRT: return "SIGABRT"; case SIGABRT:
break; return "SIGABRT";
case SIGFPE: return "SIGFPE"; break;
break; case SIGFPE:
case SIGSEGV: return "SIGSEGV"; return "SIGFPE";
break; break;
case SIGILL: return "SIGILL"; case SIGSEGV:
break; return "SIGSEGV";
case SIGTERM: return "SIGTERM"; break;
break; case SIGILL:
default: return "SIGILL";
std::ostringstream oss; break;
oss << "UNKNOWN SIGNAL(" << signal_number << ") for " << level.text; case SIGTERM:
return oss.str(); return "SIGTERM";
break;
default:
std::ostringstream oss;
oss << "UNKNOWN SIGNAL(" << signal_number << ") for " << level.text;
return oss.str();
} }
} }
// Triggered by g3log->g3LogWorker after receiving a FATAL trigger // Triggered by g3log->g3LogWorker after receiving a FATAL trigger
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught. // 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 // --- 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); const int signal_number = static_cast<int>(fatal_signal_id);
// Restore all saved signal handlers. If handling a signal which causes exiting // Restore all saved signal handlers. If handling a signal which causes exiting
// than let the original signal handlers to handle other signals. // than let the original signal handlers to handle other signals.
for (const auto& sig : gSignals) { for (const auto& sig : gSignals) {
restoreSignalHandler(sig.first); restoreSignalHandler(sig.first);
} }
std::cerr << "\n\n" << __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n" << std::flush;
std::cerr << "\n\n"
<< __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n"
<< std::flush;
raise(signal_number); raise(signal_number);
// When running as PID1 the above kill doesn't have any effect (execution simply passes through it, contrary // 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 // 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. // we must unblock the thread that received the original signal otherwise the process will never terminate.
gBlockForFatal = false; gBlockForFatal = false;
exit(signal_number); exit(signal_number);
} }
// restores the signal handler back to default // restores the signal handler back to default
@ -262,10 +249,7 @@ namespace g3 {
#endif #endif
} }
} // namespace internal
} // end g3::internal
std::string signalToStr(int signal_number) { std::string signalToStr(int signal_number) {
std::string signal_name; std::string signal_name;
@ -281,7 +265,6 @@ namespace g3 {
return signal_name; return signal_name;
} }
void restoreSignalHandler(int signal_number) { void restoreSignalHandler(int signal_number) {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) #if !(defined(DISABLE_FATAL_SIGNALHANDLING))
auto old_action_it = gSavedSigActions.find(signal_number); auto old_action_it = gSavedSigActions.find(signal_number);
@ -298,7 +281,6 @@ namespace g3 {
#endif #endif
} }
// This will override the default signal handler setup and instead // This will override the default signal handler setup and instead
// install a custom set of signals to handle // install a custom set of signals to handle
void overrideSetupSignals(const std::map<int, std::string> overrideSignals) { void overrideSetupSignals(const std::map<int, std::string> overrideSignals) {
@ -309,14 +291,13 @@ namespace g3 {
} }
gSignals = overrideSignals; gSignals = overrideSignals;
installCrashHandler(); // installs all the signal handling for gSignals installCrashHandler(); // installs all the signal handling for gSignals
} }
// installs the signal handling for whatever signal set that is currently active // installs the signal handling for whatever signal set that is currently active
// If you want to setup your own signal handling then // If you want to setup your own signal handling then
// You should instead call overrideSetupSignals() // You should instead call overrideSetupSignals()
void installCrashHandler() { void installCrashHandler() {
installSignalHandler(); installSignalHandler();
} }
} // end namespace g3 } // end namespace g3

View File

@ -10,20 +10,20 @@
#error "crashhandler_windows.cpp used but not on a windows system" #error "crashhandler_windows.cpp used but not on a windows system"
#endif #endif
#include <process.h> // getpid
#include <windows.h> #include <windows.h>
#include <atomic>
#include <csignal> #include <csignal>
#include <sstream> #include <sstream>
#include <atomic>
#include <process.h> // getpid
#include "g3log/g3log.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/stacktrace_windows.hpp" #include "g3log/g3log.hpp"
#include "g3log/logcapture.hpp" #include "g3log/logcapture.hpp"
#include "g3log/stacktrace_windows.hpp"
#define getpid _getpid #define getpid _getpid
namespace { namespace {
std::atomic<bool> gBlockForFatal {true}; std::atomic<bool> gBlockForFatal{true};
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr; LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) #if !(defined(DISABLE_FATAL_SIGNALHANDLING))
@ -34,8 +34,6 @@ namespace {
void* g_vector_exception_handler = nullptr; void* g_vector_exception_handler = nullptr;
#endif #endif
// called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM // called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
void signalHandler(int signal_number) { void signalHandler(int signal_number) {
using namespace g3::internal; using namespace g3::internal;
@ -45,7 +43,6 @@ namespace {
fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number); fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl; fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str()); LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
trigger.stream() << fatal_stream.str(); trigger.stream() << fatal_stream.str();
@ -59,9 +56,7 @@ namespace {
#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL)) #if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
__debugbreak(); __debugbreak();
#endif #endif
} // scope exit - message sent to LogWorker, wait to die... } // scope exit - message sent to LogWorker, wait to die...
// Unhandled exception catching // Unhandled exception catching
LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) { LONG WINAPI exceptionHandling(EXCEPTION_POINTERS* info, const std::string& handler) {
@ -84,14 +79,12 @@ namespace {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
// Unhandled exception catching // Unhandled exception catching
LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) { LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS* info) {
g3::internal::restoreFatalHandlingToDefault(); g3::internal::restoreFatalHandlingToDefault();
return exceptionHandling(info, "Unexpected Exception Handler"); return exceptionHandling(info, "Unexpected Exception Handler");
} }
/// Setup through (Windows API) AddVectoredExceptionHandler /// Setup through (Windows API) AddVectoredExceptionHandler
/// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx /// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) #if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
@ -108,8 +101,7 @@ namespace {
} }
} }
#endif #endif
} // end anonymous namespace } // end anonymous namespace
namespace g3 { namespace g3 {
namespace internal { namespace internal {
@ -119,7 +111,6 @@ namespace g3 {
return gBlockForFatal; return gBlockForFatal;
} }
/// Generate stackdump. Or in case a stackdump was pre-generated and /// 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 /// non-empty just use that one. i.e. the latter case is only for
/// Windows and test purposes /// Windows and test purposes
@ -131,8 +122,6 @@ namespace g3 {
return stacktrace::stackdump(); return stacktrace::stackdump();
} }
/// string representation of signal ID or Windows exception id /// 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) { if (level == g3::internal::FATAL_EXCEPTION) {
@ -140,19 +129,28 @@ namespace g3 {
} }
switch (fatal_id) { switch (fatal_id) {
case SIGABRT: return "SIGABRT"; break; case SIGABRT:
case SIGFPE: return "SIGFPE"; break; return "SIGABRT";
case SIGSEGV: return "SIGSEGV"; break; break;
case SIGILL: return "SIGILL"; break; case SIGFPE:
case SIGTERM: return "SIGTERM"; break; return "SIGFPE";
default: break;
std::ostringstream oss; case SIGSEGV:
oss << "UNKNOWN SIGNAL(" << fatal_id << ")"; return "SIGSEGV";
return oss.str(); break;
case SIGILL:
return "SIGILL";
break;
case SIGTERM:
return "SIGTERM";
break;
default:
std::ostringstream oss;
oss << "UNKNOWN SIGNAL(" << fatal_id << ")";
return oss.str();
} }
} }
// Triggered by g3log::LogWorker after receiving a FATAL trigger // Triggered by g3log::LogWorker after receiving a FATAL trigger
// which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught. // 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 // --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
@ -173,17 +171,15 @@ namespace g3 {
raise(signal_number); raise(signal_number);
} }
// Restore back to default fatal event handling // Restore back to default fatal event handling
void restoreFatalHandlingToDefault() { void restoreFatalHandlingToDefault() {
#if !(defined(DISABLE_FATAL_SIGNALHANDLING)) #if !(defined(DISABLE_FATAL_SIGNALHANDLING))
SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler); SetUnhandledExceptionFilter(g_previous_unexpected_exception_handler);
#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING)) #if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
RemoveVectoredExceptionHandler (g_vector_exception_handler); RemoveVectoredExceptionHandler(g_vector_exception_handler);
#endif #endif
if (SIG_ERR == signal(SIGABRT, SIG_DFL)) if (SIG_ERR == signal(SIGABRT, SIG_DFL))
perror("signal - SIGABRT"); perror("signal - SIGABRT");
@ -201,14 +197,11 @@ namespace g3 {
#endif #endif
} }
void installSignalHandler() { void installSignalHandler() {
g3::installSignalHandlerForThread(); g3::installSignalHandlerForThread();
} }
} // namespace internal
} // end g3::internal
/// 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 /// on Windows. This is automatically done if you do at least one LOG(...) call
@ -245,4 +238,4 @@ namespace g3 {
#endif #endif
} }
} // end namespace g3 } // end namespace g3

View File

@ -7,23 +7,22 @@
* ============================================================================*/ * ============================================================================*/
#include "g3log/filesink.hpp" #include "g3log/filesink.hpp"
#include "filesinkhelper.ipp"
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include "filesinkhelper.ipp"
namespace g3 { namespace g3 {
using namespace internal; using namespace internal;
FileSink::FileSink(const std::string &log_prefix, const std::string &log_directory, const std::string& logger_id, size_t write_to_log_every_x_message) 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_details_func(&LogMessage::DefaultLogDetailsToString),
,_log_file_with_path(log_directory) _log_file_with_path(log_directory),
, _log_prefix_backup(log_prefix) _log_prefix_backup(log_prefix),
, _outptr(new std::ofstream) _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") _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) _firstEntry(true),
, _write_counter(0) _write_counter(0),
, _write_to_log_every_x_message(write_to_log_every_x_message) _write_to_log_every_x_message(write_to_log_every_x_message) {
{
_log_prefix_backup = prefixSanityFix(log_prefix); _log_prefix_backup = prefixSanityFix(log_prefix);
if (!isValidFilename(_log_prefix_backup)) { if (!isValidFilename(_log_prefix_backup)) {
std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl; std::cerr << "g3log: forced abort due to illegal log prefix [" << log_prefix << "]" << std::endl;
@ -42,7 +41,6 @@ namespace g3 {
assert(_outptr && "cannot open log file at startup"); assert(_outptr && "cannot open log file at startup");
} }
FileSink::~FileSink() { FileSink::~FileSink() {
std::string exit_msg = {"g3log g3FileSink shutdown at: "}; std::string exit_msg = {"g3log g3FileSink shutdown at: "};
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
@ -57,12 +55,12 @@ namespace g3 {
// The actual log receiving function // The actual log receiving function
void FileSink::fileWrite(LogMessageMover message) { void FileSink::fileWrite(LogMessageMover message) {
if (_firstEntry ) { if (_firstEntry) {
addLogFileHeader(); addLogFileHeader();
_firstEntry = false; _firstEntry = false;
} }
auto data = message.get().toString(_log_details_func); auto data = message.get().toString(_log_details_func);
_write_buffer.append(data); _write_buffer.append(data);
if (++_write_counter % _write_to_log_every_x_message == 0) { if (++_write_counter % _write_to_log_every_x_message == 0) {
@ -71,7 +69,7 @@ namespace g3 {
} }
} }
std::string FileSink::changeLogFile(const std::string &directory, const std::string &logger_id) { std::string FileSink::changeLogFile(const std::string& directory, const std::string& logger_id) {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted}); auto now_formatted = g3::localtime_formatted(now, {internal::date_formatted + " " + internal::time_formatted});
@ -80,8 +78,9 @@ namespace g3 {
std::string prospect_log = directory + file_name; std::string prospect_log = directory + file_name;
std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log); std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
if (nullptr == log_stream) { if (nullptr == log_stream) {
filestream() << "\n" << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log; filestream() << "\n"
return {}; // no success << now_formatted << " Unable to change log file. Illegal filename or busy? Unsuccessful log name was: " << prospect_log;
return {}; // no success
} }
addLogFileHeader(); addLogFileHeader();
@ -115,4 +114,4 @@ namespace g3 {
void FileSink::addLogFileHeader() { void FileSink::addLogFileHeader() {
filestream() << header(_header); filestream() << header(_header);
} }
} // g3 } // namespace g3

View File

@ -16,8 +16,7 @@
// Btw: replacing g2log for g3log include is easy on Linux // 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' // 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/filesink.hpp>
#include <g3log/g3log.hpp>
#include <g3log/loglevels.hpp>
#include <g3log/logworker.hpp>

View File

@ -19,38 +19,32 @@
* ********************************************* */ * ********************************************* */
#include "g3log/g3log.hpp" #include "g3log/g3log.hpp"
#include "g3log/logworker.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/loglevels.hpp" #include "g3log/loglevels.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/logworker.hpp"
#include <mutex>
#include <memory>
#include <iostream>
#include <thread>
#include <atomic> #include <atomic>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream> #include <sstream>
#include <thread>
namespace { namespace {
std::once_flag g_initialize_flag; 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::mutex g_logging_init_mutex;
std::unique_ptr<g3::LogMessage> g_first_uninitialized_msg = {nullptr}; std::unique_ptr<g3::LogMessage> g_first_uninitialized_msg = {nullptr};
std::once_flag g_set_first_uninitialized_flag; std::once_flag g_set_first_uninitialized_flag;
std::once_flag g_save_first_uninitialized_flag; std::once_flag g_save_first_uninitialized_flag;
const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */}; const std::function<void(void)> g_pre_fatal_hook_that_does_nothing = [] { /*does nothing */ };
std::function<void(void)> g_fatal_pre_logging_hook; std::function<void(void)> g_fatal_pre_logging_hook;
std::atomic<size_t> g_fatal_hook_recursive_counter = {0}; std::atomic<size_t> g_fatal_hook_recursive_counter = {0};
} } // namespace
namespace g3 { namespace g3 {
// signalhandler and internal clock is only needed to install once // signalhandler and internal clock is only needed to install once
@ -76,7 +70,7 @@ namespace g3 {
// Save the first uninitialized message, if any // Save the first uninitialized message, if any
std::call_once(g_save_first_uninitialized_flag, [&bgworker] { std::call_once(g_save_first_uninitialized_flag, [&bgworker] {
if (g_first_uninitialized_msg) { if (g_first_uninitialized_msg) {
bgworker->save(LogMessagePtr {std::move(g_first_uninitialized_msg)}); bgworker->save(LogMessagePtr{std::move(g_first_uninitialized_msg)});
} }
}); });
@ -88,34 +82,29 @@ namespace g3 {
g_fatal_hook_recursive_counter.store(0); g_fatal_hook_recursive_counter.store(0);
} }
/** /**
* default does nothing, @ref ::g_pre_fatal_hook_that_does_nothing * 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 called just before sending the fatal message, @ref pushFatalmessageToLogger
* It will be reset to do nothing in ::initializeLogging(...) * It will be reset to do nothing in ::initializeLogging(...)
* so please call this function, if you ever need to, after 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; static std::mutex m;
std::lock_guard<std::mutex> lock(m); std::lock_guard<std::mutex> lock(m);
g_fatal_pre_logging_hook = pre_fatal_hook; g_fatal_pre_logging_hook = pre_fatal_hook;
} }
// By default this function pointer goes to \ref pushFatalMessageToLogger; // 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 /** REPLACE fatalCallToLogger for fatalCallForUnitTest
* This function switches the function pointer so that only * This function switches the function pointer so that only
* 'unitTest' mock-fatal calls are made. * '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; g_fatal_to_g3logworker_function_ptr = fatal_call;
} }
namespace internal { namespace internal {
bool isLoggingInitialized() { bool isLoggingInitialized() {
@ -129,7 +118,6 @@ namespace g3 {
void shutDownLogging() { void shutDownLogging() {
std::lock_guard<std::mutex> lock(g_logging_init_mutex); std::lock_guard<std::mutex> lock(g_logging_init_mutex);
g_logger_instance = nullptr; g_logger_instance = nullptr;
} }
/** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further /** Same as the Shutdown above but called by the destructor of the LogWorker, thus ensuring that no further
@ -150,25 +138,21 @@ namespace g3 {
return true; return true;
} }
/** explicitly 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) */ * i.e. (dlopen + dlsym) */
void saveMessage(const char* entry, const char* file, int line, const char* function, const LEVELS& level, 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) { const char* boolean_expression, int fatal_signal, const char* stack_trace) {
LEVELS msgLevel {level}; LEVELS msgLevel{level};
LogMessagePtr message {std::make_unique<LogMessage>(file, line, function, msgLevel)}; LogMessagePtr message{std::make_unique<LogMessage>(file, line, function, msgLevel)};
message.get()->write().append(entry); message.get()->write().append(entry);
message.get()->setExpression(boolean_expression); message.get()->setExpression(boolean_expression);
if (internal::wasFatal(level)) { if (internal::wasFatal(level)) {
auto fatalhook = g_fatal_pre_logging_hook; auto fatalhook = g_fatal_pre_logging_hook;
// In case the fatal_pre logging actually will cause a crash in its turn // In case the fatal_pre logging actually will cause a crash in its turn
// let's not do recursive crashing! // let's not do recursive crashing!
setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing); setFatalPreLoggingHook(g_pre_fatal_hook_that_does_nothing);
++g_fatal_hook_recursive_counter; // thread safe counter ++g_fatal_hook_recursive_counter; // thread safe counter
// "benign" race here. If two threads crashes, with recursive crashes // "benign" race here. If two threads crashes, with recursive crashes
// then it's possible that the "other" fatal stack trace will be shown // then it's possible that the "other" fatal stack trace will be shown
// that's OK since it was anyhow the first crash detected // that's OK since it was anyhow the first crash detected
@ -177,12 +161,14 @@ namespace g3 {
message.get()->write().append(stack_trace); message.get()->write().append(stack_trace);
if (g_fatal_hook_recursive_counter.load() > 1) { if (g_fatal_hook_recursive_counter.load() > 1) {
message.get()->write() message.get()->write().append(
.append("\n\n\nWARNING\n" "\n\n\nWARNING\n"
"A recursive crash detected. It is likely the hook set with 'setFatalPreLoggingHook(...)' is responsible\n\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"); .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) }; FatalMessagePtr fatal_message{std::make_unique<FatalMessage>(*(message._move_only.get()), fatal_signal)};
// At destruction, flushes fatal message to g3LogWorker // At destruction, flushes fatal message to g3LogWorker
// either we will stay here until the background worker has received the fatal // 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 // message, flushed the crash message to the sinks and exits with the same fatal signal
@ -201,7 +187,7 @@ namespace g3 {
* The first initialized log entry will also save the first uninitialized log message, if any * The first initialized log entry will also save the first uninitialized log message, if any
* @param log_entry to save to logger * @param log_entry to save to logger
*/ */
void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker void pushMessageToLogger(LogMessagePtr incoming) { // todo rename to Push SavedMessage To Worker
// Uninitialized messages are ignored but does not CHECK/crash the logger // Uninitialized messages are ignored but does not CHECK/crash the logger
if (!internal::isLoggingInitialized()) { if (!internal::isLoggingInitialized()) {
std::call_once(g_set_first_uninitialized_flag, [&] { std::call_once(g_set_first_uninitialized_flag, [&] {
@ -210,7 +196,7 @@ namespace g3 {
err.append(g_first_uninitialized_msg->message()); err.append(g_first_uninitialized_msg->message());
std::string& str = g_first_uninitialized_msg->write(); std::string& str = g_first_uninitialized_msg->write();
str.clear(); str.clear();
str.append(err); // replace content str.append(err); // replace content
std::cerr << str << std::endl; std::cerr << str << std::endl;
}); });
return; return;
@ -230,7 +216,8 @@ namespace g3 {
std::ostringstream error; std::ostringstream error;
error << "FATAL CALL but logger is NOT initialized\n" error << "FATAL CALL but logger is NOT initialized\n"
<< "CAUSE: " << message.get()->reason() << "CAUSE: " << message.get()->reason()
<< "\nMessage: \n" << message.get()->toString() << std::flush; << "\nMessage: \n"
<< message.get()->toString() << std::flush;
std::cerr << error.str() << std::flush; std::cerr << error.str() << std::flush;
internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id); internal::exitWithDefaultSignalHandler(message.get()->_level, message.get()->_signal_id);
} }
@ -246,9 +233,8 @@ namespace g3 {
* define the behaviour. * define the behaviour.
*/ */
void fatalCall(FatalMessagePtr message) { void fatalCall(FatalMessagePtr message) {
g_fatal_to_g3logworker_function_ptr(FatalMessagePtr {std::move(message)}); g_fatal_to_g3logworker_function_ptr(FatalMessagePtr{std::move(message)});
} }
} // namespace internal
} // internal } // namespace g3
} // g3

View File

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

View File

@ -6,7 +6,6 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
#pragma once #pragma once
#include <atomic> #include <atomic>
@ -14,13 +13,18 @@
namespace g3 { namespace g3 {
/// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c /// As suggested in: http://stackoverflow.com/questions/13193484/how-to-declare-a-vector-of-atomic-in-c
struct atomicbool { struct atomicbool {
private: private:
std::atomic<bool> value_; std::atomic<bool> value_;
public:
atomicbool(): value_ {false} {} public:
atomicbool(bool value): value_ {value} {} atomicbool() :
atomicbool(const std::atomic<bool>& value) : value_ {value.load(std::memory_order_acquire)} {} value_{false} {}
atomicbool(const atomicbool& other): value_ {other.value_.load(std::memory_order_acquire)} {} 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) { atomicbool& operator=(const atomicbool& other) {
value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release); value_.store(other.value_.load(std::memory_order_acquire), std::memory_order_release);
@ -32,12 +36,12 @@ namespace g3 {
return *this; return *this;
} }
bool operator==(const atomicbool& rhs) const { bool operator==(const atomicbool& rhs) const {
return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire)); return (value_.load(std::memory_order_acquire) == rhs.value_.load(std::memory_order_acquire));
} }
bool value() {return value_.load(std::memory_order_acquire);} bool value() { return value_.load(std::memory_order_acquire); }
std::atomic<bool>& get() {return value_;} std::atomic<bool>& get() { return value_; }
}; };
} // g3 } // namespace g3
// explicit whitespace/EOF for VS15 // explicit whitespace/EOF for VS15

View File

@ -8,10 +8,10 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
#include <cstdio> #include <cstdio>
#include <string>
#include <map> #include <map>
#include "g3log/loglevels.hpp" #include <string>
#include "g3log/generated_definitions.hpp" #include "g3log/generated_definitions.hpp"
#include "g3log/loglevels.hpp"
// kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp // kjell. Separera på crashhandler.hpp och crashhanlder_internal.hpp
// implementationsfilen kan vara den samma // implementationsfilen kan vara den samma
@ -28,7 +28,6 @@ namespace g3 {
SIGTERM TERMINATION (ANSI) */ SIGTERM TERMINATION (ANSI) */
void installCrashHandler(); void installCrashHandler();
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
typedef unsigned long SignalType; typedef unsigned long SignalType;
/// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
@ -43,7 +42,6 @@ namespace g3 {
// restore to whatever signal handler was used before signal handler installation // restore to whatever signal handler was used before signal handler installation
void restoreSignalHandler(int signal_number); void restoreSignalHandler(int signal_number);
/// Overrides the existing signal handling for custom signals /// Overrides the existing signal handling for custom signals
/// For example: usage of zcmq relies on its own signal handler for SIGTERM /// 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 /// so users of g3log with zcmq should then use the @ref overrideSetupSignals
@ -55,14 +53,12 @@ namespace g3 {
void overrideSetupSignals(const std::map<int, std::string> overrideSignals); void overrideSetupSignals(const std::map<int, std::string> overrideSignals);
#endif #endif
namespace internal { namespace internal {
/// Resets the fatal signal/exception handling back to default /// Resets the fatal signal/exception handling back to default
/// which might be needed in case it was previously overridden /// which might be needed in case it was previously overridden
/// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM /// The default signals are: SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGTERM
void restoreFatalHandlingToDefault(); void restoreFatalHandlingToDefault();
/** return whether or any fatal handling is still ongoing /** return whether or any fatal handling is still ongoing
* this is used by g3log::fatalCallToLogger * this is used by g3log::fatalCallToLogger
* only in the case of Windows exceptions (not fatal signals) * only in the case of Windows exceptions (not fatal signals)
@ -81,5 +77,5 @@ namespace g3 {
* This is an internal only function. Do not use it elsewhere. It is triggered * This is an internal only function. Do not use it elsewhere. It is triggered
* from g3log, g3LogWorker after flushing messages to file */ * from g3log, g3LogWorker after flushing messages to file */
void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number); void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType signal_number);
} // end g3::internal } // namespace internal
} // g3 } // namespace g3

View File

@ -7,28 +7,27 @@
* ============================================================================*/ * ============================================================================*/
#pragma once #pragma once
#include <string>
#include <memory> #include <memory>
#include <string>
#include "g3log/logmessage.hpp" #include "g3log/logmessage.hpp"
namespace g3 { namespace g3 {
class FileSink { class FileSink {
public: 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); 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(); virtual ~FileSink();
void fileWrite(LogMessageMover message); void fileWrite(LogMessageMover message);
std::string changeLogFile(const std::string &directory, const std::string &logger_id); std::string changeLogFile(const std::string& directory, const std::string& logger_id);
std::string fileName(); std::string fileName();
void overrideLogDetails(LogMessage::LogDetailsFunc func); void overrideLogDetails(LogMessage::LogDetailsFunc func);
void overrideLogHeader(const std::string& change); void overrideLogHeader(const std::string& change);
private:
private:
LogMessage::LogDetailsFunc _log_details_func; LogMessage::LogDetailsFunc _log_details_func;
std::string _log_file_with_path; 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::unique_ptr<std::ofstream> _outptr;
std::string _header; std::string _header;
bool _firstEntry; bool _firstEntry;
@ -37,14 +36,11 @@ namespace g3 {
size_t _write_to_log_every_x_message; size_t _write_to_log_every_x_message;
void addLogFileHeader(); void addLogFileHeader();
std::ofstream &filestream() { std::ofstream& filestream() {
return *(_outptr.get()); return *(_outptr.get());
} }
FileSink& operator=(const FileSink&) = delete;
FileSink &operator=(const FileSink &) = delete; FileSink(const FileSink& other) = delete;
FileSink(const FileSink &other) = delete;
}; };
} // g3 } // namespace g3

View File

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

View File

@ -18,22 +18,21 @@
* 5. Various Q&A at StackOverflow * 5. Various Q&A at StackOverflow
* ********************************************* */ * ********************************************* */
#pragma once #pragma once
#include "g3log/loglevels.hpp"
#include "g3log/logcapture.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/generated_definitions.hpp" #include "g3log/generated_definitions.hpp"
#include "g3log/logcapture.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logmessage.hpp"
#include <string>
#include <functional> #include <functional>
#include <string>
#if defined(_MSC_VER) && (defined(WINDOWS_FUNCSIG)) // Microsoft #if defined(_MSC_VER) && (defined(WINDOWS_FUNCSIG)) // Microsoft
#define G3LOG_PRETTY_FUNCTION __FUNCSIG__ #define G3LOG_PRETTY_FUNCTION __FUNCSIG__
#elif defined(__GNUC__) && defined(PRETTY_FUNCTION) // GCC compatible #elif defined(__GNUC__) && defined(PRETTY_FUNCTION) // GCC compatible
#define G3LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__ #define G3LOG_PRETTY_FUNCTION __PRETTY_FUNCTION__
#else #else
#define G3LOG_PRETTY_FUNCTION __FUNCTION__ #define G3LOG_PRETTY_FUNCTION __FUNCTION__
#endif #endif
@ -43,7 +42,6 @@
#define thread_local __declspec(thread) #define thread_local __declspec(thread)
#endif #endif
/** namespace for LOG() and CHECK() frameworks /** namespace for LOG() and CHECK() frameworks
* History lesson: Why the names 'g3' and 'g3log'?: * History lesson: Why the names 'g3' and 'g3log'?:
* The framework was made in my own free time as PUBLIC DOMAIN but the * The framework was made in my own free time as PUBLIC DOMAIN but the
@ -62,8 +60,7 @@ namespace g3 {
/** Should be called at very first startup of the software with \ref g3LogWorker /** Should be called at very first startup of the software with \ref g3LogWorker
* pointer. Ownership of the \ref g3LogWorker is the responsibility of the caller */ * pointer. Ownership of the \ref g3LogWorker is the responsibility of the caller */
void initializeLogging(LogWorker *logger); void initializeLogging(LogWorker* logger);
/** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called /** setFatalPreLoggingHook() provides an optional extra step before the fatalExitHandler is called
* *
@ -80,7 +77,7 @@ namespace g3 {
* *
* Linux: g3::setFatalPreLoggingHook([]{ raise(SIGTRAP); }); * 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 /** 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 * use "setFatalExithandler". Please see g3log.cpp and crashhandler_windows.cpp or crashhandler_unix for
@ -88,16 +85,15 @@ namespace g3 {
*/ */
void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call); void setFatalExitHandler(std::function<void(FatalMessagePtr)> fatal_call);
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
// only_change_at_initialization namespace is for changes to be done only during initialization. More specifically // 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 // items here would be called prior to calling other parts of g3log
namespace only_change_at_initialization { namespace only_change_at_initialization {
// Sets the MaxMessageSize to be used when capturing log messages. Currently this value is set to 2KB. Messages // 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 // Longer than this are bound to 2KB with the string "[...truncated...]" at the end. This function allows
// this limit to be changed. // this limit to be changed.
void setMaxMessageSize(size_t max_size); void setMaxMessageSize(size_t max_size);
} } // namespace only_change_at_initialization
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ #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 // internal namespace is for completely internal or semi-hidden from the g3 namespace due to that it is unlikely
@ -107,13 +103,12 @@ namespace g3 {
bool isLoggingInitialized(); bool isLoggingInitialized();
// Save the created LogMessage to any existing sinks // Save the created LogMessage to any existing sinks
void saveMessage(const char *message, const char *file, int line, const char *function, const LEVELS &level, 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); const char* boolean_expression, int fatal_signal, const char* stack_trace);
// forwards the message to all sinks // forwards the message to all sinks
void pushMessageToLogger(LogMessagePtr log_entry); void pushMessageToLogger(LogMessagePtr log_entry);
// forwards a FATAL message to all sinks,. after which the g3logworker // forwards a FATAL message to all sinks,. after which the g3logworker
// will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler // will trigger crashhandler / g3::internal::exitWithDefaultSignalHandler
// //
@ -122,7 +117,6 @@ namespace g3 {
// "setFatalExitHandler" // "setFatalExitHandler"
void pushFatalMessageToLogger(FatalMessagePtr message); void pushFatalMessageToLogger(FatalMessagePtr message);
// Saves the created FatalMessage to any existing sinks and exits with // Saves the created FatalMessage to any existing sinks and exits with
// the originating fatal signal,. or SIGABRT if it originated from a broken contract. // the originating fatal signal,. or SIGABRT if it originated from a broken contract.
// By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override // By default forwards to: pushFatalMessageToLogger, see "setFatalExitHandler" to override
@ -136,11 +130,11 @@ namespace g3 {
void shutDownLogging(); void shutDownLogging();
// Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized // Shutdown logging, but ONLY if the active logger corresponds to the one currently initialized
bool shutDownLoggingForActiveOnly(LogWorker *active); bool shutDownLoggingForActiveOnly(LogWorker* active);
} // internal
} // g3
} // 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_LOG_MESSAGE(level) LogCapture(__FILE__, __LINE__, static_cast<const char*>(G3LOG_PRETTY_FUNCTION), level)
#define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \ #define INTERNAL_CONTRACT_MESSAGE(boolean_expression) \
@ -228,3 +222,4 @@ And here is possible output
// (ref test_io.cpp) // (ref test_io.cpp)
#define CHECK_F(boolean_expression, printf_like_message, ...) \ #define CHECK_F(boolean_expression, printf_like_message, ...) \
if (true == (boolean_expression)) {} else 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,15 +8,15 @@
#pragma once #pragma once
#include "g3log/loglevels.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/loglevels.hpp"
#include <string>
#include <sstream>
#include <cstdarg>
#include <csignal> #include <csignal>
#include <cstdarg>
#include <sstream>
#include <string>
#ifdef _MSC_VER #ifdef _MSC_VER
# include <sal.h> #include <sal.h>
#endif #endif
/** /**
@ -27,8 +27,7 @@
*/ */
struct LogCapture { struct LogCapture {
/// Called from crash handler when a fatal signal has occurred (SIGSEGV etc) /// 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 * @file, line, function are given in g3log.hpp from macros
@ -36,48 +35,41 @@ struct LogCapture {
* @expression for CHECK calls * @expression for CHECK calls
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler * @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. // At destruction the message will be forwarded to the g3log worker.
// In the case of dynamically (at runtime) loaded libraries, the important thing to know is that // 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 // all strings are copied, so the original are not destroyed at the receiving end, only the copy
virtual ~LogCapture() noexcept (false); virtual ~LogCapture() noexcept(false);
#ifdef _MSC_VER
#if _MSC_VER >= 1400
#define G3LOG_FORMAT_STRING _Printf_format_string_
#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 #else
# define G3LOG_FORMAT_STRING #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. // Use "-Wall" to generate warnings in case of illegal printf format.
// Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html // Ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
[[gnu::format(printf, 2, 3)]] void capturef(G3LOG_FORMAT_STRING const char *printf_like_message, ...); // 2,3 ref: http://www.codemaestro.com/reviews/18 [[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 #endif
/// prettifying API for this completely open struct /// prettifying API for this completely open struct
std::ostringstream &stream() { std::ostringstream& stream() {
return _stream; return _stream;
} }
std::ostringstream _stream; std::ostringstream _stream;
std::string _stack_trace; std::string _stack_trace;
const char* _file; const char* _file;
const int _line; const int _line;
const char* _function; const char* _function;
const LEVELS &_level; const LEVELS& _level;
const char* _expression; const char* _expression;
const g3::SignalType _fatal_signal; const g3::SignalType _fatal_signal;
}; };
//} // g3 //} // g3

View File

@ -22,22 +22,26 @@
#endif #endif
#endif #endif
#include <string>
#include <algorithm> #include <algorithm>
#include <map>
#include <atomic> #include <atomic>
#include <g3log/atomicbool.hpp> #include <g3log/atomicbool.hpp>
#include <map>
#include <string>
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod // Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
struct LEVELS { struct LEVELS {
// force internal copy of the const char*. This is a simple safeguard for when g3log is used in a // 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" // "dynamic, runtime loading of shared libraries"
LEVELS(const LEVELS& other): value(other.value), text(other.text.c_str()) {} LEVELS(const LEVELS& other) :
value(other.value),
text(other.text.c_str()) {}
LEVELS(int id, const std::string& idtext) : value(id), text(idtext) {} LEVELS(int id, const std::string& idtext) :
value(id),
text(idtext) {}
bool operator==(const LEVELS& rhs) const { bool operator==(const LEVELS& rhs) const {
return (value == rhs.value && text == rhs.text); return (value == rhs.value && text == rhs.text);
} }
@ -51,13 +55,11 @@ struct LEVELS {
swap(first.text, second.text); swap(first.text, second.text);
} }
LEVELS& operator=(LEVELS other) { LEVELS& operator=(LEVELS other) {
swap(*this, other); swap(*this, other);
return *this; return *this;
} }
int value; int value;
std::string text; std::string text;
}; };
@ -89,14 +91,12 @@ namespace g3 {
static const int kWarningValue = 500; static const int kWarningValue = 500;
static const int kFatalValue = 1000; static const int kFatalValue = 1000;
static const int kInternalFatalValue = 2000; static const int kInternalFatalValue = 2000;
} // g3 } // namespace g3
const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"}, const LEVELS G3LOG_DEBUG{g3::kDebugValue, "DEBUG"},
INFO {g3::kInfoValue, "INFO"}, INFO{g3::kInfoValue, "INFO"},
WARNING {g3::kWarningValue, "WARNING"}, WARNING{g3::kWarningValue, "WARNING"},
FATAL {g3::kFatalValue, "FATAL"}; FATAL{g3::kFatalValue, "FATAL"};
namespace g3 { namespace g3 {
// Logging level and atomic status collection struct // Logging level and atomic status collection struct
@ -105,10 +105,18 @@ namespace g3 {
LEVELS level; LEVELS level;
// default operator needed for std::map compliance // default operator needed for std::map compliance
LoggingLevel(): status(false), level(INFO) {}; LoggingLevel() :
LoggingLevel(const LoggingLevel& lvl) : status(lvl.status), level(lvl.level) {} status(false),
LoggingLevel(const LEVELS& lvl): status(true), level(lvl) {}; level(INFO){};
LoggingLevel(const LEVELS& lvl, bool enabled): status(enabled), level(lvl) {}; 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() = default;
LoggingLevel& operator=(const LoggingLevel& other) { LoggingLevel& operator=(const LoggingLevel& other) {
@ -117,26 +125,22 @@ namespace g3 {
return *this; return *this;
} }
bool operator==(const LoggingLevel& rhs) const { bool operator==(const LoggingLevel& rhs) const {
return (status == rhs.status && level == rhs.level); return (status == rhs.status && level == rhs.level);
} }
}; };
} // g3 } // namespace g3
namespace g3 { namespace g3 {
namespace internal { namespace internal {
const LEVELS CONTRACT {g3::kInternalFatalValue, {"CONTRACT"}}, const LEVELS CONTRACT{g3::kInternalFatalValue, {"CONTRACT"}},
FATAL_SIGNAL {g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}}, FATAL_SIGNAL{g3::kInternalFatalValue + 1, {"FATAL_SIGNAL"}},
FATAL_EXCEPTION {kInternalFatalValue + 2, {"FATAL_EXCEPTION"}}; FATAL_EXCEPTION{kInternalFatalValue + 2, {"FATAL_EXCEPTION"}};
/// helper function to tell the logger if a log message was fatal. If it is it will force /// 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 /// a shutdown after all log entries are saved to the sinks
bool wasFatal(const LEVELS& level); bool wasFatal(const LEVELS& level);
} } // namespace internal
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
// Only safe if done at initialization in a single-thread context // Only safe if done at initialization in a single-thread context
@ -152,8 +156,7 @@ namespace g3 {
/// remove any added logging levels so that the only ones left are /// remove any added logging levels so that the only ones left are
/// {DEBUG,INFO,WARNING,FATAL} /// {DEBUG,INFO,WARNING,FATAL}
void reset(); void reset();
} // only_change_at_initialization } // namespace only_change_at_initialization
namespace log_levels { namespace log_levels {
/// Enable log level >= log_level. /// Enable log level >= log_level.
@ -169,24 +172,24 @@ namespace g3 {
void disableAll(); void disableAll();
void enableAll(); void enableAll();
/// print all levels with their disabled or enabled status
/// print all levels with their disabled or enabled status std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint);
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint);
/// print snapshot of system levels with their /// print snapshot of system levels with their
/// disabled or enabled status /// disabled or enabled status
std::string to_string(); std::string to_string();
/// Snapshot view of the current logging levels' status /// Snapshot view of the current logging levels' status
std::map<int, g3::LoggingLevel> getAll(); std::map<int, g3::LoggingLevel> getAll();
enum class status {Absent, Enabled, Disabled}; enum class status { Absent,
Enabled,
Disabled };
status getStatus(LEVELS level); status getStatus(LEVELS level);
} // log_levels } // namespace log_levels
#endif #endif
/// Enabled status for the given logging level /// Enabled status for the given logging level
bool logLevel(const LEVELS& level); bool logLevel(const LEVELS& level);
} // g3 } // namespace g3

View File

@ -8,17 +8,15 @@
#pragma once #pragma once
#include "g3log/loglevels.hpp"
#include "g3log/time.hpp"
#include "g3log/moveoncopy.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/moveoncopy.hpp"
#include "g3log/time.hpp"
#include <string>
#include <sstream>
#include <thread>
#include <memory> #include <memory>
#include <sstream>
#include <string>
#include <thread>
namespace g3 { namespace g3 {
@ -51,14 +49,14 @@ namespace g3 {
// default look is Y/M/D H:M:S // 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 timestamp(const std::string& time_format = {internal::date_formatted + " " + internal::time_formatted}) const;
std::string message() const { std::string message() const {
return _message; return _message;
} }
std::string& write() const { std::string& write() const {
return _message; return _message;
} }
std::string expression() const { std::string expression() const {
return _expression; return _expression;
} }
bool wasFatal() const { bool wasFatal() const {
@ -71,10 +69,8 @@ namespace g3 {
_expression = std::move(expression); _expression = std::move(expression);
} }
LogMessage& operator=(LogMessage other); LogMessage& operator=(LogMessage other);
LogMessage(std::string file, const int line, 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); explicit LogMessage(const std::string& fatalOsSignalCrashMessage);
@ -82,32 +78,26 @@ namespace g3 {
LogMessage(LogMessage&& other); LogMessage(LogMessage&& other);
virtual ~LogMessage() {} virtual ~LogMessage() {}
// helper log printing functions used by "toString()" // helper log printing functions used by "toString()"
static std::string splitFileName(const std::string& str); static std::string splitFileName(const std::string& str);
static std::string fatalSignalToString(const LogMessage& msg); static std::string fatalSignalToString(const LogMessage& msg);
// windows only: fatalExceptionToString // windows only: fatalExceptionToString
static std::string fatalExceptionToString(const LogMessage& msg); static std::string fatalExceptionToString(const LogMessage& msg);
static std::string fatalLogToString(const LogMessage& msg); static std::string fatalLogToString(const LogMessage& msg);
static std::string fatalCheckToString(const LogMessage& msg); static std::string fatalCheckToString(const LogMessage& msg);
static std::string normalToString(const LogMessage& msg); static std::string normalToString(const LogMessage& msg);
// the default formatting option // the default formatting option
static std::string DefaultLogDetailsToString(const LogMessage& msg); static std::string DefaultLogDetailsToString(const LogMessage& msg);
// this function can be used by the logging sink to add thread ID // 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 // see this concept and it is easy to make your own custom formatting
static std::string FullLogDetailsToString(const LogMessage& msg); static std::string FullLogDetailsToString(const LogMessage& msg);
using LogDetailsFunc = std::string (*) (const LogMessage&); using LogDetailsFunc = std::string (*)(const LogMessage&);
std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const; std::string toString(LogDetailsFunc formattingFunc = DefaultLogDetailsToString) const;
void overrideLogDetailsFunc(LogDetailsFunc func) const;
void overrideLogDetailsFunc(LogDetailsFunc func) const;
// //
// Complete access to the raw data in case the helper functions above // Complete access to the raw data in case the helper functions above
@ -121,11 +111,9 @@ namespace g3 {
int _line; int _line;
std::string _function; std::string _function;
LEVELS _level; LEVELS _level;
std::string _expression; // only with content for CHECK(...) calls std::string _expression; // only with content for CHECK(...) calls
mutable std::string _message; mutable std::string _message;
friend void swap(LogMessage& first, LogMessage& second) { friend void swap(LogMessage& first, LogMessage& second) {
using std::swap; using std::swap;
swap(first._timestamp, second._timestamp); swap(first._timestamp, second._timestamp);
@ -137,12 +125,8 @@ namespace g3 {
swap(first._expression, second._expression); swap(first._expression, second._expression);
swap(first._message, second._message); swap(first._message, second._message);
} }
}; };
/** Trigger for flushing the message queue and exiting the application /** Trigger for flushing the message queue and exiting the application
* A thread that causes a FatalMessage will sleep forever until the * A thread that causes a FatalMessage will sleep forever until the
* application has exited (after message flush) */ * application has exited (after message flush) */
@ -158,8 +142,7 @@ namespace g3 {
const SignalType _signal_id; const SignalType _signal_id;
}; };
typedef MoveOnCopy<std::unique_ptr<FatalMessage>> FatalMessagePtr; typedef MoveOnCopy<std::unique_ptr<FatalMessage>> FatalMessagePtr;
typedef MoveOnCopy<std::unique_ptr<LogMessage>> LogMessagePtr; typedef MoveOnCopy<std::unique_ptr<LogMessage>> LogMessagePtr;
typedef MoveOnCopy<LogMessage> LogMessageMover; typedef MoveOnCopy<LogMessage> LogMessageMover;
} // g3 } // namespace g3

View File

@ -11,18 +11,17 @@
* *
* PUBLIC DOMAIN and Not copyrighted. 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 "g3log/filesink.hpp"
#include "g3log/logmessage.hpp"
#include <memory> #include <memory>
#include "g3log/filesink.hpp"
#include "g3log/g3log.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/sinkhandle.hpp"
#include "g3log/sinkwrapper.hpp"
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
namespace g3 { namespace g3 {
class LogWorker; class LogWorker;
struct LogWorkerImpl; struct LogWorkerImpl;
@ -32,7 +31,7 @@ namespace g3 {
struct LogWorkerImpl final { struct LogWorkerImpl final {
typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr; typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
std::vector<SinkWrapperPtr> _sinks; 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();
~LogWorkerImpl() = default; ~LogWorkerImpl() = default;
@ -44,7 +43,6 @@ namespace g3 {
LogWorkerImpl& operator=(const LogWorkerImpl&) = delete; LogWorkerImpl& operator=(const LogWorkerImpl&) = delete;
}; };
/// Front end of the LogWorker. API that is useful is /// 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 /// addSink( sink, default_call ) which returns a handle to the sink. See below and README for usage example
/// save( msg ) : internal use /// save( msg ) : internal use
@ -57,15 +55,13 @@ namespace g3 {
LogWorker(const LogWorker&) = delete; LogWorker(const LogWorker&) = delete;
LogWorker& operator=(const LogWorker&) = delete; LogWorker& operator=(const LogWorker&) = delete;
public:
public:
~LogWorker(); ~LogWorker();
/// Creates the LogWorker with no sinks. See example below on @ref addSink for how to use it /// Creates the LogWorker with no sinks. See 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 /// if you want to use the default file logger then see below for @ref addDefaultLogger
static std::unique_ptr<LogWorker> createLogWorker(); static std::unique_ptr<LogWorker> createLogWorker();
/** /**
A convenience function to add the default g3::FileSink to the log worker A convenience function to add the default g3::FileSink to the log worker
@param log_prefix that you want @param log_prefix that you want
@ -89,26 +85,26 @@ namespace g3 {
/// @param real_sink unique_ptr ownership is passed to the log worker /// @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 /// @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 /// @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) { std::unique_ptr<g3::SinkHandle<T>> addSink(std::unique_ptr<T> real_sink, DefaultLogCall call) {
using namespace g3; using namespace g3;
using namespace g3::internal; 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); addWrappedSink(sink);
return std::make_unique<SinkHandle<T>> (sink); return std::make_unique<SinkHandle<T>>(sink);
} }
/// Removes a sink. This is a synchronous call. /// Removes a sink. This is a synchronous call.
/// You are guaranteed that the sink is removed by the time the call returns /// 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 /// @param sink_handle the ownership of the sink handle is given
template<typename T> template <typename T>
void removeSink(std::unique_ptr<SinkHandle<T>> sink_handle) { void removeSink(std::unique_ptr<SinkHandle<T>> sink_handle) {
if (sink_handle) { if (sink_handle) {
// sink_handle->sink().use_count() is 1 at this point // 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 // 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); // was made by the client: assert(sink_handle->sink().use_count() == 0);
auto weak_ptr_sink = sink_handle->sink(); { auto weak_ptr_sink = sink_handle->sink();
{
auto bg_removesink_call = [this, weak_ptr_sink] { auto bg_removesink_call = [this, weak_ptr_sink] {
auto shared_sink = weak_ptr_sink.lock(); auto shared_sink = weak_ptr_sink.lock();
if (shared_sink) { if (shared_sink) {
@ -127,13 +123,13 @@ namespace g3 {
/// This will clear/remove all the sinks. If a sink shared_ptr was retrieved via the sink /// 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 /// handle then the sink will be removed internally but will live on in the client's instance
void removeAllSinks() { void removeAllSinks() {
auto bg_clear_sink_call = [this]() noexcept { _impl._sinks.clear(); }; auto bg_clear_sink_call = [this]() noexcept {
_impl._sinks.clear();
};
auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get()); auto token_cleared = g3::spawn_task(bg_clear_sink_call, _impl._bg.get());
token_cleared.wait(); token_cleared.wait();
} }
/// internal: /// internal:
/// pushes in background thread (asynchronously) input messages to log file /// pushes in background thread (asynchronously) input messages to log file
void save(LogMessagePtr entry); void save(LogMessagePtr entry);
@ -143,7 +139,5 @@ namespace g3 {
/// this way it's ensured that all existing entries were flushed before 'fatal' /// this way it's ensured that all existing entries were flushed before 'fatal'
/// Will abort the application! /// Will abort the application!
void fatal(FatalMessagePtr fatal_message); 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 // 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. // to be wrapped and their internals "moved" when tried to be copied.
template<typename Moveable> template <typename Moveable>
struct MoveOnCopy { struct MoveOnCopy {
mutable Moveable _move_only; mutable Moveable _move_only;
explicit MoveOnCopy(Moveable &&m) : _move_only(std::move(m)) {} explicit MoveOnCopy(Moveable&& m) :
MoveOnCopy(MoveOnCopy const &t) : _move_only(std::move(t._move_only)) {} _move_only(std::move(m)) {}
MoveOnCopy(MoveOnCopy &&t) : _move_only(std::move(t._move_only)) {} 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); _move_only = std::move(other._move_only);
return *this; return *this;
} }
MoveOnCopy &operator=(MoveOnCopy && other) { MoveOnCopy& operator=(MoveOnCopy&& other) {
_move_only = std::move(other._move_only); _move_only = std::move(other._move_only);
return *this; return *this;
} }
@ -36,13 +39,13 @@ namespace g3 {
_move_only(); _move_only();
} }
Moveable &get() { Moveable& get() {
return _move_only; return _move_only;
} }
Moveable release() { Moveable release() {
return std::move(_move_only); return std::move(_move_only);
} }
}; };
} // g3 } // namespace g3

View File

@ -16,24 +16,23 @@
#pragma once #pragma once
#include <queue>
#include <mutex>
#include <exception>
#include <condition_variable> #include <condition_variable>
#include <exception>
#include <mutex>
#include <queue>
/** Multiple producer, multiple consumer thread safe queue /** Multiple producer, multiple consumer thread safe queue
* Since 'return by reference' is used this queue won't throw */ * Since 'return by reference' is used this queue won't throw */
template<typename T> template <typename T>
class shared_queue class shared_queue {
{
std::queue<T> queue_; std::queue<T> queue_;
mutable std::mutex m_; mutable std::mutex m_;
std::condition_variable data_cond_; std::condition_variable data_cond_;
shared_queue &operator=(const shared_queue &) = delete; shared_queue& operator=(const shared_queue&) = delete;
shared_queue(const shared_queue &other) = delete; shared_queue(const shared_queue& other) = delete;
public: public:
shared_queue() = default; shared_queue() = default;
void push(T item) { void push(T item) {
@ -45,7 +44,7 @@ public:
} }
/// \return immediately, with true if successful retrieval /// \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_); std::lock_guard<std::mutex> lock(m_);
if (queue_.empty()) { if (queue_.empty()) {
return false; return false;
@ -56,10 +55,9 @@ public:
} }
/// Try to retrieve, if no items, wait till an item is available and try again /// 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_); std::unique_lock<std::mutex> lock(m_);
while (queue_.empty()) while (queue_.empty()) {
{
data_cond_.wait(lock); data_cond_.wait(lock);
// This 'while' loop is equal to // This 'while' loop is equal to
// data_cond_.wait(lock, [](bool result){return !queue_.empty();}); // data_cond_.wait(lock, [](bool result){return !queue_.empty();});

View File

@ -8,19 +8,18 @@
#pragma once #pragma once
#include "g3log/sinkwrapper.hpp"
#include "g3log/active.hpp" #include "g3log/active.hpp"
#include "g3log/future.hpp" #include "g3log/future.hpp"
#include "g3log/logmessage.hpp" #include "g3log/logmessage.hpp"
#include "g3log/sinkwrapper.hpp"
#include <memory>
#include <functional> #include <functional>
#include <memory>
#include <type_traits> #include <type_traits>
namespace g3 { namespace g3 {
namespace internal { 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 /// The asynchronous Sink has an active object, incoming requests for actions
// will be processed in the background by the specific object the Sink represents. // will be processed in the background by the specific object the Sink represents.
@ -34,33 +33,32 @@ namespace g3 {
// Ref: send(Call call, Args... args) deals with calls // Ref: send(Call call, Args... args) deals with calls
// to the real sink's API // to the real sink's API
template<class T> template <class T>
struct Sink : public SinkWrapper { struct Sink : public SinkWrapper {
std::unique_ptr<T> _real_sink; std::unique_ptr<T> _real_sink;
std::unique_ptr<kjellkod::Active> _bg; std::unique_ptr<kjellkod::Active> _bg;
AsyncMessageCall _default_log_call; AsyncMessageCall _default_log_call;
template<typename DefaultLogCall > template <typename DefaultLogCall>
Sink(std::unique_ptr<T> sink, DefaultLogCall call) Sink(std::unique_ptr<T> sink, DefaultLogCall call) :
: SinkWrapper(), SinkWrapper(),
_real_sink {std::move(sink)}, _real_sink{std::move(sink)},
_bg(kjellkod::Active::createActive()), _bg(kjellkod::Active::createActive()),
_default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) { _default_log_call(std::bind(call, _real_sink.get(), std::placeholders::_1)) {
} }
Sink(std::unique_ptr<T> sink, void (T::*Call)(std::string)) :
Sink(std::unique_ptr<T> sink, void(T::*Call)(std::string) ) SinkWrapper(),
: SinkWrapper(), _real_sink{std::move(sink)},
_real_sink {std::move(sink)}, _bg(kjellkod::Active::createActive()) {
_bg(kjellkod::Active::createActive()) {
std::function<void(std::string)> adapter = std::bind(Call, _real_sink.get(), std::placeholders::_1); 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()); adapter(m.get().toString());
}; };
} }
virtual ~Sink() { virtual ~Sink() {
_bg.reset(); // TODO: to remove _bg.reset(); // TODO: to remove
} }
void send(LogMessageMover msg) override { void send(LogMessageMover msg) override {
@ -69,10 +67,10 @@ namespace g3 {
}); });
} }
template<typename Call, typename... Args> template <typename Call, typename... Args>
auto async(Call call, Args &&... args)-> std::future<std::invoke_result_t<decltype(call), T, 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()); return g3::spawn_task(std::bind(call, _real_sink.get(), std::forward<Args>(args)...), _bg.get());
} }
}; };
} // internal } // namespace internal
} // g3 } // namespace g3

View File

@ -22,22 +22,21 @@ namespace g3 {
// The real sink will be owned by g3log. 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 // calls to sink's API through the SinkHandle will return an exception embedded
// in the resulting future. Ref: SinkHandle::call // in the resulting future. Ref: SinkHandle::call
template<class T> template <class T>
class SinkHandle { class SinkHandle {
std::weak_ptr<internal::Sink<T>> _sink; std::weak_ptr<internal::Sink<T>> _sink;
public: public:
SinkHandle(std::shared_ptr<internal::Sink<T>> sink) SinkHandle(std::shared_ptr<internal::Sink<T>> sink) :
: _sink(sink) {} _sink(sink) {}
~SinkHandle() = default; ~SinkHandle() = default;
// Asynchronous call to the real sink. If the real sink is already deleted // 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 // the returned future will contain a bad_weak_ptr exception instead of the
// call result. // call result.
template<typename AsyncCall, typename... Args> template <typename AsyncCall, typename... Args>
auto call(AsyncCall func , Args&& ... args) -> std::future<std::invoke_result_t<decltype(func), T, Args...>> { auto call(AsyncCall func, Args&&... args) -> std::future<std::invoke_result_t<decltype(func), T, Args...>> {
try { try {
std::shared_ptr<internal::Sink<T>> sink(_sink); std::shared_ptr<internal::Sink<T>> sink(_sink);
return sink->async(func, std::forward<Args>(args)...); return sink->async(func, std::forward<Args>(args)...);
@ -57,6 +56,4 @@ namespace g3 {
} }
}; };
} // g3 } // namespace g3

View File

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

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

View File

@ -13,9 +13,9 @@
* PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc * PUBLIC DOMAIN and Not under copywrite protection. First published for g3log at KjellKod.cc
* ********************************************* */ * ********************************************* */
#include <chrono>
#include <ctime> #include <ctime>
#include <string> #include <string>
#include <chrono>
// FYI: // FYI:
// namespace g3::internal ONLY in g3time.cpp // namespace g3::internal ONLY in g3time.cpp
@ -28,7 +28,10 @@ namespace g3 {
typedef std::chrono::microseconds microseconds; typedef std::chrono::microseconds microseconds;
namespace internal { namespace internal {
enum class Fractional {Millisecond, Microsecond, Nanosecond, NanosecondDefault}; enum class Fractional { Millisecond,
Microsecond,
Nanosecond,
NanosecondDefault };
Fractional getFractional(const std::string& format_buffer, size_t pos); Fractional getFractional(const std::string& format_buffer, size_t pos);
std::string to_string(const g3::system_time_point& ts, Fractional fractional); 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); std::string localtime_formatted_fractions(const g3::system_time_point& ts, std::string format_buffer);
@ -38,8 +41,7 @@ namespace g3 {
// %6: microseconds: 6 digits: 000001 --- default for the time_format // %6: microseconds: 6 digits: 000001 --- default for the time_format
// %f9, %f: nanoseconds, 9 digits: 000000001 // %f9, %f: nanoseconds, 9 digits: 000000001
static const std::string time_formatted = "%H:%M:%S %f6"; static const std::string time_formatted = "%H:%M:%S %f6";
} // internal } // namespace internal
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" // 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. // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
@ -55,26 +57,22 @@ namespace g3 {
* WARNING: At time of writing there is only so-so compiler support for * 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 * std::put_time. A possible fix if your c++11 library is not updated is to
* modify this to use std::strftime instead */ * modify this to use std::strftime instead */
std::string localtime_formatted(const system_time_point& ts, const std::string& time_format) ; std::string localtime_formatted(const system_time_point& ts, const std::string& time_format);
inline system_time_point to_system_time(const high_resolution_time_point& ts) 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
// 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,
// 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
// unlike system_clock, high_resolution_clock cannot be converted to a time and date as // it usually measures reflects the time since power-up.
// 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
// 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
// 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 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
// of the two static variables but relative times within one log will be as precise as // high_resolution_clock.
// high_resolution_clock. using namespace std::chrono;
using namespace std::chrono; static const auto hrs_now = high_resolution_clock::now();
static const auto hrs_now = high_resolution_clock::now(); static const auto sys_now = system_clock::now();
static const auto sys_now = system_clock::now();
return time_point_cast<system_clock::duration>(sys_now + (ts - hrs_now)); return time_point_cast<system_clock::duration>(sys_now + (ts - hrs_now));
} }
} } // namespace g3

View File

@ -7,8 +7,8 @@
* ============================================================================*/ * ============================================================================*/
#include "g3log/logcapture.hpp" #include "g3log/logcapture.hpp"
#include "g3log/g3log.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/g3log.hpp"
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
#include <vector> #include <vector>
@ -21,7 +21,9 @@
#define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread() #define SIGNAL_HANDLER_VERIFY() g3::installSignalHandlerForThread()
#else #else
// Does nothing --- enforces that semicolon must be written // Does nothing --- enforces that semicolon must be written
#define SIGNAL_HANDLER_VERIFY() do {} while(0) #define SIGNAL_HANDLER_VERIFY() \
do { \
} while (0)
#endif #endif
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
@ -30,7 +32,7 @@ static int MaxMessageSize = 2048;
void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) { void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) {
MaxMessageSize = max_size; MaxMessageSize = max_size;
} }
#endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */ #endif /* G3_DYNAMIC_MAX_MESSAGE_SIZE */
/** logCapture is a simple struct for capturing log/fatal entries. At destruction the /** logCapture is a simple struct for capturing log/fatal entries. At destruction the
@ -38,15 +40,15 @@ void g3::only_change_at_initialization::setMaxMessageSize(size_t max_size) {
* As a safety precaution: No memory allocated here will be moved into the background * 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 * worker in case of dynamic loaded library reasons instead the arguments are copied
* inside of g3log.cpp::saveMessage*/ * inside of g3log.cpp::saveMessage*/
LogCapture::~LogCapture() noexcept (false) { LogCapture::~LogCapture() noexcept(false) {
using namespace g3::internal; using namespace g3::internal;
SIGNAL_HANDLER_VERIFY(); SIGNAL_HANDLER_VERIFY();
saveMessage(_stream.str().c_str(), _file, _line, _function, _level, _expression, _fatal_signal, _stack_trace.c_str()); 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) /// 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) {
} }
/** /**
@ -55,9 +57,14 @@ LogCapture::LogCapture(const LEVELS &level, g3::SignalType fatal_signal, const c
* @expression for CHECK calls * @expression for CHECK calls
* @fatal_signal for failed CHECK:SIGABRT or fatal signal caught in the signal handler * @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, LogCapture::LogCapture(const char* file, const int line, const char* function, const LEVELS& level,
const char *expression, g3::SignalType fatal_signal, const char *dump) const char* expression, g3::SignalType fatal_signal, const char* dump) :
: _file(file), _line(line), _function(function), _level(level), _expression(expression), _fatal_signal(fatal_signal) { _file(file),
_line(line),
_function(function),
_level(level),
_expression(expression),
_fatal_signal(fatal_signal) {
if (g3::internal::wasFatal(level)) { if (g3::internal::wasFatal(level)) {
_stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"}; _stack_trace = std::string{"\n*******\tSTACKDUMP *******\n"};
@ -65,17 +72,15 @@ LogCapture::LogCapture(const char *file, const int line, const char *function, c
} }
} }
/** /**
* capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF * capturef, used for "printf" like API in CHECKF, LOGF, LOGF_IF
* See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18 * See also for the attribute formatting ref: http://www.codemaestro.com/reviews/18
*/ */
void LogCapture::capturef(const char *printf_like_message, ...) { void LogCapture::capturef(const char* printf_like_message, ...) {
static const std::string kTruncatedWarningText = "[...truncated...]"; static const std::string kTruncatedWarningText = "[...truncated...]";
#ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE #ifdef G3_DYNAMIC_MAX_MESSAGE_SIZE
std::vector<char> finished_message_backing(MaxMessageSize); std::vector<char> finished_message_backing(MaxMessageSize);
char *finished_message = finished_message_backing.data(); char* finished_message = finished_message_backing.data();
auto finished_message_len = MaxMessageSize; auto finished_message_len = MaxMessageSize;
#else #else
static const int kMaxMessageSize = 2048; static const int kMaxMessageSize = 2048;
@ -106,5 +111,3 @@ void LogCapture::capturef(const char *printf_like_message, ...) {
stream() << finished_message; stream() << finished_message;
} }
} }

View File

@ -19,15 +19,14 @@ namespace g3 {
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
const std::map<int, LoggingLevel> g_log_level_defaults = { const std::map<int, LoggingLevel> g_log_level_defaults = {
{G3LOG_DEBUG.value,{G3LOG_DEBUG}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG}},
{INFO.value, {INFO}}, {INFO.value, {INFO}},
{WARNING.value, {WARNING}}, {WARNING.value, {WARNING}},
{FATAL.value, {FATAL}} {FATAL.value, {FATAL}}};
};
std::map<int, g3::LoggingLevel> g_log_levels = g_log_level_defaults; std::map<int, g3::LoggingLevel> g_log_levels = g_log_level_defaults;
#endif #endif
} // internal } // namespace internal
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
namespace only_change_at_initialization { namespace only_change_at_initialization {
@ -37,7 +36,6 @@ namespace g3 {
internal::g_log_levels[value] = {lvl, enabled}; internal::g_log_levels[value] = {lvl, enabled};
} }
void addLogLevel(LEVELS level) { void addLogLevel(LEVELS level) {
addLogLevel(level, true); addLogLevel(level, true);
} }
@ -45,8 +43,7 @@ namespace g3 {
void reset() { void reset() {
g3::internal::g_log_levels = g3::internal::g_log_level_defaults; g3::internal::g_log_levels = g3::internal::g_log_level_defaults;
} }
} // only_change_at_initialization } // namespace only_change_at_initialization
namespace log_levels { namespace log_levels {
@ -59,12 +56,10 @@ namespace g3 {
} else { } else {
enable(v.second.level); enable(v.second.level);
} }
} }
} }
} }
void set(LEVELS level, bool enabled) { void set(LEVELS level, bool enabled) {
auto it = internal::g_log_levels.find(level.value); auto it = internal::g_log_levels.find(level.value);
if (it != internal::g_log_levels.end()) { if (it != internal::g_log_levels.end()) {
@ -72,7 +67,6 @@ namespace g3 {
} }
} }
void disable(LEVELS level) { void disable(LEVELS level) {
set(level, false); set(level, false);
} }
@ -81,7 +75,6 @@ namespace g3 {
set(level, true); set(level, true);
} }
void disableAll() { void disableAll() {
for (auto& v : internal::g_log_levels) { for (auto& v : internal::g_log_levels) {
v.second.status = false; v.second.status = false;
@ -94,7 +87,6 @@ namespace g3 {
} }
} }
std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint) { std::string to_string(std::map<int, g3::LoggingLevel> levelsToPrint) {
std::string levels; std::string levels;
for (auto& v : levelsToPrint) { for (auto& v : levelsToPrint) {
@ -107,7 +99,6 @@ namespace g3 {
return to_string(internal::g_log_levels); return to_string(internal::g_log_levels);
} }
std::map<int, g3::LoggingLevel> getAll() { std::map<int, g3::LoggingLevel> getAll() {
return internal::g_log_levels; return internal::g_log_levels;
} }
@ -120,13 +111,11 @@ namespace g3 {
} }
return (it->second.status.get().load() ? status::Enabled : status::Disabled); return (it->second.status.get().load() ? status::Enabled : status::Disabled);
} }
} // log_levels } // namespace log_levels
#endif #endif
bool logLevel(const LEVELS& log_level) { bool logLevel(const LEVELS& log_level) {
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
int level = log_level.value; int level = log_level.value;
@ -136,4 +125,4 @@ namespace g3 {
return true; return true;
#endif #endif
} }
} // g3 } // namespace g3

View File

@ -7,12 +7,9 @@
* ============================================================================*/ * ============================================================================*/
#include "g3log/logmessage.hpp" #include "g3log/logmessage.hpp"
#include <mutex>
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/time.hpp" #include "g3log/time.hpp"
#include <mutex>
namespace g3 { namespace g3 {
@ -22,27 +19,20 @@ namespace g3 {
return str.substr(found + 1); return str.substr(found + 1);
} }
// helper for fatal signal // helper for fatal signal
std::string LogMessage::fatalSignalToString(const LogMessage& msg) { std::string LogMessage::fatalSignalToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting std::string out; // clear any previous text and formatting
out.append(msg.timestamp() out.append(msg.timestamp() + "\n\n***** FATAL SIGNAL RECEIVED ******* \n" + msg.message() + '\n');
+ "\n\n***** FATAL SIGNAL RECEIVED ******* \n"
+ msg.message() + '\n');
return out; return out;
} }
// helper for fatal exception (windows only) // helper for fatal exception (windows only)
std::string LogMessage::fatalExceptionToString(const LogMessage& msg) { std::string LogMessage::fatalExceptionToString(const LogMessage& msg) {
std::string out; // clear any previous text and formatting std::string out; // clear any previous text and formatting
out.append(msg.timestamp() out.append(msg.timestamp() + "\n\n***** FATAL EXCEPTION RECEIVED ******* \n" + msg.message() + '\n');
+ "\n\n***** FATAL EXCEPTION RECEIVED ******* \n"
+ msg.message() + '\n');
return out; return out;
} }
// helper for fatal LOG // helper for fatal LOG
std::string LogMessage::fatalLogToString(const LogMessage& msg) { std::string LogMessage::fatalLogToString(const LogMessage& msg) {
auto out = msg._logDetailsToStringFunc(msg); auto out = msg._logDetailsToStringFunc(msg);
@ -55,39 +45,23 @@ namespace g3 {
std::string LogMessage::fatalCheckToString(const LogMessage& msg) { std::string LogMessage::fatalCheckToString(const LogMessage& msg) {
auto out = msg._logDetailsToStringFunc(msg); auto out = msg._logDetailsToStringFunc(msg);
static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"}; static const std::string contractExitReason = {"EXIT trigger caused by broken Contract:"};
out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" out.append("\n\t*******\t " + contractExitReason + " CHECK(" + msg.expression() + ")\n\t" + '"' + msg.message() + '"');
+ '"' + msg. message() + '"');
return out; return out;
} }
// helper for setting the normal log details in an entry // helper for setting the normal log details in an entry
std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) { std::string LogMessage::DefaultLogDetailsToString(const LogMessage& msg) {
std::string out; std::string out;
out.append(msg.timestamp() + "\t" out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
+ msg.level()
+ " ["
+ msg.file()
+ "->"
+ msg.function()
+ ":" + msg.line() + "]\t");
return out; return out;
} }
std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) { std::string LogMessage::FullLogDetailsToString(const LogMessage& msg) {
std::string out; std::string out;
out.append(msg.timestamp() + "\t" out.append(msg.timestamp() + "\t" + msg.level() + " [" + msg.threadID() + " " + msg.file() + "->" + msg.function() + ":" + msg.line() + "]\t");
+ msg.level()
+ " ["
+ msg.threadID()
+ " "
+ msg.file()
+ "->"+ msg.function()
+ ":" + msg.line() + "]\t");
return out; return out;
} }
// helper for normal // helper for normal
std::string LogMessage::normalToString(const LogMessage& msg) { std::string LogMessage::normalToString(const LogMessage& msg) {
auto out = msg._logDetailsToStringFunc(msg); auto out = msg._logDetailsToStringFunc(msg);
@ -95,16 +69,12 @@ namespace g3 {
return out; return out;
} }
// end static functions section
void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const {
// end static functions section
void LogMessage::overrideLogDetailsFunc(LogDetailsFunc func) const{
_logDetailsToStringFunc = func; _logDetailsToStringFunc = func;
} }
// Format the log message according to it's type // Format the log message according to it's type
std::string LogMessage::toString(LogDetailsFunc formattingFunc) const { std::string LogMessage::toString(LogDetailsFunc formattingFunc) const {
overrideLogDetailsFunc(formattingFunc); overrideLogDetailsFunc(formattingFunc);
@ -137,89 +107,82 @@ namespace g3 {
return out; return out;
} }
std::string LogMessage::timestamp(const std::string& time_look) const { std::string LogMessage::timestamp(const std::string& time_look) const {
return g3::localtime_formatted(to_system_time(_timestamp), time_look); return g3::localtime_formatted(to_system_time(_timestamp), time_look);
} }
// By copy, not by reference. See this explanation for details:
// By copy, not by reference. See this explanation for details: // http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
// http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
LogMessage& LogMessage::operator=(LogMessage other) { LogMessage& LogMessage::operator=(LogMessage other) {
swap(*this, other); swap(*this, other);
return *this; return *this;
} }
LogMessage::LogMessage(std::string file, const int line, LogMessage::LogMessage(std::string file, const int line,
std::string function, const LEVELS level) std::string function, const LEVELS level) :
: _logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString) _logDetailsToStringFunc(LogMessage::DefaultLogDetailsToString),
, _timestamp(std::chrono::high_resolution_clock::now()) _timestamp(std::chrono::high_resolution_clock::now()),
, _call_thread_id(std::this_thread::get_id()) _call_thread_id(std::this_thread::get_id())
#if defined(G3_LOG_FULL_FILENAME) #if defined(G3_LOG_FULL_FILENAME)
, _file(file) ,
_file(file)
#else #else
, _file(LogMessage::splitFileName(file)) ,
_file(LogMessage::splitFileName(file))
#endif #endif
, _file_path(file) ,
, _line(line) _file_path(file),
, _function(std::move(function)) _line(line),
, _level(level) { _function(std::move(function)),
_level(level) {
} }
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) :
LogMessage::LogMessage(const std::string& fatalOsSignalCrashMessage) LogMessage({""}, 0, {""}, internal::FATAL_SIGNAL) {
: LogMessage( {""}, 0, {""}, internal::FATAL_SIGNAL) {
_message.append(fatalOsSignalCrashMessage); _message.append(fatalOsSignalCrashMessage);
} }
LogMessage::LogMessage(const LogMessage& other) LogMessage::LogMessage(const LogMessage& other) :
: _logDetailsToStringFunc(other._logDetailsToStringFunc) _logDetailsToStringFunc(other._logDetailsToStringFunc),
, _timestamp(other._timestamp) _timestamp(other._timestamp),
, _call_thread_id(other._call_thread_id) _call_thread_id(other._call_thread_id),
, _file(other._file) _file(other._file),
, _file_path(other._file_path) _file_path(other._file_path),
, _line(other._line) _line(other._line),
, _function(other._function) _function(other._function),
, _level(other._level) _level(other._level),
, _expression(other._expression) _expression(other._expression),
, _message(other._message) { _message(other._message) {
} }
LogMessage::LogMessage(LogMessage&& other) LogMessage::LogMessage(LogMessage&& other) :
: _logDetailsToStringFunc(other._logDetailsToStringFunc) _logDetailsToStringFunc(other._logDetailsToStringFunc),
, _timestamp(other._timestamp) _timestamp(other._timestamp),
, _call_thread_id(other._call_thread_id) _call_thread_id(other._call_thread_id),
, _file(std::move(other._file)) _file(std::move(other._file)),
, _file_path(std::move(other._file_path)) _file_path(std::move(other._file_path)),
, _line(other._line) _line(other._line),
, _function(std::move(other._function)) _function(std::move(other._function)),
, _level(other._level) _level(other._level),
, _expression(std::move(other._expression)) _expression(std::move(other._expression)),
, _message(std::move(other._message)) { _message(std::move(other._message)) {
} }
std::string LogMessage::threadID() const { std::string LogMessage::threadID() const {
std::ostringstream oss; std::ostringstream oss;
oss << _call_thread_id; oss << _call_thread_id;
return oss.str(); return oss.str();
} }
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 LogMessage& details, g3::SignalType signal_id) LogMessage FatalMessage::copyToLogMessage() const {
: LogMessage(details), _signal_id(signal_id) { }
FatalMessage::FatalMessage(const FatalMessage& other)
: LogMessage(other), _signal_id(other._signal_id) {}
LogMessage FatalMessage::copyToLogMessage() const {
return LogMessage(*this); return LogMessage(*this);
} }
@ -227,5 +190,4 @@ namespace g3 {
return internal::exitReasonName(_level, _signal_id); return internal::exitReasonName(_level, _signal_id);
} }
} // namespace g3
} // g3

View File

@ -7,17 +7,18 @@
* ============================================================================*/ * ============================================================================*/
#include "g3log/logworker.hpp" #include "g3log/logworker.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/active.hpp" #include "g3log/active.hpp"
#include "g3log/g3log.hpp"
#include "g3log/future.hpp"
#include "g3log/crashhandler.hpp" #include "g3log/crashhandler.hpp"
#include "g3log/future.hpp"
#include "g3log/g3log.hpp"
#include "g3log/logmessage.hpp"
#include <iostream> #include <iostream>
namespace g3 { namespace g3 {
LogWorkerImpl::LogWorkerImpl() : _bg(kjellkod::Active::createActive()) { } LogWorkerImpl::LogWorkerImpl() :
_bg(kjellkod::Active::createActive()) {}
void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) { void LogWorkerImpl::bgSave(g3::LogMessagePtr msgPtr) {
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get())); std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
@ -28,7 +29,7 @@ namespace g3 {
} }
if (_sinks.empty()) { if (_sinks.empty()) {
std::string err_msg {"g3logworker has no sinks. Message: ["}; std::string err_msg{"g3logworker has no sinks. Message: ["};
err_msg.append(uniqueMsg.get()->toString()).append("]\n"); err_msg.append(uniqueMsg.get()->toString()).append("]\n");
std::cerr << err_msg; std::cerr << err_msg;
} }
@ -43,16 +44,13 @@ namespace g3 {
const auto level = msgPtr.get()->_level; const auto level = msgPtr.get()->_level;
const auto fatal_id = msgPtr.get()->_signal_id; const auto fatal_id = msgPtr.get()->_signal_id;
std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get())); std::unique_ptr<LogMessage> uniqueMsg(std::move(msgPtr.get()));
uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level()); uniqueMsg->write().append("\nExiting after fatal event (").append(uniqueMsg->level());
// Change output in case of a fatal signal (or windows exception) // Change output in case of a fatal signal (or windows exception)
std::string exiting = {"Fatal type: "}; std::string exiting = {"Fatal type: "};
uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason) uniqueMsg->write().append("). ").append(exiting).append(" ").append(reason).append("\nLog content flushed successfully to sink\n\n");
.append("\nLog content flushed successfully to sink\n\n");
std::cerr << uniqueMsg->toString() << std::flush; std::cerr << uniqueMsg->toString() << std::flush;
for (auto& sink : _sinks) { for (auto& sink : _sinks) {
@ -60,10 +58,9 @@ namespace g3 {
sink->send(LogMessageMover(std::move(msg))); sink->send(LogMessageMover(std::move(msg)));
} }
// This clear is absolutely necessary // This clear is absolutely necessary
// All sinks are forced to receive the fatal message above before we continue // 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); internal::exitWithDefaultSignalHandler(level, fatal_id);
// should never reach this point // should never reach this point
@ -102,15 +99,17 @@ namespace g3 {
} }
void LogWorker::save(LogMessagePtr msg) { 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) { 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) { 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()); auto token_done = g3::spawn_task(bg_addsink_call, _impl._bg.get());
token_done.wait(); token_done.wait();
} }
@ -119,8 +118,8 @@ namespace g3 {
return std::unique_ptr<LogWorker>(new LogWorker); return std::unique_ptr<LogWorker>(new LogWorker);
} }
std::unique_ptr<FileSinkHandle>LogWorker::addDefaultLogger(const std::string& log_prefix, const std::string& log_directory, const std::string& default_id) { 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); return addSink(std::make_unique<g3::FileSink>(log_prefix, log_directory, default_id), &FileSink::fileWrite);
} }
} // g3 } // namespace g3

View File

@ -14,60 +14,37 @@
#include "g3log/stacktrace_windows.hpp" #include "g3log/stacktrace_windows.hpp"
#include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#include <windows.h>
#include <cassert>
#include <g3log/g3log.hpp>
#include <map> #include <map>
#include <memory> #include <memory>
#include <cassert>
#include <vector>
#include <mutex> #include <mutex>
#include <g3log/g3log.hpp> #include <vector>
#pragma comment(lib, "dbghelp.lib") #pragma comment(lib, "dbghelp.lib")
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#error "stacktrace_win.cpp used but not on a windows system" #error "stacktrace_win.cpp used but not on a windows system"
#endif #endif
#define g3_MAP_PAIR_STRINGIFY(x) \
{ x, #x }
#define g3_MAP_PAIR_STRINGIFY(x) {x, #x}
namespace { namespace {
thread_local size_t g_thread_local_recursive_crash_check = 0; thread_local size_t g_thread_local_recursive_crash_check = 0;
const std::map<g3::SignalType, std::string> kExceptionsAsText = { const std::map<g3::SignalType, std::string> kExceptionsAsText = {
g3_MAP_PAIR_STRINGIFY(EXCEPTION_ACCESS_VIOLATION) 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_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. // Using the given context, fill in all the stack frames.
// Which then later can be interpreted to human readable text // 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; DWORD machine_type = 0;
STACKFRAME64 frame = {}; // force zeroing STACKFRAME64 frame = {}; // force zeroing
frame.AddrPC.Mode = AddrModeFlat; frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat;
@ -92,8 +69,7 @@ namespace {
frame.AddrPC.Offset = context->Esp; frame.AddrPC.Offset = context->Esp;
machine_type = IMAGE_FILE_MACHINE_I386; machine_type = IMAGE_FILE_MACHINE_I386;
#endif #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, if (StackWalk64(machine_type,
GetCurrentProcess(), GetCurrentProcess(),
GetCurrentThread(), GetCurrentThread(),
@ -110,18 +86,16 @@ namespace {
} }
} }
// extract readable text from a given stack frame. All thanks to // extract readable text from a given stack frame. All thanks to
// using SymFromAddr and SymGetLineFromAddr64 with the stack pointer // 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]; auto addr = frame_pointers[index];
std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t"; std::string frame_dump = "stack dump [" + std::to_string(index) + "]\t";
DWORD64 displacement64; DWORD64 displacement64;
DWORD displacement; DWORD displacement;
alignas(SYMBOL_INFO) char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; alignas(SYMBOL_INFO) char symbol_buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(symbol_buffer); SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME; symbol->MaxNameLen = MAX_SYM_NAME;
@ -140,10 +114,9 @@ namespace {
return frame_dump; return frame_dump;
} }
// Retrieves all the symbols for the stack frames, fills them within a text representation and returns it // 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 convertFramesToText(std::vector<uint64_t>& frame_pointers) {
std::string dump; // slightly more efficient than ostringstream std::string dump; // slightly more efficient than ostringstream
const size_t kSize = frame_pointers.size(); const size_t kSize = frame_pointers.size();
for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) { for (size_t index = 0; index < kSize && frame_pointers[index]; ++index) {
dump += getSymbolInformation(index, frame_pointers); dump += getSymbolInformation(index, frame_pointers);
@ -151,10 +124,7 @@ namespace {
} }
return dump; return dump;
} }
} // anonymous } // namespace
namespace stacktrace { namespace stacktrace {
const std::string kUnknown = {"UNKNOWN EXCEPTION"}; const std::string kUnknown = {"UNKNOWN EXCEPTION"};
@ -162,7 +132,7 @@ namespace stacktrace {
/// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx /// From MSDN GetExceptionCode http://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
std::string exceptionIdToText(g3::SignalType id) { std::string exceptionIdToText(g3::SignalType id) {
const auto iter = kExceptionsAsText.find(id); const auto iter = kExceptionsAsText.find(id);
if ( iter == kExceptionsAsText.end()) { if (iter == kExceptionsAsText.end()) {
std::string unknown = {kUnknown + ":" + std::to_string(id)}; std::string unknown = {kUnknown + ":" + std::to_string(id)};
return unknown; return unknown;
} }
@ -185,17 +155,15 @@ namespace stacktrace {
} }
/// helper function: retrieve stackdump, starting from an exception pointer /// 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; auto context = info->ContextRecord;
return stackdump(context); return stackdump(context);
} }
/// main stackdump function. retrieve stackdump, from the given 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 scenarios 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"}; std::string recursive_crash = {"\n\n\n***** Recursive crash detected"};
recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n"); recursive_crash.append(", cannot continue stackdump traversal. *****\n\n\n");
return recursive_crash; return recursive_crash;
@ -208,16 +176,15 @@ namespace stacktrace {
const BOOL kLoadSymModules = TRUE; const BOOL kLoadSymModules = TRUE;
const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules); const auto initialized = SymInitialize(GetCurrentProcess(), nullptr, kLoadSymModules);
if (TRUE != initialized) { 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()); SymCleanup(GetCurrentProcess());
}); // Raii sym cleanup }); // Raii sym cleanup
constexpr size_t kmax_frame_dump_size = 64; constexpr size_t kmax_frame_dump_size = 64;
std::vector<uint64_t> frame_pointers(kmax_frame_dump_size); std::vector<uint64_t> frame_pointers(kmax_frame_dump_size);
// C++11: size set and values are zeroed // C++11: size set and values are zeroed
assert(frame_pointers.size() == kmax_frame_dump_size); assert(frame_pointers.size() == kmax_frame_dump_size);
@ -226,4 +193,4 @@ namespace stacktrace {
} }
} }
} // stacktrace } // namespace stacktrace

View File

@ -8,30 +8,38 @@
#include "g3log/time.hpp" #include "g3log/time.hpp"
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <cstring>
#include <cmath>
#include <chrono>
#include <cassert>
#include <iomanip>
#ifdef __MACH__ #ifdef __MACH__
#include <sys/time.h> #include <sys/time.h>
#endif #endif
namespace g3 { namespace g3 {
namespace internal { namespace internal {
const std::string kFractionalIdentier = "%f"; const std::string kFractionalIdentier = "%f";
const size_t kFractionalIdentierSize = 2; const size_t kFractionalIdentierSize = 2;
Fractional getFractional(const std::string& format_buffer, size_t pos) { Fractional getFractional(const std::string& format_buffer, size_t pos) {
char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0'); char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0');
Fractional type = Fractional::NanosecondDefault; Fractional type = Fractional::NanosecondDefault;
switch (ch) { switch (ch) {
case '3': type = Fractional::Millisecond; break; case '3':
case '6': type = Fractional::Microsecond; break; type = Fractional::Millisecond;
case '9': type = Fractional::Nanosecond; break; break;
default: type = Fractional::NanosecondDefault; break; case '6':
type = Fractional::Microsecond;
break;
case '9':
type = Fractional::Nanosecond;
break;
default:
type = Fractional::NanosecondDefault;
break;
} }
return type; return type;
} }
@ -46,25 +54,24 @@ namespace g3 {
duration -= sec_duration; duration -= sec_duration;
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count(); auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
auto zeroes = 9; // default ns auto zeroes = 9; // default ns
auto digitsToCut = 1; // default ns, divide by 1 makes no change auto digitsToCut = 1; // default ns, divide by 1 makes no change
switch (fractional) { switch (fractional) {
case Fractional::Millisecond : { case Fractional::Millisecond: {
zeroes = 3; zeroes = 3;
digitsToCut = 1000000; digitsToCut = 1000000;
break; break;
} }
case Fractional::Microsecond : { case Fractional::Microsecond: {
zeroes = 6; zeroes = 6;
digitsToCut = 1000; digitsToCut = 1000;
break; break;
} }
case Fractional::Nanosecond : case Fractional::Nanosecond:
case Fractional::NanosecondDefault: case Fractional::NanosecondDefault:
default: default:
zeroes = 9; zeroes = 9;
digitsToCut = 1; digitsToCut = 1;
} }
ns /= digitsToCut; ns /= digitsToCut;
@ -76,8 +83,8 @@ namespace g3 {
// iterating through every "%f" instance in the format string // iterating through every "%f" instance in the format string
auto identifierExtraSize = 0; auto identifierExtraSize = 0;
for (size_t pos = 0; for (size_t pos = 0;
(pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos; (pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos;
pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) { pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) {
// figuring out whether this is nano, micro or milli identifier // figuring out whether this is nano, micro or milli identifier
auto type = g3::internal::getFractional(format_buffer, pos); auto type = g3::internal::getFractional(format_buffer, pos);
auto value = g3::internal::to_string(ts, type); auto value = g3::internal::to_string(ts, type);
@ -92,10 +99,8 @@ namespace g3 {
return format_buffer; return format_buffer;
} }
} // internal } // namespace internal
} // g3 } // namespace g3
namespace g3 { namespace g3 {
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)" // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
@ -106,11 +111,11 @@ namespace g3 {
std::ostringstream oss; std::ostringstream oss;
oss.fill('0'); oss.fill('0');
// BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* " // 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); oss << std::put_time(const_cast<struct tm*>(tmb), c_time_format);
return oss.str(); return oss.str();
#else // LINUX #else // LINUX
const size_t size = 1024; const size_t size = 1024;
char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time. 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 // ... also ... This is way more buffer space then we need
auto success = std::strftime(buffer, size, c_time_format, tmb); auto success = std::strftime(buffer, size, c_time_format, tmb);
@ -127,23 +132,20 @@ namespace g3 {
#endif #endif
} }
tm localtime(std::time_t ts) { tm localtime(std::time_t ts) {
struct tm tm_snapshot; struct tm tm_snapshot;
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
localtime_s(&tm_snapshot, &ts); // windsows localtime_s(&tm_snapshot, &ts); // windsows
#else #else
localtime_r(&ts, &tm_snapshot); // POSIX localtime_r(&ts, &tm_snapshot); // POSIX
#endif #endif
return tm_snapshot; return tm_snapshot;
} }
std::string localtime_formatted(const g3::system_time_point& ts, const std::string& time_format) { 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 format_buffer = internal::localtime_formatted_fractions(ts, time_format);
auto time_point = std::chrono::system_clock::to_time_t(ts); auto time_point = std::chrono::system_clock::to_time_t(ts);
std::tm t = localtime(time_point); std::tm t = localtime(time_point);
return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S"); 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 <gtest/gtest.h>
#include <iostream> #include <iostream>
int main(int argc, char *argv[]) int main(int argc, char* argv[]) {
{
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
int return_value = RUN_ALL_TESTS(); 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; return return_value;
} }

View File

@ -7,10 +7,10 @@
* ============================================================================*/ * ============================================================================*/
// through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE // through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE
#include "performance.h"
#include <thread>
#include <iostream>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <thread>
#include "performance.h"
#if defined(G3LOG_PERFORMANCE) #if defined(G3LOG_PERFORMANCE)
const std::string title = "G3LOG"; const std::string title = "G3LOG";
@ -27,8 +27,7 @@ const std::string g_path = "/tmp/";
#endif #endif
using namespace g3_test; using namespace g3_test;
int main(int argc, char **argv) int main(int argc, char** argv) {
{
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
std::cerr << "G3_DYNAMIC_LOGGING is enabled" << std::endl; std::cerr << "G3_DYNAMIC_LOGGING is enabled" << std::endl;
#else #else
@ -36,61 +35,57 @@ int main(int argc, char **argv)
#endif #endif
size_t number_of_threads = 0; size_t number_of_threads = 0;
if (argc == 2) if (argc == 2) {
{
number_of_threads = atoi(argv[1]); 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; std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1; return 1;
} }
std::ostringstream thread_count_oss; std::ostringstream thread_count_oss;
thread_count_oss << number_of_threads; 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_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_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
std::ostringstream oss; std::ostringstream oss;
const uint64_t us_to_s = 1000000; 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 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; 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); writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream oss.str(""); // clear the stream
#if defined(G3LOG_PERFORMANCE) #if defined(G3LOG_PERFORMANCE)
auto worker = g3::LogWorker::createLogWorker(); 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()); g3::initializeLogging(worker.get());
#elif defined(GOOGLE_GLOG_PERFORMANCE) #elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
#endif #endif
auto start_time = std::chrono::high_resolution_clock::now(); 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 // kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas // 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; std::ostringstream count;
count << idx + 1; 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; std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(doLogWrites, thread_name); 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(); threads[idx].join();
} }
auto application_end_time = std::chrono::high_resolution_clock::now(); auto application_end_time = std::chrono::high_resolution_clock::now();
delete [] threads; delete[] threads;
#if defined(G3LOG_PERFORMANCE) #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) #elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging(); google::ShutdownGoogleLogging();
#endif #endif
@ -99,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 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(); 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 << "[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 << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << 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; 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 // through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G3LOG_PERFORMANCE
#include "performance.h" #include "performance.h"
#include <thread>
#include <vector>
#include <map>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <map>
#include <thread>
#include <vector>
#if defined(G3LOG_PERFORMANCE) #if defined(G3LOG_PERFORMANCE)
const std::string title { const std::string title{
"G3LOG" "G3LOG"};
};
#elif defined(GOOGLE_GLOG_PERFORMANCE) #elif defined(GOOGLE_GLOG_PERFORMANCE)
const std::string title { const std::string title{
"GOOGLE__GLOG" "GOOGLE__GLOG"};
};
#else #else
#error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined #error G3LOG_PERFORMANCE or GOOGLE_GLOG_PERFORMANCE was not defined
#endif #endif
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
const std::string g_path { const std::string g_path{
"./" "./"};
};
#else #else
const std::string g_path { const std::string g_path{
"/tmp/" "/tmp/"};
};
#endif #endif
using namespace g3_test; using namespace g3_test;
// //
// OK: The code below isn't pretty but it works. Lots and lots of log entries // OK: The code below isn't pretty but it works. Lots and lots of log entries
// to keep track of! // to keep track of!
// //
int main(int argc, char** argv) int main(int argc, char** argv) {
{ size_t number_of_threads{0};
size_t number_of_threads {0}; if (argc == 2) {
if (argc == 2)
{
number_of_threads = atoi(argv[1]); 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; std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1; return 1;
} }
std::ostringstream thread_count_oss; std::ostringstream thread_count_oss;
thread_count_oss << number_of_threads; 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_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_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 std::string g_measurement_bucket_dump = g_path + g_prefix_log_name + "_RESULT_buckets.txt";
const uint64_t us_to_ms { const uint64_t us_to_ms{
1000 1000};
}; const uint64_t us_to_s{
const uint64_t us_to_s { 1000000};
1000000
};
std::ostringstream oss; 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 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 { const uint64_t xtra_margin{
2 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); writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream oss.str(""); // clear the stream
#if defined(G3LOG_PERFORMANCE) #if defined(G3LOG_PERFORMANCE)
auto worker = g3::LogWorker::createLogWorker(); 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()); g3::initializeLogging(worker.get());
#elif defined(GOOGLE_GLOG_PERFORMANCE) #elif defined(GOOGLE_GLOG_PERFORMANCE)
@ -97,31 +83,27 @@ int main(int argc, char** argv)
// kiss: just loop, create threads, store them then join // kiss: just loop, create threads, store them then join
// could probably do this more elegant with lambdas // 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); threads_result[idx].reserve(g_iterations);
} }
auto start_time = std::chrono::high_resolution_clock::now(); 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; std::ostringstream count;
count << idx + 1; 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; std::cout << "Creating thread: " << thread_name << std::endl;
threads[idx] = std::thread(measurePeakDuringLogWrites, thread_name, std::ref(threads_result[idx])); threads[idx] = std::thread(measurePeakDuringLogWrites, thread_name, std::ref(threads_result[idx]));
} }
// wait for thread finishing // 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(); threads[idx].join();
} }
auto application_end_time = std::chrono::high_resolution_clock::now(); auto application_end_time = std::chrono::high_resolution_clock::now();
delete [] threads; delete[] threads;
#if defined(G3LOG_PERFORMANCE) #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) #elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging(); google::ShutdownGoogleLogging();
#endif #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 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(); 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 << "[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 << "[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 << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us / (number_of_threads * g_iterations) << " us]" << 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) for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
{ std::vector<uint64_t>& t_result = threads_result[idx];
std::vector<uint64_t> &t_result = threads_result[idx];
uint64_t worstUs = (*std::max_element(t_result.begin(), t_result.end())); 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); writeTextToFile(g_measurement_dump, oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl; 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 // now split the result in buckets of 10ms each so that it's obvious how the peaks go
std::vector<uint64_t> all_measurements; std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations * number_of_threads); all_measurements.reserve(g_iterations * number_of_threads);
for (uint64_t idx = 0; idx < number_of_threads; ++idx) for (uint64_t idx = 0; idx < number_of_threads; ++idx) {
{ std::vector<uint64_t>& t_result = threads_result[idx];
std::vector<uint64_t> &t_result = threads_result[idx];
all_measurements.insert(all_measurements.end(), t_result.begin(), t_result.end()); 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;
std::map<uint64_t, uint64_t> value_amounts_for_0ms_bucket; std::map<uint64_t, uint64_t> value_amounts_for_0ms_bucket;
for (auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter) for (auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter) {
{ uint64_t value = (*iter) / us_to_ms; // convert to ms
uint64_t value = (*iter) / us_to_ms; // convert to ms ++value_amounts[value]; // asuming uint64_t is default 0 when initialized
++value_amounts[value]; // asuming uint64_t is default 0 when initialized
if (0 == value) { if (0 == value) {
++value_amounts_for_0ms_bucket[*iter]; ++value_amounts_for_0ms_bucket[*iter];
@ -172,11 +152,12 @@ int main(int argc, char** argv)
oss.str(""); oss.str("");
oss << "Number of values rounded to milliseconds and put to [millisecond bucket] were dumped to file: " << g_measurement_bucket_dump << std::endl; 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()) { 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"; oss << "\n";
} } else {
else { oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n"
oss << "Format:bucket_of_ms, number_of_values_in_bucket\n\n" << std::endl; << std::endl;
} }
std::cout << oss.str() << 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"; 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; 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; return 0;
} }

View File

@ -11,17 +11,13 @@
# ============================================================================ # ============================================================================
# TEST OPTIONS: Turn OFF the ones that is of no interest to you # TEST OPTIONS: Turn OFF the ones that is of no interest to you
# ---- by default all is OFF: except 'g3log-FATAL-example ----- # ---- by default unit tests and g3log-FATAL-example are enabled.
# ---- the reason for this is that # Performance tests are turned off by default since they were not tested on Windows.
# ----- 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
# ============================================================================ # ============================================================================
# Unit test for g3log (cmake -DUSE_G3LOG_UNIT_TEST=ON ..) # Unit test for g3log (cmake -DUSE_G3LOG_UNIT_TEST=ON ..)
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 # 4. create the unit tests for g3log --- ONLY TESTED THE UNIT TEST ON LINUX

View File

@ -8,19 +8,19 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
#include <thread> #include <thread>
#include <chrono> #include <vector>
#include <atomic>
#include "testing_helpers.h"
#include "g3log/sink.hpp"
#include "g3log/sinkwrapper.hpp"
#include "g3log/sinkhandle.hpp"
#include "g3log/logmessage.hpp"
#include "g3log/generated_definitions.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 std;
using namespace testing_helpers; using namespace testing_helpers;
@ -29,24 +29,25 @@ class CoutSink {
stringstream buffer; stringstream buffer;
unique_ptr<ScopedOut> scope_ptr; unique_ptr<ScopedOut> scope_ptr;
CoutSink() : scope_ptr(std::make_unique<ScopedOut>(std::cout, &buffer)) {} CoutSink() :
public: scope_ptr(std::make_unique<ScopedOut>(std::cout, &buffer)) {}
public:
void clear() { buffer.str(""); } void clear() { buffer.str(""); }
std::string string() { return buffer.str(); } std::string string() { return buffer.str(); }
void save(g3::LogMessageMover msg) { std::cout << msg.get().message(); } void save(g3::LogMessageMover msg) { std::cout << msg.get().message(); }
virtual ~CoutSink() final {} 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 { struct StringSink {
std::string raw; 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() { std::string string() {
return raw; return raw;
} }
}; };
namespace { namespace {
typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr; typedef std::shared_ptr<g3::internal::SinkWrapper> SinkWrapperPtr;
} }
@ -54,7 +55,7 @@ namespace {
namespace g3 { namespace g3 {
class Worker { 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; std::unique_ptr<kjellkod::Active> _bg;
void bgSave(std::string msg) { void bgSave(std::string msg) {
@ -65,11 +66,10 @@ namespace g3 {
} }
} }
public: public:
Worker() :
Worker() : _bg { _bg{
kjellkod::Active::createActive() kjellkod::Active::createActive()} {
} {
} }
~Worker() { ~Worker() {
@ -82,35 +82,31 @@ namespace g3 {
_bg->send([this, msg] { bgSave(msg); }); _bg->send([this, msg] { bgSave(msg); });
} }
template <typename T, typename DefaultLogCall>
template<typename T, typename DefaultLogCall> std::unique_ptr<SinkHandle<T>> addSink(std::unique_ptr<T> unique, DefaultLogCall call) {
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 sink = std::make_shared < internal::Sink<T> > (std::move(unique), call); auto add_sink_call = [this, sink] {
auto add_sink_call = [this, sink] { _container.push_back(sink); }; _container.push_back(sink);
};
auto wait_result = g3::spawn_task(add_sink_call, _bg.get()); auto wait_result = g3::spawn_task(add_sink_call, _bg.get());
wait_result.wait(); wait_result.wait();
auto handle = std::make_unique< SinkHandle<T> >(sink); auto handle = std::make_unique<SinkHandle<T>>(sink);
return handle; return handle;
} }
}; };
} // g3 } // namespace g3
using namespace g3; using namespace g3;
using namespace g3::internal; using namespace g3::internal;
TEST(ConceptSink, CreateHandle) { TEST(ConceptSink, CreateHandle) {
Worker worker; Worker worker;
auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save); auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save);
ASSERT_NE(nullptr, handle.get()); ASSERT_NE(nullptr, handle.get());
} }
TEST(ConceptSink, OneSink__VerifyMsgIn) { TEST(ConceptSink, OneSink__VerifyMsgIn) {
Worker worker; Worker worker;
auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save); auto handle = worker.addSink(CoutSink::createSink(), &CoutSink::save);
@ -122,19 +118,16 @@ TEST(ConceptSink, OneSink__VerifyMsgIn) {
ASSERT_NE(pos, std::string::npos); ASSERT_NE(pos, std::string::npos);
} }
TEST(ConceptSink, DualSink__VerifyMsgIn) { TEST(ConceptSink, DualSink__VerifyMsgIn) {
Worker worker; Worker worker;
auto h1 = worker.addSink(CoutSink::createSink(), &CoutSink::save); auto h1 = worker.addSink(CoutSink::createSink(), &CoutSink::save);
auto h2 = worker.addSink(std::make_unique<StringSink>(), &StringSink::append); auto h2 = worker.addSink(std::make_unique<StringSink>(), &StringSink::append);
worker.save("Hello World!"); worker.save("Hello World!");
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto first = h1->call(&CoutSink::string); auto first = h1->call(&CoutSink::string);
auto second = h2->call(&StringSink::string); auto second = h2->call(&StringSink::string);
ASSERT_EQ("Hello World!", first.get()); ASSERT_EQ("Hello World!", first.get());
ASSERT_EQ("Hello World!", second.get()); ASSERT_EQ("Hello World!", second.get());
} }
@ -151,10 +144,10 @@ TEST(ConceptSink, DeletedSink__Exptect_badweak_ptr___exception) {
namespace { namespace {
using AtomicBooleanPtr = std::shared_ptr<std::atomic<bool>>; using AtomicBooleanPtr = std::shared_ptr<std::atomic<bool>>;
using AtomicIntegerPtr = std::shared_ptr<std::atomic<int>> ; using AtomicIntegerPtr = std::shared_ptr<std::atomic<int>>;
using BoolList = std::vector<AtomicBooleanPtr> ; using BoolList = std::vector<AtomicBooleanPtr>;
using IntVector = std::vector<AtomicIntegerPtr>; using IntVector = std::vector<AtomicIntegerPtr>;
} } // namespace
TEST(ConceptSink, OneHundredSinks_part1) { TEST(ConceptSink, OneHundredSinks_part1) {
BoolList flags; BoolList flags;
@ -162,8 +155,8 @@ TEST(ConceptSink, OneHundredSinks_part1) {
size_t NumberOfItems = 100; size_t NumberOfItems = 100;
for (size_t index = 0; index < NumberOfItems; ++index) { for (size_t index = 0; index < NumberOfItems; ++index) {
flags.push_back(make_shared < atomic<bool >> (false)); flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared < atomic<int >> (0)); counts.push_back(make_shared<atomic<int>>(0));
} }
{ {
@ -190,10 +183,9 @@ TEST(ConceptSink, OneHundredSinks_part1) {
cout << "test one hundred sinks is finished\n"; cout << "test one hundred sinks is finished\n";
} }
TEST(ConceptSink, OneHundredSinks_part2) { TEST(ConceptSink, OneHundredSinks_part2) {
using BoolPtrVector = std::vector<AtomicBooleanPtr> ; using BoolPtrVector = std::vector<AtomicBooleanPtr>;
using IntPtrVector = vector<AtomicIntegerPtr> ; using IntPtrVector = vector<AtomicIntegerPtr>;
BoolPtrVector flags; BoolPtrVector flags;
IntPtrVector counts; IntPtrVector counts;
@ -207,18 +199,18 @@ TEST(ConceptSink, OneHundredSinks_part2) {
auto worker = g3::LogWorker::createLogWorker(); auto worker = g3::LogWorker::createLogWorker();
size_t index = 0; size_t index = 0;
for (auto& flag : flags) { for (auto& flag : flags) {
auto& count = counts[index++]; auto& count = counts[index++];
// ignore the handle // ignore the handle
worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg); worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
} }
// 100 logs // 100 logs
for (int index = 0; index < NumberOfItems; ++index) { for (int index = 0; index < NumberOfItems; ++index) {
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)}; LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
message.get()->write().append("Hello to 100 receivers :)"); message.get()->write().append("Hello to 100 receivers :)");
worker->save(message); worker->save(message);
} }
} // RAII exit } // RAII exit
// 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 // 100 logging receivers will get their message to exit after all messages are
@ -229,18 +221,17 @@ TEST(ConceptSink, OneHundredSinks_part2) {
for (auto& flag : flags) { for (auto& flag : flags) {
auto& count = counts[index++]; auto& count = counts[index++];
EXPECT_TRUE(flag->load()); EXPECT_TRUE(flag->load());
EXPECT_EQ(NumberOfItems, count->load()); EXPECT_EQ(NumberOfItems, count->load());
} }
} }
TEST(ConceptSink, OneSinkWithHandleOutOfScope) { TEST(ConceptSink, OneSinkWithHandleOutOfScope) {
AtomicBooleanPtr flag = make_shared<atomic<bool>>(false); AtomicBooleanPtr flag = make_shared<atomic<bool>>(false);
AtomicIntegerPtr count = make_shared<atomic<int>>(0); AtomicIntegerPtr count = make_shared<atomic<int>>(0);
{ {
auto worker = g3::LogWorker::createLogWorker(); auto worker = g3::LogWorker::createLogWorker();
{ {
auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg); auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
} }
EXPECT_FALSE(flag->load()); EXPECT_FALSE(flag->load());
EXPECT_TRUE(0 == count->load()); EXPECT_TRUE(0 == count->load());

View File

@ -9,95 +9,85 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <chrono> #include <chrono>
#include <thread>
#include <future>
#include <string>
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <future>
#include <memory> #include <memory>
#include "g3log/time.hpp" #include <string>
#include <thread>
#include "g3log/future.hpp" #include "g3log/future.hpp"
#include "g3log/time.hpp"
std::future<std::string> sillyFutureReturn() {
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::packaged_task<std::string()> task([](){return std::string("Hello Future");}); // wrap the function std::thread(std::move(task)).detach(); // launch on a thread
std::future<std::string> result = task.get_future(); // get a future std::cout << "Waiting...";
std::thread(std::move(task)).detach(); // launch on a thread result.wait();
std::cout << "Waiting..."; return result; // already wasted
result.wait();
return result; // already wasted
} }
TEST(Configuration, FutureSilly) {
TEST(Configuration, FutureSilly) std::string hello = sillyFutureReturn().get();
{ ASSERT_STREQ(hello.c_str(), "Hello Future");
std::string hello = sillyFutureReturn().get();
ASSERT_STREQ(hello.c_str(), "Hello Future");
} }
struct MsgType struct MsgType {
{ std::string msg_;
std::string msg_; MsgType(std::string m) :
MsgType(std::string m): msg_(m){}; msg_(m){};
std::string msg(){return msg_;} std::string msg() { return msg_; }
}; };
TEST(TestOf_CopyableCall, Expecting_SmoothSailing) {
TEST(TestOf_CopyableCall, Expecting_SmoothSailing) using namespace kjellkod;
{ const std::string str("Hello from struct");
using namespace kjellkod; MsgType type(str);
const std::string str("Hello from struct"); std::unique_ptr<Active> bgWorker(Active::createActive());
MsgType type(str); std::future<std::string> fstring =
std::unique_ptr<Active> bgWorker(Active::createActive()); g3::spawn_task(std::bind(&MsgType::msg, type), bgWorker.get());
std::future<std::string> fstring = ASSERT_STREQ(str.c_str(), fstring.get().c_str());
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);
TEST(TestOf_CopyableLambdaCall, Expecting_AllFine) auto fstring_standalone = g3::spawn_task(msg_lambda, bgWorker.get());
{ ASSERT_STREQ(expected.c_str(), fstring_standalone.get().c_str());
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;
template<typename F> vec.push_back(g3::MoveOnCopy<task_type>(std::move(task)));
std::future<std::invoke_result_t<F>> ObsoleteSpawnTask(F f) std::thread(std::move(vec.back())).detach();
{ result.wait();
typedef std::invoke_result_t<F> result_type; return std::move(result);
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) TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString) {
{ std::string str("Hello");
std::string str("Hello"); std::string expected(str + str);
std::string expected(str+str); auto msg_lambda = [=]() {
auto msg_lambda=[=](){return (str+str);}; return (str + str);
auto future_string = ObsoleteSpawnTask(msg_lambda); };
auto future_string = ObsoleteSpawnTask(msg_lambda);
ASSERT_STREQ(expected.c_str(), future_string.get().c_str()); ASSERT_STREQ(expected.c_str(), future_string.get().c_str());
} }
// gcc thread example below // gcc thread example below
// tests code below copied from mail-list conversion between // tests code below copied from mail-list conversion between
@ -105,67 +95,55 @@ TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString)
// http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html // http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
// -------------------------------------------------------------- // --------------------------------------------------------------
namespace WORKING namespace WORKING {
{ using namespace g3;
using namespace g3;
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <iostream>
#include <future> #include <future>
#include <iostream>
#include <thread> #include <thread>
#include <vector> #include <vector>
std::vector<std::function<void()>> vec; std::vector<std::function<void()>> vec;
template<typename F> template <typename F>
std::future<std::invoke_result_t<F>> spawn_task(F f) std::future<std::invoke_result_t<F>> spawn_task(F f) {
{ typedef std::invoke_result_t<F> result_type;
typedef std::invoke_result_t<F> result_type; typedef std::packaged_task<result_type()> task_type;
typedef std::packaged_task<result_type()> task_type;
task_type task(std::move(f)); task_type task(std::move(f));
std::future<result_type> res = task.get_future(); std::future<result_type> res = task.get_future();
vec.push_back( vec.push_back(
MoveOnCopy<task_type>( MoveOnCopy<task_type>(
std::move(task))); std::move(task)));
std::thread([]() std::thread([]() {
{ auto task = std::move(vec.back());
auto task = std::move(vec.back()); vec.pop_back();
vec.pop_back(); task();
task(); }).detach();
}
).detach();
return std::move(res); return std::move(res);
} }
double get_res() {
return 42.2;
}
std::string msg3() {
return "msg3";
}
} // namespace WORKING
double get_res() TEST(Yalla, Testar) {
{ using namespace WORKING;
return 42.2; auto f = spawn_task(get_res);
} ASSERT_EQ(42.2, f.get());
std::string msg3(){return "msg3";} auto f2 = spawn_task(msg3);
} // WORKING ASSERT_EQ("msg3", f2.get());
TEST(Yalla, Testar) ASSERT_TRUE(true);
{
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,13 +6,11 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
#include <gtest/gtest.h> #include <gtest/gtest.h>
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#include "g3log/stacktrace_windows.hpp"
#include <windows.h> #include <windows.h>
#include "g3log/stacktrace_windows.hpp"
TEST(CrashHandler_Windows, ExceptionType) { TEST(CrashHandler_Windows, ExceptionType) {
EXPECT_EQ(stacktrace::exceptionIdToText(123), "UNKNOWN EXCEPTION:123"); EXPECT_EQ(stacktrace::exceptionIdToText(123), "UNKNOWN EXCEPTION:123");

View File

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

View File

@ -8,19 +8,19 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "g3log/g3log.hpp" #include "g3log/g3log.hpp"
#include "g3log/generated_definitions.hpp"
#include "g3log/loglevels.hpp"
#include "g3log/logworker.hpp" #include "g3log/logworker.hpp"
#include "testing_helpers.h" #include "testing_helpers.h"
#include "g3log/loglevels.hpp"
#include "g3log/generated_definitions.hpp"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <exception>
#include <iomanip>
#include <memory> #include <memory>
#include <string> #include <string>
#include <cstdio>
#include <thread> #include <thread>
#include <chrono>
#include <exception>
#include <algorithm>
#include <iomanip>
namespace { namespace {
const std::string log_directory = "./"; const std::string log_directory = "./";
@ -36,12 +36,10 @@ namespace {
++g_fatal_counter; ++g_fatal_counter;
} }
} // end anonymous namespace } // end anonymous namespace
using namespace testing_helpers; using namespace testing_helpers;
/// THIS MUST BE THE FIRST UNIT TEST TO RUN! If any unit test run before this /// 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 /// 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 /// instantiation so we do different test for dynamic logging levels
@ -71,18 +69,18 @@ TEST(Initialization, No_Logger_Initialized___Expecting_LOG_calls_to_be_Still_OKi
std::string err_msg1 = "Hey. I am not instantiated but I still should not crash. (I am g3log)"; std::string err_msg1 = "Hey. I am not instantiated but I still should not crash. (I am g3log)";
std::string err_msg3_ignored = "This uninitialized message should be ignored"; std::string err_msg3_ignored = "This uninitialized message should be ignored";
try { try {
LOG(INFO) << err_msg1; // nothing happened. level not ON LOG(INFO) << err_msg1; // nothing happened. level not ON
LOG(INFO) << err_msg3_ignored; // nothing happened. level not ON LOG(INFO) << err_msg3_ignored; // nothing happened. level not ON
} catch (std::exception& e) { } catch (std::exception& e) {
ADD_FAILURE() << "Should never have thrown even if it is not instantiated. Ignored exception: " << e.what(); 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"; std::string good_msg1 = "This message could have pulled in the uninitialized_call message";
LOG(INFO) << good_msg1; 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_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]";
ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]"; ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]";
ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]";
@ -105,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(); 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"; std::string good_msg1 = "This message will pull in also the uninitialized_call message";
LOG(INFO) << good_msg1; 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_TRUE(verifyContent(content, err_msg1)) << "Content: [" << content << "]";
ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]"; ASSERT_FALSE(verifyContent(content, err_msg3_ignored)) << "Content: [" << content << "]";
ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]"; ASSERT_TRUE(verifyContent(content, good_msg1)) << "Content: [" << content << "]";
} }
#endif // #ifdef G3_DYNAMIC_LOGGING #endif // #ifdef G3_DYNAMIC_LOGGING
TEST(Basics, Levels_StdFind) { TEST(Basics, Levels_StdFind) {
std::vector<LEVELS> levels = {INFO, WARNING, FATAL}; std::vector<LEVELS> levels = {INFO, WARNING, FATAL};
@ -140,7 +138,6 @@ TEST(Basics, Levels_StdFind) {
EXPECT_FALSE(wasNotFoundIterator != levels.end()); EXPECT_FALSE(wasNotFoundIterator != levels.end());
} }
TEST(Basics, Levels_Operator) { TEST(Basics, Levels_Operator) {
auto info = INFO; auto info = INFO;
auto warning = WARNING; auto warning = WARNING;
@ -159,10 +156,10 @@ TEST(Basics, Shutdown) {
LOG(INFO) << "First message buffered, then flushed"; LOG(INFO) << "First message buffered, then flushed";
LOG(INFO) << "Second message still in the buffer"; LOG(INFO) << "Second message still in the buffer";
LOG(INFO) << "Not yet shutdown. This message should make it"; 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)"; 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 file_content = readFileToText(logger.logFile()); // logger is already reset
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, "First message buffered, then flushed")); 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, "Second message still in the buffer"));
@ -175,11 +172,11 @@ TEST(Basics, Shutdownx2) {
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
LOG(INFO) << "Not yet shutdown. This message should make it"; 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)
g3::internal::shutDownLogging(); // already called in reset, but safe to call again 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)"; LOG(INFO) << "Logger is shutdown,. this message will not make it (but it's safe to try)";
file_content = readFileToText(logger.logFile()); // already reset file_content = readFileToText(logger.logFile()); // already reset
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, "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)")); EXPECT_FALSE(verifyContent(file_content, "Logger is shutdown,. this message will not make it (but it's safe to try)"));
@ -193,9 +190,10 @@ TEST(Basics, ShutdownActiveLogger) {
EXPECT_TRUE(g3::internal::shutDownLoggingForActiveOnly(logger._scope->get())); 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)"; LOG(INFO) << "Logger is shutdown,. this message will not make it (but it's safe to try)";
file_content = logger.resetAndRetrieveContent(); 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)")); EXPECT_FALSE(verifyContent(file_content, "Logger is shutdown,. this message will not make it (but it's safe to try)"));
} }
@ -208,13 +206,12 @@ TEST(Basics, DoNotShutdownActiveLogger) {
EXPECT_FALSE(g3::internal::shutDownLoggingForActiveOnly(duplicateLogWorker.get())); EXPECT_FALSE(g3::internal::shutDownLoggingForActiveOnly(duplicateLogWorker.get()));
LOG(INFO) << "Logger is (NOT) shutdown,. this message WILL make it"; LOG(INFO) << "Logger is (NOT) shutdown,. this message WILL make it";
file_content = logger.resetAndRetrieveContent(); 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, "Not yet shutdown. This message should make it"));
EXPECT_TRUE(verifyContent(file_content, "Logger is (NOT) shutdown,. this message WILL make it")) << file_content; EXPECT_TRUE(verifyContent(file_content, "Logger is (NOT) shutdown,. this message WILL make it")) << file_content;
} }
TEST(LOGTest, LOG) { TEST(LOGTest, LOG) {
std::string file_content; std::string file_content;
{ {
@ -222,20 +219,17 @@ TEST(LOGTest, LOG) {
EXPECT_TRUE(g3::logLevel(INFO)); EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(FATAL)); EXPECT_TRUE(g3::logLevel(FATAL));
LOG(INFO) << "test LOG(INFO)"; LOG(INFO) << "test LOG(INFO)";
logger.reset(); // force flush of logger logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile()); 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(verifyContent(file_content, "test LOG(INFO)"));
EXPECT_TRUE(g3::logLevel(INFO)); EXPECT_TRUE(g3::logLevel(INFO));
EXPECT_TRUE(g3::logLevel(FATAL)); EXPECT_TRUE(g3::logLevel(FATAL));
} }
// printf-type log // printf-type log
TEST(LogTest, LOG_F) { TEST(LogTest, LOG_F) {
std::string file_content; std::string file_content;
{ {
@ -245,18 +239,15 @@ TEST(LogTest, LOG_F) {
LOGF(INFO, std::string(t_info + "%d").c_str(), 123); LOGF(INFO, std::string(t_info + "%d").c_str(), 123);
LOGF(G3LOG_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"); 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()); 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_info2));
ASSERT_TRUE(verifyContent(file_content, t_debug3)); ASSERT_TRUE(verifyContent(file_content, t_debug3));
ASSERT_TRUE(verifyContent(file_content, t_warning3)); ASSERT_TRUE(verifyContent(file_content, t_warning3));
} }
// stream-type log // stream-type log
TEST(LogTest, LOG) { TEST(LogTest, LOG) {
std::string file_content; std::string file_content;
@ -265,9 +256,9 @@ TEST(LogTest, LOG) {
LOG(INFO) << t_info << 123; LOG(INFO) << t_info << 123;
LOG(G3LOG_DEBUG) << t_debug << std::setprecision(7) << 1.123456f; LOG(G3LOG_DEBUG) << t_debug << std::setprecision(7) << 1.123456f;
LOG(WARNING) << t_warning << "yello"; LOG(WARNING) << t_warning << "yello";
logger.reset(); // force flush of logger logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile()); 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_info2));
ASSERT_TRUE(verifyContent(file_content, t_debug3)); ASSERT_TRUE(verifyContent(file_content, t_debug3));
@ -278,12 +269,12 @@ TEST(LogTest, LOG_after_if) {
std::string file_content; std::string file_content;
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
if(false == file_content.empty()) if (false == file_content.empty())
LOG(INFO) << "This-should-NOT-show-up"; LOG(INFO) << "This-should-NOT-show-up";
else else
LOG(INFO) << "This-should-show-up"; LOG(INFO) << "This-should-show-up";
logger.reset(); // force flush of logger logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile()); file_content = readFileToText(logger.logFile());
} }
@ -291,18 +282,17 @@ TEST(LogTest, LOG_after_if) {
ASSERT_TRUE(verifyContent(file_content, "This-should-show-up")); ASSERT_TRUE(verifyContent(file_content, "This-should-show-up"));
} }
TEST(LogTest, LOG_after_if_with_parentesis) { TEST(LogTest, LOG_after_if_with_parentesis) {
std::string file_content; std::string file_content;
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
if(false == file_content.empty()) { if (false == file_content.empty()) {
LOG(INFO) << "This-should-NOT-show-up"; LOG(INFO) << "This-should-NOT-show-up";
} else { } else {
LOG(INFO) << "This-should-show-up"; LOG(INFO) << "This-should-show-up";
} }
logger.reset(); // force flush of logger logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile()); file_content = readFileToText(logger.logFile());
} }
@ -310,32 +300,29 @@ TEST(LogTest, LOG_after_if_with_parentesis) {
ASSERT_TRUE(verifyContent(file_content, "This-should-show-up")); ASSERT_TRUE(verifyContent(file_content, "This-should-show-up"));
} }
TEST(LogTest, LOG_F_IF) { TEST(LogTest, LOG_F_IF) {
std::string file_content; std::string file_content;
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
LOGF_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123); LOGF_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123);
LOGF_IF(G3LOG_DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456); LOGF_IF(G3LOG_DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456);
logger.reset(); // force flush of logger logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile()); 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_TRUE(verifyContent(file_content, t_info2));
ASSERT_FALSE(verifyContent(file_content, t_debug3)); ASSERT_FALSE(verifyContent(file_content, t_debug3));
} }
TEST(LogTest, LOG_IF) { TEST(LogTest, LOG_IF) {
std::string file_content; std::string file_content;
{ {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
LOG_IF(INFO, (2 == 2)) << t_info << 123; LOG_IF(INFO, (2 == 2)) << t_info << 123;
LOG_IF(G3LOG_DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f; LOG_IF(G3LOG_DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f;
logger.reset(); // force flush of logger logger.reset(); // force flush of logger
file_content = readFileToText(logger.logFile()); 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_TRUE(verifyContent(file_content, t_info2));
EXPECT_FALSE(verifyContent(file_content, t_debug3)); EXPECT_FALSE(verifyContent(file_content, t_debug3));
@ -365,39 +352,38 @@ TEST(LogTest, FatalSIGTERM__UsingDefaultHandler) {
EXPECT_EQ(g_fatal_counter.load(), size_t{1}); EXPECT_EQ(g_fatal_counter.load(), size_t{1});
} }
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
namespace { namespace {
std::atomic<size_t> customFatalCounter = {0}; std::atomic<size_t> customFatalCounter = {0};
std::atomic<int> lastEncounteredSignal = {0}; std::atomic<int> lastEncounteredSignal = {0};
void customSignalHandler(int signal_number, siginfo_t* info, void* unused_context) { void customSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
lastEncounteredSignal.store(signal_number); lastEncounteredSignal.store(signal_number);
++customFatalCounter; ++customFatalCounter;
} }
void installCustomSIGTERM() { void installCustomSIGTERM() {
struct sigaction action; struct sigaction action;
memset(&action, 0, sizeof(action)); memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask); sigemptyset(&action.sa_mask);
action.sa_sigaction = &customSignalHandler; action.sa_sigaction = &customSignalHandler;
action.sa_flags = SA_SIGINFO; action.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &action, nullptr); sigaction(SIGTERM, &action, nullptr);
} }
std::atomic<bool> oldSigTermCheck = {false};
std::atomic<bool> oldSigTermCheck = {false};
void customOldSignalHandler(int signal_number, siginfo_t* info, void* unused_context) { void customOldSignalHandler(int signal_number, siginfo_t* info, void* unused_context) {
lastEncounteredSignal.store(signal_number); lastEncounteredSignal.store(signal_number);
oldSigTermCheck.store(true); oldSigTermCheck.store(true);
} }
void installCustomOldSIGTERM() { void installCustomOldSIGTERM() {
struct sigaction action; struct sigaction action;
memset(&action, 0, sizeof(action)); memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask); sigemptyset(&action.sa_mask);
action.sa_sigaction = &customOldSignalHandler; action.sa_sigaction = &customOldSignalHandler;
action.sa_flags = SA_SIGINFO; action.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &action, nullptr); sigaction(SIGTERM, &action, nullptr);
} }
} // anonymous } // namespace
// Override of signal handling and testing of it should be fairly easy to port to windows // 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 // ref: https://github.com/KjellKod/g3log/blob/master/src/crashhandler_windows.cpp
@ -420,15 +406,14 @@ namespace {
// //
//void installCustomSIGTERM() { //void installCustomSIGTERM() {
// ASSERT_TRUE(SIG_ERR != signal(SIGTERM, customSignalHandler)); // ASSERT_TRUE(SIG_ERR != signal(SIGTERM, customSignalHandler));
//} //}
TEST(LogTest, FatalSIGTERM__UsingCustomHandler) { TEST(LogTest, FatalSIGTERM__UsingCustomHandler) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
g_fatal_counter.store(0); g_fatal_counter.store(0);
g3::setFatalPreLoggingHook(fatalCounter); g3::setFatalPreLoggingHook(fatalCounter);
installCustomSIGTERM(); installCustomSIGTERM();
g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}}); g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}});
installCustomSIGTERM(); installCustomSIGTERM();
EXPECT_EQ(customFatalCounter.load(), size_t{0}); EXPECT_EQ(customFatalCounter.load(), size_t{0});
@ -449,8 +434,8 @@ TEST(LogTest, FatalSIGTERM__VerifyingOldCustomHandler) {
g3::setFatalPreLoggingHook(fatalCounter); g3::setFatalPreLoggingHook(fatalCounter);
installCustomOldSIGTERM(); installCustomOldSIGTERM();
g3::overrideSetupSignals({ {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGTERM, "SIGTERM"}}); g3::overrideSetupSignals({{SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGILL, "SIGILL"}, {SIGTERM, "SIGTERM"}});
g3::restoreSignalHandler(SIGTERM); // revert SIGTERM installation g3::restoreSignalHandler(SIGTERM); // revert SIGTERM installation
EXPECT_EQ(customFatalCounter.load(), size_t{0}); EXPECT_EQ(customFatalCounter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), 0); EXPECT_EQ(lastEncounteredSignal.load(), 0);
@ -459,13 +444,9 @@ TEST(LogTest, FatalSIGTERM__VerifyingOldCustomHandler) {
logger.reset(); logger.reset();
EXPECT_EQ(g_fatal_counter.load(), size_t{0}); EXPECT_EQ(g_fatal_counter.load(), size_t{0});
EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM); EXPECT_EQ(lastEncounteredSignal.load(), SIGTERM);
EXPECT_TRUE(oldSigTermCheck.load()); EXPECT_TRUE(oldSigTermCheck.load());
} }
#endif #endif
#endif #endif
@ -490,19 +471,15 @@ TEST(LogTest, LOG_preFatalLogging_hook) {
} }
} }
TEST(LogTest, LOG_FATAL) { TEST(LogTest, LOG_FATAL) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
LOG(FATAL) << "This message is fatal"; LOG(FATAL) << "This message is fatal";
EXPECT_TRUE(mockFatalWasCalled()); EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal")) EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message is fatal"))
<< "\ncontent: [[" << mockFatalMessage() << "]]"; << "\ncontent: [[" << mockFatalMessage() << "]]";
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
logger.reset(); logger.reset();
@ -516,7 +493,8 @@ TEST(LogTest, LOGF_IF__FATAL) {
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
LOGF_IF(FATAL, (2 < 3), "This message %s be worse", "could"); LOGF_IF(FATAL, (2 < 3), "This message %s be worse", "could");
EXPECT_TRUE(mockFatalWasCalled()); EXPECT_TRUE(mockFatalWasCalled());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")) << "\n" << mockFatalMessage(); EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")) << "\n"
<< mockFatalMessage();
EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "FATAL"));
EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message could be worse")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "This message could be worse"));
@ -549,7 +527,6 @@ TEST(LogTest, LOG_IF__FATAL__NO_THROW) {
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
} }
// CHECK_F // CHECK_F
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) { TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
@ -563,10 +540,9 @@ TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg) {
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by ")); EXPECT_TRUE(verifyContent(mockFatalMessage(), "EXIT trigger caused by "));
EXPECT_TRUE(verifyContent(file_content, "CONTRACT")) << "**** " << mockFatalMessage(); EXPECT_TRUE(verifyContent(file_content, "CONTRACT")) << "**** " << mockFatalMessage();
} }
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) { TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
std::string msg = "This message is added to throw %s and %s"; std::string msg = "This message is added to throw %s and %s";
@ -580,7 +556,6 @@ TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg) {
EXPECT_TRUE(verifyContent(file_content, "CONTRACT")); EXPECT_TRUE(verifyContent(file_content, "CONTRACT"));
} }
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) { TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
std::string msg = "This message is added to throw message and log"; std::string msg = "This message is added to throw message and log";
@ -610,24 +585,24 @@ TEST(CHECK, CHECK_runtimeError) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
g3::setFatalExitHandler([](g3::FatalMessagePtr msg) { g3::setFatalExitHandler([](g3::FatalMessagePtr msg) {
throw std::runtime_error("fatal test handler"); throw std::runtime_error("fatal test handler");
}); });
class dynamic_int_array { class dynamic_int_array {
std::unique_ptr<int[]> data_; std::unique_ptr<int[]> data_;
const int size_; const int size_;
public:
explicit dynamic_int_array(int size)
: data_{std::make_unique<int[]>(size)}
, size_(size)
{}
int& at(int i) { public:
CHECK(i < size_); explicit dynamic_int_array(int size) :
data_{std::make_unique<int[]>(size)},
size_(size) {}
// unreachable if i >= size_ int& at(int i) {
return data_[i]; CHECK(i < size_);
}
// unreachable if i >= size_
return data_[i];
}
}; };
dynamic_int_array arr{3}; dynamic_int_array arr{3};
@ -637,22 +612,25 @@ TEST(CHECK, CHECK_runtimeError) {
TEST(CustomLogLevels, AddANonFatal) { TEST(CustomLogLevels, AddANonFatal) {
RestoreFileLogger logger(log_directory); 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 #ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::addLogLevel(MYINFO, true); g3::only_change_at_initialization::addLogLevel(MYINFO, true);
#endif #endif
// clang-format off
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected; << "\n\nExpected: \n"
<< expected;
} }
TEST(CustomLogLevels, AddFatal) { TEST(CustomLogLevels, AddFatal) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS DEADLY {FATAL.value + 1, {"DEADLY"}}; const LEVELS DEADLY{FATAL.value + 1, {"DEADLY"}};
EXPECT_TRUE(g3::internal::wasFatal(DEADLY)); EXPECT_TRUE(g3::internal::wasFatal(DEADLY));
g_fatal_counter.store(0); g_fatal_counter.store(0);
ASSERT_FALSE(mockFatalWasCalled()); ASSERT_FALSE(mockFatalWasCalled());
@ -660,8 +638,9 @@ TEST(CustomLogLevels, AddFatal) {
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
g3::only_change_at_initialization::addLogLevel(DEADLY, true); g3::only_change_at_initialization::addLogLevel(DEADLY, true);
#endif #endif
// clang-format off
LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__; LOG(DEADLY) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset(); logger.reset();
ASSERT_TRUE(mockFatalWasCalled()); ASSERT_TRUE(mockFatalWasCalled());
EXPECT_EQ(size_t{1}, g_fatal_counter.load()); EXPECT_EQ(size_t{1}, g_fatal_counter.load());
@ -670,18 +649,17 @@ TEST(CustomLogLevels, AddFatal) {
std::string expected; std::string expected;
expected += "DEADLY [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line); expected += "DEADLY [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected; << "\n\nExpected: \n"
g_fatal_counter.store(0); // restore << expected;
g_fatal_counter.store(0); // restore
} }
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
namespace { namespace {
// Restore dynamic levels if turned off // Restore dynamic levels if turned off
struct RestoreDynamicLoggingLevels { struct RestoreDynamicLoggingLevels {
RestoreDynamicLoggingLevels() { RestoreDynamicLoggingLevels(){};
};
~RestoreDynamicLoggingLevels() { ~RestoreDynamicLoggingLevels() {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false); g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false);
@ -690,12 +668,11 @@ namespace {
g3::only_change_at_initialization::addLogLevel(FATAL, false); g3::only_change_at_initialization::addLogLevel(FATAL, false);
} }
}; };
} // anonymous } // namespace
TEST(CustomLogLevels, AddANonFatal__ThenReset) { TEST(CustomLogLevels, AddANonFatal__ThenReset) {
RestoreFileLogger logger(log_directory); 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)); EXPECT_FALSE(g3::logLevel(MYINFO));
g3::only_change_at_initialization::addLogLevel(MYINFO, true); g3::only_change_at_initialization::addLogLevel(MYINFO, true);
EXPECT_TRUE(g3::logLevel(MYINFO)); EXPECT_TRUE(g3::logLevel(MYINFO));
@ -703,78 +680,87 @@ TEST(CustomLogLevels, AddANonFatal__ThenReset) {
EXPECT_FALSE(g3::logLevel(MYINFO)); EXPECT_FALSE(g3::logLevel(MYINFO));
} }
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue1) { TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue1) {
RestoreFileLogger logger(log_directory); 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__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp:" + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp:" + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::log_levels::to_string(); << "\n\nExpected: \n"
<< expected << "\nLevels:\n"
<< g3::log_levels::to_string();
} }
TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2) { TEST(CustomLogLevels, AddANonFatal__DidNotAddItToEnabledValue2) {
RestoreFileLogger logger(log_directory); 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)); EXPECT_FALSE(g3::logLevel(MYINFO));
// clang-format off
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp:" + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp:" + std::to_string(line);
EXPECT_FALSE(verifyContent(file_content, expected)) << file_content EXPECT_FALSE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected << "\nLevels:\n" << g3::log_levels::to_string(); << "\n\nExpected: \n"
<< expected << "\nLevels:\n"
<< g3::log_levels::to_string();
} }
TEST(CustomLogLevels, AddANonFatal__DidtAddItToEnabledValue) { TEST(CustomLogLevels, AddANonFatal__DidtAddItToEnabledValue) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
const LEVELS MYINFO {WARNING.value + 3, {"MY_INFO_LEVEL"}}; const LEVELS MYINFO{WARNING.value + 3, {"MY_INFO_LEVEL"}};
g3::only_change_at_initialization::addLogLevel(MYINFO, true); g3::only_change_at_initialization::addLogLevel(MYINFO, true);
// clang-format off
LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__; LOG(MYINFO) << "Testing my own custom level"; auto line = __LINE__;
// clang-format on
logger.reset(); logger.reset();
std::string file_content = readFileToText(logger.logFile()); std::string file_content = readFileToText(logger.logFile());
std::string expected; std::string expected;
expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line); expected += "MY_INFO_LEVEL [test_io.cpp->" + std::string(G3LOG_PRETTY_FUNCTION) + ":" + std::to_string(line);
EXPECT_TRUE(verifyContent(file_content, expected)) << file_content EXPECT_TRUE(verifyContent(file_content, expected)) << file_content
<< "\n\nExpected: \n" << expected; << "\n\nExpected: \n"
<< expected;
} }
TEST(DynamicLogging, DynamicLogging_IS_ENABLED) { TEST(DynamicLogging, DynamicLogging_IS_ENABLED) {
RestoreDynamicLoggingLevels raiiLevelRestore; RestoreDynamicLoggingLevels raiiLevelRestore;
ASSERT_TRUE(g3::logLevel(G3LOG_DEBUG)); ASSERT_TRUE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(INFO)); ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false); g3::only_change_at_initialization::addLogLevel(G3LOG_DEBUG, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG)); ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
ASSERT_TRUE(g3::logLevel(INFO)); ASSERT_TRUE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(INFO, false); g3::only_change_at_initialization::addLogLevel(INFO, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG)); ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
ASSERT_FALSE(g3::logLevel(INFO)); ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_TRUE(g3::logLevel(WARNING)); ASSERT_TRUE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(WARNING, false); g3::only_change_at_initialization::addLogLevel(WARNING, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG)); ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
ASSERT_FALSE(g3::logLevel(INFO)); ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_FALSE(g3::logLevel(WARNING)); ASSERT_FALSE(g3::logLevel(WARNING));
ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective. ASSERT_TRUE(g3::logLevel(FATAL)); // Yes FATAL can be turned off. Thereby rendering it ineffective.
g3::only_change_at_initialization::addLogLevel(FATAL, false); g3::only_change_at_initialization::addLogLevel(FATAL, false);
ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG)); ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG));
ASSERT_FALSE(g3::logLevel(INFO)); ASSERT_FALSE(g3::logLevel(INFO));
ASSERT_FALSE(g3::logLevel(WARNING)); 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) { TEST(DynamicLogging, DynamicLogging_No_Logs_If_Disabled) {
{ {
@ -828,7 +814,6 @@ TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
clearMockFatal(); clearMockFatal();
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
g3::only_change_at_initialization::addLogLevel(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"; std::string msg3 = "This is NOT fatal (not crash, since it is unit test. FATAL is disabled";
LOG(FATAL) << msg3; LOG(FATAL) << msg3;
@ -836,7 +821,6 @@ TEST(DynamicLogging, DynamicLogging_No_Fatal_If_Disabled) {
EXPECT_TRUE(mockFatalMessage().empty()); EXPECT_TRUE(mockFatalMessage().empty());
} }
TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disabled) { TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disabled) {
RestoreFileLogger logger(log_directory); RestoreFileLogger logger(log_directory);
RestoreDynamicLoggingLevels raiiLevelRestore; RestoreDynamicLoggingLevels raiiLevelRestore;
@ -858,16 +842,10 @@ TEST(DynamicLogging, DynamicLogging_Check_WillAlsoBeTurnedOffWhen_Fatal_Is_Disab
EXPECT_FALSE(mockFatalWasCalled()); EXPECT_FALSE(mockFatalWasCalled());
} }
#else #else
TEST(DynamicLogging, DynamicLogging_IS_NOT_ENABLED) { TEST(DynamicLogging, DynamicLogging_IS_NOT_ENABLED) {
ASSERT_TRUE(g3::logLevel(G3LOG_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. //g3::addLogLevel(G3LOG_DEBUG, false); this line will not compile since G3_DYNAMIC_LOGGING is not enabled. Kept for show.
//ASSERT_FALSE(g3::logLevel(G3LOG_DEBUG)); //ASSERT_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 * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
#include <g3log/filesink.hpp>
#include <g3log/g3log.hpp> #include <g3log/g3log.hpp>
#include <g3log/logworker.hpp> #include <g3log/logworker.hpp>
#include <g3log/filesink.hpp>
#include <memory> #include <memory>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include "tester_sharedlib.h"
#include <dlfcn.h> #include <dlfcn.h>
#include "tester_sharedlib.h"
struct LogMessageCounter { struct LogMessageCounter {
std::vector<std::string>& bank; 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) { void countMessages(std::string msg) {
@ -31,18 +31,18 @@ struct LogMessageCounter {
TEST(DynamicLoadOfLibrary, JustLoadAndExit) { TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
std::vector<std::string> receiver; 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 worker = g3::LogWorker::createLogWorker();
auto handle = worker->addSink(std::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 // add another sink just for more throughput of data
auto fileHandle = worker->addSink(std::make_unique<g3::FileSink>("runtimeLoadOfDynamiclibs", "/tmp"), &g3::FileSink::fileWrite); auto fileHandle = worker->addSink(std::make_unique<g3::FileSink>("runtimeLoadOfDynamiclibs", "/tmp"), &g3::FileSink::fileWrite);
g3::initializeLogging(worker.get()); g3::initializeLogging(worker.get());
void* libHandle = dlopen("libtester_sharedlib.so", RTLD_LAZY | RTLD_GLOBAL); void* libHandle = dlopen("libtester_sharedlib.so", RTLD_LAZY | RTLD_GLOBAL);
EXPECT_FALSE(nullptr == libHandle); EXPECT_FALSE(nullptr == libHandle);
LibraryFactory* factory = reinterpret_cast<LibraryFactory*> ((dlsym(libHandle, "testRealFactory"))); LibraryFactory* factory = reinterpret_cast<LibraryFactory*>((dlsym(libHandle, "testRealFactory")));
EXPECT_FALSE(nullptr == factory); EXPECT_FALSE(nullptr == factory);
SomeLibrary* loadedLibrary = factory->CreateLibrary(); SomeLibrary* loadedLibrary = factory->CreateLibrary();
@ -52,7 +52,7 @@ TEST(DynamicLoadOfLibrary, JustLoadAndExit) {
delete loadedLibrary; delete loadedLibrary;
dlclose(libHandle); dlclose(libHandle);
} // scope exit. All log entries must be flushed now } // scope exit. All log entries must be flushed now
const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library const size_t numberOfMessages = 2 + 300 + 1; // 2 library construction, 300 loop, 1 destoyed library
EXPECT_EQ(receiver.size(), numberOfMessages); EXPECT_EQ(receiver.size(), numberOfMessages);
} }

View File

@ -7,14 +7,14 @@
* ============================================================================*/ * ============================================================================*/
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <testing_helpers.h>
#include <cstdlib>
#include <ctime>
#include <g3log/filesink.hpp>
#include <g3log/g3log.hpp> #include <g3log/g3log.hpp>
#include <g3log/generated_definitions.hpp>
#include <g3log/time.hpp> #include <g3log/time.hpp>
#include <iostream> #include <iostream>
#include <ctime>
#include <cstdlib>
#include <g3log/generated_definitions.hpp>
#include <testing_helpers.h>
#include <g3log/filesink.hpp>
namespace { namespace {
// https://www.epochconverter.com/ // https://www.epochconverter.com/
// epoc value for: Thu, 27 Apr 2017 06:22:49 GMT // epoc value for: Thu, 27 Apr 2017 06:22:49 GMT
@ -27,9 +27,7 @@ namespace {
const LEVELS kLevel = INFO; const LEVELS kLevel = INFO;
const std::string testdirectory = "./"; const std::string testdirectory = "./";
} // namespace
}
TEST(Message, DefaultLogDetals_toString) { TEST(Message, DefaultLogDetals_toString) {
using namespace g3; using namespace g3;
@ -47,7 +45,6 @@ TEST(Message, Default_toString) {
testing_helpers::verifyContent(output, details); testing_helpers::verifyContent(output, details);
} }
TEST(Message, UseOverride_4_DetailsWithThreadID_toString) { TEST(Message, UseOverride_4_DetailsWithThreadID_toString) {
using namespace g3; using namespace g3;
LogMessage msg{kFile, kLine, kFunction, kLevel}; LogMessage msg{kFile, kLine, kFunction, kLevel};
@ -79,25 +76,21 @@ TEST(Message, UseLogCall_4_DetailsWithThreadID_toString) {
std::cout << output << std::endl; std::cout << output << std::endl;
} }
TEST(Message, DefaultFormattingToLogFile) { TEST(Message, DefaultFormattingToLogFile) {
using namespace g3; using namespace g3;
std::string file_content; std::string file_content;
{ {
testing_helpers::RestoreFileLogger logger(testdirectory); testing_helpers::RestoreFileLogger logger(testdirectory);
LOG(WARNING) << "testing"; LOG(WARNING) << "testing";
logger.reset(); // force flush of logger (which will trigger a shutdown) logger.reset(); // force flush of logger (which will trigger a shutdown)
file_content = testing_helpers::readFileToText(logger.logFile()); // logger is already reset file_content = testing_helpers::readFileToText(logger.logFile()); // logger is already reset
} }
std::ostringstream thread_id_oss; std::ostringstream thread_id_oss;
thread_id_oss << " [" << std::this_thread::get_id() << " "; thread_id_oss << " [" << std::this_thread::get_id() << " ";
EXPECT_FALSE(testing_helpers::verifyContent(file_content, thread_id_oss.str())); EXPECT_FALSE(testing_helpers::verifyContent(file_content, thread_id_oss.str()));
} }
TEST(Message, FullFormattingToLogFile) { TEST(Message, FullFormattingToLogFile) {
using namespace g3; using namespace g3;
std::string file_content; std::string file_content;
@ -106,17 +99,15 @@ TEST(Message, FullFormattingToLogFile) {
logger._handle->call(&FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString); logger._handle->call(&FileSink::overrideLogDetails, &LogMessage::FullLogDetailsToString);
LOG(WARNING) << "testing"; LOG(WARNING) << "testing";
logger.reset(); // force flush of logger (which will trigger a shutdown) logger.reset(); // force flush of logger (which will trigger a shutdown)
file_content = testing_helpers::readFileToText(logger.logFile()); // logger is already reset file_content = testing_helpers::readFileToText(logger.logFile()); // logger is already reset
} }
std::ostringstream thread_id_oss; std::ostringstream thread_id_oss;
thread_id_oss << " [" << std::this_thread::get_id() << " "; thread_id_oss << " [" << std::this_thread::get_id() << " ";
EXPECT_TRUE(testing_helpers::verifyContent(file_content, thread_id_oss.str())); EXPECT_TRUE(testing_helpers::verifyContent(file_content, thread_id_oss.str()));
} }
TEST(Message, CppSupport) { TEST(Message, CppSupport) {
// ref: http://www.cplusplus.com/reference/clibrary/ctime/strftime/ // ref: http://www.cplusplus.com/reference/clibrary/ctime/strftime/
// ref: http://en.cppreference.com/w/cpp/io/manip/put_time // ref: http://en.cppreference.com/w/cpp/io/manip/put_time
@ -125,27 +116,26 @@ TEST(Message, CppSupport) {
// --- For formatting options to std::put_time that are NOT YET implemented on Windows fatal errors/assert will occurr // --- 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. // --- the last example is such an example.
try { try {
std::cout << g3::localtime_formatted(std::chrono::system_clock::now(), "%a %b %d %H:%M:%S %Y") << std::endl; 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::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; 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__)) #if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
std::cerr << "Formatting options skipped due to VS2012, C++11 non-conformance for" << std::endl; 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; 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 #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; 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 #endif
} }
// This does not work. Other kinds of fatal exits (on Windows) seems to be used instead of exceptions // 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 // Maybe a signal handler catch would be better? --- TODO: Make it better, both failing and correct
catch (...) { catch (...) {
ADD_FAILURE() << "On this platform the library does not support given (C++11?) specifiers"; ADD_FAILURE() << "On this platform the library does not support given (C++11?) specifiers";
return; return;
} }
ASSERT_TRUE(true); // no exception. all good ASSERT_TRUE(true); // no exception. all good
} }
TEST(Message, GetFractional_Empty_buffer_ExpectDefaults) { TEST(Message, GetFractional_Empty_buffer_ExpectDefaults) {
auto fractional = g3::internal::getFractional("", 0); auto fractional = g3::internal::getFractional("", 0);
const auto expected = g3::internal::Fractional::NanosecondDefault; const auto expected = g3::internal::Fractional::NanosecondDefault;
@ -200,8 +190,6 @@ TEST(Message, GetFractional_All) {
EXPECT_EQ(fractional, expected); EXPECT_EQ(fractional, expected);
} }
TEST(Message, FractionalToString_SizeCheck) { TEST(Message, FractionalToString_SizeCheck) {
auto value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Nanosecond); auto value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Nanosecond);
EXPECT_EQ("000000000", value); EXPECT_EQ("000000000", value);
@ -211,7 +199,7 @@ TEST(Message, FractionalToString_SizeCheck) {
// us // us
value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Microsecond); value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Microsecond);
EXPECT_EQ("000000", value); EXPECT_EQ("000000", value);
// ms // ms
value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Millisecond); value = g3::internal::to_string(kTimePoint_2017_April_27th, g3::internal::Fractional::Millisecond);
EXPECT_EQ("000", value); EXPECT_EQ("000", value);
} }
@ -233,16 +221,13 @@ TEST(Message, FractionalToString12NanoPadded) {
EXPECT_EQ("000000000", value); EXPECT_EQ("000000000", value);
} }
TEST(Message, FractionalToStringMicroPadded) { TEST(Message, FractionalToStringMicroPadded) {
auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Microsecond); auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Microsecond);
EXPECT_EQ("000000", value); EXPECT_EQ("000000", value);
value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Microsecond); value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Microsecond);
EXPECT_EQ("000000", value); EXPECT_EQ("000000", value);
} }
TEST(Message, FractionalToStringMilliPadded) { TEST(Message, FractionalToStringMilliPadded) {
auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Millisecond); auto value = g3::internal::to_string(k1970_January_1st, g3::internal::Fractional::Millisecond);
EXPECT_EQ("000", value); EXPECT_EQ("000", value);
@ -250,7 +235,6 @@ TEST(Message, FractionalToStringMilliPadded) {
EXPECT_EQ("000", value); EXPECT_EQ("000", value);
} }
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) #if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
TEST(Message, localtime_formatted) { TEST(Message, localtime_formatted) {
@ -262,19 +246,17 @@ TEST(Message, localtime_formatted) {
else else
unsetenv("TZ"); unsetenv("TZ");
tzset(); tzset();
}); });
tz = getenv("TZ"); tz = getenv("TZ");
setenv("TZ", "", 1); setenv("TZ", "", 1);
tzset(); tzset();
auto time_point = std::chrono::system_clock::from_time_t(k2017_April_27th); 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 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"}; std::string expected = {"2017-04-27 06:22:27"};
EXPECT_EQ(expected, format); EXPECT_EQ(expected, format);
auto us_format = g3::localtime_formatted(time_point, g3::internal::time_formatted); // "%H:%M:%S %f6"; auto us_format = g3::localtime_formatted(time_point, g3::internal::time_formatted); // "%H:%M:%S %f6";
EXPECT_EQ("06:22:27 000000", us_format); EXPECT_EQ("06:22:27 000000", us_format);
auto ns_format = g3::localtime_formatted(time_point, "%H:%M:%S %f"); auto ns_format = g3::localtime_formatted(time_point, "%H:%M:%S %f");
@ -282,51 +264,45 @@ TEST(Message, localtime_formatted) {
auto ms_format = g3::localtime_formatted(time_point, "%H:%M:%S %f3"); auto ms_format = g3::localtime_formatted(time_point, "%H:%M:%S %f3");
EXPECT_EQ("06:22:27 000", ms_format); EXPECT_EQ("06:22:27 000", ms_format);
} }
#endif // timezone #endif // timezone
#if defined(CHANGE_G3LOG_DEBUG_TO_DBUG) #if defined(CHANGE_G3LOG_DEBUG_TO_DBUG)
TEST(Level, G3LogDebug_is_DBUG) { TEST(Level, G3LogDebug_is_DBUG) {
LOG(DBUG) << "DBUG equals G3LOG_DEBUG"; LOG(DBUG) << "DBUG equals G3LOG_DEBUG";
LOG(G3LOG_DEBUG) << "G3LOG_DEBUG equals DBUG"; LOG(G3LOG_DEBUG) << "G3LOG_DEBUG equals DBUG";
} }
#else #else
TEST(Level, G3LogDebug_is_DEBUG) { TEST(Level, G3LogDebug_is_DEBUG) {
LOG(DEBUG) << "DEBUG equals G3LOG_DEBUG"; LOG(DEBUG) << "DEBUG equals G3LOG_DEBUG";
LOG(G3LOG_DEBUG) << "G3LOG_DEBUG equals DEBUG"; LOG(G3LOG_DEBUG) << "G3LOG_DEBUG equals DEBUG";
} }
#endif #endif
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
namespace { namespace {
using LevelsContainer = std::map<int, g3::LoggingLevel>; using LevelsContainer = std::map<int, g3::LoggingLevel>;
const LevelsContainer g_test_log_level_defaults = { const LevelsContainer g_test_log_level_defaults = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG}},
{INFO.value, {INFO}}, {INFO.value, {INFO}},
{WARNING.value, {WARNING}}, {WARNING.value, {WARNING}},
{FATAL.value, {FATAL}} {FATAL.value, {FATAL}}};
};
const LevelsContainer g_test_all_disabled = { const LevelsContainer g_test_all_disabled = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG,false}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, false}},
{INFO.value, {INFO, false}}, {INFO.value, {INFO, false}},
{WARNING.value, {WARNING, false}}, {WARNING.value, {WARNING, false}},
{FATAL.value, {FATAL, false}} {FATAL.value, {FATAL, false}}};
};
bool mapCompare(LevelsContainer const& lhs, LevelsContainer const& rhs) {
bool mapCompare (LevelsContainer const& lhs, LevelsContainer const& rhs) { auto pred = [](auto a, auto b) {
auto pred = [] (auto a, auto b) {
return (a.first == b.first) && return (a.first == b.first) &&
(a.second == b.second); (a.second == b.second);
}; };
return lhs.size() == rhs.size() return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
&& std::equal(lhs.begin(), lhs.end(), rhs.begin(), pred);
} }
} // anonymous } // namespace
TEST(Level, Default) { TEST(Level, Default) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
auto defaults = g3::log_levels::getAll(); auto defaults = g3::log_levels::getAll();
@ -349,8 +325,7 @@ TEST(Level, DefaultChanged_only_change_at_initialization) {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, false}}, {INFO.value, {INFO, false}},
{WARNING.value, {WARNING, true}}, {WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}} {FATAL.value, {FATAL, true}}};
};
EXPECT_TRUE(mapCompare(defaults, defaultsWithInfoChangged)); EXPECT_TRUE(mapCompare(defaults, defaultsWithInfoChangged));
} }
@ -369,8 +344,7 @@ TEST(Level, DefaultChanged_log_levels) {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, false}}, {INFO.value, {INFO, false}},
{WARNING.value, {WARNING, true}}, {WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}} {FATAL.value, {FATAL, true}}};
};
EXPECT_TRUE(mapCompare(defaults, defaultsWithInfoChangged)); EXPECT_TRUE(mapCompare(defaults, defaultsWithInfoChangged));
} }
@ -386,20 +360,14 @@ TEST(Level, Reset) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
all_levels = g3::log_levels::getAll(); all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, g_test_log_level_defaults)); EXPECT_TRUE(mapCompare(all_levels, g_test_log_level_defaults));
} }
TEST(Level, AllDisabled) { TEST(Level, AllDisabled) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) { std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_EQ(all_levels.size(), g_test_all_disabled.size()); EXPECT_EQ(all_levels.size(), g_test_all_disabled.size());
EXPECT_FALSE(mapCompare(all_levels, g_test_all_disabled)); EXPECT_FALSE(mapCompare(all_levels, g_test_all_disabled));
@ -409,79 +377,64 @@ TEST(Level, AllDisabled) {
EXPECT_TRUE(mapCompare(all_levels, g_test_all_disabled)); EXPECT_TRUE(mapCompare(all_levels, g_test_all_disabled));
} }
TEST(Level, setHighestLogLevel_high_end) { TEST(Level, setHighestLogLevel_high_end) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) { std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
g3::log_levels::enableAll(); g3::log_levels::enableAll();
g3::log_levels::disable(FATAL); g3::log_levels::disable(FATAL);
g3::log_levels::setHighest(FATAL); g3::log_levels::setHighest(FATAL);
LevelsContainer expected = { LevelsContainer expected = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, false}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, false}},
{INFO.value, {INFO, false}}, {INFO.value, {INFO, false}},
{WARNING.value, {WARNING, false}}, {WARNING.value, {WARNING, false}},
{FATAL.value, {FATAL, true}} {FATAL.value, {FATAL, true}}};
};
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, expected)) << g3::log_levels::to_string(); EXPECT_TRUE(mapCompare(all_levels, expected)) << g3::log_levels::to_string();
} }
TEST(Level, setHighestLogLevel_low_end) { TEST(Level, setHighestLogLevel_low_end) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) { std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
g3::log_levels::disableAll(); g3::log_levels::disableAll();
g3::log_levels::setHighest(G3LOG_DEBUG); g3::log_levels::setHighest(G3LOG_DEBUG);
LevelsContainer expected = { LevelsContainer expected = {
{G3LOG_DEBUG.value,{G3LOG_DEBUG, true}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, true}}, {INFO.value, {INFO, true}},
{WARNING.value, {WARNING, true}}, {WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}} {FATAL.value, {FATAL, true}}};
};
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, expected)) << g3::log_levels::to_string(); EXPECT_TRUE(mapCompare(all_levels, expected)) << g3::log_levels::to_string();
} }
TEST(Level, setHighestLogLevel_middle) { TEST(Level, setHighestLogLevel_middle) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) { std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
g3::log_levels::enableAll(); g3::log_levels::enableAll();
g3::log_levels::setHighest(WARNING); g3::log_levels::setHighest(WARNING);
LevelsContainer expected = { LevelsContainer expected = {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, false}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, false}},
{INFO.value, {INFO, false}}, {INFO.value, {INFO, false}},
{WARNING.value, {WARNING, true}}, {WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}} {FATAL.value, {FATAL, true}}};
};
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, expected)); EXPECT_TRUE(mapCompare(all_levels, expected));
} }
TEST(Level, setHighestLogLevel_StepWiseDisableAll) { TEST(Level, setHighestLogLevel_StepWiseDisableAll) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) { std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
@ -492,8 +445,7 @@ TEST(Level, setHighestLogLevel_StepWiseDisableAll) {
{G3LOG_DEBUG.value, {G3LOG_DEBUG, true}}, {G3LOG_DEBUG.value, {G3LOG_DEBUG, true}},
{INFO.value, {INFO, true}}, {INFO.value, {INFO, true}},
{WARNING.value, {WARNING, true}}, {WARNING.value, {WARNING, true}},
{FATAL.value, {FATAL, true}} {FATAL.value, {FATAL, true}}};
};
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, g_test_log_level_defaults)); EXPECT_TRUE(mapCompare(all_levels, g_test_log_level_defaults));
@ -503,11 +455,9 @@ TEST(Level, setHighestLogLevel_StepWiseDisableAll) {
g3::log_levels::setHighest(lvl.second.level); g3::log_levels::setHighest(lvl.second.level);
all_levels = g3::log_levels::getAll(); all_levels = g3::log_levels::getAll();
ASSERT_TRUE(mapCompare(all_levels, changing_levels)) << ASSERT_TRUE(mapCompare(all_levels, changing_levels)) << "counter: " << counter << "\nsystem:\n"
"counter: " << counter << "\nsystem:\n" << << g3::log_levels::to_string(all_levels) << "\nexpected:\n"
g3::log_levels::to_string(all_levels) << << g3::log_levels::to_string(changing_levels);
"\nexpected:\n" <<
g3::log_levels::to_string(changing_levels);
++counter; ++counter;
if (counter != changing_levels.size()) { if (counter != changing_levels.size()) {
@ -516,26 +466,20 @@ TEST(Level, setHighestLogLevel_StepWiseDisableAll) {
} }
} }
// in the end all except the last should be disabled // in the end all except the last should be disabled
auto mostly_disabled = g_test_all_disabled; auto mostly_disabled = g_test_all_disabled;
mostly_disabled[FATAL.value].status = true; mostly_disabled[FATAL.value].status = true;
EXPECT_TRUE(mapCompare(changing_levels, mostly_disabled)); EXPECT_TRUE(mapCompare(changing_levels, mostly_disabled));
all_levels = g3::log_levels::getAll(); all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(all_levels, mostly_disabled)) << EXPECT_TRUE(mapCompare(all_levels, mostly_disabled)) << "\nsystem:\n"
"\nsystem:\n" << << g3::log_levels::to_string(all_levels) << "\nexpected:\n"
g3::log_levels::to_string(all_levels) << << g3::log_levels::to_string(mostly_disabled);
"\nexpected:\n" <<
g3::log_levels::to_string(mostly_disabled);
} }
TEST(Level, Print) { TEST(Level, Print) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
std::string expected = std::string{"name: DEBUG level: 100 status: 1\n"} 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";
+ "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); EXPECT_EQ(g3::log_levels::to_string(), expected);
} }
@ -544,19 +488,16 @@ TEST(Level, AddOneEnabled_option1) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
LEVELS MYINFO {WARNING.value + 1, "MyInfoLevel"};
g3::only_change_at_initialization::addLogLevel(MYINFO, true); g3::only_change_at_initialization::addLogLevel(MYINFO, true);
auto modified = g_test_log_level_defaults; auto modified = g_test_log_level_defaults;
modified[MYINFO.value] = MYINFO; modified[MYINFO.value] = MYINFO;
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(modified, all_levels)) << "\nsystem:\n" << EXPECT_TRUE(mapCompare(modified, all_levels)) << "\nsystem:\n"
g3::log_levels::to_string(all_levels) << << g3::log_levels::to_string(all_levels) << "\nexpected:\n"
"\nexpected:\n" << << g3::log_levels::to_string(modified);
g3::log_levels::to_string(modified);
} }
TEST(Level, AddOneEnabled_option2) { TEST(Level, AddOneEnabled_option2) {
@ -564,30 +505,24 @@ TEST(Level, AddOneEnabled_option2) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
LEVELS MYINFO {WARNING.value + 1, "MyInfoLevel"};
g3::only_change_at_initialization::addLogLevel(MYINFO); g3::only_change_at_initialization::addLogLevel(MYINFO);
auto modified = g_test_log_level_defaults; auto modified = g_test_log_level_defaults;
modified[MYINFO.value] = MYINFO; modified[MYINFO.value] = MYINFO;
auto all_levels = g3::log_levels::getAll(); auto all_levels = g3::log_levels::getAll();
EXPECT_TRUE(mapCompare(modified, all_levels)) << "\nsystem:\n" << EXPECT_TRUE(mapCompare(modified, all_levels)) << "\nsystem:\n"
g3::log_levels::to_string(all_levels) << << g3::log_levels::to_string(all_levels) << "\nexpected:\n"
"\nexpected:\n" << << g3::log_levels::to_string(modified);
g3::log_levels::to_string(modified);
} }
TEST(Level, Addlevel_using_addLevel) { TEST(Level, Addlevel_using_addLevel) {
std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) { std::shared_ptr<void> RaiiLeveReset(nullptr, [&](void*) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
LEVELS MYINFO {WARNING.value + 1, "MyInfoLevel"}; LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO); auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent); EXPECT_EQ(status, g3::log_levels::status::Absent);
@ -601,7 +536,7 @@ TEST(Level, Addlevel_using_addLogLevel_disabled) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
LEVELS MYINFO {WARNING.value + 1, "MyInfoLevel"}; LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO); auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent); EXPECT_EQ(status, g3::log_levels::status::Absent);
@ -615,7 +550,7 @@ TEST(Level, Addlevel__disabled) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
LEVELS MYINFO {WARNING.value + 1, "MyInfoLevel"}; LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO); auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent); EXPECT_EQ(status, g3::log_levels::status::Absent);
@ -637,15 +572,13 @@ TEST(Level, Addlevel__enabled) {
g3::only_change_at_initialization::reset(); g3::only_change_at_initialization::reset();
}); });
LEVELS MYINFO {WARNING.value + 1, "MyInfoLevel"}; LEVELS MYINFO{WARNING.value + 1, "MyInfoLevel"};
auto status = g3::log_levels::getStatus(MYINFO); auto status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Absent); EXPECT_EQ(status, g3::log_levels::status::Absent);
g3::only_change_at_initialization::addLogLevel(MYINFO); g3::only_change_at_initialization::addLogLevel(MYINFO);
status = g3::log_levels::getStatus(MYINFO); status = g3::log_levels::getStatus(MYINFO);
EXPECT_EQ(status, g3::log_levels::status::Enabled); EXPECT_EQ(status, g3::log_levels::status::Enabled);
} }
#endif // G3_DYNAMIC_LOGGING #endif // G3_DYNAMIC_LOGGING

View File

@ -7,26 +7,25 @@
* ============================================================================*/ * ============================================================================*/
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <iostream>
#include <atomic> #include <atomic>
#include <vector>
#include <memory>
#include <thread>
#include <chrono> #include <chrono>
#include <string>
#include <future> #include <future>
#include <g3log/generated_definitions.hpp> #include <g3log/generated_definitions.hpp>
#include "testing_helpers.h" #include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "g3log/logmessage.hpp" #include "g3log/logmessage.hpp"
#include "g3log/logworker.hpp" #include "g3log/logworker.hpp"
#include "testing_helpers.h"
using namespace testing_helpers; using namespace testing_helpers;
using namespace std; using namespace std;
TEST(Sink, OneSink) { TEST(Sink, OneSink) {
using namespace g3; using namespace g3;
AtomicBoolPtr flag = make_shared < atomic<bool >> (false); AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared < atomic<int >> (0); AtomicIntPtr count = make_shared<atomic<int>>(0);
{ {
auto worker = g3::LogWorker::createLogWorker(); auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg); auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
@ -40,12 +39,10 @@ TEST(Sink, OneSink) {
EXPECT_TRUE(1 == count->load()); EXPECT_TRUE(1 == count->load());
} }
TEST(Sink, OneSinkRemove) { TEST(Sink, OneSinkRemove) {
using namespace g3; using namespace g3;
AtomicBoolPtr flag = make_shared < atomic<bool >> (false); AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared < atomic<int >> (0); AtomicIntPtr count = make_shared<atomic<int>>(0);
{ {
auto worker = g3::LogWorker::createLogWorker(); auto worker = g3::LogWorker::createLogWorker();
auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg); auto handle = worker->addSink(std::make_unique<ScopedSetTrue>(flag, count), &ScopedSetTrue::ReceiveMsg);
@ -67,14 +64,14 @@ TEST(Sink, OneSinkRemove) {
} }
// just compile test // just compile test
TEST(Sink, DefaultSinkRemove){ TEST(Sink, DefaultSinkRemove) {
using namespace g3; using namespace g3;
AtomicBoolPtr flag = make_shared < atomic<bool >> (false); AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared < atomic<int >> (0); AtomicIntPtr count = make_shared<atomic<int>>(0);
{ {
auto worker = g3::LogWorker::createLogWorker(); auto worker = g3::LogWorker::createLogWorker();
auto handle1 = worker->addDefaultLogger("test1", "./"); auto handle1 = worker->addDefaultLogger("test1", "./");
auto handle2 = worker->addDefaultLogger("test2", "./"); auto handle2 = worker->addDefaultLogger("test2", "./");
worker->removeSink(std::move(handle1)); worker->removeSink(std::move(handle1));
worker->removeAllSinks(); worker->removeAllSinks();
} }
@ -82,8 +79,8 @@ TEST(Sink, DefaultSinkRemove){
TEST(Sink, NullSinkRemove) { TEST(Sink, NullSinkRemove) {
using namespace g3; using namespace g3;
AtomicBoolPtr flag = make_shared < atomic<bool >> (false); AtomicBoolPtr flag = make_shared<atomic<bool>>(false);
AtomicIntPtr count = make_shared < atomic<int >> (0); AtomicIntPtr count = make_shared<atomic<int>>(0);
{ {
auto worker = g3::LogWorker::createLogWorker(); auto worker = g3::LogWorker::createLogWorker();
std::unique_ptr<g3::SinkHandle<ScopedSetTrue>> nullsink; std::unique_ptr<g3::SinkHandle<ScopedSetTrue>> nullsink;
@ -91,13 +88,11 @@ TEST(Sink, NullSinkRemove) {
} }
} }
namespace { namespace {
using AtomicBoolPtr = std::shared_ptr<std::atomic<bool>>; using AtomicBoolPtr = std::shared_ptr<std::atomic<bool>>;
using AtomicIntPtr = std::shared_ptr<std::atomic<int>>; using AtomicIntPtr = std::shared_ptr<std::atomic<int>>;
using BoolList = vector<AtomicBoolPtr>; using BoolList = vector<AtomicBoolPtr>;
using IntVector = vector<AtomicIntPtr>; using IntVector = vector<AtomicIntPtr>;
size_t countDestroyedFlags(BoolList& flags) { size_t countDestroyedFlags(BoolList& flags) {
size_t destroyed_count = 0; size_t destroyed_count = 0;
@ -125,8 +120,7 @@ namespace {
return total_count; return total_count;
} }
} // namespace
}
TEST(ConceptSink, OneHundredSinks) { TEST(ConceptSink, OneHundredSinks) {
using namespace g3; using namespace g3;
@ -135,13 +129,13 @@ TEST(ConceptSink, OneHundredSinks) {
size_t kNumberOfItems = 100; size_t kNumberOfItems = 100;
for (size_t index = 0; index < kNumberOfItems; ++index) { for (size_t index = 0; index < kNumberOfItems; ++index) {
flags.push_back(make_shared < atomic<bool >> (false)); flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared < atomic<int >> (0)); counts.push_back(make_shared<atomic<int>>(0));
} }
{ {
RestoreFileLogger logger{"./"}; RestoreFileLogger logger{"./"};
g3::LogWorker* worker = logger._scope->get(); //g3LogWorker::createLogWorker(); g3::LogWorker* worker = logger._scope->get(); //g3LogWorker::createLogWorker();
size_t index = 0; size_t index = 0;
for (auto& flag : flags) { for (auto& flag : flags) {
auto& count = counts[index++]; auto& count = counts[index++];
@ -180,7 +174,6 @@ void AddManySinks(size_t kNumberOfSinks, BoolList& flags, IntVector& counts,
flags.push_back(make_shared<atomic<bool>>(false)); flags.push_back(make_shared<atomic<bool>>(false));
counts.push_back(make_shared<atomic<int>>(0)); counts.push_back(make_shared<atomic<int>>(0));
sink_handles.push_back(worker->addSink(std::make_unique<ScopedSetTrue>(flags[idx], counts[idx]), &ScopedSetTrue::ReceiveMsg)); sink_handles.push_back(worker->addSink(std::make_unique<ScopedSetTrue>(flags[idx], counts[idx]), &ScopedSetTrue::ReceiveMsg));
} }
} }
@ -192,7 +185,7 @@ TEST(ConceptSink, OneHundredSinksRemoved) {
std::vector<SinkHandleT> sink_handles; std::vector<SinkHandleT> sink_handles;
{ {
RestoreFileLogger logger{"./"}; RestoreFileLogger logger{"./"};
g3::LogWorker* worker = logger._scope->get(); //think: g3LogWorker::createLogWorker(); g3::LogWorker* worker = logger._scope->get(); //think: g3LogWorker::createLogWorker();
AddManySinks(kNumberOfItems, flags, counts, sink_handles, worker); AddManySinks(kNumberOfItems, flags, counts, sink_handles, worker);
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)}; LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
auto& write = message.get()->write(); auto& write = message.get()->write();
@ -217,7 +210,7 @@ TEST(ConceptSink, OneHundredRemoveAllSinks) {
std::vector<SinkHandleT> sink_handles; std::vector<SinkHandleT> sink_handles;
{ {
RestoreFileLogger logger{"./"}; RestoreFileLogger logger{"./"};
g3::LogWorker* worker = logger._scope->get(); //think: g3LogWorker::createLogWorker(); g3::LogWorker* worker = logger._scope->get(); //think: g3LogWorker::createLogWorker();
AddManySinks(kNumberOfItems, flags, counts, sink_handles, worker); AddManySinks(kNumberOfItems, flags, counts, sink_handles, worker);
LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)}; LogMessagePtr message{std::make_unique<LogMessage>("test", 0, "test", DEBUG)};
@ -232,12 +225,13 @@ TEST(ConceptSink, OneHundredRemoveAllSinks) {
} }
} }
struct VoidReceiver { struct VoidReceiver {
std::atomic<int>* _atomicCounter; std::atomic<int>* _atomicCounter;
explicit VoidReceiver(std::atomic<int>* counter) : _atomicCounter(counter) {} explicit VoidReceiver(std::atomic<int>* counter) :
_atomicCounter(counter) {}
void receiveMsg(std::string msg) { /*ignored*/} void receiveMsg(std::string msg) { /*ignored*/
}
void incrementAtomic() { void incrementAtomic() {
(*_atomicCounter)++; (*_atomicCounter)++;
} }
@ -267,20 +261,21 @@ TEST(ConceptSink, VoidCall__TwoCalls_ExpectingTwoAdd) {
{ {
std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()}; std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
auto handle = worker->addSink(std::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg); auto handle = worker->addSink(std::make_unique<VoidReceiver>(&counter), &VoidReceiver::receiveMsg);
auto voidFuture1 = handle->call(&VoidReceiver::incrementAtomic); auto voidFuture1 = handle->call(&VoidReceiver::incrementAtomic);
auto voidFuture2 = handle->call(&VoidReceiver::incrementAtomic); auto voidFuture2 = handle->call(&VoidReceiver::incrementAtomic);
voidFuture1.wait(); voidFuture1.wait();
EXPECT_TRUE(counter >= 1); EXPECT_TRUE(counter >= 1);
} }
EXPECT_EQ(counter, 2); EXPECT_EQ(counter, 2);
} }
struct IntReceiver { struct IntReceiver {
std::atomic<int>* _atomicCounter; std::atomic<int>* _atomicCounter;
explicit IntReceiver(std::atomic<int>* counter) : _atomicCounter(counter) {} explicit IntReceiver(std::atomic<int>* counter) :
_atomicCounter(counter) {}
void receiveMsgDoNothing(std::string msg) { /*ignored*/} void receiveMsgDoNothing(std::string msg) { /*ignored*/
}
void receiveMsgIncrementAtomic(std::string msg) { incrementAtomic(); } void receiveMsgIncrementAtomic(std::string msg) { incrementAtomic(); }
int incrementAtomic() { int incrementAtomic() {
(*_atomicCounter)++; (*_atomicCounter)++;
@ -300,14 +295,11 @@ TEST(ConceptSink, IntCall__TwoCalls_ExpectingTwoAdd) {
auto intFuture2 = handle->call(&IntReceiver::incrementAtomic); auto intFuture2 = handle->call(&IntReceiver::incrementAtomic);
EXPECT_EQ(intFuture2.get(), 2); EXPECT_EQ(intFuture2.get(), 2);
} }
EXPECT_EQ(counter, 2); EXPECT_EQ(counter, 2);
} }
void DoLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
void DoLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
while (doWhileTrue->load()) { while (doWhileTrue->load()) {
LOG(INFO) << "Calling from #" << counter; LOG(INFO) << "Calling from #" << counter;
std::cout << "-"; std::cout << "-";
@ -315,12 +307,12 @@ void DoLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
} }
} }
void DoSlowLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) { void DoSlowLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
size_t messages = 0; size_t messages = 0;
while (doWhileTrue->load()) { while (doWhileTrue->load()) {
LOG(INFO) << "Calling from #" << counter; LOG(INFO) << "Calling from #" << counter;
++messages; ++messages;
int random = rand() % 10 + 1; // Range 1-10 int random = rand() % 10 + 1; // Range 1-10
std::this_thread::sleep_for(std::chrono::microseconds(random)); std::this_thread::sleep_for(std::chrono::microseconds(random));
} }
@ -328,11 +320,10 @@ void DoSlowLogCalls(std::atomic<bool>* doWhileTrue, size_t counter) {
std::cout << out; std::cout << out;
} }
TEST(ConceptSink, CannotCallSpawnTaskOnNullptrWorker) { TEST(ConceptSink, CannotCallSpawnTaskOnNullptrWorker) {
auto FailedHelloWorld = [] { std::cout << "Hello World" << std::endl; }; auto FailedHelloWorld = [] {
std::cout << "Hello World" << std::endl;
};
kjellkod::Active* active = nullptr; kjellkod::Active* active = nullptr;
auto failed = g3::spawn_task(FailedHelloWorld, active); auto failed = g3::spawn_task(FailedHelloWorld, active);
EXPECT_ANY_THROW(failed.get()); EXPECT_ANY_THROW(failed.get());
@ -364,7 +355,7 @@ TEST(ConceptSink, AggressiveThreadCallsDuringAddAndRemoveSink) {
std::atomic<int> atomicCounter{0}; std::atomic<int> atomicCounter{0};
std::cout << "Add sinks, remove sinks, " << numberOfCycles << " times\n\tWhile " << numberOfThreads << " threads are continously doing LOG calls" << std::endl; 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) { for (size_t create = 0; create < numberOfCycles; ++create) {
worker->removeAllSinks(); worker->removeAllSinks();
sink_handles.clear(); sink_handles.clear();
@ -380,7 +371,7 @@ TEST(ConceptSink, AggressiveThreadCallsDuringAddAndRemoveSink) {
std::this_thread::yield(); std::this_thread::yield();
} }
} // g3log worker exists: 1) shutdownlogging 2) flush of queues and shutdown of sinks } // g3log worker exists: 1) shutdownlogging 2) flush of queues and shutdown of sinks
worker.reset(); worker.reset();
// exit the threads // exit the threads
keepRunning = false; keepRunning = false;
@ -390,7 +381,6 @@ TEST(ConceptSink, AggressiveThreadCallsDuringAddAndRemoveSink) {
std::cout << "\nAll threads are joined " << std::endl; std::cout << "\nAll threads are joined " << std::endl;
} }
// This test is commented out but kept here for documentation purposes. // 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. // 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 // the are several initial setups that happen ONCE and the logger relies on the client
@ -424,7 +414,6 @@ TEST(ConceptSink, AggressiveThreadCallsDuringAddAndRemoveSink) {
// for (size_t create = 0; create < numberOfCycles; ++create) { // for (size_t create = 0; create < numberOfCycles; ++create) {
// std::cout << "."; // std::cout << ".";
// std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()}; // std::unique_ptr<g3::LogWorker> worker{g3::LogWorker::createLogWorker()};
// auto handle = worker->addSink(std::make_unique<IntReceiver>(&atomicCounter), &IntReceiver::receiveMsgIncrementAtomic); // auto handle = worker->addSink(std::make_unique<IntReceiver>(&atomicCounter), &IntReceiver::receiveMsgIncrementAtomic);
// g3::initializeLogging(worker.get()); // g3::initializeLogging(worker.get());

View File

@ -6,10 +6,9 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
#include "tester_sharedlib.h"
#include <g3log/g3log.hpp> #include <g3log/g3log.hpp>
#include <g3log/logworker.hpp> #include <g3log/logworker.hpp>
#include "tester_sharedlib.h"
struct RuntimeLoadedLib : public SomeLibrary { struct RuntimeLoadedLib : public SomeLibrary {
@ -34,4 +33,3 @@ struct RealLibraryFactory : public LibraryFactory {
}; };
RealLibraryFactory testRealFactory; RealLibraryFactory testRealFactory;

View File

@ -6,17 +6,15 @@
* For more information see g3log/LICENSE or refer refer to http://unlicense.org * For more information see g3log/LICENSE or refer refer to http://unlicense.org
* ============================================================================*/ * ============================================================================*/
#include <g3log/g3log.hpp> #include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3log/logmessage.hpp> #include <g3log/logmessage.hpp>
#include <g3log/logworker.hpp>
#include "testing_helpers.h" #include "testing_helpers.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <iostream>
#include <fstream> #include <fstream>
#include <iostream>
using namespace std; using namespace std;
using namespace g3; using namespace g3;
@ -27,7 +25,7 @@ namespace testing_helpers {
int g_mockFatal_signal = -1; int g_mockFatal_signal = -1;
bool g_mockFatalWasCalled = false; bool g_mockFatalWasCalled = false;
const size_t kFlushToDiskWithThisInterval = 2; const size_t kFlushToDiskWithThisInterval = 2;
std::string mockFatalMessage() { std::string mockFatalMessage() {
return g_mockFatal_message; return g_mockFatal_message;
} }
@ -45,7 +43,7 @@ namespace testing_helpers {
g_mockFatal_signal = fatal_message.get()->_signal_id; g_mockFatal_signal = fatal_message.get()->_signal_id;
g_mockFatalWasCalled = true; g_mockFatalWasCalled = true;
LogMessagePtr message{fatal_message.release()}; LogMessagePtr message{fatal_message.release()};
g3::internal::pushMessageToLogger(message); //fatal_message.copyToLogMessage()); g3::internal::pushMessageToLogger(message); //fatal_message.copyToLogMessage());
} }
void clearMockFatal() { void clearMockFatal() {
@ -58,7 +56,7 @@ namespace testing_helpers {
return (0 == std::remove(path_to_file.c_str())); return (0 == std::remove(path_to_file.c_str()));
} }
bool verifyContent(const std::string &total_text, std::string msg_to_find) { bool verifyContent(const std::string& total_text, std::string msg_to_find) {
std::string content(total_text); std::string content(total_text);
size_t location = content.find(msg_to_find); size_t location = content.find(msg_to_find);
return (location != std::string::npos); return (location != std::string::npos);
@ -68,9 +66,7 @@ namespace testing_helpers {
std::ifstream in; std::ifstream in;
in.open(filename.c_str(), std::ios_base::in); in.open(filename.c_str(), std::ios_base::in);
if (!in.is_open()) { if (!in.is_open()) {
return return {}; // error just return empty string - test will 'fault'
{
}; // error just return empty string - test will 'fault'
} }
std::ostringstream oss; std::ostringstream oss;
oss << in.rdbuf(); oss << in.rdbuf();
@ -82,7 +78,6 @@ namespace testing_helpers {
return logs_to_clean_.size(); return logs_to_clean_.size();
} }
LogFileCleaner::~LogFileCleaner() { LogFileCleaner::~LogFileCleaner() {
std::lock_guard<std::mutex> lock(g_mutex); std::lock_guard<std::mutex> lock(g_mutex);
{ {
@ -92,7 +87,7 @@ namespace testing_helpers {
} }
} }
logs_to_clean_.clear(); logs_to_clean_.clear();
} // mutex } // mutex
} }
void LogFileCleaner::addLogToClean(std::string path_to_log) { void LogFileCleaner::addLogToClean(std::string path_to_log) {
@ -103,23 +98,25 @@ namespace testing_helpers {
} }
} }
ScopedLogger::ScopedLogger() : _currentWorker(g3::LogWorker::createLogWorker()) {} ScopedLogger::ScopedLogger() :
_currentWorker(g3::LogWorker::createLogWorker()) {}
ScopedLogger::~ScopedLogger() {} ScopedLogger::~ScopedLogger() {}
g3::LogWorker* ScopedLogger::get() { g3::LogWorker* ScopedLogger::get() {
return _currentWorker.get(); return _currentWorker.get();
} }
RestoreFileLogger::RestoreFileLogger(std::string directory) RestoreFileLogger::RestoreFileLogger(std::string directory) :
: _scope(new ScopedLogger), _handle(_scope->get()->addSink(std::make_unique<g3::FileSink>("UNIT_TEST_LOGGER", _scope(new ScopedLogger),
directory, "g3log", kFlushToDiskWithThisInterval), &g3::FileSink::fileWrite)) { _handle(_scope->get()->addSink(std::make_unique<g3::FileSink>("UNIT_TEST_LOGGER", directory, "g3log", kFlushToDiskWithThisInterval), &g3::FileSink::fileWrite)) {
using namespace g3; using namespace g3;
g3::initializeLogging(_scope->_currentWorker.get()); g3::initializeLogging(_scope->_currentWorker.get());
clearMockFatal(); clearMockFatal();
setFatalExitHandler(&mockFatalCall); setFatalExitHandler(&mockFatalCall);
auto filename = _handle->call(&FileSink::fileName); auto filename = _handle->call(&FileSink::fileName);
if (!filename.valid()) ADD_FAILURE(); if (!filename.valid())
ADD_FAILURE();
_log_file = filename.get(); _log_file = filename.get();
#ifdef G3_DYNAMIC_LOGGING #ifdef G3_DYNAMIC_LOGGING
@ -131,18 +128,18 @@ namespace testing_helpers {
} }
RestoreFileLogger::~RestoreFileLogger() { RestoreFileLogger::~RestoreFileLogger() {
g3::internal::shutDownLogging(); // is done at reset. Added for test clarity g3::internal::shutDownLogging(); // is done at reset. Added for test clarity
reset(); reset();
if (!removeFile(_log_file)) if (!removeFile(_log_file))
ADD_FAILURE(); ADD_FAILURE();
} }
std::string RestoreFileLogger::logFile() { std::string RestoreFileLogger::logFile() {
if (_scope) { if (_scope) {
// beware for race condition // beware for race condition
// example: // example:
// LOG(INFO) << ... // LOG(INFO) << ...
// auto file = logger.logFile() // auto file = logger.logFile()
// auto content = ReadContentFromFile(file) // auto content = ReadContentFromFile(file)
// ... it is not guaranteed that the content will contain (yet) the LOG(INFO) // ... it is not guaranteed that the content will contain (yet) the LOG(INFO)
@ -152,14 +149,14 @@ namespace testing_helpers {
return _log_file; return _log_file;
} }
// Beware of race between LOG(...) and this function. // Beware of race between LOG(...) and this function.
// since LOG(...) passes two queues but the handle::call only passes one queue // since LOG(...) passes two queues but the handle::call only passes one queue
// the handle::call can happen faster // the handle::call can happen faster
std::string RestoreFileLogger::resetAndRetrieveContent() { std::string RestoreFileLogger::resetAndRetrieveContent() {
std::future<std::string> filename = _handle->call(&g3::FileSink::fileName); std::future<std::string> filename = _handle->call(&g3::FileSink::fileName);
reset(); // flush all queues to sinks reset(); // flush all queues to sinks
EXPECT_TRUE(filename.valid()); EXPECT_TRUE(filename.valid());
auto file = filename.get(); auto file = filename.get();
return readFileToText(file); return readFileToText(file);
} }
} // testing_helpers } // namespace testing_helpers