mirror of
https://github.com/KjellKod/g3log.git
synced 2024-12-13 10:42:56 +01:00
Initial commit of g2log after moving it to it's own repository from "KjellKOd"
This commit is contained in:
commit
b9459af2f3
2
3rdParty/README
vendored
Normal file
2
3rdParty/README
vendored
Normal 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
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
66
3rdParty/gtest/3rdparty_gtest.pro
vendored
Normal 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
BIN
3rdParty/gtest/gtest-1.6.0__stripped.zip
vendored
Normal file
Binary file not shown.
50
README
Normal file
50
README
Normal 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
|
70
active-object_c++0x/CMakeLists.txt
Normal file
70
active-object_c++0x/CMakeLists.txt
Normal 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)
|
||||
|
||||
|
||||
|
||||
|
21
active-object_c++0x/README.txt
Normal file
21
active-object_c++0x/README.txt
Normal 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
|
58
active-object_c++0x/src/active.cpp
Normal file
58
active-object_c++0x/src/active.cpp
Normal 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;
|
||||
}
|
57
active-object_c++0x/src/active.h
Normal file
57
active-object_c++0x/src/active.h
Normal 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
|
59
active-object_c++0x/src/backgrounder.h
Normal file
59
active-object_c++0x/src/backgrounder.h
Normal 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);
|
||||
}
|
||||
};
|
130
active-object_c++0x/src/main.cpp
Normal file
130
active-object_c++0x/src/main.cpp
Normal 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;
|
||||
}
|
77
active-object_c++0x/src/shared_queue.h
Normal file
77
active-object_c++0x/src/shared_queue.h
Normal 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
123
g2log/CMakeLists.txt
Normal 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
161
g2log/src/g2log.cpp
Normal 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
202
g2log/src/g2log.h
Normal 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
37
g2log/src/logworker.cpp
Normal 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
43
g2log/src/logworker.h
Normal 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
100
g2log/src/main.cpp
Normal 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;
|
||||
}
|
101
g2log/src/privatelogworker.cpp
Normal file
101
g2log/src/privatelogworker.cpp
Normal 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(¤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
|
||||
};
|
||||
} // 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;
|
||||
}
|
||||
|
41
g2log/src/privatelogworker.h
Normal file
41
g2log/src/privatelogworker.h
Normal 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
|
72
g2log/test/main_2threads_mean.cpp
Normal file
72
g2log/test/main_2threads_mean.cpp
Normal 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;
|
||||
}
|
111
g2log/test/main_2threads_worst.cpp
Normal file
111
g2log/test/main_2threads_worst.cpp
Normal 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
69
g2log/test/main_mean.cpp
Normal 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
123
g2log/test/performance.h
Normal 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_
|
171
g2log/test/performance_io.cpp
Normal file
171
g2log/test/performance_io.cpp
Normal 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
392
g2log/test/test_io.cpp
Normal 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
19
test_main/test_main.cpp
Normal 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user