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