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