mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-12 10:23:50 +01:00
Merged in changes from development repository.
* threadsafe use of localtime * changing/retrieving log filename at runtime (using futures)
This commit is contained in:
parent
d0042cf730
commit
a4c49a5549
30
README
30
README
@ -1,3 +1,18 @@
|
||||
LATEST CHANGES
|
||||
=====================
|
||||
2012, Oct 14th
|
||||
* Complete threadsafe use of localtime for timestamp. Reference g2time
|
||||
* Runtime change of filename, request of filename. Reference g2logwoker g2future
|
||||
Tested on
|
||||
x86 Windows7: Visual Studio 2012 Desktop Express
|
||||
x86 Ubuntu 11.04: gcc 4.7.3
|
||||
x64 Ubuntu 12...: gcc 4.7.2
|
||||
x64 Windows7: Visual Studio 2012 Professional
|
||||
|
||||
NOTE: It does NOT work with "Visual Studio 11 Beta"
|
||||
|
||||
|
||||
|
||||
HOW TO BUILD
|
||||
===================
|
||||
This g2log is a snapshot from KjellKod repository.
|
||||
@ -5,19 +20,14 @@ It contains what is needed to build example, unit test and performance test of g
|
||||
|
||||
justthread C++11 thread library is no longer needed.
|
||||
On Windows it is enough to use Visual Studio 11 (2012)
|
||||
On Linux it is enough to use gcc4.7.
|
||||
The CMakeFile.txt is updated to reflect that, the justthread parts is
|
||||
On Linux it is enough to use gcc4.7.2. The CMakeFile.txt
|
||||
is updated to reflect that, the justthread parts is
|
||||
commented away in case someone still needs that.
|
||||
If you do not have gcc 4.7.2
|
||||
|
||||
|
||||
If you want to integrate g2log in your own software you need
|
||||
the following files
|
||||
g2log/src/
|
||||
g2log.cpp/h
|
||||
g2logworker.cpp/h
|
||||
crashhandler.h (crashhandler_win.cpp or crashhandler_unix.cpp)
|
||||
shared_queue.h
|
||||
active.h/cpp
|
||||
the files under g2log/src
|
||||
|
||||
|
||||
BUILDING g2log:
|
||||
@ -58,7 +68,7 @@ glog is not included and must be installed if you want to run the comparison tes
|
||||
|
||||
About the Active Object
|
||||
--------------------
|
||||
Is made with standard C++ components with the help of the latest C++0x and std::thread features (thanks to justthread). For more details see www.kjellkod.cc/active-object-with-cpp0x. An example is provided. Other examples on pre C++0x Active Objects can also be found at www.kjellkod.cc (code page)
|
||||
Is made with standard C++ components with the help of the latest C++11 and std::thread features (thanks to justthread). For more details see www.kjellkod.cc/active-object-with-cpp0x. An example is provided. Other examples on pre C++0x Active Objects can also be found at www.kjellkod.cc (code page)
|
||||
|
||||
If you like it (or not) it would be nice with some feedback. That way
|
||||
I can improve g2log and it is also nice to see if someone is using it.
|
||||
|
@ -19,7 +19,8 @@
|
||||
|
||||
|
||||
# WINDOWS == README: Example how to setup environment + running an example
|
||||
# 1. please use the "Visual Studio Command Prompt)"
|
||||
# Below written for VS11 (2012)
|
||||
# 1. please use the "Visual Studio Command Prompt 11 (2012)"
|
||||
# 2. from the g2log folder
|
||||
# mkdir build
|
||||
# cd build;
|
||||
@ -51,6 +52,15 @@ MESSAGE(" LOG_SRC = : ${LOG_SRC}")
|
||||
include_directories(${LOG_SRC})
|
||||
SET(ACTIVE_CPP0xx_DIR "Release")
|
||||
|
||||
# Detect 64 or 32 bit
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
# 64-bit project
|
||||
SET(64_BIT_OS TRUE)
|
||||
MESSAGE("A 64-bit OS detected")
|
||||
else()
|
||||
SET(64_BIT_OS FALSE)
|
||||
MESSAGE("A 32-bit OS detected")
|
||||
endif()
|
||||
|
||||
|
||||
IF(UNIX)
|
||||
@ -74,8 +84,6 @@ IF(UNIX)
|
||||
# include_directories("/usr/include/justthread")
|
||||
ENDIF(UNIX)
|
||||
|
||||
|
||||
|
||||
if (MSVC)
|
||||
# VC11 bug: http://code.google.com/p/googletest/issues/detail?id=408
|
||||
# add_definition(-D_VARIADIC_MAX=10)
|
||||
@ -86,7 +94,8 @@ IF(UNIX)
|
||||
# "set_target_properties(unit_test PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0")
|
||||
endif ()
|
||||
|
||||
#Visual Studio 2010 -- must use justthread
|
||||
|
||||
#Visual Studio 2010 -- must use justthread. For now hardcoded for x64
|
||||
IF(MSVC10)
|
||||
MESSAGE("")
|
||||
MESSAGE("Windows: Please run the command [cmake -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 10\" ..]")
|
||||
@ -94,6 +103,7 @@ IF(MSVC10)
|
||||
MESSAGE("then run 'Release\\g2log-FATAL-example.exe' or whatever performance test you feel like trying")
|
||||
MESSAGE("")
|
||||
set(PLATFORM_LINK_LIBRIES $ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/lib/justthread_vc10_mdd.lib)
|
||||
#set(PLATFORM_LINK_LIBRIES $ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/lib/justthread_vc10x64_mdd.lib)
|
||||
set(SRC_PLATFORM_SPECIFIC ${LOG_SRC}/crashhandler_win.cpp)
|
||||
include_directories("$ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/include")
|
||||
ENDIF(MSVC10)
|
||||
@ -111,8 +121,8 @@ ENDIF(MSVC11)
|
||||
|
||||
# GENERIC STEPS
|
||||
# CODE SOURCES these +
|
||||
set(SRC_CPP ${LOG_SRC}/g2logworker.cpp ${LOG_SRC}/g2log.cpp)
|
||||
set(SRC_H ${LOG_SRC}/g2logworker.h ${LOG_SRC}/g2log.h ${LOG_SRC}/crashhandler.h)
|
||||
set(SRC_CPP ${LOG_SRC}/g2logworker.cpp ${LOG_SRC}/g2log.cpp ${LOG_SRC}/g2time.cpp)
|
||||
set(SRC_H ${LOG_SRC}/g2logworker.h ${LOG_SRC}/g2log.h ${LOG_SRC}/crashhandler.h ${LOG_SRC}/g2time.h ${LOG_SRC}/g2future.h)
|
||||
set(SRC_FILES ${SRC_CPP} ${SRC_H} ${SRC_PLATFORM_SPECIFIC})
|
||||
|
||||
|
||||
@ -121,6 +131,8 @@ ENDIF(MSVC11)
|
||||
add_library(lib_activeobject ${LOG_SRC}/active.cpp ${LOG_SRC}/active.h ${LOG_SRC}/shared_queue.h)
|
||||
set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
|
||||
|
||||
# add a g2log library
|
||||
include_directories(src)
|
||||
include_directories(${LOG_SRC})
|
||||
@ -242,10 +254,19 @@ ENDIF(MSVC11)
|
||||
add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc)
|
||||
enable_testing(true)
|
||||
|
||||
add_executable(g2log-unit_test ../test_main/test_main.cpp ${DIR_UNIT_TEST}/test_io.cpp)
|
||||
add_executable(g2log-unit_test ../test_main/test_main.cpp ${DIR_UNIT_TEST}/test_io.cpp ${DIR_UNIT_TEST}/test_configuration.cpp)
|
||||
# obs see this: http://stackoverflow.com/questions/9589192/how-do-i-change-the-number-of-template-arguments-supported-by-msvcs-stdtupl
|
||||
set_target_properties(g2log-unit_test PROPERTIES COMPILE_DEFINITIONS "_VARIADIC_MAX=10")
|
||||
# and this: http://stackoverflow.com/questions/2257464/google-test-and-visual-studio-2010-rc
|
||||
set_target_properties(g2log-unit_test PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0")
|
||||
target_link_libraries(g2log-unit_test lib_activeobject lib_g2logger gtest_160_lib ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
|
||||
add_executable(g2log-unit_test_filechange ${DIR_UNIT_TEST}/test_filechange.cpp)
|
||||
set_target_properties(g2log-unit_test_filechange PROPERTIES COMPILE_DEFINITIONS "_VARIADIC_MAX=10")
|
||||
set_target_properties(g2log-unit_test_filechange PROPERTIES COMPILE_DEFINITIONS "GTEST_USE_OWN_TR1_TUPLE=0")
|
||||
target_link_libraries(g2log-unit_test_filechange lib_activeobject lib_g2logger gtest_160_lib ${PLATFORM_LINK_LIBRIES})
|
||||
|
||||
endif (USE_G2LOG_UNIT_TEST)
|
||||
|
||||
|
||||
|
@ -4,17 +4,17 @@
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
*
|
||||
* Example of a Active Object, using C++0x std::thread mechanisms to make it
|
||||
* Example of a Active Object, using C++11 std::thread mechanisms to make it
|
||||
* safe for thread communication.
|
||||
*
|
||||
* This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x
|
||||
* and inspired from Herb Sutter's C++0x Active Object
|
||||
* and inspired from Herb Sutter's C++11 Active Object
|
||||
* http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads
|
||||
*
|
||||
* The code below uses JustSoftware Solutions Inc std::thread implementation
|
||||
* http://www.justsoftwaresolutions.co.uk
|
||||
*
|
||||
* Last update 2011-06-23, by Kjell Hedstrom,
|
||||
* Last update 2012-10-10, by Kjell Hedstrom,
|
||||
* e-mail: hedstrom at kjellkod dot cc
|
||||
* linkedin: http://linkedin.com/se/kjellkod */
|
||||
|
||||
|
@ -4,17 +4,17 @@
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
*
|
||||
* Example of a Active Object, using C++0x std::thread mechanisms to make it
|
||||
* Example of a Active Object, using C++11 std::thread mechanisms to make it
|
||||
* safe for thread communication.
|
||||
*
|
||||
* This was originally published at http://sites.google.com/site/kjellhedstrom2/active-object-with-cpp0x
|
||||
* and inspired from Herb Sutter's C++0x Active Object
|
||||
* and inspired from Herb Sutter's C++11 Active Object
|
||||
* http://herbsutter.com/2010/07/12/effective-concurrency-prefer-using-active-objects-instead-of-naked-threads
|
||||
*
|
||||
* The code below uses JustSoftware Solutions Inc std::thread implementation
|
||||
* http://www.justsoftwaresolutions.co.uk
|
||||
*
|
||||
* Last update 2011-06-23, by Kjell Hedstrom,
|
||||
* Last update 2012-10-10, by Kjell Hedstrom,
|
||||
* e-mail: hedstrom at kjellkod dot cc
|
||||
* linkedin: http://linkedin.com/se/kjellkod */
|
||||
|
||||
@ -36,15 +36,15 @@ class Active {
|
||||
private:
|
||||
Active(const Active&); // c++11 feature not yet in vs2010 = delete;
|
||||
Active& operator=(const Active&); // c++11 feature not yet in vs2010 = delete;
|
||||
|
||||
Active(); // Construction ONLY through factory createActive();
|
||||
|
||||
void doDone(){done_ = true;}
|
||||
void run();
|
||||
|
||||
shared_queue<Callback> mq_;
|
||||
std::thread thd_;
|
||||
bool done_; // finished flag to be set through msg queue by ~Active
|
||||
|
||||
|
||||
public:
|
||||
virtual ~Active();
|
||||
void send(Callback msg_);
|
||||
|
70
g2log/src/g2future.h
Normal file
70
g2log/src/g2future.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef G2FUTURE_H
|
||||
#define G2FUTURE_H
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2future.h
|
||||
* Helper functionality to put packaged_tasks in standard container. This
|
||||
* is especially helpful for background thread processing a la async but through
|
||||
* an actor pattern (active object), thread pool or similar.
|
||||
* Created: 2012 by Kjell Hedström
|
||||
*
|
||||
* COMMUNITY THANKS:
|
||||
* The code below is in large thanks to exemplifying code snippets from StackOverflow
|
||||
* question/answer: http://stackoverflow.com/questions/6230893/developing-c-concurrency-library-with-futures-or-similar-paradigm
|
||||
* and a discussion between Lars Gullik Bjønnes and Jonathan Wakely's at: http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
|
||||
*
|
||||
* Both are highly recommended reads if you are interested in c++ concurrency library
|
||||
* - Kjell, 2012
|
||||
*
|
||||
* PUBLIC DOMAIN and NOT under copywrite protection.
|
||||
* ********************************************* */
|
||||
|
||||
|
||||
|
||||
#include <future>
|
||||
#include "active.h"
|
||||
|
||||
namespace g2 {
|
||||
|
||||
// A straightforward technique to move around packaged_tasks.
|
||||
// Instances of std::packaged_task are MoveConstructible and MoveAssignable, but
|
||||
// not CopyConstructible or CopyAssignable. To put them in a std container they need
|
||||
// to be wrapped and their internals "moved" when tried to be copied.
|
||||
template<typename Moveable>
|
||||
struct PretendToBeCopyable
|
||||
{
|
||||
explicit PretendToBeCopyable(Moveable&& m) : move_only_(std::move(m)) {}
|
||||
PretendToBeCopyable(PretendToBeCopyable& p) : move_only_(std::move(p.move_only_)){}
|
||||
PretendToBeCopyable(PretendToBeCopyable&& p) : move_only_(std::move(p.move_only_)){} // = default; // so far only on gcc
|
||||
void operator()() { move_only_(); } // execute
|
||||
private:
|
||||
Moveable move_only_;
|
||||
};
|
||||
|
||||
|
||||
// Generic helper function to avoid repeating the steps for managing
|
||||
// asynchronous task job (by active object) that returns a future results
|
||||
// could of course be made even more generic if done more in the way of
|
||||
// std::async, ref: http://en.cppreference.com/w/cpp/thread/async
|
||||
//
|
||||
// Example usage:
|
||||
// std::unique_ptr<Active> bgWorker{Active::createActive()};
|
||||
// ...
|
||||
// auto msg_call=[=](){return ("Hello from the Background");};
|
||||
// auto future_msg = g2::spawn_task(msg_lambda, bgWorker.get());
|
||||
template <typename Func>
|
||||
std::future<typename std::result_of<Func()>::type> spawn_task(Func func, kjellkod::Active* worker)
|
||||
{
|
||||
typedef typename std::result_of<Func()>::type result_type;
|
||||
typedef std::packaged_task<result_type()> task_type;
|
||||
task_type task(std::move(func));
|
||||
|
||||
std::future<result_type> result = task.get_future();
|
||||
worker->send(PretendToBeCopyable<task_type>(std::move(task)));
|
||||
return std::move(result);
|
||||
}
|
||||
} // end namespace g2
|
||||
#endif // G2FUTURE_H
|
@ -7,7 +7,7 @@
|
||||
* Filename:g2log.cpp Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and at least in "spirit" influenced
|
||||
* from the following sources
|
||||
* 1. kjellkod.cc ;)
|
||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited since it was built on public-domain software and influenced
|
||||
* from the following sources
|
||||
* at least in "spirit" from the following sources
|
||||
* 1. kjellkod.cc ;)
|
||||
* 2. Dr.Dobbs, Petru Marginean: http://drdobbs.com/article/printableArticle.jhtml?articleId=201804215&dept_url=/cpp/
|
||||
* 3. Dr.Dobbs, Michael Schulze: http://drdobbs.com/article/printableArticle.jhtml?articleId=225700666&dept_url=/cpp/
|
||||
@ -162,14 +162,14 @@ namespace internal
|
||||
{
|
||||
typedef const std::string& LogEntry;
|
||||
|
||||
|
||||
/** By default the g2log will call g2LogWorker::fatal(...) which will abort() the system after flushing
|
||||
* the logs to file. This makes unit test of FATAL level cumbersome. A work around is to change the 'fatal call'
|
||||
* which can be done here */
|
||||
void changeFatalInitHandlerForUnitTesting();
|
||||
|
||||
// TODO: LogEntry och FatalMessage borde kunna slås ihop till samma!
|
||||
|
||||
/** Trigger for flushing the message queue and exiting the applicaition
|
||||
/** Trigger for flushing the message queue and exiting the application
|
||||
A thread that causes a FatalMessage will sleep forever until the
|
||||
application has exited (after message flush) */
|
||||
struct FatalMessage
|
||||
@ -195,7 +195,7 @@ struct FatalTrigger
|
||||
class LogMessage
|
||||
{
|
||||
public:
|
||||
LogMessage(const std::string &file, const int line, const std::string& function_, const std::string &level);
|
||||
LogMessage(const std::string &file, const int line, const std::string& function, const std::string &level);
|
||||
virtual ~LogMessage(); // at destruction will flush the message
|
||||
|
||||
std::ostringstream& messageStream(){return stream_;}
|
||||
|
@ -1,65 +1,45 @@
|
||||
/** ==========================================================================
|
||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2LogWorker.cpp Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2LogWorker.cpp Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not under copywrite protection. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
#include "g2logworker.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <functional>
|
||||
|
||||
|
||||
#include "active.h"
|
||||
#include "g2log.h"
|
||||
#include "crashhandler.h"
|
||||
#include "g2time.h"
|
||||
#include "g2future.h"
|
||||
|
||||
using namespace g2;
|
||||
using namespace g2::internal;
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef std::chrono::steady_clock::time_point time_point;
|
||||
typedef std::chrono::duration<long,std::ratio<1, 1000> > millisecond;
|
||||
typedef std::chrono::duration<long long,std::ratio<1, 1000000> > microsecond;
|
||||
|
||||
|
||||
struct LogTime
|
||||
{
|
||||
LogTime()
|
||||
{
|
||||
time_t current_time = time(nullptr);
|
||||
ctime(¤t_time); // fill with time right now
|
||||
struct tm* ptm = ::localtime(¤t_time); // fill time struct with data
|
||||
year = ptm->tm_year + 1900;
|
||||
month = (ptm->tm_mon) +1;
|
||||
day = ptm->tm_mday;
|
||||
hour = ptm->tm_hour;
|
||||
minute = ptm->tm_min;
|
||||
second = ptm->tm_sec;
|
||||
}
|
||||
int year; // Year - 1900
|
||||
int month; // [1-12]
|
||||
int day; // [1-31]
|
||||
int hour; // [0-23]
|
||||
int minute; // [0-59]
|
||||
int second; // [0-60], 1 leap second
|
||||
};
|
||||
static const std::string date_formatted = "%Y/%m/%d";
|
||||
static const std::string time_formatted = "%H:%M:%S";
|
||||
static const std::string file_name_time_formatted = "%Y%m%d-%H%M%S";
|
||||
|
||||
// check for filename validity - filename should not be part of PATH
|
||||
bool isValidFilename(const std::string prefix_filename)
|
||||
{
|
||||
|
||||
std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* ");
|
||||
size_t pos = prefix_filename.find_first_of(illegal_characters,0);
|
||||
if(pos != std::string::npos)
|
||||
@ -72,16 +52,78 @@ bool isValidFilename(const std::string prefix_filename)
|
||||
std::cerr << "Empty filename prefix is not allowed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Clean up the path if put in by mistake in the prefix
|
||||
std::string prefixSanityFix(const std::string& prefix)
|
||||
{
|
||||
std::string real_prefix = prefix;
|
||||
std::remove( real_prefix.begin(), real_prefix.end(), '/');
|
||||
std::remove( real_prefix.begin(), real_prefix.end(), '\\');
|
||||
std::remove( real_prefix.begin(), real_prefix.end(), '.');
|
||||
if(!isValidFilename(real_prefix))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return real_prefix;
|
||||
}
|
||||
|
||||
|
||||
std::string createLogFileName(const std::string& verified_prefix)
|
||||
{
|
||||
std::stringstream oss_name;
|
||||
oss_name.fill('0');
|
||||
oss_name << verified_prefix << ".g2log.";
|
||||
oss_name << g2::localtime_formatted(g2::systemtime_now(), file_name_time_formatted);
|
||||
oss_name << ".log";
|
||||
return oss_name.str();
|
||||
}
|
||||
|
||||
|
||||
bool openLogFile(const std::string& complete_file_with_path, std::ofstream& outstream)
|
||||
{
|
||||
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
|
||||
mode |= std::ios_base::trunc;
|
||||
outstream.open(complete_file_with_path, mode);
|
||||
if(!outstream.is_open())
|
||||
{
|
||||
std::ostringstream ss_error;
|
||||
ss_error << "FILE ERROR: could not open log file:[" << complete_file_with_path << "]";
|
||||
ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
|
||||
std::cerr << ss_error.str().c_str() << std::endl << std::flush;
|
||||
outstream.close();
|
||||
return false;
|
||||
}
|
||||
std::ostringstream ss_entry;
|
||||
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
|
||||
ss_entry << "\t\tg2log created log file at: "<< g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
|
||||
ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss.uuu* LEVEL FILE:LINE] message\n\n"; // TODO: if(header)
|
||||
outstream << ss_entry.str() << std::flush;
|
||||
outstream.fill('0');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<std::ofstream> createLogFile(const std::string& file_with_full_path)
|
||||
{
|
||||
std::unique_ptr<std::ofstream> out(new std::ofstream);
|
||||
std::ofstream& stream(*(out.get()));
|
||||
bool success_with_open_file = openLogFile(file_with_full_path, stream);
|
||||
if(false == success_with_open_file)
|
||||
{
|
||||
out.release(); // nullptr contained ptr<file> signals error in creating the log file
|
||||
}
|
||||
return out;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
/** The actual background worker, while g2LogWorker gives the
|
||||
* asynchronous API to put job in the background the g2LogWorkerImpl
|
||||
* does the actual background thread work */
|
||||
/** The Real McCoy Background worker, while g2LogWorker gives the
|
||||
* asynchronous API to put job in the background the g2LogWorkerImpl
|
||||
* does the actual background thread work */
|
||||
struct g2LogWorkerImpl
|
||||
{
|
||||
g2LogWorkerImpl(const std::string& log_prefix, const std::string& log_directory);
|
||||
@ -89,15 +131,19 @@ struct g2LogWorkerImpl
|
||||
|
||||
void backgroundFileWrite(g2::internal::LogEntry message);
|
||||
void backgroundExitFatal(g2::internal::FatalMessage fatal_message);
|
||||
std::string backgroundChangeLogFile(const std::string& directory);
|
||||
std::string backgroundFileName();
|
||||
|
||||
std::string log_file_with_path_;
|
||||
std::string log_prefix_backup_; // needed in case of future log file changes of directory
|
||||
std::unique_ptr<kjellkod::Active> bg_;
|
||||
std::ofstream out;
|
||||
time_point start_time_;
|
||||
std::unique_ptr<std::ofstream> outptr_;
|
||||
steady_time_point steady_start_time_;
|
||||
|
||||
private:
|
||||
g2LogWorkerImpl& operator=(const g2LogWorkerImpl&); // c++11 feature not yet in vs2010 = delete;
|
||||
g2LogWorkerImpl(const g2LogWorkerImpl& other); // c++11 feature not yet in vs2010 = delete;
|
||||
std::ofstream& filestream(){return *(outptr_.get());}
|
||||
};
|
||||
|
||||
|
||||
@ -106,112 +152,136 @@ private:
|
||||
// Private API implementation : g2LogWorkerImpl
|
||||
g2LogWorkerImpl::g2LogWorkerImpl(const std::string& log_prefix, const std::string& log_directory)
|
||||
: log_file_with_path_(log_directory)
|
||||
, log_prefix_backup_(log_prefix)
|
||||
, bg_(kjellkod::Active::createActive())
|
||||
, start_time_(std::chrono::steady_clock::now())
|
||||
, outptr_(new std::ofstream)
|
||||
, steady_start_time_(std::chrono::steady_clock::now()) // TODO: ha en timer function steadyTimer som har koll på start
|
||||
{
|
||||
std::string real_prefix = log_prefix;
|
||||
// if through a debugger the debugger CAN just throw in the whole path
|
||||
// replace the path delimiters (unix?)
|
||||
std::remove( real_prefix.begin(), real_prefix.end(), '/');
|
||||
std::remove( real_prefix.begin(), real_prefix.end(), '\\');
|
||||
if(!isValidFilename(real_prefix))
|
||||
log_prefix_backup_ = prefixSanityFix(log_prefix);
|
||||
if(!isValidFilename(log_prefix_backup_))
|
||||
{
|
||||
// illegal prefix, refuse to start
|
||||
std::cerr << "g2log: forced abort due to illegal log prefix [" << log_prefix <<"]" << std::endl << std::flush;
|
||||
abort();
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
LogTime t;
|
||||
ostringstream oss_name;
|
||||
oss_name.fill('0');
|
||||
oss_name << real_prefix << ".g2log.";
|
||||
oss_name << t.year << setw(2) << t.month << setw(2) << t.day;
|
||||
oss_name << "-" << setw(2) << t.hour << setw(2) << t.minute << setw(2) << t.second;
|
||||
oss_name << ".log";
|
||||
log_file_with_path_ += oss_name.str();
|
||||
|
||||
// open the log file
|
||||
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
|
||||
mode |= std::ios_base::trunc;
|
||||
out.open(log_file_with_path_, mode);
|
||||
if(!out.is_open())
|
||||
{
|
||||
std::ostringstream ss_error;
|
||||
ss_error << "Fatal error could not open log file:[" << log_file_with_path_ << "]";
|
||||
ss_error << "\n\t\t std::ios_base state = " << out.rdstate();
|
||||
std::cerr << ss_error.str().c_str() << std::endl << std::flush;
|
||||
assert(false && "cannot open log file at startup");
|
||||
}
|
||||
std::ostringstream ss_entry;
|
||||
time_t creation_time;
|
||||
time(&creation_time);
|
||||
ss_entry << "\t\tg2log created log file at: " << ctime(&creation_time);
|
||||
ss_entry << "\t\tLOG format: [YYYY/MM/DD hh:mm:ss.uuu* LEVEL FILE:LINE] message\n\n";
|
||||
out << ss_entry.str() << std::flush;
|
||||
out.fill('0');
|
||||
std::string file_name = createLogFileName(log_prefix_backup_);
|
||||
log_file_with_path_ = log_directory + file_name;
|
||||
outptr_ = createLogFile(log_file_with_path_);
|
||||
assert((nullptr != outptr_) && "cannot open log file at startup");
|
||||
}
|
||||
|
||||
|
||||
g2LogWorkerImpl::~g2LogWorkerImpl()
|
||||
{
|
||||
std::ostringstream ss_exit;
|
||||
time_t exit_time;
|
||||
time(&exit_time);
|
||||
bg_.reset(); // flush the log queue
|
||||
ss_exit << "\n\t\tg2log file shutdown at: " << ctime(&exit_time);
|
||||
out << ss_exit.str() << std::flush;
|
||||
ss_exit << "\n\t\tg2log file shutdown at: " << g2::localtime_formatted(g2::systemtime_now(), time_formatted);
|
||||
filestream() << ss_exit.str() << std::flush;
|
||||
}
|
||||
|
||||
|
||||
void g2LogWorkerImpl::backgroundFileWrite(LogEntry message)
|
||||
{
|
||||
using namespace std;
|
||||
LogTime t;
|
||||
auto timesnapshot = chrono::steady_clock::now();
|
||||
out << "\n" << t.year << "/" << setw(2) << t.month << "/" << setw(2) << t.day;
|
||||
out << " " << setw(2) << t.hour << ":"<< setw(2) << t.minute <<":"<< setw(2) << t.second;
|
||||
out << "." << chrono::duration_cast<microsecond>(timesnapshot - start_time_).count(); //microseconds
|
||||
std::ofstream& out(filestream());
|
||||
auto system_time = g2::systemtime_now();
|
||||
auto steady_time = std::chrono::steady_clock::now();
|
||||
out << "\n" << g2::localtime_formatted(system_time, date_formatted);
|
||||
out << " " << g2::localtime_formatted(system_time, time_formatted); // TODO: time kommer från LogEntry
|
||||
out << "." << chrono::duration_cast<std::chrono::microseconds>(steady_time - steady_start_time_).count(); //microseconds TODO: ta in min g2clocka här StopWatch
|
||||
out << "\t" << message << std::flush;
|
||||
}
|
||||
|
||||
|
||||
void g2LogWorkerImpl::backgroundExitFatal(FatalMessage fatal_message)
|
||||
{
|
||||
backgroundFileWrite(fatal_message.message_);
|
||||
backgroundFileWrite("Log flushed successfully to disk: exiting");
|
||||
std::cout << "g2log exiting successfully after receiving fatal event" << std::endl;
|
||||
std::cout << "Log file at: [" << log_file_with_path_ << "]\n" << std::endl << std::flush;
|
||||
out.close();
|
||||
backgroundFileWrite("Log flushed successfully to disk \nExiting");
|
||||
std::cerr << "g2log exiting after receiving fatal event" << std::endl;
|
||||
std::cerr << "Log file at: [" << log_file_with_path_ << "]\n" << std::endl << std::flush;
|
||||
filestream().close();
|
||||
exitWithDefaultSignalHandler(fatal_message.signal_id_);
|
||||
perror("g2log exited after receiving FATAL trigger. Flush message status: "); // should never reach this point
|
||||
}
|
||||
|
||||
|
||||
std::string g2LogWorkerImpl::backgroundChangeLogFile(const std::string& directory)
|
||||
{
|
||||
std::string file_name = createLogFileName(log_prefix_backup_);
|
||||
std::string prospect_log = directory + file_name;
|
||||
std::unique_ptr<std::ofstream> log_stream = createLogFile(prospect_log);
|
||||
if(nullptr == log_stream)
|
||||
{
|
||||
backgroundFileWrite("Unable to change log file. Illegal filename or busy? Unsuccessful log name was:" + prospect_log);
|
||||
return ""; // no success
|
||||
}
|
||||
|
||||
// BELOW g2LogWorker
|
||||
std::ostringstream ss_change;
|
||||
ss_change << "\n\tChanging log file from : " << log_file_with_path_;
|
||||
ss_change << "\n\tto new location: " << prospect_log << "\n";
|
||||
backgroundFileWrite(ss_change.str().c_str());
|
||||
ss_change.str("");
|
||||
|
||||
// setting the new log as active
|
||||
std::string old_log = log_file_with_path_;
|
||||
log_file_with_path_ = prospect_log;
|
||||
outptr_ = std::move(log_stream);
|
||||
ss_change << "\n\tNew log file. The previous log file was at: ";
|
||||
ss_change << old_log;
|
||||
backgroundFileWrite(ss_change.str());
|
||||
return log_file_with_path_;
|
||||
}
|
||||
|
||||
std::string g2LogWorkerImpl::backgroundFileName()
|
||||
{
|
||||
return log_file_with_path_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// ***** BELOW g2LogWorker *****
|
||||
// Public API implementation
|
||||
g2LogWorker::g2LogWorker(const std::string& log_prefix, const std::string& log_directory)
|
||||
//
|
||||
g2LogWorker::g2LogWorker(const std::string& log_prefix, const std::string& log_directory)
|
||||
: pimpl_(new g2LogWorkerImpl(log_prefix, log_directory))
|
||||
, log_file_with_path_(pimpl_->log_file_with_path_)
|
||||
{
|
||||
}
|
||||
{
|
||||
assert((pimpl_ != nullptr) && "shouild never happen");
|
||||
}
|
||||
|
||||
g2LogWorker::~g2LogWorker()
|
||||
{
|
||||
g2LogWorker::~g2LogWorker()
|
||||
{
|
||||
pimpl_.reset();
|
||||
std::cout << "\nExiting, log location: " << log_file_with_path_ << std::endl << std::flush;
|
||||
}
|
||||
std::cerr << "\nExiting, log location: " << log_file_with_path_ << std::endl << std::flush;
|
||||
}
|
||||
|
||||
void g2LogWorker::save(g2::internal::LogEntry msg)
|
||||
{
|
||||
void g2LogWorker::save(g2::internal::LogEntry msg)
|
||||
{
|
||||
pimpl_->bg_->send(std::bind(&g2LogWorkerImpl::backgroundFileWrite, pimpl_.get(), msg));
|
||||
}
|
||||
}
|
||||
|
||||
void g2LogWorker::fatal(g2::internal::FatalMessage fatal_message)
|
||||
{
|
||||
void g2LogWorker::fatal(g2::internal::FatalMessage fatal_message)
|
||||
{
|
||||
pimpl_->bg_->send(std::bind(&g2LogWorkerImpl::backgroundExitFatal, pimpl_.get(), fatal_message));
|
||||
}
|
||||
}
|
||||
|
||||
std::string g2LogWorker::logFileName() const
|
||||
{
|
||||
return log_file_with_path_;
|
||||
}
|
||||
|
||||
std::future<std::string> g2LogWorker::changeLogFile(const std::string& log_directory)
|
||||
{
|
||||
kjellkod::Active* bgWorker = pimpl_->bg_.get();
|
||||
//auto future_result = g2::spawn_task(std::bind(&g2LogWorkerImpl::backgroundChangeLogFile, pimpl_.get(), log_directory), bgWorker);
|
||||
auto bg_call = [this, log_directory]() {return pimpl_->backgroundChangeLogFile(log_directory);};
|
||||
auto future_result = g2::spawn_task(bg_call, bgWorker);
|
||||
return std::move(future_result);
|
||||
}
|
||||
|
||||
std::future<std::string> g2LogWorker::logFileName()
|
||||
{
|
||||
kjellkod::Active* bgWorker = pimpl_->bg_.get();
|
||||
auto bg_call=[&](){return pimpl_->backgroundFileName();};
|
||||
auto future_result = g2::spawn_task(bg_call ,bgWorker);
|
||||
return std::move(future_result);
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
#ifndef G2_LOG_WORKER_H_
|
||||
#define G2_LOG_WORKER_H_
|
||||
/** ==========================================================================
|
||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2logworker.h Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
* 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2logworker.h Framework for Logging and Design By Contract
|
||||
* Created: 2011 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
#include "g2log.h"
|
||||
|
||||
struct g2LogWorkerImpl;
|
||||
@ -34,8 +37,13 @@ public:
|
||||
/// Will abort the application!
|
||||
void fatal(g2::internal::FatalMessage fatal_message);
|
||||
|
||||
/// basically only needed for unit-testing or specific log management post logging
|
||||
std::string logFileName() const;
|
||||
/// Attempt to change the current log file to another name/location.
|
||||
/// returns filename with full path if successful, else empty string
|
||||
std::future<std::string> changeLogFile(const std::string& log_directory);
|
||||
|
||||
/// Probably only needed for unit-testing or specific log management post logging
|
||||
/// request to get log name is processed in FIFO order just like any other background job.
|
||||
std::future<std::string> logFileName();
|
||||
|
||||
private:
|
||||
std::unique_ptr<g2LogWorkerImpl> pimpl_;
|
||||
@ -46,30 +54,4 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/* Possible improvement --- for making localtime thread safe
|
||||
Is there really no C++11 localtime replacement?!
|
||||
|
||||
// localtime_r (POSIX) or localtime_s (WIN) thanks to http://stackoverflow.com/questions/7313919/c11-alternative-to-localtime-r
|
||||
namespace query {
|
||||
char localtime_r( ... );
|
||||
|
||||
struct has_localtime_r
|
||||
{ enum { value = sizeof localtime_r( std::declval< std::time_t * >(), std::declval< std::tm * >() )
|
||||
== sizeof( std::tm * ) }; };
|
||||
|
||||
|
||||
template< bool available > struct safest_localtime {
|
||||
static std::tm *call( std::time_t const *t, std::tm *r )
|
||||
{ return localtime_r( t, r ); }
|
||||
};
|
||||
|
||||
template<> struct safest_localtime< false > {
|
||||
static std::tm *call( std::time_t const *t, std::tm *r )
|
||||
{ return std::localtime( t ); }
|
||||
};
|
||||
}
|
||||
std::tm *localtime( std::time_t const *t, std::tm *r )
|
||||
{ return query::safest_localtime< query::has_localtime_r::value >().call( t, r ); }
|
||||
*/
|
||||
|
||||
#endif // LOG_WORKER_H_
|
||||
|
78
g2log/src/g2time.cpp
Normal file
78
g2log/src/g2time.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2time.cpp cross-platform, thread-safe replacement for C++11 non-thread-safe
|
||||
* localtime (and similar)
|
||||
* Created: 2012 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not under copywrite protection. First published for g2log at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
#include "g2time.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
namespace g2 { namespace internal {
|
||||
// This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
|
||||
// This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
|
||||
// return value is SIMPLIFIED to only return a std::string
|
||||
std::string put_time(const struct tm* tmb, const char* c_time_format)
|
||||
{
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
std::ostringstream oss;
|
||||
oss.fill('0');
|
||||
oss << std::put_time(const_cast<struct tm*>(tmb), c_time_format); // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
|
||||
return oss.str();
|
||||
#else // LINUX
|
||||
const size_t size = 1024;
|
||||
char buffer[size]; // OBS: kolla om inte std::put_time finns. This is way more buffer space then we need
|
||||
auto success = std::strftime(buffer, size, c_time_format, tmb); // Ta över denna funktion till BitBucket/code/g2log sen då denna är utvecklingsbranchen
|
||||
if (0 == success)
|
||||
return c_time_format; // error return result indeterminate due to buffer overflow - should throw instead?
|
||||
return buffer; // implicit conversion to std::string
|
||||
#endif
|
||||
}
|
||||
} // internal
|
||||
} // g2
|
||||
|
||||
|
||||
|
||||
namespace g2
|
||||
{
|
||||
std::time_t systemtime_now()
|
||||
{
|
||||
system_time_point system_now = std::chrono::system_clock::now();
|
||||
return std::chrono::system_clock::to_time_t(system_now);
|
||||
}
|
||||
|
||||
|
||||
tm localtime(const std::time_t& time)
|
||||
{
|
||||
struct tm tm_snapshot;
|
||||
#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
localtime_r(&time, &tm_snapshot); // POSIX
|
||||
#else
|
||||
localtime_s(&tm_snapshot, &time); // windsows
|
||||
#endif
|
||||
return tm_snapshot;
|
||||
}
|
||||
|
||||
/// returns a std::string with content of time_t as localtime formatted by input format string
|
||||
/// * format string must conform to std::put_time
|
||||
/// This is similar to std::put_time(std::localtime(std::time_t*), time_format.c_str());
|
||||
std::string localtime_formatted(const std::time_t& time_snapshot, const std::string& time_format)
|
||||
{
|
||||
std::tm t = localtime(time_snapshot); // could be const, but cannot due to VS2012 is non conformant for C++11's std::put_time (see above)
|
||||
std::stringstream buffer;
|
||||
buffer << g2::internal::put_time(&t, time_format.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
|
||||
return buffer.str();
|
||||
}
|
||||
} // g2
|
45
g2log/src/g2time.h
Normal file
45
g2log/src/g2time.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef G2_TIME_H_
|
||||
#define G2_TIME_H_
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================
|
||||
* Filename:g2time.h cross-platform, thread-safe replacement for C++11 non-thread-safe
|
||||
* localtime (and similar)
|
||||
* Created: 2012 by Kjell Hedström
|
||||
*
|
||||
* PUBLIC DOMAIN and Not under copywrite protection. First published for g2log at KjellKod.cc
|
||||
* ********************************************* */
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
// FYI:
|
||||
// namespace g2::internal ONLY in g2time.cpp
|
||||
// std::string put_time(const struct tm* tmb, const char* c_time_format)
|
||||
|
||||
namespace g2
|
||||
{
|
||||
typedef std::chrono::steady_clock::time_point steady_time_point;
|
||||
typedef std::chrono::time_point<std::chrono::system_clock> system_time_point;
|
||||
typedef std::chrono::milliseconds milliseconds;
|
||||
typedef std::chrono::microseconds microseconds;
|
||||
|
||||
// wrap for std::chrono::system_clock::now()
|
||||
std::time_t systemtime_now();
|
||||
|
||||
/** return time representing POD struct (ref ctime + wchar) that is normally
|
||||
* retrieved with std::localtime. g2::localtime is threadsafe which std::localtime is not.
|
||||
* g2::localtime is probably used together with @ref g2::systemtime_now */
|
||||
tm localtime(const std::time_t& time);
|
||||
|
||||
/** format string must conform to std::put_time's demands.
|
||||
* WARNING: At time of writing there is only so-so compiler support for
|
||||
* std::put_time. A possible fix if your c++11 library is not updated is to
|
||||
* modify this to use std::strftime instead */
|
||||
std::string localtime_formatted(const std::time_t& time_snapshot, const std::string& time_format) ;
|
||||
}
|
||||
|
||||
#endif
|
@ -9,7 +9,7 @@
|
||||
* the help from the std::thread library from JustSoftwareSolutions
|
||||
* ref: http://www.stdthread.co.uk/doc/headers/mutex.html
|
||||
*
|
||||
* This exampel was inspired by Anthony Williams lock-based data structures in
|
||||
* This exampel was totally inspired by Anthony Williams lock-based data structures in
|
||||
* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */
|
||||
|
||||
#ifndef SHARED_QUEUE
|
||||
@ -47,7 +47,7 @@ public:
|
||||
if(queue_.empty()){
|
||||
return false;
|
||||
}
|
||||
popped_item=queue_.front();
|
||||
popped_item=std::move(queue_.front());
|
||||
queue_.pop();
|
||||
return true;
|
||||
}
|
||||
@ -59,7 +59,7 @@ public:
|
||||
{ // The 'while' loop below is equal to
|
||||
data_cond_.wait(lock); //data_cond_.wait(lock, [](bool result){return !queue_.empty();});
|
||||
}
|
||||
popped_item=queue_.front();
|
||||
popped_item=std::move(queue_.front());
|
||||
queue_.pop();
|
||||
}
|
||||
|
||||
|
@ -12,12 +12,32 @@
|
||||
namespace
|
||||
{
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
const std::string path_to_log_file = "./";
|
||||
const std::string path_to_log_file = "./";
|
||||
#else
|
||||
const std::string path_to_log_file = "/tmp/";
|
||||
const std::string path_to_log_file = "/tmp/";
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace example_fatal
|
||||
{
|
||||
void killWithContractFailureIfNonEqual(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";
|
||||
}
|
||||
|
||||
// on Ubunti this caused get a compiler warning with gcc4.6
|
||||
// from gcc 4.7.2 (at least) it causes a crash (as expected)
|
||||
// On windows it'll probably crash too.
|
||||
void tryToKillWithIllegalPrintout()
|
||||
{
|
||||
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush;
|
||||
std::cout << "************************************************************\n\n" << std::endl << std::flush;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const std::string logging = "logging";
|
||||
LOGF(DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str());
|
||||
}
|
||||
} // example fatal
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
double pi_d = 3.1415926535897932384626433832795;
|
||||
@ -26,11 +46,12 @@ int main(int argc, char** argv)
|
||||
|
||||
g2LogWorker logger(argv[0], path_to_log_file);
|
||||
g2::initializeLogging(&logger);
|
||||
std::future<std::string> log_file_name = logger.logFileName();
|
||||
std::cout << "*** This is an example of g2log " << std::endl;
|
||||
std::cout << "*** It WILL exit by a FATAL trigger in the end" << std::endl;
|
||||
std::cout << "*** Please see the generated log and compare to " << std::endl;
|
||||
std::cout << "*** the code at g2log/test_example/main.cpp" << std::endl;
|
||||
std::cout << "\n*** Log file: [" << logger.logFileName() << "]\n\n" << std::endl;
|
||||
std::cout << "\n*** Log file: [" << log_file_name.get() << "]\n\n" << std::endl;
|
||||
|
||||
LOGF(INFO, "Hi log %d", 123);
|
||||
LOG(INFO) << "Test SLOG INFO";
|
||||
@ -55,19 +76,22 @@ int main(int argc, char** argv)
|
||||
LOG_IF(FATAL, (2>3)) << "This message should NOT throw";
|
||||
LOGF(DEBUG, "This API is popular with some %s", "programmers");
|
||||
LOGF_IF(DEBUG, (1<2), "If true, then this %s will be logged", "message");
|
||||
{
|
||||
// OK --- on Ubunti this WILL get a compiler warning
|
||||
// On windows it'll probably crash std::cout << "\n\n***** Be ready on Windows this example will 'abort' " << std::endl;
|
||||
std::cout << "\n\n***** Be ready this last example may 'abort' if on Windows/Linux_gcc4.7 " << std::endl << std::flush;
|
||||
std::cout << "************************************************************\n\n" << std::endl << std::flush;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const std::string logging = "logging";
|
||||
LOGF(DEBUG, "ILLEGAL PRINTF_SYNTAX EXAMPLE. WILL GENERATE compiler warning.\n\nbadly formatted message:[Printf-type %s is the number 1 for many %s]", logging.c_str());
|
||||
}
|
||||
// OK --- on Ubunti this caused get a compiler warning with gcc4.6
|
||||
// from gcc 4.7.2 (at least) it causes a crash (as expected)
|
||||
// On windows itll probably crash
|
||||
// ---- IF you want to try 'FATAL' contract failure please comment away
|
||||
// ----- the 'illegalPrinout' call below
|
||||
example_fatal::tryToKillWithIllegalPrintout();
|
||||
|
||||
|
||||
std::cout << "\n\n***** Be ready this last example will trigger 'abort' " << std::endl;
|
||||
std::cout << "************************************************************\n\n" << std::endl;
|
||||
CHECK(1<2) << "SHOULD NOT SEE THIS MESSAGE";
|
||||
CHECK(1>2) << "Test to see if contract works: onetwothree: " << 123 << ". This should be at the end of the log, and will exit this example";
|
||||
|
||||
CHECK(1<2) << "SHOULD NOT SEE THIS MESSAGE"; // non-failure contract
|
||||
|
||||
std::cout << "\n\n***** Be ready this last example WILL trigger 'abort' (if not done earlier)" << std::endl;
|
||||
// exit by contract failure. See the dumped log, the function
|
||||
// that caused the fatal exit should be shown in the stackdump
|
||||
int smaller = 1;
|
||||
int larger = 2;
|
||||
example_fatal::killWithContractFailureIfNonEqual(smaller, larger);
|
||||
}
|
||||
|
||||
|
195
g2log/test_unit/test_configuration.cpp
Normal file
195
g2log/test_unit/test_configuration.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "g2time.h"
|
||||
#include "g2future.h"
|
||||
|
||||
TEST(Configuration, LOG)
|
||||
{ // ref: http://www.cplusplus.com/reference/clibrary/ctime/strftime/
|
||||
// ref: http://en.cppreference.com/w/cpp/io/manip/put_time
|
||||
// Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
|
||||
// --- WARNING: The try/catch setup does NOT work,. but for fun and for fake-clarity I leave it
|
||||
// --- For formatting options to std::put_time that are NOT YET implemented on Windows fatal errors/assert will occurr
|
||||
// --- the last example is such an example.
|
||||
try
|
||||
{
|
||||
std::cout << g2::localtime_formatted(g2::systemtime_now(), "%a %b %d %H:%M:%S %Y") << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::cout << g2::localtime_formatted(g2::systemtime_now(), "%%Y/%%m/%%d %%H:%%M:%%S = %Y/%m/%d %H:%M:%S") << std::endl;
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
|
||||
std::cerr << "Formatting options skipped due to VS2012, C++11 non-conformance for" << std::endl;
|
||||
std::cerr << " some formatting options. The skipped code was:\n\t\t %EX %Ec, \n(see http://en.cppreference.com/w/cpp/io/manip/put_time for details)" << std::endl;
|
||||
#else
|
||||
std::cout << "C++11 new formatting options:\n" << g2::localtime_formatted(g2::systemtime_now(), "%%EX: %EX\n%%z: %z\n%%Ec: %Ec") << std::endl;
|
||||
#endif
|
||||
}
|
||||
// This does not work. Other kinds of fatal exits (on Windows) seems to be used instead of exceptions
|
||||
// Maybe a signal handler catch would be better? --- TODO: Make it better, both failing and correct
|
||||
catch(...)
|
||||
{
|
||||
ADD_FAILURE() << "On this platform the library does not support given (C++11?) specifiers";
|
||||
return;
|
||||
}
|
||||
ASSERT_TRUE(true); // no exception. all good
|
||||
}
|
||||
|
||||
std::future<std::string> sillyFutureReturn()
|
||||
{
|
||||
std::packaged_task<std::string()> task([](){return std::string("Hello Future");}); // wrap the function
|
||||
std::future<std::string> result = task.get_future(); // get a future
|
||||
std::thread(std::move(task)).detach(); // launch on a thread
|
||||
std::cout << "Waiting...";
|
||||
result.wait();
|
||||
return result; // already wasted
|
||||
}
|
||||
TEST(Configuration, FutureSilly)
|
||||
{
|
||||
std::string hello = sillyFutureReturn().get();
|
||||
ASSERT_STREQ(hello.c_str(), "Hello Future");
|
||||
}
|
||||
|
||||
struct MsgType
|
||||
{
|
||||
std::string msg_;
|
||||
MsgType(std::string m): msg_(m){};
|
||||
std::string msg(){return msg_;}
|
||||
};
|
||||
|
||||
|
||||
TEST(TestOf_CopyableCall, Expecting_SmoothSailing)
|
||||
{
|
||||
using namespace kjellkod;
|
||||
const std::string str("Hello from struct");
|
||||
MsgType type(str);
|
||||
std::unique_ptr<Active> bgWorker(Active::createActive());
|
||||
std::future<std::string> fstring =
|
||||
g2::spawn_task(std::bind(&MsgType::msg, type), bgWorker.get());
|
||||
ASSERT_STREQ(str.c_str(), fstring.get().c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST(TestOf_CopyableLambdaCall, Expecting_AllFine)
|
||||
{
|
||||
using namespace kjellkod;
|
||||
std::unique_ptr<Active> bgWorker(Active::createActive());
|
||||
|
||||
// lambda task
|
||||
const std::string str_standalone("Hello from standalone");
|
||||
auto msg_lambda=[=](){return (str_standalone+str_standalone);};
|
||||
std::string expected(str_standalone+str_standalone);
|
||||
|
||||
auto fstring_standalone = g2::spawn_task(msg_lambda, bgWorker.get());
|
||||
ASSERT_STREQ(expected.c_str(), fstring_standalone.get().c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename F>
|
||||
std::future<typename std::result_of<F()>::type> ObsoleteSpawnTask(F f)
|
||||
{
|
||||
typedef typename std::result_of<F()>::type result_type;
|
||||
typedef std::packaged_task<result_type()> task_type;
|
||||
|
||||
task_type task(std::move(f));
|
||||
std::future<result_type> result = task.get_future();
|
||||
|
||||
std::vector<std::function<void()>> vec;
|
||||
vec.push_back(g2::PretendToBeCopyable<task_type>(std::move(task)));
|
||||
std::thread(std::move(vec.back())).detach();
|
||||
result.wait();
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
TEST(TestOf_ObsoleteSpawnTaskWithStringReturn, Expecting_FutureString)
|
||||
{
|
||||
std::string str("Hello");
|
||||
std::string expected(str+str);
|
||||
auto msg_lambda=[=](){return (str+str);};
|
||||
auto future_string = ObsoleteSpawnTask(msg_lambda);
|
||||
|
||||
ASSERT_STREQ(expected.c_str(), future_string.get().c_str());
|
||||
}
|
||||
// gcc thread example below
|
||||
// tests code below copied from mail-list conversion between
|
||||
// Lars Gullik Bjønnes and Jonathan Wakely
|
||||
// http://gcc.gnu.org/ml/gcc-help/2011-11/msg00052.html
|
||||
|
||||
// --------------------------------------------------------------
|
||||
namespace WORKING
|
||||
{
|
||||
using namespace g2;
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::function<void()>> vec;
|
||||
|
||||
template<typename F>
|
||||
std::future<typename std::result_of<F()>::type> spawn_task(F f)
|
||||
{
|
||||
typedef typename std::result_of<F()>::type result_type;
|
||||
typedef std::packaged_task<result_type()> task_type;
|
||||
|
||||
task_type task(std::move(f));
|
||||
std::future<result_type> res = task.get_future();
|
||||
|
||||
vec.push_back(
|
||||
PretendToBeCopyable<task_type>(
|
||||
std::move(task)));
|
||||
|
||||
std::thread([]()
|
||||
{
|
||||
auto task = std::move(vec.back());
|
||||
vec.pop_back();
|
||||
task();
|
||||
}
|
||||
).detach();
|
||||
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
double get_res()
|
||||
{
|
||||
return 42.2;
|
||||
}
|
||||
|
||||
std::string msg2(){return "msg2";}
|
||||
} // WORKING
|
||||
|
||||
TEST(Yalla, Testar)
|
||||
{
|
||||
using namespace WORKING;
|
||||
auto f = spawn_task(get_res);
|
||||
std::cout << "Res = " << f.get() << std::endl;
|
||||
|
||||
auto f2 = spawn_task(msg2);
|
||||
std::cout << "Res2 = " << f2.get() << std::endl;
|
||||
|
||||
|
||||
ASSERT_TRUE(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
188
g2log/test_unit/test_filechange.cpp
Normal file
188
g2log/test_unit/test_filechange.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/** ==========================================================================
|
||||
* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
|
||||
* with no warranties. This code is yours to share, use and modify with no
|
||||
* strings attached and no restrictions or obligations.
|
||||
* ============================================================================*/
|
||||
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "g2log.h"
|
||||
#include "g2logworker.h"
|
||||
|
||||
|
||||
namespace { // anonymous
|
||||
const char* name_path_1 = "./some_fake_DirectoryOrName_1_";
|
||||
const char* name_path_2 = "./some_fake_DirectoryOrName_3_";
|
||||
|
||||
g2LogWorker* g_logger_ptr = nullptr;
|
||||
|
||||
|
||||
bool isTextAvailableInContent(const std::string &total_text,std::string msg_to_find)
|
||||
{
|
||||
std::string content(total_text);
|
||||
size_t location = content.find(msg_to_find);
|
||||
return (location != std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
std::string readFileToText(std::string filename)
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(filename.c_str(),std::ios_base::in);
|
||||
if(!in.is_open())
|
||||
{
|
||||
return ""; // error just return empty string - test will 'fault'
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << in.rdbuf();
|
||||
std::string content(oss.str());
|
||||
return content;
|
||||
}
|
||||
|
||||
bool removeFile(std::string path_to_file)
|
||||
{
|
||||
return (0 == std::remove(path_to_file.c_str()));
|
||||
}
|
||||
|
||||
class LogFileCleaner // RAII cluttering files cleanup
|
||||
{
|
||||
private:
|
||||
std::vector<std::string> logs_to_clean_;
|
||||
std::mutex g_mutex;
|
||||
public:
|
||||
size_t size(){return logs_to_clean_.size();}
|
||||
virtual ~LogFileCleaner() {
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
{
|
||||
for (std::string p : logs_to_clean_)
|
||||
{
|
||||
if(false == removeFile(p))
|
||||
{
|
||||
ADD_FAILURE() << "UNABLE to remove: " << p.c_str() << std::endl;
|
||||
}
|
||||
}
|
||||
logs_to_clean_.clear();
|
||||
} // mutex
|
||||
}
|
||||
|
||||
void addLogToClean(std::string path_to_log) {
|
||||
std::lock_guard<std::mutex> lock(g_mutex);
|
||||
{
|
||||
if (std::find(logs_to_clean_.begin(), logs_to_clean_.end(), path_to_log.c_str()) == logs_to_clean_.end())
|
||||
logs_to_clean_.push_back(path_to_log);
|
||||
}
|
||||
}
|
||||
}; LogFileCleaner* g_cleaner_ptr = nullptr;
|
||||
|
||||
std::string changeDirectoryOrName(std::string new_file_to_create)
|
||||
{
|
||||
static std::mutex m;
|
||||
static int count;
|
||||
std::lock_guard<std::mutex> lock(m);
|
||||
{
|
||||
std::string add_count = std::to_string(++count) + "_";
|
||||
auto new_log = g_logger_ptr->changeLogFile(new_file_to_create+add_count).get();
|
||||
if(!new_log.empty()) g_cleaner_ptr->addLogToClean(new_log);
|
||||
return new_log;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: this must change. Initialization of this is done here! and not in a special test_main.cpp
|
||||
// which MAY be OK ... however it is also very redundant with test_io
|
||||
|
||||
|
||||
|
||||
TEST(TestOf_GetFileName, Expecting_ValidLogFile)
|
||||
{
|
||||
LOG(INFO) << "test_filechange, Retrieving file name: ";
|
||||
ASSERT_NE(g_logger_ptr, nullptr);
|
||||
std::future<std::string> f_get_old_name = g_logger_ptr->logFileName();
|
||||
ASSERT_TRUE(f_get_old_name.valid());
|
||||
ASSERT_FALSE(f_get_old_name.get().empty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST(TestOf_ChangingLogFile, Expecting_NewLogFileUsed)
|
||||
{
|
||||
auto old_log = g_logger_ptr->logFileName().get();
|
||||
std::string name = changeDirectoryOrName(name_path_1);
|
||||
auto new_log = g_logger_ptr->changeLogFile(name).get();
|
||||
}
|
||||
|
||||
|
||||
TEST(TestOf_ManyThreadsChangingLogFileName, Expecting_EqualNumberLogsCreated)
|
||||
{
|
||||
auto old_log = g_logger_ptr->logFileName().get();
|
||||
if(!old_log.empty()) g_cleaner_ptr->addLogToClean(old_log);
|
||||
|
||||
LOG(INFO) << "SoManyThreadsAllDoingChangeFileName";
|
||||
std::vector<std::thread> threads;
|
||||
auto max = 2;
|
||||
auto size = g_cleaner_ptr->size();
|
||||
for(auto count = 0; count < max; ++count)
|
||||
{
|
||||
std::string drive = ((count % 2) == 0) ? "./_threadEven_" : "./_threaOdd_";
|
||||
threads.push_back(std::thread(changeDirectoryOrName, drive));
|
||||
}
|
||||
for(auto& thread : threads)
|
||||
thread.join();
|
||||
|
||||
// check that all logs were created
|
||||
ASSERT_EQ(size+max, g_cleaner_ptr->size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST(TestOf_IllegalLogFileName, Expecting_NoChangeToOriginalFileName)
|
||||
{
|
||||
std::string original = g_logger_ptr->logFileName().get();
|
||||
std::cerr << "Below WILL print 'FiLE ERROR'. This is part of the testing and perfectly OK" << std::endl;
|
||||
std::cerr << "****" << std::endl;
|
||||
std::future<std::string> perhaps_a_name = g_logger_ptr->changeLogFile("XY:/"); // does not exist
|
||||
ASSERT_TRUE(perhaps_a_name.get().empty());
|
||||
std::cerr << "****" << std::endl;
|
||||
std::string post_illegal = g_logger_ptr->logFileName().get();
|
||||
ASSERT_STREQ(original.c_str(), post_illegal.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
LogFileCleaner cleaner;
|
||||
g_cleaner_ptr = &cleaner;
|
||||
int return_value = 1;
|
||||
|
||||
std::string last_log_file;
|
||||
{
|
||||
g2LogWorker logger("ReplaceLogFile", name_path_2);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
g_logger_ptr = &logger; // ugly but fine for this test
|
||||
g2::initializeLogging(g_logger_ptr);
|
||||
cleaner.addLogToClean(g_logger_ptr->logFileName().get());
|
||||
return_value = RUN_ALL_TESTS();
|
||||
last_log_file = g_logger_ptr->logFileName().get();
|
||||
g2::shutDownLogging();
|
||||
}
|
||||
std::cout << "FINISHED WITH THE TESTING" << std::endl;
|
||||
// cleaning up
|
||||
cleaner.addLogToClean(last_log_file);
|
||||
return return_value;
|
||||
}
|
@ -15,17 +15,18 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
const int k_wait_time = 5; // 5s wait between LOG/CHECK FATAL till we say it's too long time
|
||||
const int k_wait_time = 5; // 5s wait between LOG/CHECK FATAL till we say it's too long time
|
||||
const std::string log_directory = "./";
|
||||
|
||||
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);
|
||||
size_t location = content.find(msg_to_find);
|
||||
return (location != std::string::npos);
|
||||
}
|
||||
}
|
||||
|
||||
std::string readFileToText(std::string filename)
|
||||
{
|
||||
std::string readFileToText(std::string filename)
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(filename.c_str(),std::ios_base::in);
|
||||
if(!in.is_open())
|
||||
@ -36,7 +37,7 @@ std::string readFileToText(std::string filename)
|
||||
oss << in.rdbuf();
|
||||
std::string content(oss.str());
|
||||
return content;
|
||||
}
|
||||
}
|
||||
} // end anonymous namespace
|
||||
|
||||
// RAII temporarily replace of logger
|
||||
@ -45,21 +46,24 @@ struct RestoreLogger
|
||||
{
|
||||
RestoreLogger();
|
||||
~RestoreLogger();
|
||||
|
||||
void reset();
|
||||
std::string readFileToText();
|
||||
|
||||
std::unique_ptr<g2LogWorker> logger_;
|
||||
const std::string log_file_;
|
||||
std::string logFile(){return log_file_;}
|
||||
private:
|
||||
std::string log_file_;
|
||||
|
||||
};
|
||||
|
||||
RestoreLogger::RestoreLogger()
|
||||
: logger_(new g2LogWorker("UNIT_TEST_LOGGER", "./"))
|
||||
, log_file_(logger_->logFileName())
|
||||
: logger_(new g2LogWorker("UNIT_TEST_LOGGER", log_directory))
|
||||
{
|
||||
g2::initializeLogging(logger_.get());
|
||||
g2::internal::changeFatalInitHandlerForUnitTesting();
|
||||
|
||||
std::future<std::string> filename(logger_->logFileName());
|
||||
EXPECT_TRUE(filename.valid());
|
||||
log_file_ = filename.get();
|
||||
}
|
||||
|
||||
RestoreLogger::~RestoreLogger()
|
||||
@ -76,7 +80,6 @@ void RestoreLogger::reset()
|
||||
|
||||
|
||||
|
||||
|
||||
// LOG
|
||||
TEST(LOGTest, LOG)
|
||||
{
|
||||
@ -85,34 +88,35 @@ TEST(LOGTest, LOG)
|
||||
RestoreLogger logger;
|
||||
LOG(INFO) << "test LOG(INFO)";
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.log_file_);
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, "test LOG(INFO)"));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const std::string t_info = "test INFO ";
|
||||
const std::string t_info2 = "test INFO 123";
|
||||
const std::string t_debug = "test DEBUG ";
|
||||
const std::string t_debug2 = "test DEBUG 1.123456";
|
||||
const std::string t_warning = "test WARNING ";
|
||||
const std::string t_warning2 = "test WARNING yello";
|
||||
|
||||
namespace {
|
||||
const std::string t_info = "test INFO ";
|
||||
const std::string t_info2 = "test INFO 123";
|
||||
const std::string t_debug = "test DEBUG ";
|
||||
const std::string t_debug2 = "test DEBUG 1.123456";
|
||||
const std::string t_warning = "test WARNING ";
|
||||
const std::string t_warning2 = "test WARNING yello";
|
||||
}
|
||||
|
||||
// printf-type log
|
||||
TEST(LogTest, LOG_F)
|
||||
{
|
||||
|
||||
std::string file_content;
|
||||
{
|
||||
RestoreLogger logger;
|
||||
std::cout << "logfilename: " << logger.logFile() << std::flush << std::endl;
|
||||
|
||||
LOGF(INFO, std::string(t_info + "%d").c_str(), 123);
|
||||
LOGF(DEBUG, std::string(t_debug + "%f").c_str(), 1.123456);
|
||||
LOGF(WARNING, std::string(t_warning + "%s").c_str(), "yello");
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.log_file_);
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
@ -120,6 +124,9 @@ TEST(LogTest, LOG_F)
|
||||
ASSERT_TRUE(verifyContent(file_content, t_warning2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// stream-type log
|
||||
TEST(LogTest, LOG)
|
||||
{
|
||||
@ -130,7 +137,7 @@ TEST(LogTest, LOG)
|
||||
LOG(DEBUG) << t_debug << std::setprecision(7) << 1.123456f;
|
||||
LOG(WARNING) << t_warning << "yello";
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.log_file_);
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
@ -147,7 +154,7 @@ TEST(LogTest, LOG_F_IF)
|
||||
LOGF_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123);
|
||||
LOGF_IF(DEBUG, (2 != 2), std::string(t_debug + "%f").c_str(), 1.123456);
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.log_file_);
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
@ -162,7 +169,7 @@ TEST(LogTest, LOG_IF)
|
||||
LOG_IF(INFO, (2 == 2)) << t_info << 123;
|
||||
LOG_IF(DEBUG, (2 != 2)) << t_debug << std::setprecision(7) << 1.123456f;
|
||||
logger.reset(); // force flush of logger
|
||||
file_content = readFileToText(logger.log_file_);
|
||||
file_content = readFileToText(logger.logFile());
|
||||
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
|
||||
}
|
||||
ASSERT_TRUE(verifyContent(file_content, t_info2));
|
||||
@ -179,7 +186,7 @@ TEST(LogTest, LOGF__FATAL)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
std::cerr << file_content << std::endl << std::flush;
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
@ -207,7 +214,7 @@ TEST(LogTest, LOG_FATAL)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw"))
|
||||
@ -234,7 +241,7 @@ TEST(LogTest, LOGF_IF__FATAL)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw"))
|
||||
@ -262,7 +269,7 @@ TEST(LogTest, LOG_IF__FATAL)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, "This message should throw") &&
|
||||
@ -308,7 +315,7 @@ TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL"))
|
||||
{
|
||||
@ -334,7 +341,7 @@ TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, msg2))
|
||||
@ -360,7 +367,7 @@ TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg)
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
logger.reset();
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
if(verifyContent(e.what(), "EXIT trigger caused by ") &&
|
||||
verifyContent(file_content, "FATAL") &&
|
||||
verifyContent(file_content, msg2))
|
||||
@ -391,7 +398,7 @@ TEST(CHECK, CHECK_ThatWontThrow)
|
||||
ADD_FAILURE() << "Should never have thrown";
|
||||
}
|
||||
|
||||
std::string file_content = readFileToText(logger.log_file_);
|
||||
std::string file_content = readFileToText(logger.logFile());
|
||||
ASSERT_FALSE(verifyContent(file_content, msg2));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user