Initial commit of g2log after moving it to it's own repository from "KjellKOd"

This commit is contained in:
Kjell Hedstrom 2011-11-05 17:36:07 +01:00
commit b9459af2f3
27 changed files with 2355 additions and 0 deletions

2
3rdParty/README vendored Normal file
View File

@ -0,0 +1,2 @@
Well, you can download glog and install it yourself. I just provided this for convenience
--- KjellKod

BIN
3rdParty/glog/glog-0.3.1-1.tar.gz vendored Normal file

Binary file not shown.

66
3rdParty/gtest/3rdparty_gtest.pro vendored Normal file
View File

@ -0,0 +1,66 @@
#-------------------------------------------------
#
# Project created by QtCreator 2011-07-01T10:35:00
#
#-------------------------------------------------
QT -= core
TARGET = 3rdparty_gtest
TEMPLATE = lib
CONFIG += staticlib
CONFIG += create_prl
CONFIG += link_prl
builddir = ../../build
DESTDIR = $$builddir
# specify builddir BEFORE include make-settings.pri
! include( ../../make-test-settings.pri ) {
error( Couldn't find the make-test-settings.pri file! )
}
GTEST_DIR = gtest-1.6.0__stripped
GTEST_INCLUDE = $$GTEST_DIR/include
GTEST_INCLUDE_DIR = $$GTEST_DIR/include/gtest
GTEST_SRC_DIR = $$GTEST_DIR/src
HEADERS = $$GTEST_INCLUDE_DIR/gtest-death-test.h \
$$GTEST_INCLUDE_DIR/gtest.h \
$$GTEST_INCLUDE_DIR/gtest-message.h \
$$GTEST_INCLUDE_DIR/gtest-param-test.h \
$$GTEST_INCLUDE_DIR/gtest-param-test.h.pump \
$$GTEST_INCLUDE_DIR/gtest_pred_impl.h \
$$GTEST_INCLUDE_DIR/gtest-printers.h \
$$GTEST_INCLUDE_DIR/gtest_prod.h \
$$GTEST_INCLUDE_DIR/gtest-spi.h \
$$GTEST_INCLUDE_DIR/gtest-test-part.h \
$$GTEST_INCLUDE_DIR/gtest-typed-test.h
# Add internals to headers
HEADERS += $$GTEST_INCLUDE_DIR/internal/gtest-death-test-internal.h \
$$GTEST_INCLUDE_DIR/internal/gtest-filepath.h \
$$GTEST_INCLUDE_DIR/internal/gtest-internal.h \
$$GTEST_INCLUDE_DIR/internal/gtest-linked_ptr.h \
$$GTEST_INCLUDE_DIR/internal/gtest-param-util-generated.h \
$$GTEST_INCLUDE_DIR/internal/gtest-param-util-generated.h.pump \
$$GTEST_INCLUDE_DIR/internal/gtest-param-util.h \
$$GTEST_INCLUDE_DIR/internal/gtest-port.h \
$$GTEST_INCLUDE_DIR/internal/gtest-string.h \
$$GTEST_INCLUDE_DIR/internal/gtest-tuple.h \
$$GTEST_INCLUDE_DIR/internal/gtest-tuple.h.pump \
$$GTEST_INCLUDE_DIR/internal/gtest-type-util.h \
$$GTEST_INCLUDE_DIR/internal/gtest-type-util.h.pump
SOURCES = $$GTEST_SRC_DIR/gtest-all.cc \
$$GTEST_SRC_DIR/gtest.cc \
$$GTEST_SRC_DIR/gtest-death-test.cc \
$$GTEST_SRC_DIR/gtest-filepath.cc \
# $$GTEST_SRC_DIR/gtest-internal-inl.h \
$$GTEST_SRC_DIR/gtest_main.cc \
$$GTEST_SRC_DIR/gtest-port.cc \
$$GTEST_SRC_DIR/gtest-printers.cc \
$$GTEST_SRC_DIR/gtest-test-part.cc \
$$GTEST_SRC_DIR/gtest-typed-test.cc
INCLUDEPATH += $${GTEST_INCLUDE}
INCLUDEPATH += $${GTEST_DIR}

BIN
3rdParty/gtest/gtest-1.6.0__stripped.zip vendored Normal file

Binary file not shown.

50
README Normal file
View File

@ -0,0 +1,50 @@
HOW TO BUILD
===================
This g2log is a snapshot from KjellKod repository.
It contains what is needed to build example, unit test and performance test of g2log.
If you want to integrate g2log in your own software you need the following files
g2log/src/
g2log.cpp/h
logworker.cpp/h
privatelogworker.cpp/h
active_object_c++0x/src
shared_queue.h
active.h/cpp
BUILDING IT
-----------
cd g2log
mkdir build
cd build
cmake ..
make
CONTENTS
===========================
3rdParty -- gtest, glog.
-----------------------
Both are needed to compile the unit test and the logger comparison tests
gtest is included by the CMake and you don't need to do anything with it.
glog is not included and must be installed if you want to run the comparison tests
If you do NOT want to compile and run some tests, like the performance test, then you can always
just comment them away from the CMake configuration.
active-object_c++0x
--------------------
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
Good luck. Any questions? Contact me on my blog
or Hedstrom@kjellkod.cc
Regards
Kjell

View File

@ -0,0 +1,70 @@
# ================WINDOWS==================
# On Windows
# mkdir build; cd build;
# cmake -G "Visual Studio 10" ..
# msbuild ActiveObj_by_KjellKod.sln
# Debug\ActiveObj_by_KjellKod.exe
#
#
# ================LINUX==================
# On Linux, make sure that the environmental variables are set either in .profile or otherwise
# PATH=$PATH:/usr/include/justthread
# export PATH=/usr/lib/:$PATH
#
# from a++0x__active_object do
# mkdir build; cd build; cmake ..; make
#
# In case of CMake problems, Compile command line
#g++ src/main.cpp src/active.cpp -I /home/kjhm/Desktop/KjellKod/active-object_c++0x/src -o ActiveObj -std=c++0x -I/usr/include/justthread -pthread -ljustthread -lrt
#
# Or to simplify things even more, put all the source code in the same folder and run
# g++ main.cpp active.cpp -o ActiveObj -std=c++0x -I/usr/include/justthread -pthread -ljustthread -lrt
#
#
cmake_minimum_required (VERSION 2.6)
project (ActiveObjCpp0x)
IF(UNIX)
set(CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS_DEBUG} -pthread -I/usr/include/justthread")
# make the src directory available for test classes
include_directories("/usr/include/justthread")
include_directories(../src)
# create the test executable
#add_executable(ActiveObjCpp0x ../src/main.cpp ../src/active.cpp )
add_executable(ActiveObjCpp0x ../src/main.cpp ../src/active.cpp ../src/shared_queue.h ../src/active.h ../src/backgrounder.h)
target_link_libraries(ActiveObjCpp0x justthread rt)
ENDIF(UNIX)
IF(WIN32)
include_directories("C:/program files/JustSoftwareSolutions/JustThread/include")
include_directories(../src)
add_executable(ActiveObjCpp0x ../src/main.cpp ../src/active.cpp )
#Visual Studio 2010
IF(CMAKE_CXX_COMPILER STREQUAL "C:/Program Files/Microsoft Visual Studio 10.0/VC/bin/cl.exe")
target_link_libraries(ActiveObjCpp0x $ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/lib/justthread_vc10_mdd.lib)
ENDIF(CMAKE_CXX_COMPILER STREQUAL "C:/Program Files/Microsoft Visual Studio 10.0/VC/bin/cl.exe")
# Visual Studio 2008 DOES NOT WORK
# ===================================
# 1. std::function must be std::tr1::function but
# std::unique_ptr does not exist (ref C:\Program Files\Microsoft Visual Studio 9.0\VC\include\memory.h)
# while it exist for Visual Studio 10 (ref C:\Program Files\Microsoft Visual Studio 10.0\VC\include\memory.h)
# ...Also this bug was discovered...
# Ref: https://connect.microsoft.com/VisualStudio/feedback/details/339810/tr1-functional-doesnt-work-when-compiled-with-fastcall
#....
# However if you decide to use it anyhow and replacing the unique_ptr for shared_ptr and using std::tr1 then the
# following might be of use
#IF(CMAKE_CXX_COMPILER STREQUAL "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin/cl.exe")
# target_link_libraries(ActiveObjCpp0x $ENV{PROGRAMFILES}/JustSoftwareSolutions/JustThread/lib/justthread_vc90_mdd.lib)
#ENDIF(CMAKE_CXX_COMPILER STREQUAL "C:/Program Files/Microsoft Visual Studio 9.0/VC/bin/cl.exe")
ENDIF(WIN32)

View File

@ -0,0 +1,21 @@
# ================WINDOWS==================
# On Windows
# mkdir build; cd build;
# cmake -G "Visual Studio 10" ..
# msbuild ActiveObj_by_KjellKod.sln
# Debug\ActiveObj_by_KjellKod.exe
#
#
# ================LINUX==================
# On Linux, make sure that the environmental variables are set either in .profile or otherwise
# PATH=$PATH:/usr/include/justthread
# export PATH=/usr/lib/:$PATH
#
# from the top directory (active-object_c++0x)
# mkdir build; cd build; cmake ..; make
#
# In case of CMake problems, Compile command line
#g++ src/main.cpp src/active.cpp -I /home/kjhm/Desktop/KjellKod/active-object_c++0x/src -o ActiveObj -std=c++0x -I/usr/include/justthread -pthread -ljustthread -lrt
#
# Or to simplify things even more, put all the source code in the same folder and run
# g++ main.cpp active.cpp -o ActiveObj -std=c++0x -I/usr/include/justthread -pthread -ljustthread -lrt

View File

@ -0,0 +1,58 @@
/** 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================
*
* Example of a Active Object, using C++0x 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
* 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,
* e-mail: hedstrom at kjellkod dot cc
* linkedin: http://linkedin.com/se/kjellkod */
#include "active.h"
#include <cassert>
using namespace kjellkod;
Active::Active(): done_(false){}
Active::~Active() {
Callback quit_token = std::bind(&Active::doDone, this);
send(quit_token); // tell thread to exit
thd_.join();
}
// Add asynchronously a work-message to queue
void Active::send(Callback msg_){
mq_.push(msg_);
}
// Will wait for msgs if queue is empty
// A great explanation of how this is done (using Qt's library):
// http://doc.qt.nokia.com/stable/qwaitcondition.html
void Active::run() {
while (!done_) {
// wait till job is available, then retrieve it and
// executes the retrieved job in this thread (background)
Callback func;
mq_.wait_and_pop(func);
func();
}
}
// Factory: safe construction of object before thread start
std::unique_ptr<Active> Active::createActive(){
std::unique_ptr<Active> aPtr(new Active());
aPtr->thd_ = std::thread(&Active::run, aPtr.get());
return aPtr;
}

View File

@ -0,0 +1,57 @@
/** ==========================================================================
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================
*
* Example of a Active Object, using C++0x 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
* 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,
* e-mail: hedstrom at kjellkod dot cc
* linkedin: http://linkedin.com/se/kjellkod */
#ifndef ACTIVE_H_
#define ACTIVE_H_
#include <thread>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <memory>
#include "shared_queue.h"
namespace kjellkod {
typedef std::function<void()> Callback;
class Active {
private:
Active(const Active&) = delete;
Active& operator=(const Active&) = 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_);
static std::unique_ptr<Active> createActive(); // Factory: safe construction & thread start
};
} // end namespace kjellkod
#endif

View File

@ -0,0 +1,59 @@
/** ==========================================================================
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================
*
* Example of a Background worker that uses an Active object (by composition)
* to process jobs in the background.
* Calling the Background worker to do a job is an asynchronous call, returning
* almost immediately. The Backgrounder will create a job and push it onto a
* queue that is processed in FIFO order by the Active object. */
#include <vector>
#include <chrono>
#include <memory>
#include "active.h"
/// Silly test background worker that only receives dummy encapsuled data and stores them in a vector
/// the worker eceives the "jobs" and sends them to a work queue to be asynchronously
// executed in FIFO order by the background thread.
template<typename T>
class Backgrounder {
private:
std::unique_ptr<kjellkod::Active> active;
std::vector<T>& receivedQ;
unsigned int c_processTimeUs; // to fake processing time, in microseconds
// Container for faking some imporant stuff type instead of a dummy value
// so that it 'makes sense' storing it in an unique_ptr
struct Data {
Data(T v_) :value(v_) {}
const T value;
};
// bg processing, FAKING that each job takes a few ms
void bgStoreData(std::shared_ptr<Data> msg_){
receivedQ.push_back(msg_->value);
// fake processing time
std::this_thread::sleep_for(std::chrono::microseconds(c_processTimeUs));
}
public:
explicit Backgrounder(std::vector<T>& saveQ_)
: active(kjellkod::Active::createActive())
, receivedQ(saveQ_)
, c_processTimeUs(1){}
virtual ~Backgrounder(){}
// Asynchronous msg API, for sending jobs for bg thread processing
void saveData(const T value_){
using namespace kjellkod;
std::shared_ptr<Data> ptrBg(new Data(value_));
Callback func = std::bind(&Backgrounder::bgStoreData, this, ptrBg);
active->send(func);
}
};

View File

@ -0,0 +1,130 @@
/** ==========================================================================
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================
* Please See readme or CMakeList.txt for building instructions */
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <thread>
#include <memory>
#include <cmath>
#include <ctime>
#include <cassert>
#include "backgrounder.h"
namespace {
void printPercentageLeft(const unsigned nbr_, unsigned & progress_, const unsigned max_){
float percent = 100 * ((float)nbr_/max_);
unsigned int rounded = ((int)percent/10)*10;
if(rounded != progress_){
std::cout << 100 - progress_ << " " << std::flush;
progress_ = rounded;
return;
}
}
template<typename T>
void printProgress(const std::vector<T>& out_,const std::vector<T>& in_, const unsigned max_)
{
std::cout << "\nLeft to Process [%]: ";
unsigned progress = 100;
do{
unsigned pSize = in_.size();
unsigned remaining = pSize - out_.size();
printPercentageLeft(remaining, progress, max_);
}while((out_.size() < max_) && progress <= 100);
}
}
void runStrWorkers(const int c_nbrItems)
{
std::vector<std::string> saveToQ;
std::vector<std::string> compareQ;
const clock_t start = clock();
{
Backgrounder<std::string> worker(saveToQ);
srand((unsigned)time(0));
for(int idx=0; idx < c_nbrItems; ++idx)
{
unsigned random = rand();
std::ostringstream oss;
oss << random;
compareQ.push_back(oss.str());
worker.saveData(oss.str());
}
double pushTime = ((clock() - start)/(double)CLOCKS_PER_SEC);
std::cout<<"Finished pushing #"<<c_nbrItems<<" jobs to bg worker";
std::cout<<" in "<<pushTime<<" [s]"<< std::endl;
printProgress(saveToQ, compareQ, c_nbrItems);
} // Trigger Backgrounder to go out of scope
double workTime = ((clock() - start)/(double)CLOCKS_PER_SEC);
std::cout << "\nBackgrounder finished with processing jobs in ";
std::cout <<workTime<<" [s]"<< std::endl;
// just dummy to make sure that nothing was lost
assert(std::equal(compareQ.begin(), compareQ.end(), saveToQ.begin()));
assert(saveToQ.size() == c_nbrItems);
}
void runIntWorkers(const int c_nbrItems)
{
std::vector<int> saveToQ;
std::vector<int> compareQ;
const clock_t start = clock();
{
Backgrounder<int> worker(saveToQ);
srand((unsigned)time(0));
// all except one is random, save space for "zero" after the
// loop
for(int idx=0; idx < c_nbrItems-1; ++idx)
{
unsigned random = rand();
compareQ.push_back(random);
worker.saveData(random);
}
// extra case for empty item
compareQ.push_back(0);
worker.saveData(0);
double pushTime = ((clock() - start)/(double)CLOCKS_PER_SEC);
std::cout<<"Finished pushing #"<<c_nbrItems<<" jobs to bg worker";
std::cout<<" in "<<pushTime<<" [s]"<< std::endl;
printProgress(saveToQ, compareQ, c_nbrItems);
} // Trigger Backgrounder to go out of scope
double workTime = ((clock() - start)/(double)CLOCKS_PER_SEC);
std::cout << "\nBackgrounder finished with processing jobs in ";
std::cout<<" in "<<workTime<<" [s]"<< std::endl;
// just dummy to make sure that nothing was lost
assert(std::equal(compareQ.begin(), compareQ.end(), saveToQ.begin()));
assert(saveToQ.size() == c_nbrItems);
}
int main(int argc, char** argv)
{
const int c_nbrItems = 100000;
std::cout << c_nbrItems << " transactions of std::string/int" << std::endl;
runStrWorkers(c_nbrItems);
std::cout << "\n\n" << c_nbrItems << " transactions of int" << std::endl;
runIntWorkers(c_nbrItems);
return 0;
}

View File

@ -0,0 +1,77 @@
/** ==========================================================================
* 2010 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================
*
* Example of a normal std::queue protected by a mutex for operations,
* making it safe for thread communication, using std::mutex from C++0x with
* 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
* Ref: "C++ Concurrency In Action" http://www.manning.com/williams */
#ifndef SHARED_QUEUE
#define SHARED_QUEUE
#include <queue>
#include <mutex>
#include <exception>
#include <condition_variable>
/** Multiple producer, multiple consumer thread safe queue
* Since 'return by reference' is used this queue won't throw */
template<typename T>
class shared_queue
{
std::queue<T> queue_;
mutable std::mutex m_;
std::condition_variable data_cond_;
shared_queue& operator=(const shared_queue&) = delete;
shared_queue(const shared_queue& other) = delete;
public:
shared_queue(){}
void push(T item){
std::lock_guard<std::mutex> lock(m_);
queue_.push(item);
data_cond_.notify_one();
}
/// \return immediately, with true if successful retrieval
bool try_and_pop(T& popped_item){
std::lock_guard<std::mutex> lock(m_);
if(queue_.empty()){
return false;
}
popped_item=queue_.front();
queue_.pop();
return true;
}
/// Try to retrieve, if no items, wait till an item is available and try again
void wait_and_pop(T& popped_item){
std::unique_lock<std::mutex> lock(m_); // note: unique_lock is needed for std::condition_variable::wait
while(queue_.empty())
{ // The 'while' loop below is equal to
data_cond_.wait(lock); //data_cond_.wait(lock, [](bool result){return !queue_.empty();});
}
popped_item=queue_.front();
queue_.pop();
}
bool empty() const{
std::lock_guard<std::mutex> lock(m_);
return queue_.empty();
}
unsigned size() const{
std::lock_guard<std::mutex> lock(m_);
return queue_.size();
}
};
#endif

123
g2log/CMakeLists.txt Normal file
View File

@ -0,0 +1,123 @@
# CMakeLists.txt cmake configuration for g2log test
# 2011 @author Kjell Hedström, hedstrom@kjellkod.cc */
#
# g2log is a KjellKod Logger
#
# == README: Example how to setup environment + running tests ===============
# 1. Install gtest
# cmake
# make
# make install (possibly as root)
#
#
#
# 2. update path to libraries
# sudo /sbin/ldconfig -v | grep gtest
#
# the grep is only to verify that it works. It should give something like
# ... other stuff ...
# libgtest.so.0 -> libgtest.so.0.0.0
# libgtest_main.so.0 -> libgtest_main.so.0.0.0
#
#
#
# 3. To try this out from folder g2log:
# mkdir build
# cd build
# cmake .. # create makefiles in g2log/build directory
# make # link active_object, g2log and example code to get an "example" executable
# ./g2log-example
#
# ============================================================================
cmake_minimum_required (VERSION 2.6)
project (kjellkod_logger)
set(LOG_SRC ${kjellkod_logger_SOURCE_DIR}/src)
MESSAGE(" LOG_SRC = : ${LOG_SRC}")
IF(UNIX)
set(CMAKE_CXX_FLAGS "-Wall -Wunused -std=c++0x ${CMAKE_CXX_FLAGS_DEBUG} -pthread -I/usr/include/justthread")
# SETUP for GTEST
set(GTEST_DIR ../3rdParty/gtest/gtest-1.6.0__stripped)
set(GTEST_INCLUDE_DIRECTORIES ${GTEST_DIR}/include ${GTEST_DIR} ${GTEST_DIR}/src)
include_directories(${GTEST_INCLUDE_DIRECTORIES})
add_library(gtest_160_lib ${GTEST_DIR}/src/gtest-all.cc ${GTEST_DIR}/src/gtest_main.cc)
enable_testing(true)
# make the src directory available
include_directories(/usr/include/justthread) #not necessarily needed if it's in the path
# add a ActiveObject library
set(ACTIVE_DIR ${LOG_SRC}/../../active-object_c++0x/src)
include_directories(${ACTIVE_DIR})
MESSAGE(" ACTIVE_DIR = : ${ACTIVE_DIR}")
SET(ACTIVE_CPP0xx_DIR "Release")
add_library(lib_activeobject ${ACTIVE_DIR}/active.cpp ${ACTIVE_DIR}/active.h ${ACTIVE_DIR}/shared_queue.h)
set_target_properties(lib_activeobject PROPERTIES LINKER_LANGUAGE CXX)
include_directories(src)
include_directories(${LOG_SRC})
#MESSAGE(" LOG_SRC = : ${LOG_SRC}")
add_library(lib_logger ${LOG_SRC}/logworker.h ${LOG_SRC}/logworker.cpp ${LOG_SRC}/privatelogworker.cpp ${LOG_SRC}/privatelogworker.h ${LOG_SRC}/g2log.h ${LOG_SRC}/g2log.cpp )
set_target_properties(lib_logger PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(lib_logger lib_activeobject)
# create the the example EXECUTABLE
add_executable(g2log-example src/main.cpp)
# link executable with the src library
target_link_libraries(g2log-example lib_activeobject lib_logger justthread rt)
# Below are g2log unit testTEST
# and PERFORMANCE comparisons between g2log and google's glog
#
#
include_directories(build)
# create the the TEST executable
add_executable(g2log-unit_test ../test_main/test_main.cpp test/test_io.cpp)
target_link_libraries(g2log-unit_test lib_activeobject lib_logger gtest_160_lib justthread rt)
# ---- Below g2log Performance -----
# create the the g2log MEAN_PERFORMANCE executable
add_executable(g2log-performance-mean test/main_mean.cpp test/performance.h)
set_target_properties(g2log-performance-mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
target_link_libraries(g2log-performance-mean lib_activeobject lib_logger justthread rt)
# create the the g2log TWO_THREADS_MEAN_PERFORMANCE executable
add_executable(g2log-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h)
set_target_properties(g2log-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
target_link_libraries(g2log-performance-2threads_mean lib_activeobject lib_logger justthread rt)
# create the the g2log TWO_THREADS_WORST_CASE_PERFORMANCE executable
add_executable(g2log-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h)
set_target_properties(g2log-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "G2LOG_PERFORMANCE=1")
target_link_libraries(g2log-performance-2threads_worst lib_activeobject lib_logger justthread rt)
#
# ---- Below GOOGLE glog Performance -----
# create the the GOOGLE MEAN_PERFORMANCE executable
# Generate the DEFINE (for glog) needed to differentiate between the glog and the g2log test
add_executable(google_glog-performance-mean test/main_mean.cpp test/performance.h)
set_target_properties(google_glog-performance-mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1")
target_link_libraries(google_glog-performance-mean lib_activeobject glog justthread rt)
# create the the GOOGLE MEAN_PERFORMANCE executable
add_executable(google_glog-performance-2threads_mean test/main_2threads_mean.cpp test/performance.h)
set_target_properties(google_glog-performance-2threads_mean PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1")
target_link_libraries(google_glog-performance-2threads_mean lib_activeobject glog justthread rt)
# create the the GOOGLE MEAN_PERFORMANCE executable
add_executable(google_glog-performance-2threads_worst test/main_2threads_worst.cpp test/performance.h)
set_target_properties(google_glog-performance-2threads_worst PROPERTIES COMPILE_DEFINITIONS "GOOGLE_GLOG_PERFORMANCE=1")
target_link_libraries(google_glog-performance-2threads_worst lib_activeobject glog justthread rt)
ENDIF(UNIX)

161
g2log/src/g2log.cpp Normal file
View File

@ -0,0 +1,161 @@
/* *************************************************
* 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
* 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/
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
* 5. Various Q&A at StackOverflow
* ********************************************* */
#include "g2log.h"
#include <iostream>
#include <sstream>
#include <string>
#include <stdexcept> // exceptions
#include <cstdio> // vsnprintf
#include <cassert>
#include <mutex>
#include "logworker.h"
namespace g2
{
namespace constants
{
const int kMaxMessageSize = 2048;
const std::string kTruncatedWarningText = "[...truncated...]";
}
namespace internal
{
static LogWorker* g_logger_instance = nullptr; // instantiated and OWNED somewhere else (main)
static std::mutex g_logging_init_mutex;
bool isLoggingInitialized(){return g_logger_instance != nullptr; }
/** thanks to: http://www.cplusplus.com/reference/string/string/find_last_of/
* Splits string at the last '/' or '\\' separator
* example: "/mnt/something/else.cpp" --> "else.cpp"
* "c:\\windows\\hello.h" --> hello.h
* "this.is.not-a-path.h" -->"this.is.not-a-path.h" */
std::string splitFileName(const std::string& str)
{
size_t found;
found = str.find_last_of("(/\\");
return str.substr(found+1);
}
} // end namespace g2::internal
void initializeLogging(LogWorker *bgworker)
{
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
CHECK(!internal::isLoggingInitialized());
CHECK(bgworker != nullptr);
internal::g_logger_instance = bgworker;
}
LogWorker* shutDownLogging()
{
std::lock_guard<std::mutex> lock(internal::g_logging_init_mutex);
CHECK(internal::isLoggingInitialized());
LogWorker *backup = internal::g_logger_instance;
internal::g_logger_instance = nullptr;
return backup;
}
namespace internal
{
LogContractMessage::LogContractMessage(const std::string &file, const int line,
const std::string& function, const std::string &boolean_expression)
: LogMessage(file, line, function, "FATAL")
, expression_(boolean_expression)
{}
LogContractMessage::~LogContractMessage()
{
std::ostringstream oss;
if(0 == expression_.compare(k_fatal_log_expression))
{
oss << "[ *******\tRUNTIME EXCEPTION caused by LOG(FATAL):\t";
}
else
{
oss << "\nRUNTIME EXCEPTION caused by broken Contract: [" << expression_ << "]\t";
}
log_entry_ = oss.str();
}
LogMessage::LogMessage(const std::string &file, const int line, const std::string& function, const std::string &level)
: file_(file)
, line_(line)
, function_(function)
, level_(level)
{}
LogMessage::~LogMessage()
{
std::ostringstream oss;
const bool fatal = (0 == level_.compare("FATAL"));
oss << level_ << " [" << internal::splitFileName(file_);
if(fatal)
oss << " F: " << function_ ;
oss << " L: " << line_ << "]\t";
const std::string str(stream_.str());
if(!str.empty())
{
oss << '"' << str << '"';
}
log_entry_ += oss.str();
if(!internal::isLoggingInitialized() )
{
std::cerr << "Did you forget to call g2::InitializeLogging(LogWorker*) in your main.cpp?" << std::endl;
std::cerr << log_entry_ << std::endl << std::flush;
throw std::runtime_error("Logger not initialized with g2::InitializeLogging(LogWorker*) for msg:\n" + log_entry_);
}
internal::g_logger_instance->save(log_entry_); // message saved
if(fatal)
{
std::cerr << log_entry_ << "\t******* ]" << std::endl << std::flush;
throw std::runtime_error(log_entry_);
}
}
void LogMessage::messageSave(const char *printf_like_message, ...)
{
char finished_message[constants::kMaxMessageSize];
va_list arglist;
va_start(arglist, printf_like_message);
const int nbrcharacters = vsnprintf(finished_message, sizeof(finished_message), printf_like_message, arglist);
va_end(arglist);
if (nbrcharacters <= 0)
{
stream_ << "\n\tERROR LOG MSG NOTIFICATION: Failure to parse successfully the message";
stream_ << '"' << printf_like_message << '"' << std::endl;
}
else if (nbrcharacters > constants::kMaxMessageSize)
{
stream_ << finished_message << constants::kTruncatedWarningText;
}
else
{
stream_ << finished_message;
}
}
} // end of namespace g2::internal
} // end of namespace g2

202
g2log/src/g2log.h Normal file
View File

@ -0,0 +1,202 @@
/* *************************************************
* Filename:g2log.h 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
* 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/
* 4. Google 'glog': http://google-glog.googlecode.com/svn/trunk/doc/glog.html
* 5. Various Q&A at StackOverflow
* ********************************************* */
#ifndef G2LOG_H
#define G2LOG_H
#include <string>
#include <sstream>
#include <iostream>
#include <cstdarg>
#include <chrono>
class LogWorker;
// Levels for logging, made so that it would be easy to change, remove, add levels -- KjellKod
const int DEBUG = 0, INFO = 1, WARNING = 2, FATAL = 3;
static const std::string k_fatal_log_expression = ""; // using LogContractMessage but no boolean expression
// GCC Predefined macros: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
// and http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
//
// The ## macro is helpful as it gives compile time error in case there's a typo
// Example: MYLEVEL doesn't exist so LOG(MYLEVEL) << "bla bla bla"; would
// generate a compile error when it is rolled out by the
// macro as G2_LOG_MYLEVEL, since "#define G2_LOG_MYLEVEL" doesn't exist
// BELOW -- LOG stream syntax
#define G2_LOG_DEBUG g2::internal::LogMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,"DEBUG")
#define G2_LOG_INFO g2::internal::LogMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,"INFO")
#define G2_LOG_WARNING g2::internal::LogMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,"WARNING")
#define G2_LOG_FATAL g2::internal::LogContractMessage(__FILE__,__LINE__,__PRETTY_FUNCTION__,k_fatal_log_expression)
// LOG(level) is the API for the stream log
#define LOG(level) G2_LOG_##level.messageStream()
// conditional stream log
#define LOG_IF(level, boolean_expression) \
if(true == boolean_expression) \
G2_LOG_##level.messageStream()
// Design By Contract, stream API. Throws std::runtime_eror if contract breaks
#define CHECK(boolean_expression) \
if (false == (boolean_expression)) \
g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__, #boolean_expression).messageStream()
// BELOW -- LOG "printf" syntax
/**
* For details please see this
* REFERENCE: http://www.cppreference.com/wiki/io/c/printf_format
* \verbatim
*
There are different %-codes for different variable types, as well as options to
limit the length of the variables and whatnot.
Code Format
%[flags][width][.precision][length]specifier
SPECIFIERS
----------
%c character
%d signed integers
%i signed integers
%e scientific notation, with a lowercase e
%E scientific notation, with a uppercase E
%f floating point
%g use %e or %f, whichever is shorter
%G use %E or %f, whichever is shorter
%o octal
%s a string of characters
%u unsigned integer
%x unsigned hexadecimal, with lowercase letters
%X unsigned hexadecimal, with uppercase letters
%p a pointer
%n the argument shall be a pointer to an integer into which is placed the number of characters written so far
For flags, width, precision etc please see the above references.
EXAMPLES:
{
LOG_F(INFO, "Characters: %c %c \n", 'a', 65);
LOG_F(INFO, "Decimals: %d %ld\n", 1977, 650000L); // printing long
LOG_F(INFO, "Preceding with blanks: %10d \n", 1977);
LOG_F(INFO, "Preceding with zeros: %010d \n", 1977);
LOG_F(INFO, "Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
LOG_F(INFO, "floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
LOG_F(INFO, "Width trick: %*d \n", 5, 10);
LOG_F(INFO, "%s \n", "A string");
return 0;
}
And here is possible output
: Characters: a A
: Decimals: 1977 650000
: Preceding with blanks: 1977
: Preceding with zeros: 0000001977
: Some different radixes: 100 64 144 0x64 0144
: floats: 3.14 +3e+000 3.141600E+000
: Width trick: 10
: A string \endverbatim */
#define G2_LOG_F_INFO g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,"INFO")
#define G2_LOG_F_DEBUG g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,"DEBUG")
#define G2_LOG_F_WARNING g2::internal::LogMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,"WARNING")
#define G2_LOG_F_FATAL g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,k_fatal_log_expression)
// LOG_F(level,msg,...) is the API for the "printf" like log
#define LOG_F(level, printf_like_message, ...) \
G2_LOG_F_##level.messageSave(printf_like_message, __VA_ARGS__);
// conditional log printf syntax
#define LOG_F_IF(level,boolean_expression, printf_like_message, ...) \
if(true == boolean_expression) \
G2_LOG_##level.messageSave(printf_like_message, __VA_ARGS__);
// Design By Contract, printf-like API syntax with variadic input parameters. Throws std::runtime_eror if contract breaks */
#define CHECK_F(boolean_expression, printf_like_message, ...) \
if (false == (boolean_expression)) \
g2::internal::LogContractMessage(__FILE__, __LINE__, __PRETTY_FUNCTION__,#boolean_expression).messageSave(printf_like_message, __VA_ARGS__);
/** namespace for LOG() and CHECK() frameworks
* Histroy lesson:
* Why the names 'g2' and 'g2log'?:
* --> The framework was made in my own free time as PUBLIC DOMAIN but the first commercial project to use it
* used 'g2' as an internal denominator for the current project. g2 as in 'generation 2'. I decided to keep the g2 and g2log names to
* give credit to the people in that project (you know who you are :) and I guess also for 'sentimental' reasons.
* That a big influence was google's glog is just a happy concidence or subconscious choice. Either way g2log became the name for this logger.
* --- Thanks for a great 2011 and good luck with 'g2' --- KjellKod */
namespace g2
{
/** Should be called at very first startup of the software with \ref LogWorker pointer. Ownership of the \ref LogWorker is
* the responsibilkity of the caller */
void initializeLogging(LogWorker *logger);
/** Shutdown the logging by making the pointer to the background logger to nullptr
* The \ref pointer to the LogWorker is owned by the instantniater \ref initializeLogging
* and is not deleted. By restoring the ptr to nullptr we can re-initialize it later again. This is
* kept for test reasons and should normally not be used */
LogWorker* shutDownLogging();
// defined here but should't not have to be used outside the g2log
namespace internal
{
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;
typedef const std::string& LogEntry;
// Log message for 'printf-like' or stream logging, it's a temporary message constructions
class LogMessage
{
public:
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_;}
// The __attribute__ generates compiler warnings if illegal "printf" format
// IMPORTANT: You muse enable the compiler flag '-Wall' for this to work!
// ref: http://www.unixwiz.net/techtips/gnu-c-attributes.html
//
// Coder note: Since it's C++ and not C EVERY CLASS FUNCTION always get a first
// compiler given argument 'this' this must be supplied as well, hence '2,3'
// ref: http://www.codemaestro.com/reviews/18 -- ref KjellKod
void messageSave(const char *printf_like_message, ...)
__attribute__((format(printf,2,3) ));
protected:
const std::string file_;
const int line_;
const std::string function_;
const std::string level_;
std::ostringstream stream_;
std::string log_entry_;
};
// 'Design-by-Contract' temporary messsage construction
class LogContractMessage : public LogMessage
{
public:
LogContractMessage(const std::string &file, const int line,
const std::string &function, const std::string &boolean_expression);
virtual ~LogContractMessage(); // at destruction will flush the message
protected:
const std::string expression_;
};
} // end namespace internal
} // end namespace g2
#endif // G2LOG_H

37
g2log/src/logworker.cpp Normal file
View File

@ -0,0 +1,37 @@
/* *************************************************
* Filename:logworker.cpp Framework for Logging and Design By Contract
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#include "logworker.h"
#include <iostream>
#include <string>
#include <functional>
#include "privatelogworker.h"
LogWorker::LogWorker(const std::string& log_prefix, const std::string& log_directory)
: background_worker_(new PrivateLogWorker(log_prefix, log_directory))
, log_file_with_path_(background_worker_->log_file_with_path_)
{
}
LogWorker::~LogWorker()
{
background_worker_.reset();
//std::cout << "\nLogWorker finished with log: " << log_file_with_path_ << std::endl << std::flush;
}
void LogWorker::save(g2::internal::LogEntry msg)
{
background_worker_->bg_->send(std::tr1::bind(&PrivateLogWorker::backgroundFileWrite, background_worker_.get(), msg));
}
std::string LogWorker::logFileName() const
{
return log_file_with_path_;
}

43
g2log/src/logworker.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef LOG_WORKER_H_
#define LOG_WORKER_H_
/* *************************************************
* Filename:logworker.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 <string>
#include <memory>
#include "g2log.h"
class PrivateLogWorker;
/**
* \param log_prefix is the 'name' of the binary, this give the log name 'LOG-'name'-...
* \param log_directory gives the directory to put the log files */
class LogWorker
{
public:
LogWorker(const std::string& log_prefix, const std::string& log_directory);
virtual ~LogWorker();
/// pushes in background thread (asynchronously) input messages to log file
void save(g2::internal::LogEntry);
/// basically only needed for unit-testing or specific log management post logging
std::string logFileName() const;
private:
std::unique_ptr<PrivateLogWorker> background_worker_;
const std::string log_file_with_path_;
LogWorker(const LogWorker&) = delete; // no assignment, no copy
LogWorker& operator=(const LogWorker&) = delete;
};
#endif // LOG_WORKER_H_

100
g2log/src/main.cpp Normal file
View File

@ -0,0 +1,100 @@
/* *************************************************
* Filename: main.cpp
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copy-writed
* ********************************************* */
#include "logworker.h"
#include "g2log.h"
#include "logworker.h"
#include <iomanip>
int main(int argc, char** argv)
{
double pi_d = 3.1415926535897932384626433832795;
float pi_f = 3.1415926535897932384626433832795f;
LogWorker logger(argv[0], "/tmp/");
g2::initializeLogging(&logger);
std::cout << "****** A NUMBER of 'runtime exceptions' will be printed on this screen" << std::endl;
std::cout << "****** that's all good and part of the 'example'. " << std::endl;
std::cout << "****** please see g2log/src/main.cpp and he finished log file to " << std::endl;
std::cout << "****** follow what is done in this example\n\n" << std::endl;
LOG_F(INFO, "Hi log %d", 123);
LOG(INFO) << "Test SLOG INFO";
LOG(DEBUG) << "Test SLOG DEBUG";
LOG(INFO) << "one: " << 1;
LOG(INFO) << "two: " << 2;
LOG(INFO) << "one and two: " << 1 << " and " << 2;
LOG(DEBUG) << "float 2.14: " << 1000/2.14f;
LOG(DEBUG) << "pi double: " << pi_d;
LOG(DEBUG) << "pi float: " << pi_f;
LOG(DEBUG) << "pi float (width 10): " << std::setprecision(10) << pi_f;
LOG_F(INFO, "pi float printf:%f", pi_f);
//
// START: LOG Entris that were in the article
//
//LOG(UNKNOWN_LEVEL) << "This log attempt will cause a compiler error";
LOG(INFO) << "Simple to use with streaming syntax, easy as abc or " << 123;
LOG_F(WARNING, "Printf-style syntax is also %s", "available");
// ....
try
{
LOG_F(FATAL, "FATAL has a special meaning. This %s will throw an exception", "message");
}
catch(...)
{
std::cout << "\n **** All good expected the 'FATAL has a special meaning' runtime exception\n\n\n" << std::endl;
}
LOG_IF(INFO, (1 < 2)) << "If true this text will be logged";
LOG_F_IF(INFO, (1<2), "if %d<%d : then this text will be logged", 1,2);
LOG_IF(FATAL, (2>3)) << "This message should NOT throw";
LOG_F(DEBUG, "This API is popular with some %s", "programmers");
LOG_F_IF(DEBUG, (1<2), "If true, then this %s will be logged", "message");
{
const std::string logging = "logging";
// OK --- this WILL get a compiler warning
LOG_F(DEBUG, "Printf-type %s is the number 1 for many %s", logging.c_str());
}
CHECK(1 != 2); // true: won't throw
try
{
CHECK(1 > 2) << "CHECK(false) will put this message to the throw exception message and our log";
}
catch(...)
{
std::cout << "\n **** All good expected the 'CHECK(false) will put ...' runtime exception\n\n\n" << std::endl;
}
//
// END: LOG Entris that were in the article
//
try
{
const std::string arg = "CHECK_F";
CHECK_F(1 > 2, "This is a test to see if %s works", arg.c_str());
}
catch(...)
{
std::cout << "\n **** All good expected the 'CHECK(1>2) This is a test ...' runtime exception\n\n\n" << std::endl;
}
try
{
std::cout << "\n\n***** Be ready the example will show a 'runtime exception' " << std::endl;
CHECK(1<2) << "SHOULD NOT SEE THIS MESSAGE";
CHECK(1>2) << "Test to see if contract works: onetwothree: " << 123 << ". This should be inside an exception";
}
catch(std::exception& exc)
{
std::cout << "\n***** All good, the 'exception' was part of the example\n\n\n" << std::endl;
return 0;
}
std::cerr << "Unexpected ending, expected an exception" << std::endl << std::flush;
return 1;
}

View File

@ -0,0 +1,101 @@
/* *************************************************
* Filename: privatelogworker.cpp Framework for Logging and Design By Contract
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#include "privatelogworker.h"
#include <sstream>
#include <iostream>
#include <cassert>
#include <iomanip>
#include <ctime>
using namespace g2::internal;
namespace
{
struct LogTime
{
LogTime()
{
time_t current_time = time(nullptr);
ctime(&current_time); // fill with time right now
struct tm* ptm = localtime(&current_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
};
} // end anonymous namespace
PrivateLogWorker::PrivateLogWorker(const std::string& log_prefix, const std::string& log_directory)
: log_file_with_path_(log_directory)
, bg_(kjellkod::Active::createActive())
, start_time_(std::chrono::steady_clock::now())
{
using namespace std;
LogTime t;
ostringstream oss_name;
oss_name.fill('0');
oss_name << log_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');
}
PrivateLogWorker::~PrivateLogWorker()
{
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;
}
void PrivateLogWorker::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
out << "\t" << message;
}

View File

@ -0,0 +1,41 @@
/* *************************************************
* Filename: privatelogworker.h Framework for Logging and Design By Contract
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#ifndef PRIVATE_LOG_WORKER_H_
#define PRIVATE_LOG_WORKER_H_
#include <string>
#include <memory>
#include <fstream>
#include "active.h"
#include "g2log.h"
/** The actual background worker, while LogWorker gives the
* asynchronous API to put job in the background the PrivateLogWorker
* does the actual background thread work */
struct PrivateLogWorker
{
PrivateLogWorker(const std::string& log_prefix, const std::string& log_directory);
~PrivateLogWorker();
void backgroundFileWrite(g2::internal::LogEntry message);
std::string log_file_with_path_;
std::unique_ptr<kjellkod::Active> bg_;
std::ofstream out;
g2::internal::time_point start_time_;
private:
PrivateLogWorker& operator=(const PrivateLogWorker&) = delete; // no assignment, no copy
PrivateLogWorker(const PrivateLogWorker& other) = delete;
};
#endif

View File

@ -0,0 +1,72 @@
/* *************************************************
// * Filename: g2log-main_mean.cpp
// * Created: 2011 by Kjell Hedström
// *
// * PUBLIC DOMAIN and Not copy-writed
// * ********************************************* */
// through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G2LOG_PERFORMANCE
#include "performance.h"
#include <thread>
#if defined(G2LOG_PERFORMANCE)
const std::string title = "G2LOG";
#elif defined(GOOGLE_GLOG_PERFORMANCE)
const std::string title = "GOOGLE__GLOG";
#else
const std::string title = not_defined_this_will_be_compiler_error;
#endif
const std::string g_prefix_log_name = title + "-performance-2threads-MEAN_LOG";
const std::string g_measurement_dump= "/tmp/" + g_prefix_log_name + "_RESULT.txt";
const std::string g_path = "/tmp/";
using namespace g2_test;
int main(int argc, char** argv)
{
std::ostringstream oss;
oss << "\n\n" << title << " performance 2 threads MEAN times\n";
oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const size_t xtra_margin = 2;
oss << "*** It can take som time. Please wait: Approximate maximum wait time is: " << (long long) (g_iterations * 10 * xtra_margin / 1000000 ) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
#if defined(G2LOG_PERFORMANCE)
LogWorker* logger = new LogWorker(g_prefix_log_name, g_path);
g2::initializeLogging(logger);
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]);
#endif
auto start_time = std::chrono::steady_clock::now();
// running both threads
std::thread t1(doLogWrites, title + "_T1");
std::thread t2(doLogWrites, title + "_T2");
t1.join();
t2.join();
auto application_end_time = std::chrono::steady_clock::now();
#if defined(G2LOG_PERFORMANCE)
delete logger; // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
#endif
auto worker_end_time = std::chrono::steady_clock::now();
auto application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
auto total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
oss << "\n" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk"<< std::endl;
oss << "[Application(2threads):\t\t:" << application_time_us/1000 << " ms]" << std::endl;
oss << "[Background thread to finish\t:" << total_time_us/1000 << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us/g_iterations << " us]" << std::endl;
oss << "[Background+Application: " << total_time_us/g_iterations << " us]" << std::endl;
writeTextToFile(g_measurement_dump,oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl;
return 0;
}

View File

@ -0,0 +1,111 @@
/* *************************************************
// * Filename: g2log-main_mean.cpp
// * Created: 2011 by Kjell Hedström
// *
// * PUBLIC DOMAIN and Not copy-writed
// * ********************************************* */
// through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G2LOG_PERFORMANCE
#include "performance.h"
#include <thread>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>
#if defined(G2LOG_PERFORMANCE)
const std::string title = "G2LOG";
#elif defined(GOOGLE_GLOG_PERFORMANCE)
const std::string title = "GOOGLE__GLOG";
#else
const std::string title = not_defined_this_will_be_compiler_error;
#endif
const std::string g_prefix_log_name = title + "-performance-2threads-WORST_LOG";
const std::string g_measurement_dump= "/tmp/" + g_prefix_log_name + "_RESULT.txt";
const std::string g_measurement_bucket_dump= "/tmp/" + g_prefix_log_name + "_RESULT_buckets.txt";
const std::string g_path = "/tmp/";
using namespace g2_test;
int main(int argc, char** argv)
{
std::ostringstream oss;
oss << "\n\n" << title << " performance 2 threads WORST (PEAK) times\n";
oss << "Each thread running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const size_t xtra_margin = 2;
oss << "*** It can take som time. Please wait: Approximate maximum wait time is: " << (long long) (g_iterations * 10 * xtra_margin / 1000000 ) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
#if defined(G2LOG_PERFORMANCE)
LogWorker* logger = new LogWorker(g_prefix_log_name, g_path);
g2::initializeLogging(logger);
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]);
#endif
std::vector<long long> t1_result;
std::vector<long long> t2_result;
t1_result.reserve(g_iterations);
t2_result.reserve(g_iterations);
auto start_time = std::chrono::steady_clock::now();
// running both threads
std::thread t1(measurePeakDuringLogWrites, "g2log_T1", std::ref(t1_result));
std::thread t2(measurePeakDuringLogWrites, "g2log_T2", std::ref(t2_result));
t1.join();
t2.join();
auto application_end_time = std::chrono::steady_clock::now();
#if defined(G2LOG_PERFORMANCE)
delete logger; // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
#endif
auto worker_end_time = std::chrono::steady_clock::now();
auto application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
auto total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
oss << "\n" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk"<< std::endl;
oss << "[Application(2threads+overhead time for measurement):\t" << application_time_us/1000 << " ms]" << std::endl;
oss << "[Background thread to finish:\t\t\t\t" << total_time_us/1000 << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us/g_iterations << " us]" << std::endl;
oss << "[Application t1: worst took: " << (*std::max_element(t1_result.begin(), t1_result.end())) / 1000 << " ms]" << std::endl;
oss << "[Application t2: worst took: " << (*std::max_element(t2_result.begin(), t2_result.end())) / 1000 << " ms]" << std::endl;
writeTextToFile(g_measurement_dump,oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl;
// now split the result in buckets of 10ms each so that it's obvious how the peaks go
std::vector<long long> all_measurements;
all_measurements.reserve(t1_result.size() + t1_result.size());
all_measurements.insert(all_measurements.end(), t1_result.begin(), t1_result.end());
all_measurements.insert(all_measurements.end(), t2_result.begin(), t2_result.end());
std::sort (all_measurements.begin(), all_measurements.end());
std::map<size_t, size_t> value_amounts;
// for(long long& idx : all_measurements) --- didn't work?!
for(auto iter = all_measurements.begin(); iter != all_measurements.end(); ++iter)
{
auto value = (*iter)/1000; // convert to ms
//auto bucket=floor(value*10+0.5)/10;
++value_amounts[value]; // asuming size_t is default 0 when initialized
}
oss.str("");
oss << "Number of values rounded to tenths of ms dumped to file: " << g_measurement_bucket_dump << std::endl;
oss << "Format: bucket_of_ms, number_of_values_in_bucket";
std::cout << oss.str() << std::endl;
for(auto iter = value_amounts.begin(); iter != value_amounts.end(); ++iter)
{
oss << iter->first << "\t, " << iter->second << std::endl;
}
writeTextToFile(g_measurement_bucket_dump,oss.str(), kAppend, false);
return 0;
}

69
g2log/test/main_mean.cpp Normal file
View File

@ -0,0 +1,69 @@
/* *************************************************
// * Filename: g2log-main_mean.cpp
// * Created: 2011 by Kjell Hedström
// *
// * PUBLIC DOMAIN and Not copy-writed
// * ********************************************* */
// through CMakeLists.txt #define of GOOGLE_GLOG_PERFORMANCE and G2LOG_PERFORMANCE
#include "performance.h"
#include <thread>
#if defined(G2LOG_PERFORMANCE)
const std::string title = "G2LOG";
#elif defined(GOOGLE_GLOG_PERFORMANCE)
const std::string title = "GOOGLE__GLOG";
#else
const std::string title = not_defined_this_will_be_compiler_error;
#endif
const std::string g_prefix_log_name = title + "-performance-MEAN_LOG";
const std::string g_measurement_dump= "/tmp/" + g_prefix_log_name + "_RESULT.txt";
const std::string g_path = "/tmp/";
using namespace g2_test;
int main(int argc, char** argv)
{
std::ostringstream oss;
oss << "\n\n" << title << " performance MEAN times\n";
oss << "Running #: " << g_loop << " * " << g_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
const size_t xtra_margin = 2;
oss << "*** It can take som time. Please wait: Approximate maximum wait time is: " << (long long) (g_iterations * 10 * xtra_margin / 1000000 ) << " seconds" << std::endl;
writeTextToFile(g_measurement_dump, oss.str(), kAppend);
oss.str(""); // clear the stream
#if defined(G2LOG_PERFORMANCE)
LogWorker* logger = new LogWorker(g_prefix_log_name, g_path);
g2::initializeLogging(logger);
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::InitGoogleLogging(argv[0]);
#endif
auto start_time = std::chrono::steady_clock::now();
doLogWrites(title);
auto application_end_time = std::chrono::steady_clock::now();
#if defined(G2LOG_PERFORMANCE)
delete logger; // will flush anything in the queue to file
#elif defined(GOOGLE_GLOG_PERFORMANCE)
google::ShutdownGoogleLogging();
#endif
auto worker_end_time = std::chrono::steady_clock::now();
auto application_time_us = std::chrono::duration_cast<microsecond>(application_end_time - start_time).count();
auto total_time_us = std::chrono::duration_cast<microsecond>(worker_end_time - start_time).count();
oss << "\n" << g_iterations << " log entries took: [" << total_time_us / 1000000 << " s] to write to disk"<< std::endl;
oss << "[Application: \t\t\t:" << application_time_us/1000 << " ms]" << std::endl;
oss << "[Background thread to finish\t:" << total_time_us/1000 << " ms]" << std::endl;
oss << "\nAverage time per log entry:" << std::endl;
oss << "[Application: " << application_time_us/g_iterations << " us]" << std::endl;
oss << "[Background+Application: " << total_time_us/g_iterations << " us]" << std::endl;
writeTextToFile(g_measurement_dump,oss.str(), kAppend);
std::cout << "Result can be found at:" << g_measurement_dump << std::endl;
return 0;
}

123
g2log/test/performance.h Normal file
View File

@ -0,0 +1,123 @@
/* *************************************************
* Filename: performance_io.cpp
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copy-writed
* ********************************************* */
#ifndef PERFORMANCE_G2_TEST_H_
#define PERFORMANCE_G2_TEST_H_
#include <iostream>
#include <sstream>
#include <fstream>
#include <ctime>
#include <cstdio>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include <chrono>
#include <cassert>
#if defined(G2LOG_PERFORMANCE)
#include "g2log.h"
#include "logworker.h"
#elif defined(GOOGLE_GLOG_PERFORMANCE)
#include <glog/logging.h>
#endif
//typedef std::chrono::duration<long, std::ratio<1, 1000> > millisecond;
typedef std::chrono::duration<long long, std::ratio<1, 1000000> > microsecond;
namespace g2_test
{
enum WriteMode
{
kAppend = 0,
kTruncate = 1
};
const size_t g_loop = 1;
const size_t g_iterations = 1000000;
const char* charptrmsg = "\tmessage by char*";
const std::string strmsg = "\tmessage by string";
float pi_f = 3.1415926535897932384626433832795f;
bool writeTextToFile(const std::string& filename, const std::string& msg, const WriteMode write_mode, bool push_out = true)
{
if(push_out)
{
std::cout << msg << std::flush;
}
std::ofstream out;
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
(kTruncate == write_mode) ? mode |= std::ios_base::trunc : mode |= std::ios_base::app;
out.open(filename.c_str(), mode);
if (!out.is_open())
{
std::ostringstream ss_error;
ss_error << "Fatal error could not open log file:[" << filename << "]";
ss_error << "\n\t\t std::ios_base state = " << out.rdstate();
std::cerr << ss_error.str().c_str() << std::endl << std::flush;
return false;
}
out << msg;
return true;
}
long long mean(const std::vector<long long> &v)
{
long long total = std::accumulate(v.begin(), v.end(), 0 ); // '0' is the initial value
return total/v.size();
}
void measurePeakDuringLogWrites(const std::string& title, std::vector<long long>& result);
inline void measurePeakDuringLogWrites(const std::string& title, std::vector<long long>& result)
{
#if defined(G2LOG_PERFORMANCE)
std::cout << "G2LOG (" << title << ") WORST_PEAK PERFORMANCE TEST" << std::endl;
#elif defined(GOOGLE_GLOG_PERFORMANCE)
std::cout << "GOOGLE_GLOG (" << title << ") WORST_PEAK PERFORMANCE TEST" << std::endl;
#else
std::cout << "ERROR no performance type chosen" << std::endl;
assert(false);
#endif
for(size_t count = 0; count < g_iterations; ++count)
{
auto start_time = std::chrono::steady_clock::now();
LOG(INFO) << title << " iteration #" << count << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f;
auto stop_time = std::chrono::steady_clock::now();
auto time_us = std::chrono::duration_cast<microsecond>(stop_time - start_time).count();
result.push_back(time_us);
}
}
void doLogWrites(const std::string& title);
inline void doLogWrites(const std::string& title)
{
#if defined(G2LOG_PERFORMANCE)
std::cout << "G2LOG (" << title << ") PERFORMANCE TEST" << std::endl;
#elif defined(GOOGLE_GLOG_PERFORMANCE)
std::cout << "GOOGLE_GLOG (" << title << ") PERFORMANCE TEST" << std::endl;
#else
std::cout << "ERROR no performance type chosen" << std::endl;
assert(false);
#endif
for(size_t count = 0; count < g_iterations; ++count)
{
LOG(INFO) << title << " iteration #" << count << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f;
}
}
} // end namespace
#endif // fPERFORMANCE_G2_TEST_H_

View File

@ -0,0 +1,171 @@
/* *************************************************
* Filename: performance_io.cpp
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copy-writed
* ********************************************* */
#include <iostream>
#include <sstream>
#include <fstream>
#include <ctime>
#include <cstdio>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include "loghelper.h"
#include "logworker.h"
#include "g2log.h"
//typedef std::chrono::duration<long, std::ratio<1, 1000> > millisecond;
typedef std::chrono::duration<long long, std::ratio<1, 1000000> > microsecond;
namespace
{
enum WriteMode
{
kAppend = 0,
kTruncate = 1
};
enum TestType
{
kTypeSimpleStream, kTypeComplexStream, kTypeSimpleF, kTypeComplexF
};
const int k_loop = 1; //3;
const int k_iterations = 1000000; //1000000;
const std::string measure_g2log = "/tmp/g2log-measure_performance.txt";
const std::string raw_log_name = "g2log-by-kjellkod"; // needed by the g2log instantiation
const std::string g_path = "/tmp/";
long long g_simple_max_us = 0;
long long g_complex_max_us = 0;
long long g_simpleF_max_us = 0;
long long g_complexF_max_us = 0;
const char* charptrmsg = "\tmessage by char*";
const std::string strmsg = "\tmessage by string";
float pi_f = 3.1415926535897932384626433832795f;
bool writeTextToFile(const std::string& filename, const std::string& msg, const WriteMode write_mode)
{
std::cout << msg << std::flush;
std::ofstream out;
std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
(kTruncate == write_mode) ? mode |= std::ios_base::trunc : mode |= std::ios_base::app;
out.open(filename.c_str(), mode);
if (!out.is_open())
{
std::ostringstream ss_error;
ss_error << "Fatal error could not open log file:[" << filename << "]";
ss_error << "\n\t\t std::ios_base state = " << out.rdstate();
std::cerr << ss_error.str().c_str() << std::endl << std::flush;
return false;
}
out << msg;
return true;
}
long long mean(const std::vector<long long> &v)
{
long long total = std::accumulate(v.begin(), v.end(), 0 ); // '0' is the initial value
return total/v.size();
}
long long iterativeLog(const TestType type,std::vector<long long> &measurements)
{
auto start_total_time = std::chrono::system_clock::now();
for(int i= 0; i < k_iterations; ++i)
{
auto maxmin_start_time = std::chrono::system_clock::now();
if(kTypeSimpleStream == type)
{
LOG(INFO) << "#1 g2log STREAM";
}
else if(kTypeSimpleF == type)
{
LOG_F(INFO, "#2 g2log LOG_F simple %d",0);
}
else if(kTypeComplexStream == type)
{
LOG(INFO) << "#3 g2log STREAM, iteration #" << i << " " << charptrmsg << strmsg << " and a float: " << std::setprecision(6) << pi_f;
}
else // kTypeComplexF
{
LOG_F(INFO, "#4, iteration #%d %s%s and a float:%f ", i,charptrmsg,strmsg.c_str(),pi_f);
}
auto maxmin_stop_time = std::chrono::system_clock::now();
auto maxmin_log_time = std::chrono::duration_cast<microsecond>(maxmin_stop_time - maxmin_start_time).count();
measurements.push_back(maxmin_log_time);
}
auto now_total = std::chrono::system_clock::now();
return std::chrono::duration_cast<microsecond>(now_total - start_total_time).count();
}
void measureLogPerformance(const TestType type, long long& test_max_us,std::vector<long long> &measurements, std::string msg)
{
std::ostringstream oss;
oss << std::endl << msg << " ";
{ // local scope PRINTF SYNTAX
long long app_time_us = 0;
auto start_time = std::chrono::system_clock::now();
{ // local scope - forcing exit
LogWorker logger(raw_log_name, g_path);
g2::initializeLogging(&logger);
app_time_us = iterativeLog(type, measurements);
} // scope -exit will flush the background logger
auto now = std::chrono::system_clock::now();
auto total_exec_time_us = std::chrono::duration_cast<microsecond>(now - start_time).count();
oss << app_time_us << "," << total_exec_time_us << " us [OR: ";
oss << app_time_us/1000 << " , " << total_exec_time_us/1000 << " ms] " << std::endl;
oss << ": (app) log mean value : " << mean(measurements) << " us" << "\ttotal(app+bg) average time: " << total_exec_time_us/k_iterations << " us" << std::endl;
auto calc_max = std::max_element(measurements.begin(), measurements.end());
test_max_us = *calc_max;
oss << ": app max-peak time : " << test_max_us/1000 << " ms" << std::endl;
g2::shutDownLogging();
}
writeTextToFile(measure_g2log, "\n" + oss.str(), kAppend);
measurements.clear();
}
} // end anonymous namespace
int main(int argc, char** argv)
{
std::vector<long long> measurements;
measurements.reserve(k_iterations);
std::ostringstream oss;
oss << "Running #: " << k_loop << " * " << k_iterations << " iterations of log entries" << std::endl; // worst mean case is about 10us per log entry
oss << "It can take som time. Please wait: Approximate maximum wait time is: " << (long long) (k_iterations * 10* 4 / 1000000 ) << " seconds" << std::endl;
writeTextToFile(measure_g2log, oss.str(), kAppend);
oss.str(""); // clear the stream
measureLogPerformance(kTypeSimpleStream, g_simple_max_us, measurements, "#1 SIMPLE LOG STREAM (app/total)");
measureLogPerformance(kTypeSimpleF, g_simpleF_max_us, measurements, "#2 SIMPLE LOG_F (app/total)");
measureLogPerformance(kTypeComplexStream, g_complex_max_us, measurements, "#3 Complex LOG STREAM (app/total)");
measureLogPerformance(kTypeComplexF, g_complexF_max_us, measurements, "#4 Complex LOG_F (app/total)");
oss << "\nSIMPLE max: " << g_simple_max_us << " us" << " [" << g_simple_max_us/1000 << " ms]" << std::endl;
oss << "SIMPLE_F max: " << g_simpleF_max_us << " us" << " [" << g_simpleF_max_us/1000 << " ms]" << std::endl;
oss << "COMPLEX max: " << g_complex_max_us << " us" << " [" << g_complex_max_us/1000 << " ms]" << std::endl;
oss << "COMPLEX_F max: " << g_complexF_max_us<< " us" << " [" << g_complexF_max_us/1000 << " ms]" << std::endl;
writeTextToFile(measure_g2log,oss.str(), kAppend);
return 0;
}

392
g2log/test/test_io.cpp Normal file
View File

@ -0,0 +1,392 @@
/* *************************************************
* Filename: test_io.cpp
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copy-writed
* ********************************************* */
#include <gtest/gtest.h>
#include "g2log.h"
#include "logworker.h"
#include <memory>
#include <string>
#include <fstream>
#include <cstdio>
namespace
{
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::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;
}
} // end anonymous namespace
// RAII temporarily replace of logger
// and restoration of original logger at scope end
struct RestoreLogger
{
RestoreLogger();
~RestoreLogger();
void reset();
std::string readFileToText();
std::unique_ptr<LogWorker> logger_;
const std::string log_file_;
};
RestoreLogger::RestoreLogger()
: logger_(new LogWorker("UNIT_TEST_LOGGER", "./"))
, log_file_(logger_->logFileName())
{
g2::initializeLogging(logger_.get());
}
RestoreLogger::~RestoreLogger()
{
reset();
g2::shutDownLogging();
EXPECT_EQ(0, remove(log_file_.c_str()));
}
void RestoreLogger::reset()
{
logger_.reset();
}
// LOG
TEST(LOGTest, LOG)
{
std::string file_content;
{
RestoreLogger logger;
LOG(INFO) << "test LOG(INFO)";
logger.reset(); // force flush of logger
file_content = readFileToText(logger.log_file_);
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";
}
// printf-type log
TEST(LogTest, LOG_F)
{
std::string file_content;
{
RestoreLogger logger;
LOG_F(INFO, std::string(t_info + "%d").c_str(), 123);
LOG_F(DEBUG, std::string(t_debug + "%f").c_str(), 1.123456);
LOG_F(WARNING, std::string(t_warning + "%s").c_str(), "yello");
logger.reset(); // force flush of logger
file_content = readFileToText(logger.log_file_);
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_TRUE(verifyContent(file_content, t_debug2));
ASSERT_TRUE(verifyContent(file_content, t_warning2));
}
// stream-type log
TEST(LogTest, LOG)
{
std::string file_content;
{
RestoreLogger logger;
LOG(INFO) << t_info << 123;
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_);
SCOPED_TRACE("LOG_INFO"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_TRUE(verifyContent(file_content, t_debug2));
ASSERT_TRUE(verifyContent(file_content, t_warning2));
}
TEST(LogTest, LOG_F_IF)
{
std::string file_content;
{
RestoreLogger logger;
LOG_F_IF(INFO, (2 == 2), std::string(t_info + "%d").c_str(), 123);
LOG_F_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_);
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_FALSE(verifyContent(file_content, t_debug2));
}
TEST(LogTest, LOG_IF)
{
std::string file_content;
{
RestoreLogger logger;
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_);
SCOPED_TRACE("LOG_IF"); // Scope exit be prepared for destructor failure
}
ASSERT_TRUE(verifyContent(file_content, t_info2));
ASSERT_FALSE(verifyContent(file_content, t_debug2));
}
TEST(LogTest, LOG_F__FATAL)
{
RestoreLogger logger;
try
{
LOG_F(FATAL, "This message should throw %d",0);
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL") &&
verifyContent(file_content, "This message should throw"))
{
SUCCEED();
return;
}
else
{
ADD_FAILURE() << "Didn't throw exception as expected";
}
}
ADD_FAILURE() << "Didn't throw exception at ALL";
}
TEST(LogTest, LOG_FATAL)
{
RestoreLogger logger;
try
{
LOG(FATAL) << "This message should throw";
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL") &&
verifyContent(file_content, "This message should throw"))
{
SUCCEED();
return;
}
else
{
ADD_FAILURE() << "Didn't throw exception as expected";
}
}
ADD_FAILURE() << "Didn't throw exception at ALL";
}
TEST(LogTest, LOG_F_IF__FATAL)
{
RestoreLogger logger;
try
{
LOG_F_IF(FATAL, (2<3), "This message%sshould throw"," ");
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL") &&
verifyContent(file_content, "This message should throw"))
{
SUCCEED();
return;
}
else
{
ADD_FAILURE() << "Didn't throw exception as expected";
}
}
ADD_FAILURE() << "Didn't throw exception at ALL";
}
TEST(LogTest, LOG_IF__FATAL)
{
RestoreLogger logger;
try
{
LOG_IF(WARNING, (0 != t_info.compare(t_info))) << "This message should NOT be written";
LOG_IF(FATAL, (0 != t_info.compare(t_info2))) << "This message should throw";
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL") &&
verifyContent(file_content, "This message should throw") &&
(false == verifyContent(file_content, "This message should NOT be written")))
{
SUCCEED();
return;
}
else
{
ADD_FAILURE() << "Didn't throw exception as expected";
}
}
ADD_FAILURE() << "Didn't throw exception at ALL";
}
TEST(LogTest, LOG_IF__FATAL__NO_THROW)
{
RestoreLogger logger;
try
{
LOG_IF(FATAL, (2>3)) << "This message%sshould NOT throw";
}
catch (std::exception const &e)
{
logger.reset();
ADD_FAILURE() << "Didn't throw exception as expected";
}
logger.reset();
SUCCEED();
}
// CHECK_F
TEST(CheckTest, CHECK_F__thisWILL_PrintErrorMsg)
{
RestoreLogger logger;
try
{
CHECK(1 == 2);
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL"))
{
SUCCEED();
return;
}
}
ADD_FAILURE() << "Didn't throw exception as expected";
}
TEST(CHECK_F_Test, CHECK_F__thisWILL_PrintErrorMsg)
{
RestoreLogger logger;
std::string msg = "This message is added to throw %s and %s";
std::string msg2 = "This message is added to throw message and log";
std::string arg1 = "message";
std::string arg2 = "log";
try
{
CHECK_F(1 >= 2, msg.c_str(), arg1.c_str(), arg2.c_str());
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL") &&
verifyContent(file_content, msg2))
{
SUCCEED();
return;
}
}
ADD_FAILURE() << "Didn't throw exception as expected";
}
TEST(CHECK_Test, CHECK__thisWILL_PrintErrorMsg)
{
RestoreLogger logger;
std::string msg = "This message is added to throw %s and %s";
std::string msg2 = "This message is added to throw message and log";
std::string arg1 = "message";
std::string arg2 = "log";
try
{
CHECK(1 >= 2) << msg2;
}
catch (std::exception const &e)
{
logger.reset();
std::string file_content = readFileToText(logger.log_file_);
if(verifyContent(e.what(), "RUNTIME EXCEPTION") &&
verifyContent(file_content, "FATAL") &&
verifyContent(file_content, msg2))
{
SUCCEED();
return;
}
}
ADD_FAILURE() << "Didn't throw exception as expected";
}
TEST(CHECK, CHECK_ThatWontThrow)
{
RestoreLogger logger;
std::string msg = "This %s should never appear in the %s";
std::string msg2 = "This message should never appear in the log";
std::string arg1 = "message";
std::string arg2 = "log";
try
{
CHECK(1 == 1);
CHECK_F(1==1, msg.c_str(), "message", "log");
}
catch (std::exception const &e)
{
ADD_FAILURE() << "Should never have thrown";
}
std::string file_content = readFileToText(logger.log_file_);
ASSERT_FALSE(verifyContent(file_content, msg2));
}

19
test_main/test_main.cpp Normal file
View File

@ -0,0 +1,19 @@
/* *************************************************
* Filename:test_main.cpp Framework for Logging and Design By Contract
* Created: 2011 by Kjell Hedström
*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */
#include <gtest/gtest.h>
#include <iostream>
#include <chrono>
int main(int argc, char *argv[])
{
testing::InitGoogleTest(&argc, argv);
int return_value = RUN_ALL_TESTS();
std::cout << "FINISHED WITH THE TESTING" << std::endl;
return return_value;
}