From c1b31c7ea603506a675cc8b063906a7526476d5f Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 17 Jun 2020 11:49:33 +0200 Subject: [PATCH 01/15] Set default CXX standard to 14. Don't pass USE_CXX11 to external projects --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bee5cb8..077daec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ SET( ILCSOFT_CMAKE_MODULES_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/cmakemodules ) include( ExternalProject ) if(NOT "${CMAKE_CXX_STANDARD}") - set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD 14) endif() MESSAGE(STATUS "CMAKE_CXX_STANDARD set to ${CMAKE_CXX_STANDARD}") @@ -36,21 +36,21 @@ ExternalProject_Add( cmakemodules ExternalProject_Add( streamlog SOURCE_DIR "${PROJECT_SOURCE_DIR}/streamlog" - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmakemodules -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DUSE_CXX11=${USE_CXX11} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmakemodules -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} INSTALL_COMMAND ${CMAKE_BUILD_TOOL} install ) ExternalProject_Add( ilctest DEPENDS cmakemodules SOURCE_DIR "${PROJECT_SOURCE_DIR}/ilctest" - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmakemodules -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DUSE_CXX11=${USE_CXX11} + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmakemodules -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} INSTALL_COMMAND ${CMAKE_BUILD_TOOL} install ) ExternalProject_Add( example DEPENDS ilctest streamlog SOURCE_DIR "${PROJECT_SOURCE_DIR}/example" - CMAKE_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmakemodules -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} -DUSE_CXX11=${USE_CXX11} + CMAKE_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_INSTALL_PREFIX} -DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmakemodules -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "no install step needed" TEST_COMMAND ${CMAKE_BUILD_TOOL} test ) -- GitLab From 1bfe417eda2b31fb7a07ba80c8df94f49beb31d4 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 13:17:42 +0200 Subject: [PATCH 02/15] Added Threads to streamlog link libraries --- streamlog/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/streamlog/CMakeLists.txt b/streamlog/CMakeLists.txt index fa79012..f5d1318 100644 --- a/streamlog/CMakeLists.txt +++ b/streamlog/CMakeLists.txt @@ -16,6 +16,7 @@ SET( ${PROJECT_NAME}_VERSION_PATCH 0 ) INCLUDE( ilcsoft_default_settings ) +FIND_PACKAGE( Threads REQUIRED ) ### DOCUMENTATION ########################################################### @@ -38,7 +39,7 @@ INSTALL( DIRECTORY ./source/include DESTINATION . PATTERN ".svn" EXCLUDE ) AUX_SOURCE_DIRECTORY( ./source/src library_sources ) ADD_SHARED_LIBRARY( ${PROJECT_NAME} ${library_sources} ) INSTALL( TARGETS ${PROJECT_NAME} DESTINATION lib ) - +TARGET_LINK_LIBRARIES( ${PROJECT_NAME} PUBLIC Threads::Threads ) ADD_SUBDIRECTORY( ./examples ) -- GitLab From b88bc8945d4a8151588295edca8386b0e18a8368 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 15:22:43 +0200 Subject: [PATCH 03/15] Added log_levels and log_levels_map global functions --- .../source/include/streamlog/loglevels.h | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/streamlog/source/include/streamlog/loglevels.h b/streamlog/source/include/streamlog/loglevels.h index d2e04aa..34b9452 100644 --- a/streamlog/source/include/streamlog/loglevels.h +++ b/streamlog/source/include/streamlog/loglevels.h @@ -31,6 +31,10 @@ #include "streamlog/baselevels.h" +#include <string> +#include <vector> +#include <map> + namespace streamlog{ @@ -95,6 +99,66 @@ namespace streamlog{ // use this to turn of all logging messages DEFINE_STREAMLOG_LEVEL( SILENT, "SILENT" , STREAMLOG_MAX_LEVEL , false ) + std::vector<std::string> log_levels() { + return { + "DEBUG", "DEBUG0", "DEBUG1", "DEBUG2", "DEBUG3", "DEBUG4", "DEBUG5", "DEBUG6", "DEBUG7", "DEBUG8", "DEBUG9", + "MESSAGE", "MESSAGE0", "MESSAGE1", "MESSAGE2", "MESSAGE3", "MESSAGE4", "MESSAGE5", "MESSAGE6", "MESSAGE7", "MESSAGE8", "MESSAGE9", + "WARNING", "WARNING0", "WARNING1", "WARNING2", "WARNING3", "WARNING4", "WARNING5", "WARNING6", "WARNING7", "WARNING8", "WARNING9", + "ERROR", "ERROR0", "ERROR1", "ERROR2", "ERROR3", "ERROR4", "ERROR5", "ERROR6", "ERROR7", "ERROR8", "ERROR9", + "SILENT" + }; + } + + std::map<std::string, unsigned> log_levels_map() { + return { + {DEBUG::name(), DEBUG::level}, + {DEBUG0::name(), DEBUG0::level}, + {DEBUG1::name(), DEBUG1::level}, + {DEBUG2::name(), DEBUG2::level}, + {DEBUG3::name(), DEBUG3::level}, + {DEBUG4::name(), DEBUG4::level}, + {DEBUG5::name(), DEBUG5::level}, + {DEBUG6::name(), DEBUG6::level}, + {DEBUG7::name(), DEBUG7::level}, + {DEBUG8::name(), DEBUG8::level}, + {DEBUG9::name(), DEBUG9::level}, + {MESSAGE::name(), MESSAGE::level}, + {MESSAGE0::name(), MESSAGE0::level}, + {MESSAGE1::name(), MESSAGE1::level}, + {MESSAGE2::name(), MESSAGE2::level}, + {MESSAGE3::name(), MESSAGE3::level}, + {MESSAGE4::name(), MESSAGE4::level}, + {MESSAGE5::name(), MESSAGE5::level}, + {MESSAGE6::name(), MESSAGE6::level}, + {MESSAGE7::name(), MESSAGE7::level}, + {MESSAGE8::name(), MESSAGE8::level}, + {MESSAGE9::name(), MESSAGE9::level}, + {WARNING::name(), WARNING::level}, + {WARNING0::name(), WARNING0::level}, + {WARNING1::name(), WARNING1::level}, + {WARNING2::name(), WARNING2::level}, + {WARNING3::name(), WARNING3::level}, + {WARNING4::name(), WARNING4::level}, + {WARNING5::name(), WARNING5::level}, + {WARNING6::name(), WARNING6::level}, + {WARNING7::name(), WARNING7::level}, + {WARNING8::name(), WARNING8::level}, + {WARNING9::name(), WARNING9::level}, + {ERROR::name(), ERROR::level}, + {ERROR0::name(), ERROR0::level}, + {ERROR1::name(), ERROR1::level}, + {ERROR2::name(), ERROR2::level}, + {ERROR3::name(), ERROR3::level}, + {ERROR4::name(), ERROR4::level}, + {ERROR5::name(), ERROR5::level}, + {ERROR6::name(), ERROR6::level}, + {ERROR7::name(), ERROR7::level}, + {ERROR8::name(), ERROR8::level}, + {ERROR9::name(), ERROR9::level}, + {SILENT::name(), SILENT::level} + }; + } + } #endif -- GitLab From bc3db572accadf7136819cebb426802d2901566c Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 15:32:53 +0200 Subject: [PATCH 04/15] Removed logbuffer.h --- .../source/include/streamlog/logbuffer.h | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 streamlog/source/include/streamlog/logbuffer.h diff --git a/streamlog/source/include/streamlog/logbuffer.h b/streamlog/source/include/streamlog/logbuffer.h deleted file mode 100644 index 57e7aea..0000000 --- a/streamlog/source/include/streamlog/logbuffer.h +++ /dev/null @@ -1,73 +0,0 @@ -// -*- mode: c++; -#ifndef logbuffer_h -#define logbuffer_h - -#include <sstream> -#include <cstdio> -#include "streamlog/logstream.h" - -namespace streamlog{ - - - /** Helper class that adds a prefix to every new line that is written to its - * std::ostream. - * - * @author F. Gaede, DESY - * @version $Id: logbuffer.h,v 1.1.1.1 2007-07-12 17:14:48 gaede Exp $ - */ - class logbuffer : public std::streambuf { - - std::streambuf* _sbuf = nullptr ; - logstream* _ls = nullptr ; - - logbuffer(); - - - public: - logbuffer(const logbuffer&) = delete ; - logbuffer& operator=(const logbuffer&) = delete ; - logbuffer( std::streambuf* sbuf, logstream* logstream ) : _sbuf( sbuf ), _ls(logstream) {} - - ~logbuffer() { - - } - - - /** This is where the logstream's current prefix is added to every - * new line written to the std::ostream that has this logbuffer. - * Idea taken from J.Samson, DESY. - */ - inline virtual int overflow( int c = EOF ) { - - static bool hasNewLine = true ; - - if ( c == EOF ) - return EOF ; - - bool success = true; - - if ( hasNewLine == true ) { - - std::string pre = (* _ls->prefix() )() ; - - success &= ( (unsigned) _sbuf->sputn( pre.c_str() , pre.size() ) == pre.size() ) ; - - hasNewLine = false; - } - - if ( c == '\n' ) - hasNewLine = true; - - if ( success ) - success &= ( _sbuf->sputc(c) != EOF ) ; - - if( success ) - return 0 ; - - return EOF ; - } - - } ; - -} -#endif -- GitLab From 8d0f5ac98d6be7d5556327330fa86ced184852f1 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 15:37:16 +0200 Subject: [PATCH 05/15] Turned the prefix base+impl classes to a simple std function --- streamlog/source/include/streamlog/prefix.h | 58 ++++----------------- streamlog/source/src/prefix.cc | 10 ---- 2 files changed, 11 insertions(+), 57 deletions(-) delete mode 100644 streamlog/source/src/prefix.cc diff --git a/streamlog/source/include/streamlog/prefix.h b/streamlog/source/include/streamlog/prefix.h index 2c0381b..f3378ce 100644 --- a/streamlog/source/include/streamlog/prefix.h +++ b/streamlog/source/include/streamlog/prefix.h @@ -2,56 +2,20 @@ #ifndef prefix_h #define prefix_h -#include <sstream> +#include <functional> +#include <string> -namespace streamlog{ - - class logscope ; - class logstream ; - - - /** Base class for log message prefix formating. Subclasses need to define - * std::string operator()() using this->_levelName and this->_name. - * - * @author F. Gaede, DESY - * @version $Id: prefix.h,v 1.1.1.1 2007-07-12 17:14:48 gaede Exp $ - */ - class prefix_base{ - - friend class logscope ; - friend class logstream ; - - protected: - std::string _name{} ; - std::string _levelName{} ; - - public: - prefix_base() ; - virtual ~prefix_base() ; - - virtual std::string operator()() =0 ; +namespace streamlog { - void setLevelName( const std::string& lName) { _levelName = lName ; } + using logprefix = std::function<std::string()> ; - }; - - /** Default log message prefix: [ LevelName "ScopeName"]. - * - * @author F. Gaede, DESY - * @version $Id: prefix.h,v 1.1.1.1 2007-07-12 17:14:48 gaede Exp $ + /** Generate a simple prefix function based on a logger name and log level */ - - class prefix : public prefix_base{ - public: - virtual std::string operator()() { - std::stringstream ss ; - ss << "[ " << this->_levelName << " \"" << this->_name << "\"] " ; - return ss.str() ; - } - - }; - - - + inline static logprefix std_prefix( std::string name, std::string level ) { + return [name=std::move(name),level=std::move(level)](){ + return "[ " + level + " \"" + name + "\"] " ; + }; + } } + #endif diff --git a/streamlog/source/src/prefix.cc b/streamlog/source/src/prefix.cc deleted file mode 100644 index cff1208..0000000 --- a/streamlog/source/src/prefix.cc +++ /dev/null @@ -1,10 +0,0 @@ -#include "streamlog/prefix.h" - -namespace streamlog{ - - prefix_base::prefix_base() : _name("UNKOWN") , _levelName("VERBOSE") { - } - - prefix_base::~prefix_base() { - } -} -- GitLab From e7da03caeb693f92a6ab0c6a965861bbcb319e32 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 15:40:08 +0200 Subject: [PATCH 06/15] Added printthread internal classes for thread safe impl of buffers --- .../source/include/streamlog/logstream.h | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/streamlog/source/include/streamlog/logstream.h b/streamlog/source/include/streamlog/logstream.h index f34a0f0..6207cc4 100644 --- a/streamlog/source/include/streamlog/logstream.h +++ b/streamlog/source/include/streamlog/logstream.h @@ -6,12 +6,54 @@ #include <iostream> #include <map> +#include <mutex> +#include <sstream> namespace streamlog{ class prefix_base ; class logbuffer ; class logscope ; + + class logconfig ; + + /** Thread safe helper class that collects streamed data + * and sends it to the actual ostream on deletion. + * Idea taken from https://stackoverflow.com/questions/14718124 + * @author F,Gaede, DESY + * @date Apr 2020 + */ + class printthread: public std::stringstream { + protected: + std::string _pref{}; + std::ostream* _o = nullptr ; + public: + printthread(const printthread &) = delete ; + printthread &operator=(const printthread &) = delete ; + /// returns an invalid stream + printthread() : printthread::basic_ios( 0 ) {} ; + /// initialize w/ prefix and final ostream + printthread(std::string prefix,std::ostream* o):_pref(prefix),_o(o){}; + /// copy c'tor copies stringbugf from local stringstream + printthread( printthread&& rhs ){ + printthread::basic_ostream<char>( rhs.rdbuf() ) ; + } + /// on deletion we actually write to the output + ~printthread(){ + if( nullptr == _o ) { + return ; + } + // prepend the prefix to every newline before dumping to ostream + std::ostringstream oss; + for (std::string line; std::getline(*this, line, '\n');) { + oss << _pref << line << '\n'; + } + std::lock_guard<std::mutex> guard(_mutexPrint); + *_o << oss.str() ; + } + private: + static std::mutex _mutexPrint; + }; /** Class defining a log stream that is used to print log messages depending @@ -52,8 +94,7 @@ namespace streamlog{ class logstream { friend class logscope ; - friend class logbuffer ; - + friend class logconfig ; typedef std::map< std::string, unsigned > LevelMap ; -- GitLab From 14cac0af88af3d273f915cd9b25a0e4ead32bc6c Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 15:40:46 +0200 Subject: [PATCH 07/15] Missing reference --- streamlog/source/include/streamlog/logstream.h | 2 +- streamlog/source/src/logstream.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/streamlog/source/include/streamlog/logstream.h b/streamlog/source/include/streamlog/logstream.h index 6207cc4..4bf757b 100644 --- a/streamlog/source/include/streamlog/logstream.h +++ b/streamlog/source/include/streamlog/logstream.h @@ -110,7 +110,7 @@ namespace streamlog{ * Only first call to this method has an effect, subsequent calls are ignored. * */ - void init( std::ostream& os , const std::string name ) ; + void init( std::ostream& os , const std::string &name ) ; /** True if next log message of the current level (class T ) will be written, i.e. * the next call to std::ostream& operator()() will return a valid outstream. diff --git a/streamlog/source/src/logstream.cc b/streamlog/source/src/logstream.cc index 835b63a..a30c729 100644 --- a/streamlog/source/src/logstream.cc +++ b/streamlog/source/src/logstream.cc @@ -38,7 +38,7 @@ namespace streamlog{ } } - void logstream::init( std::ostream& os , const std::string name ) { + void logstream::init( std::ostream& os , const std::string &name ) { static bool first=true ; -- GitLab From 75cc129498e179368879c824cd1c506ba048462f Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 18 Jun 2020 16:06:38 +0200 Subject: [PATCH 08/15] Added printthread to operator() implementation --- streamlog/source/include/streamlog/logstream.h | 2 +- streamlog/source/src/logstream.cc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/streamlog/source/include/streamlog/logstream.h b/streamlog/source/include/streamlog/logstream.h index 4bf757b..54b1b76 100644 --- a/streamlog/source/include/streamlog/logstream.h +++ b/streamlog/source/include/streamlog/logstream.h @@ -137,7 +137,7 @@ namespace streamlog{ /** Return the actual std::ostream where the message should be writen to - will return * a nullstream unless prepended by a successfull call to write<MESSAGELEVEL>() */ - std::ostream& operator()() ; + printthread operator()() ; /** Adds a level name to the stream for setting the log level with a string through * a scope class. Only names added with this method will have an effect - other log diff --git a/streamlog/source/src/logstream.cc b/streamlog/source/src/logstream.cc index a30c729..ee03bea 100644 --- a/streamlog/source/src/logstream.cc +++ b/streamlog/source/src/logstream.cc @@ -77,16 +77,16 @@ namespace streamlog{ } - std::ostream& logstream::operator()() { + printthread logstream::operator()() { if( _active && _os ) { _active = false ; - return *_os ; + return printthread{(_prefix)(),_os} ; } else - return *_ns ; + return printthread{} ; } -- GitLab From de470178fdf2ad155f4691dfd57ad3d24f4bfb59 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 2 Sep 2020 11:26:42 +0200 Subject: [PATCH 09/15] Fixed include directory in source code --- streamlog/source/include/streamlog/streamlog.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/streamlog/source/include/streamlog/streamlog.h b/streamlog/source/include/streamlog/streamlog.h index 0b785cb..304926b 100644 --- a/streamlog/source/include/streamlog/streamlog.h +++ b/streamlog/source/include/streamlog/streamlog.h @@ -18,9 +18,9 @@ #ifdef USE_STREAMLOG -#include "logstream.h" -#include "logscope.h" -#include "loglevels.h" +#include "streamlog/logstream.h" +#include "streamlog/logscope.h" +#include "streamlog/loglevels.h" #define streamlog_level( MLEVEL ) ( streamlog::out.would_write< streamlog::MLEVEL >() ) -- GitLab From 6eec96413aed934e08e4b10d3b87db4a2aa8506f Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 2 Sep 2020 14:32:04 +0200 Subject: [PATCH 10/15] Cleanup: indent, blanklines, ... --- streamlog/source/include/streamlog/logscope.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/streamlog/source/include/streamlog/logscope.h b/streamlog/source/include/streamlog/logscope.h index cc93307..4715bb9 100644 --- a/streamlog/source/include/streamlog/logscope.h +++ b/streamlog/source/include/streamlog/logscope.h @@ -29,19 +29,17 @@ namespace streamlog{ /** Instantiate a scope object for the given logstream. */ - logscope(logstream& ls) : _ls(&ls) , _name("") , _level(-1) { } + logscope(logstream& ls) : _ls(&ls) , _name("") , _level(-1) { /* nop */ } /** Reset old name and level if set through this object. */ - ~logscope(){ - + ~logscope() { if( _name.size() > 0 ) { - _ls->prefix()->_name=_name ; - //std::cerr << " ~logscope() reset name to " << _name << std::endl ; - + _ls->prefix()->_name=_name ; + //std::cerr << " ~logscope() reset name to " << _name << std::endl ; } if( _level > -1 ) - _ls->setLevel( _level ) ; + _ls->setLevel( _level ) ; } /** Change current log scope name for the lifetime of this object */ @@ -70,7 +68,6 @@ namespace streamlog{ logstream* _ls = nullptr ; std::string _name{} ; long _level{}; - }; } -- GitLab From 46c876aaf5e7ce31aa293964669113eb40c254ed Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Thu, 10 Sep 2020 11:07:08 +0200 Subject: [PATCH 11/15] Added logconfig class --- streamlog/source/src/logstream.cc | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/streamlog/source/src/logstream.cc b/streamlog/source/src/logstream.cc index ee03bea..51be753 100644 --- a/streamlog/source/src/logstream.cc +++ b/streamlog/source/src/logstream.cc @@ -5,6 +5,40 @@ namespace streamlog{ + class logconfig { + using LevelMap = logstream::LevelMap ; + public: + logconfig() = default ; + ~logconfig() = default ; + logconfig &operator=( const logconfig & ) = delete ; + logconfig( const logconfig & ) = delete ; + + void init( std::ostream *os, const std::string &name, const std::string &levelName, const LevelMap &levels ) { + std::lock_guard <std::mutex> lock( _mutex ) ; + _stream = os ; + _name = name ; + _levelName = levelName ; + _levelMap = levels ; + } + + void apply( logstream *ls ) { + std::lock_guard <std::mutex> lock( _mutex ) ; + ls->set_stream( _stream ) ; + ls->set_name( _name ) ; + ls->set_level( _levelName ) ; + if( not _levelMap.empty() ) { + ls->_map = _levelMap ; + } + } + + private: + std::mutex _mutex {} ; + std::ostream *_stream {&std::cout} ; + std::string _name {"Main"} ; + std::string _levelName {MESSAGE::name()} ; + LevelMap _levelMap {} ; + }; + logstream::logstream() : _ns( new nullstream ) , _os(0) , -- GitLab From b3f3a371674321601d6929e64d2dcf5a9fe7ba93 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 16 Sep 2020 14:14:05 +0200 Subject: [PATCH 12/15] Thread-safe re-implementation of logstream class. Updated Doxygen documentation accordingly --- .../source/include/streamlog/logstream.h | 133 ++++++++++++------ streamlog/source/src/logstream.cc | 118 +++++++--------- 2 files changed, 140 insertions(+), 111 deletions(-) diff --git a/streamlog/source/include/streamlog/logstream.h b/streamlog/source/include/streamlog/logstream.h index 54b1b76..ec08828 100644 --- a/streamlog/source/include/streamlog/logstream.h +++ b/streamlog/source/include/streamlog/logstream.h @@ -3,6 +3,7 @@ #define logstream_h #include "streamlog/prefix.h" +#include "streamlog/loglevels.h" #include <iostream> #include <map> @@ -59,11 +60,12 @@ namespace streamlog{ /** Class defining a log stream that is used to print log messages depending * on current log level. Can be initialized with any std::ostream, typically either * std::cout or an std::ofstream file handler. - * There is one global instance of this class defined in the library: logstream::out <br> + * There is one global (thread local) instance of this class defined in the + * library: logstream::out <br> * Typically only this instance is needed, e.g.: <br> * <pre> * // in int main() : - * streamlog::out.init( std::cout, argv[0] ) ; + * streamlog::global_init( std::cout, argv[0], streamlog::MESSAGE::name() ) ; * * //... * @@ -89,6 +91,7 @@ namespace streamlog{ * @see logscope * * @author F. Gaede, DESY + * @author R. Ete, DESY (2020) * @version $Id: logstream.h,v 1.3 2007-08-08 13:08:34 gaede Exp $ */ class logstream { @@ -101,9 +104,37 @@ namespace streamlog{ public : logstream() ; + logstream( logconfig *lcfg ) ; logstream(const logstream&) = delete ; logstream& operator=(const logstream&) = delete ; - ~logstream() ; + ~logstream() = default ; + + /** Initialize the global configuration. + * This sets the global logger settings (stream, name, current level, levels map) + * that are shared by all thread local instances. On new thread creation, the global + * thread local logger instance receives the global configuration in its constructor. + */ + static void global_init( std::ostream *os, const std::string &name, + const std::string &levelName, const LevelMap &lm = streamlog::log_levels_map() ) ; + + /** Get the map of builtin log levels + */ + static LevelMap builtin_levels() ; + + /** Set the stream for this logger + */ + void set_stream( std::ostream *os ) { + if( nullptr != os ) { + _os = os ; + } + } + + /** Set the logger name + */ + void set_name( const std::string &n ) { + _name = n ; + update_prefix() ; + } /** Initialize the logstream with an std::ostream, e.g. std::cout and * the main scope name, e.g. argv[0]. @@ -116,11 +147,10 @@ namespace streamlog{ * the next call to std::ostream& operator()() will return a valid outstream. * */ - template<class T> + template <class T> inline bool write() { - // dont' call chek_level if T::active == false - return ( T::active && check_level<T>() ) ; + return ( T::active && check_level<T>() ) ; } /** True if next log message of the current level (class T ) would be written @@ -128,10 +158,9 @@ namespace streamlog{ * the outstream. * */ - template<class T> + template <class T> inline bool would_write() { - - return ( T::active && T::level >= _level ) ; + return ( T::active && T::level >= _level ) ; } /** Return the actual std::ostream where the message should be writen to - will return @@ -149,60 +178,82 @@ namespace streamlog{ */ template <class T> void addLevelName() { - _map[ T::name() ] = T::level ; } - - - // interface for friend classes: scope and logbuffer - + + /** Get the logger name + */ + const std::string &name() const { + return _name ; + } + + /** Get the logger name + */ + unsigned int level() const { + return _level ; + } + protected: - + + template <class T> + void set_level() { + set_level( T::name() ) ; + } + /** Set the current level - user need to use a streamlog::logscope object * to do this. */ - void setLevel( unsigned level ) { _level = level ; } + void set_level( unsigned int level ) { + _level = level ; + } /** Set the current level through its name - only level previously made known to * the stream through addLevelName will have an effect. */ - unsigned setLevel( const std::string& levelName ) ; + unsigned int set_level( const std::string& levelName ) ; - /** Returns the prefix for the logbuffer object */ - prefix_base* prefix() { return _prefix ; } + /** Get the log prefix formatter + */ + logprefix &prefix() { + return _prefix ; + } /** used internally by write<T> */ template<class T> bool check_level() { - - if( T::level >= _level ){ - _active = true ; - _prefix->_levelName = T::name() ; + if( T::level >= _level ) { + _active = true ; + _levelName = T::name() ; + update_prefix() ; } return _active ; } + /** Used internally to update the prefix before writing + */ + void update_prefix() { + _prefix = std_prefix( _name, _levelName ) ; + } private: + /// Wrapper for actual ostream + std::ostream *_os = {&std::cout} ; + /// The logger name + std::string _name {"Main"} ; + /// The log level name + std::string _levelName {MESSAGE::name()} ; + /// current log level + unsigned int _level {MESSAGE::level} ; + /// boolean helper + bool _active {false} ; + /// prefix formatter + logprefix _prefix {} ; + /// string map of level names + LevelMap _map {}; + }; - /** Private helper class returned if message log level not reached */ - class nullstream : public std::ostream { - public: - nullstream() : std::ios( 0 ), std::ostream( 0 ) {} ; - } ; - - - nullstream* _ns = nullptr ; // the nullstream - std::ostream* _os = nullptr ; // wrapper for actual ostream - unsigned _level {}; // current log level - bool _active {}; // boolean helper - logbuffer* _lb = nullptr ; // log buffer adds prefix to everu log message - prefix_base* _prefix= nullptr ; // prefix formatter - LevelMap _map {}; // string map of level names - - } ; - - extern logstream out ; + /// The thread local global instance of a logstream object + extern thread_local logstream out ; } #endif diff --git a/streamlog/source/src/logstream.cc b/streamlog/source/src/logstream.cc index 51be753..0dadb4d 100644 --- a/streamlog/source/src/logstream.cc +++ b/streamlog/source/src/logstream.cc @@ -1,10 +1,15 @@ #include "streamlog/logstream.h" - -#include "streamlog/logbuffer.h" #include "streamlog/prefix.h" namespace streamlog{ + /** + * @brief logconfig class. + * Private class managing the global logger configuration. + * Each time a thread is created, a thread local instance of a + * logstream is created and takes it's configuration from this + * global config. A mutex is used to ensure the thread safety. + */ class logconfig { using LevelMap = logstream::LevelMap ; public: @@ -13,6 +18,8 @@ namespace streamlog{ logconfig &operator=( const logconfig & ) = delete ; logconfig( const logconfig & ) = delete ; + /** Initialize the logconfig object + */ void init( std::ostream *os, const std::string &name, const std::string &levelName, const LevelMap &levels ) { std::lock_guard <std::mutex> lock( _mutex ) ; _stream = os ; @@ -21,6 +28,8 @@ namespace streamlog{ _levelMap = levels ; } + /** Apply the logconfig data to the logstream object + */ void apply( logstream *ls ) { std::lock_guard <std::mutex> lock( _mutex ) ; ls->set_stream( _stream ) ; @@ -32,98 +41,67 @@ namespace streamlog{ } private: + /// Synchronisation mutex std::mutex _mutex {} ; + /// The global shared ostream std::ostream *_stream {&std::cout} ; + /// The global logger name std::string _name {"Main"} ; + /// The global log level name std::string _levelName {MESSAGE::name()} ; + /// The global map of possible log levels LevelMap _levelMap {} ; }; - logstream::logstream() : - _ns( new nullstream ) , - _os(0) , - _level(0) , - _active(false) , - _lb(0), - _prefix( new streamlog::prefix) { - - } - - logstream::~logstream() { - - if( _ns ){ - delete _ns ; - _ns = NULL ; - } - - if( _os ){ - delete _os ; - _os = NULL ; - } - - if( _lb ){ - delete _lb ; - _lb = NULL ; - } - - if( _prefix ){ - delete _prefix ; - _prefix = NULL ; - } + logstream::logstream() { + update_prefix() ; } - void logstream::init( std::ostream& os , const std::string &name ) { - - static bool first=true ; - - if( first && os ) { - - // _name = name ; - - // create a new log buffer and attach this to a wrapper to the given ostream - - _lb = new logbuffer( os.rdbuf() , this ) ; - - _os = new std::ostream( _lb ) ; - - //attach also the original stream to the logger... - //os.rdbuf( _lb ) ; // this needs some work - // FIXME : this needs to go to the c'tor !!!! - - _prefix->_name = name ; - - first = false ; - } - - else if( !os) { - std::cerr << "ERROR: logstream::init() invalid ostream given " << std::endl ; - } + logstream::logstream( logconfig *lcfg ) { + update_prefix() ; + lcfg->apply( this ) ; + } + + void logstream::init( std::ostream& os , const std::string &nam ) { + set_stream( &os ) ; + set_name( nam ) ; } - unsigned logstream::setLevel( const std::string& levelName ) { - + unsigned logstream::set_level( const std::string& levelName ) { unsigned l = _level ; LevelMap::iterator it = _map.find( levelName ) ; if( it != _map.end() ) { _level = it->second ; + _levelName = levelName ; + update_prefix() ; } return l ; } printthread logstream::operator()() { - if( _active && _os ) { - _active = false ; - - return printthread{(_prefix)(),_os} ; + return printthread {(_prefix)(),_os} ; + } + else { + return printthread {} ; } - else - return printthread{} ; - } - /** global instance of logstream */ - logstream out ; + // static global logger config, protected by mutex + // shared by thread local global logger instances + static logconfig __logconfig__ ; + + /** thread global instance of logstream */ + thread_local logstream out( &__logconfig__ ) ; + + /// global mutex for actual io + std::mutex printthread::_mutexPrint{}; + + void logstream::global_init( std::ostream *os, const std::string &name, const std::string &levelName, const LevelMap &lm ) { + __logconfig__.init( os, name, levelName, lm ) ; + __logconfig__.apply( &out ) ; + } + } -- GitLab From 18e5069402bc74ddd932b4f3f69aec1cd5e47451 Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 16 Sep 2020 15:07:23 +0200 Subject: [PATCH 13/15] Updated logscope with new logstream implementation --- streamlog/source/include/streamlog/logscope.h | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/streamlog/source/include/streamlog/logscope.h b/streamlog/source/include/streamlog/logscope.h index 4715bb9..24297e7 100644 --- a/streamlog/source/include/streamlog/logscope.h +++ b/streamlog/source/include/streamlog/logscope.h @@ -35,25 +35,24 @@ namespace streamlog{ */ ~logscope() { if( _name.size() > 0 ) { - _ls->prefix()->_name=_name ; - //std::cerr << " ~logscope() reset name to " << _name << std::endl ; + _ls->set_name( _name ) ; } if( _level > -1 ) - _ls->setLevel( _level ) ; + _ls->set_level( _level ) ; } /** Change current log scope name for the lifetime of this object */ - void setName(const std::string name) { - _name = _ls->prefix()->_name ; - _ls->prefix()->_name = name ; + void setName(const std::string &name) { + _name = _ls->name() ; + _ls->set_name( name ) ; } /** Change current log level for the lifetime of this object */ template <class T> void setLevel(){ - _level = _ls->_level ; - _ls->setLevel( T::level ) ; + _level = _ls->level() ; + _ls->set_level( T::level ) ; } /** Change current log level for the lifetime of this object @@ -61,13 +60,13 @@ namespace streamlog{ * via logstream::addLevelName - otherwise the call will have no effect. */ void setLevel(const std::string& level){ - _level = _ls->setLevel( level ) ; + _level = _ls->set_level( level ) ; } protected: - logstream* _ls = nullptr ; - std::string _name{} ; - long _level{}; + logstream *_ls = nullptr ; + std::string _name {} ; + int _level {-1} ; }; } -- GitLab From e1b71c9abe8c6951c052275bf90768fff4e540da Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 16 Sep 2020 15:08:08 +0200 Subject: [PATCH 14/15] Added two examples for streamlog: std and MT --- example/CMakeLists.txt | 7 +++++ example/streamlog.cc | 36 +++++++++++++++++++++++++ example/streamlog_mt.cc | 58 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 example/streamlog.cc create mode 100644 example/streamlog_mt.cc diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 0f379e4..c4192ef 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -13,6 +13,7 @@ INCLUDE( ilcsoft_default_settings ) FIND_PACKAGE( ILCTEST REQUIRED ) FIND_PACKAGE( streamlog REQUIRED ) +FIND_PACKAGE( Threads REQUIRED ) INCLUDE( ilcsoft_default_enable_ctest ) @@ -21,3 +22,9 @@ INCLUDE( MacroAddILCTest ) ADD_ILCTEST( hello_world ./hello_world.cc ) +ADD_EXECUTABLE( streamlog ./streamlog.cc ) +TARGET_LINK_LIBRARIES( streamlog ${streamlog_LIBRARIES} ) + +ADD_EXECUTABLE( streamlog_mt ./streamlog_mt.cc ) +TARGET_LINK_LIBRARIES( streamlog_mt ${streamlog_LIBRARIES} Threads::Threads ) + diff --git a/example/streamlog.cc b/example/streamlog.cc new file mode 100644 index 0000000..2aa00fa --- /dev/null +++ b/example/streamlog.cc @@ -0,0 +1,36 @@ + +#include <streamlog/streamlog.h> + +using namespace streamlog ; + +int main(int argc, char **argv) { + + logstream::global_init( &std::cout, argv[0], MESSAGE::name() ) ; + + // Standard printing + streamlog_out(DEBUG) << "This is not printed" << std::endl ; + streamlog_out(MESSAGE) << "This is a message" << std::endl ; + streamlog_out(WARNING) << "This is a warning" << std::endl ; + streamlog_out(ERROR) << "This is an error" << std::endl ; + + // logscope test + { + streamlog::logscope scope( streamlog::out ) ; + scope.setName( "ScopedLogging" ) ; + scope.setLevel< streamlog::MESSAGE3 >() ; + + // Standard printing + streamlog_out(DEBUG) << "This is not printed" << std::endl ; + streamlog_out(MESSAGE) << "This is not printed" << std::endl ; + streamlog_out(WARNING) << "This is a warning" << std::endl ; + streamlog_out(ERROR) << "This is an error" << std::endl ; + } + + // Standard printing after logscope + streamlog_out(DEBUG) << "This is not printed" << std::endl ; + streamlog_out(MESSAGE) << "This is a message" << std::endl ; + streamlog_out(WARNING) << "This is a warning" << std::endl ; + streamlog_out(ERROR) << "This is an error" << std::endl ; + + return 0 ; +} \ No newline at end of file diff --git a/example/streamlog_mt.cc b/example/streamlog_mt.cc new file mode 100644 index 0000000..0f08131 --- /dev/null +++ b/example/streamlog_mt.cc @@ -0,0 +1,58 @@ + +#include <streamlog/streamlog.h> + +#include <thread> +#include <vector> + +using namespace streamlog ; + +void print() { + // Standard printing + streamlog_out(DEBUG) << "This is a debug" << std::endl ; + streamlog_out(MESSAGE) << "This is a message" << std::endl ; + streamlog_out(WARNING) << "This is a warning" << std::endl ; + streamlog_out(ERROR) << "This is an error" << std::endl ; +} + +void print_thread( int n=0 ) { + for(int i=0 ; i<n ; ++i) { + streamlog_out(DEBUG) << "Thread: " << std::this_thread::get_id() << ", i:" << i << ")" << std::endl ; + streamlog_out(MESSAGE) << "Thread: " << std::this_thread::get_id() << ", i:" << i << ")" << std::endl ; + streamlog_out(WARNING) << "Thread: " << std::this_thread::get_id() << ", i:" << i << ")" << std::endl ; + streamlog_out(ERROR) << "Thread: " << std::this_thread::get_id() << ", i:" << i << ")" << std::endl ; + } +} + +int main(int argc, char **argv) { + + logstream::global_init( &std::cout, "global", MESSAGE::name() ) ; + const unsigned int nthr = argc > 1 ? atoi( argv[1] ) : std::thread::hardware_concurrency() ; + + print() ; + + std::vector<std::thread> threads {nthr} ; + + for( auto &thread : threads ) { + thread = std::thread( [](){ + // Standard printing test + print_thread( 10 ) ; + // Change logger scope inside the thread + // This affects the thread local global logger streamlog::out + { + streamlog::logscope scope( streamlog::out ) ; + std::stringstream ss ; ss << "scope_" << std::this_thread::get_id() ; + scope.setName( ss.str() ) ; + scope.setLevel< streamlog::MESSAGE3 >() ; + print_thread( 10 ) ; + } + // standard printing test again after logscope + print_thread( 10 ) ; + } ) ; + } + + for ( auto &thread : threads ) { + thread.join() ; + } + + return 0 ; +} \ No newline at end of file -- GitLab From ae8b0463a6c4780d6a0d25cc88112c3e1b9c0a6c Mon Sep 17 00:00:00 2001 From: Remi Ete <remi.ete@gmail.com> Date: Wed, 16 Sep 2020 15:33:53 +0200 Subject: [PATCH 15/15] Added pre-processor flag for new implementation --- streamlog/source/include/streamlog/streamlog.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/streamlog/source/include/streamlog/streamlog.h b/streamlog/source/include/streamlog/streamlog.h index 304926b..049cdbd 100644 --- a/streamlog/source/include/streamlog/streamlog.h +++ b/streamlog/source/include/streamlog/streamlog.h @@ -18,6 +18,8 @@ #ifdef USE_STREAMLOG +#define STREAMLOG_NEW_TS_IMPL 1 + #include "streamlog/logstream.h" #include "streamlog/logscope.h" #include "streamlog/loglevels.h" -- GitLab