diff options
Diffstat (limited to '')
-rw-r--r-- | ml/dlib/dlib/logger/logger_kernel_1.h | 687 |
1 files changed, 687 insertions, 0 deletions
diff --git a/ml/dlib/dlib/logger/logger_kernel_1.h b/ml/dlib/dlib/logger/logger_kernel_1.h new file mode 100644 index 000000000..528bd6f67 --- /dev/null +++ b/ml/dlib/dlib/logger/logger_kernel_1.h @@ -0,0 +1,687 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LOGGER_KERNEl_1_ +#define DLIB_LOGGER_KERNEl_1_ + +#include <limits> +#include <memory> +#include <cstring> +#include <streambuf> +#include <vector> + +#include "../threads.h" +#include "../misc_api.h" +#include "../set.h" +#include "logger_kernel_abstract.h" +#include "../algs.h" +#include "../assert.h" +#include "../uintn.h" +#include "../map.h" +#include "../member_function_pointer.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + class log_level + { + public: + log_level( + int priority_, + const char* name_ + ) : + priority(priority_) + { + strncpy(name,name_,19); + name[19] = '\0'; + } + + bool operator< (const log_level& rhs) const { return priority < rhs.priority; } + bool operator<=(const log_level& rhs) const { return priority <= rhs.priority; } + bool operator> (const log_level& rhs) const { return priority > rhs.priority; } + bool operator>=(const log_level& rhs) const { return priority >= rhs.priority; } + + int priority; + char name[20]; + }; + + inline std::ostream& operator<< (std::ostream& out, const log_level& item) + { + out << item.name; + return out; + } + + 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 + ); + + typedef void (*print_header_type)( + std::ostream& out, + const std::string& logger_name, + const log_level& l, + const uint64 thread_id + ); + + void set_all_logging_headers ( + const print_header_type& new_header + ); + +// ---------------------------------------------------------------------------------------- + + void print_default_logger_header ( + std::ostream& out, + const std::string& logger_name, + const log_level& l, + const uint64 thread_id + ); + + template < + typename T + > + void set_all_logging_output_hooks ( + T& object, + void (T::*hook_)(const std::string& logger_name, + const log_level& l, + const uint64 thread_id, + const char* message_to_log) + ); + + template < + typename T + > + void set_all_logging_output_hooks ( + T& object + ) + { + set_all_logging_output_hooks(object, &T::log); + } + +// ---------------------------------------------------------------------------------------- + + class logger + { + /*! + INITIAL VALUE + - print_header == print_default_logger_header + - out.rdbuf() == std::cout.rdbuf() + - cur_level == LERROR + - auto_flush_enabled == true + - hook.is_set() == false + + CONVENTION + - print_header == logger_header() + - if (hook.is_set() == false) then + - out.rdbuf() == output_streambuf() + - else + - out.rdbuf() == &gd.hookbuf + - output_streambuf() == 0 + + - 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; + }; // end of class logger_stream + + // ------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------ + + friend class logger_stream; + public: + + typedef member_function_pointer<const std::string&, const log_level&, + const uint64, const char*> hook_mfp; + + logger ( + const std::string& 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); + + // if there is an output hook set then we are supposed to return 0. + if (hook) + return 0; + else + return out.rdbuf(); + } + + template < + typename T + > + void set_output_hook ( + T& object, + void (T::*hook_)(const std::string& logger_name, + const log_level& l, + const uint64 thread_id, + const char* message_to_log) + ) + { + auto_mutex M(gd.m); + hook.set(object, hook_); + + gd.loggers.reset(); + while (gd.loggers.move_next()) + { + if (gd.loggers.element()->is_child_of(*this)) + { + gd.loggers.element()->out.rdbuf(&gd.hookbuf); + gd.loggers.element()->hook = hook; + } + } + + gd.set_output_hook(logger_name, hook); + gd.set_output_stream(logger_name, gd.hookbuf); + } + + 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.loggers.element()->hook.clear(); + } + } + + gd.set_output_stream(logger_name, out_); + + hook.clear(); + gd.set_output_hook(logger_name, hook); + } + + 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; + + // Make a very simple streambuf that writes characters into a std::vector<char>. We can + // use this as the output target for hooks. The reason we don't just use a std::ostringstream + // instead is that this way we can be guaranteed that logging doesn't perform memory allocations. + // This is because a std::vector never frees memory. I.e. its capacity() doesn't go down when + // you resize it back to 0. It just stays the same. + class hook_streambuf : public std::streambuf + { + public: + std::vector<char> buffer; + int_type overflow ( int_type c) + { + if (c != EOF) buffer.push_back(static_cast<char>(c)); + return c; + } + + std::streamsize xsputn ( const char* s, std::streamsize num) + { + buffer.insert(buffer.end(), s, s+num); + return num; + } + }; + + hook_streambuf hookbuf; + + 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,std::unique_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,std::unique_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,std::unique_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() + !*/ + + void set_output_stream ( + const std::string& name, + std::streambuf& buf + ); + /*! + ensures + - for all children C of name: + - #output_streambuf(C) == &buf + - if name == "" then + - for all loggers L: + - #output_streambuf(L) == &buf + !*/ + + struct output_hook_container + { + hook_mfp val; + map<std::string,std::unique_ptr<output_hook_container> >::kernel_1b_c table; + } hook_table; + + hook_mfp output_hook ( + const std::string& name + ); + /*! + ensures + - returns the hook loggers with the given name are supposed + to have + !*/ + + void set_output_hook ( + const std::string& name, + const hook_mfp& hook + ); + /*! + ensures + - for all children C of name: + - #output_hook(C) == hook + - if name == "" then + - for all loggers L: + - #output_hook(L) == hook + !*/ + + struct logger_header_container + { + print_header_type val; + map<std::string,std::unique_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_headers ( + const print_header_type& new_header + ); + + friend void set_all_logging_output_streams ( + std::ostream& out + ); + + template < + typename T + > + friend void set_all_logging_output_hooks ( + T& object, + void (T::*hook_)(const std::string& logger_name, + const log_level& l, + const uint64 thread_id, + const char* message_to_log) + ) + { + logger::hook_mfp hook; + + // There is a bug in one of the versions (but not all apparently) of + // Visual studio 2005 that causes it to error out if <T> isn't in the + // following line of code. However, there is also a bug in gcc-3.3 + // that causes it to error out if <T> is present. So this works around + // this problem. +#if defined(_MSC_VER) && _MSC_VER == 1400 + hook.set<T>(object, hook_); +#else + hook.set(object, hook_); +#endif + + logger::global_data& gd = logger::get_global_data(); + auto_mutex M(gd.m); + gd.loggers.reset(); + while (gd.loggers.move_next()) + { + gd.loggers.element()->out.rdbuf(&gd.hookbuf); + gd.loggers.element()->hook = hook; + } + + gd.set_output_stream("",gd.hookbuf); + gd.set_output_hook("",hook); + } + + // ------------------------------------------------------------------------------------ + + global_data& gd; + + const std::string logger_name; + + print_header_type print_header; + bool auto_flush_enabled; + std::ostream out; + log_level cur_level; + + hook_mfp hook; + + + // 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_ + |