// Copyright (C) 2006 Davis E. King (davisking@users.sourceforge.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_LOGGER_KERNEl_1_ #define DLIB_LOGGER_KERNEl_1_ #include "../threads.h" #include "../misc_api.h" #include "../set.h" #include "logger_kernel_abstract.h" #include <limits> #include <cstring> #include "../algs.h" #include "../assert.h" #include "../uintn.h" #include "../map.h" #include "../smart_pointers.h" namespace dlib { // ---------------------------------------------------------------------------------------- class log_level { public: log_level( int priority_, const char* name_ ) : priority(priority_) { strncpy(name,name_,19); name[19] = '\0'; } int priority; char name[20]; }; const log_level LALL (std::numeric_limits<int>::min(),"ALL"); const log_level LNONE (std::numeric_limits<int>::max(),"NONE"); const log_level LTRACE(-100,"TRACE"); const log_level LDEBUG(0 ,"DEBUG"); const log_level LINFO (100,"INFO "); const log_level LWARN (200,"WARN "); const log_level LERROR(300,"ERROR"); const log_level LFATAL(400,"FATAL"); // ---------------------------------------------------------------------------------------- void set_all_logging_output_streams ( std::ostream& out ); void set_all_logging_levels ( const log_level& new_level ); // ---------------------------------------------------------------------------------------- void print_default_logger_header ( std::ostream& out, const std::string& logger_name, const log_level& l, const uint64 thread_id ); // ---------------------------------------------------------------------------------------- class logger { /*! INITIAL VALUE - print_header == print_default_logger_header - out.rdbuf() == std::cout.rdbuf() - cur_level == LERROR - auto_flush_enabled == true CONVENTION - print_header == logger_header() - out.rdbuf() == output_streambuf() - cur_level == level() - logger_name == name() - auto_flush_enabled == auto_flush() - logger::gd::loggers == a set containing all currently existing loggers. - logger::gd::m == the mutex used to lock everything in the logger - logger::gd::thread_names == a map of thread ids to thread names. - logger::gd::next_thread_name == the next thread name that will be given out to a thread when we find that it isn't already in thread_names. !*/ class logger_stream { /*! INITIAL VALUE - been_used == false CONVENTION - enabled == is_enabled() - if (been_used) then - logger::gd::m is locked - someone has used the << operator to write something to the output stream. !*/ public: logger_stream ( const log_level& l_, logger& log_ ) : l(l_), log(log_), been_used(false), enabled (l.priority >= log.cur_level.priority) {} inline ~logger_stream( ) { if (!been_used) { return; } else { print_end_of_line(); } } bool is_enabled ( ) const { return enabled; } template <typename T> inline logger_stream& operator << ( const T& item ) { if (!enabled) { return *this; } else { print_header_and_stuff(); log.out << item; return *this; } } private: void print_header_and_stuff ( ); /*! ensures - if (!been_used) then - prints the logger header - locks log.gd.m - #been_used == true !*/ void print_end_of_line ( ); /*! ensures - prints a newline to log.out - unlocks log.gd.m !*/ const log_level& l; logger& log; bool been_used; const bool enabled; }; friend class logger_stream; public: logger ( const char* name_ ); virtual ~logger ( ); const std::string& name ( ) const { return logger_name; } logger_stream operator << ( const log_level& l ) const { return logger_stream(l,const_cast<logger&>(*this)); } bool is_child_of ( const logger& log ) const { return (name().find(log.name() + ".") == 0) || (log.name() == name()); } const log_level level ( ) const { auto_mutex M(gd.m); return log_level(cur_level); }; void set_level ( const log_level& new_level ) { auto_mutex M(gd.m); gd.loggers.reset(); while (gd.loggers.move_next()) { if (gd.loggers.element()->is_child_of(*this)) gd.loggers.element()->cur_level = new_level; } gd.set_level(logger_name, new_level); } bool auto_flush ( ) const { auto_mutex M(gd.m); return auto_flush_enabled; }; void set_auto_flush ( bool enabled ) { auto_mutex M(gd.m); gd.loggers.reset(); while (gd.loggers.move_next()) { if (gd.loggers.element()->is_child_of(*this)) gd.loggers.element()->auto_flush_enabled = enabled; } gd.set_auto_flush(logger_name, enabled); } std::streambuf* output_streambuf ( ) { auto_mutex M(gd.m); return out.rdbuf(); } void set_output_stream ( std::ostream& out_ ) { auto_mutex M(gd.m); gd.loggers.reset(); while (gd.loggers.move_next()) { if (gd.loggers.element()->is_child_of(*this)) gd.loggers.element()->out.rdbuf(out_.rdbuf()); } gd.set_output_stream(logger_name, out_); } typedef void (*print_header_type)( std::ostream& out, const std::string& logger_name, const log_level& l, const uint64 thread_id ); print_header_type logger_header ( ) const { return print_header; } void set_logger_header ( print_header_type ph ) { auto_mutex M(gd.m); gd.loggers.reset(); while (gd.loggers.move_next()) { if (gd.loggers.element()->is_child_of(*this)) gd.loggers.element()->print_header = ph; } gd.set_logger_header(logger_name, ph); } private: struct global_data { rmutex m; set<logger*>::kernel_1b loggers; map<thread_id_type,uint64>::kernel_1b thread_names; uint64 next_thread_name; global_data ( ); ~global_data( ); uint64 get_thread_name ( ); /*! requires - m is locked ensures - returns a unique id for the calling thread. also makes the number small and nice unlike what you get from get_thread_id() !*/ void thread_end_handler ( ); /*! ensures - removes the terminated thread from thread_names !*/ struct level_container { level_container (); log_level val; map<std::string,scoped_ptr<level_container> >::kernel_1b_c table; } level_table; const log_level level ( const std::string& name ) const; /*! ensures - returns the level loggers with the given name are supposed to have !*/ void set_level ( const std::string& name, const log_level& new_level ); /*! ensures - for all children C of name: - #level(C) == new_level - if name == "" then - for all loggers L: - #level(L) == new_level !*/ struct auto_flush_container { bool val; map<std::string,scoped_ptr<auto_flush_container> >::kernel_1b_c table; } auto_flush_table; bool auto_flush ( const std::string& name ) const; /*! ensures - returns the auto_flush value loggers with the given name are supposed to have !*/ void set_auto_flush ( const std::string& name, bool enabled ); /*! ensures - for all children C of name: - #auto_flush_enabled(C) == enabled - if name == "" then - for all loggers L: - #auto_flush_enabled(L) == enabled !*/ struct output_streambuf_container { std::streambuf* val; map<std::string,scoped_ptr<output_streambuf_container> >::kernel_1b_c table; } streambuf_table; std::streambuf* output_streambuf ( const std::string& name ); /*! ensures - returns the streambuf loggers with the given name are supposed to have !*/ void set_output_stream ( const std::string& name, std::ostream& out_ ); /*! ensures - for all children C of name: - #output_streambuf(C) == out_.rdbuf() - if name == "" then - for all loggers L: - #output_streambuf(L) == out_.rdbuf() !*/ struct logger_header_container { print_header_type val; map<std::string,scoped_ptr<logger_header_container> >::kernel_1b_c table; } header_table; print_header_type logger_header ( const std::string& name ); /*! ensures - returns the header function loggers with the given name are supposed to have !*/ void set_logger_header ( const std::string& name, print_header_type ph ); /*! ensures - for all children C of name: - #logger_header(C) == ph - if name == "" then - for all loggers L: - #logger_header(L) == ph !*/ }; // end of struct global_data static global_data& get_global_data(); friend void set_all_logging_levels ( const log_level& new_level ); friend void set_all_logging_output_streams ( std::ostream& out ); global_data& gd; const std::string logger_name; print_header_type print_header; bool auto_flush_enabled; std::ostream out; log_level cur_level; // restricted functions logger(const logger&); // copy constructor logger& operator=(const logger&); // assignment operator }; // ---------------------------------------------------------------------------------------- } #ifdef NO_MAKEFILE #include "logger_kernel_1.cpp" #endif #endif // DLIB_LOGGER_KERNEl_1_