diff options
Diffstat (limited to 'ml/dlib/dlib/logger')
-rw-r--r-- | ml/dlib/dlib/logger/extra_logger_headers.cpp | 40 | ||||
-rw-r--r-- | ml/dlib/dlib/logger/extra_logger_headers.h | 41 | ||||
-rw-r--r-- | ml/dlib/dlib/logger/logger_config_file.cpp | 214 | ||||
-rw-r--r-- | ml/dlib/dlib/logger/logger_config_file.h | 135 | ||||
-rw-r--r-- | ml/dlib/dlib/logger/logger_kernel_1.cpp | 498 | ||||
-rw-r--r-- | ml/dlib/dlib/logger/logger_kernel_1.h | 687 | ||||
-rw-r--r-- | ml/dlib/dlib/logger/logger_kernel_abstract.h | 429 |
7 files changed, 2044 insertions, 0 deletions
diff --git a/ml/dlib/dlib/logger/extra_logger_headers.cpp b/ml/dlib/dlib/logger/extra_logger_headers.cpp new file mode 100644 index 000000000..becc1ab2b --- /dev/null +++ b/ml/dlib/dlib/logger/extra_logger_headers.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_EXTRA_LOGGER_HEADERs_CPP_ +#define DLIB_EXTRA_LOGGER_HEADERs_CPP_ + +#include "extra_logger_headers.h" +#include <ctime> +#include <cstring> + +// ---------------------------------------------------------------------------------------- + +namespace dlib +{ + + void print_datetime_logger_header ( + std::ostream& out, + const std::string& logger_name, + const log_level& l, + const uint64 thread_id + ) + { + using namespace std; + char* buf; + + time_t t = time(0); + buf = ctime(&t); + // remove the trailing '\n' + size_t size = strlen(buf); + buf[size-1] = '\0'; + + out << l.name << " (" << buf << ") [" << thread_id << "] " << logger_name << ": "; + } + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_EXTRA_LOGGER_HEADERs_CPP_ + + diff --git a/ml/dlib/dlib/logger/extra_logger_headers.h b/ml/dlib/dlib/logger/extra_logger_headers.h new file mode 100644 index 000000000..6eb24d84c --- /dev/null +++ b/ml/dlib/dlib/logger/extra_logger_headers.h @@ -0,0 +1,41 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_EXTRA_LOGGER_HEADERs_ +#define DLIB_EXTRA_LOGGER_HEADERs_ + +#include "logger_kernel_abstract.h" +#include "logger_kernel_1.h" +#include <iostream> +#include <string> +#include "../uintn.h" + +// ---------------------------------------------------------------------------------------- + +namespace dlib +{ + + void print_datetime_logger_header ( + std::ostream& out, + const std::string& logger_name, + const log_level& l, + const uint64 thread_id + ); + /*! + requires + - is not called more than once at a time (i.e. is not called from multiple + threads at the same time). + ensures + - let DATE be the current date and time (e.g. Thu Aug 31 16:41:52 2006). + - prints a string to out in the form: "l.name (DATE) [thread_id] logger_name:" + !*/ + +} + +// ---------------------------------------------------------------------------------------- + +#ifdef NO_MAKEFILE +#include "extra_logger_headers.cpp" +#endif + +#endif // DLIB_EXTRA_LOGGER_HEADERs_ + diff --git a/ml/dlib/dlib/logger/logger_config_file.cpp b/ml/dlib/dlib/logger/logger_config_file.cpp new file mode 100644 index 000000000..108f66c8c --- /dev/null +++ b/ml/dlib/dlib/logger/logger_config_file.cpp @@ -0,0 +1,214 @@ +// Copyright (C) 2007 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LOGGER_CONFIg_FILE_CPP +#define DLIB_LOGGER_CONFIg_FILE_CPP + +#include "logger_config_file.h" +#include <string> +#include "../config_reader.h" +#include <fstream> +#include <sstream> +#include "../error.h" +#include "../map.h" +#include "../string.h" + +// ---------------------------------------------------------------------------------------- + +namespace dlib +{ + + namespace logger_config_file_helpers + { + +// ---------------------------------------------------------------------------------------- + + std::ostream& get_file_stream ( + const std::string& file_name + ) + { + using namespace std; + static dlib::mutex m; + auto_mutex M(m); + static dlib::map<string,ostream*>::kernel_1a_c file_map; + + if (file_map.is_in_domain(file_name) == false) + { + // We won't ever delete this output stream. It should be around for the + // entire life of the program so just let the OS take care of it. + ostream* fout = new ofstream(file_name.c_str()); + if (!(*fout)) + { + delete fout; + throw error("logger_config: unable to open output file " + file_name); + } + + // add this file to our file map + string temp(file_name); + file_map.add(temp,fout); + } + + return *file_map[file_name]; + } + +// ---------------------------------------------------------------------------------------- + + log_level string_to_log_level ( + const std::string& level + ) + { + using namespace std; + if (level == "LALL" || level == "ALL" || level == "all") + return LALL; + else if (level == "LNONE" || level == "NONE" || level == "none") + return LNONE; + else if (level == "LTRACE" || level == "TRACE" || level == "trace") + return LTRACE; + else if (level == "LDEBUG" || level == "DEBUG" || level == "debug") + return LDEBUG; + else if (level == "LINFO" || level == "INFO" || level == "info") + return LINFO; + else if (level == "LWARN" || level == "WARN" || level == "warn") + return LWARN; + else if (level == "LERROR" || level == "ERROR" || level == "error") + return LERROR; + else if (level == "LFATAL" || level == "FATAL" || level == "fatal") + return LFATAL; + else + { + const int priority = string_cast<int>(level); + return log_level(priority,"CONFIG_FILE_DEFINED"); + } + } + +// ---------------------------------------------------------------------------------------- + + void configure_sub_blocks ( + const config_reader& cr, + const std::string& name + ) + { + using namespace std; + + logger dlog(name.c_str()); + + if (cr.is_key_defined("logging_level")) + { + dlog.set_level(string_to_log_level(cr["logging_level"])); + } + + if (cr.is_key_defined("output")) + { + string output = cr["output"]; + if (output == "cout") + dlog.set_output_stream(cout); + else if (output == "cerr") + dlog.set_output_stream(cerr); + else if (output == "clog") + dlog.set_output_stream(clog); + else + { + istringstream sin(output); + string one, two, three; + sin >> one; + sin >> two; + sin >> three; + if (one == "file" && three.size() == 0) + dlog.set_output_stream(get_file_stream(two)); + else + throw error("logger_config: invalid argument to output option: " + output); + } + + } // if (cr.is_key_defined("output")) + + // now configure all the sub-blocks + std_vector_c<std::string> blocks; + cr.get_blocks(blocks); + for (unsigned long i = 0; i < blocks.size(); ++i) + { + configure_sub_blocks(cr.block(blocks[i]), name + "." + blocks[i]); + } + + } + +// ---------------------------------------------------------------------------------------- + + } // namespace + +// ---------------------------------------------------------------------------------------- + + void configure_loggers_from_file ( + const std::string& file_name + ) + { + std::ifstream fin(file_name.c_str()); + + if (!fin) + throw logger_config_file_error("logger_config: unable to open config file " + file_name); + + config_reader temp(fin); + configure_loggers_from_file(temp); + } + +// ---------------------------------------------------------------------------------------- + + void configure_loggers_from_file ( + const config_reader& main_cr + ) + { + using namespace logger_config_file_helpers; + using namespace std; + + if (main_cr.is_block_defined("logger_config")) + { + const config_reader& cr = main_cr.block("logger_config"); + + if (cr.is_key_defined("logging_level")) + { + set_all_logging_levels(string_to_log_level(cr["logging_level"])); + } + + if (cr.is_key_defined("output")) + { + string output = cr["output"]; + if (output == "cout") + set_all_logging_output_streams(cout); + else if (output == "cerr") + set_all_logging_output_streams(cerr); + else if (output == "clog") + set_all_logging_output_streams(clog); + else + { + istringstream sin(output); + string one, two, three; + sin >> one; + sin >> two; + sin >> three; + if (one == "file" && three.size() == 0) + set_all_logging_output_streams(get_file_stream(two)); + else + throw logger_config_file_error("logger_config: invalid argument to output option: " + output); + } + + } // if (cr.is_key_defined("output")) + + // now configure all the sub-blocks + std_vector_c<std::string> blocks; + cr.get_blocks(blocks); + for (unsigned long i = 0; i < blocks.size(); ++i) + { + configure_sub_blocks(cr.block(blocks[i]), blocks[i]); + } + + } + } + +// ---------------------------------------------------------------------------------------- + +} + +// ---------------------------------------------------------------------------------------- + +#endif // DLIB_LOGGER_CONFIg_FILE_CPP + + + diff --git a/ml/dlib/dlib/logger/logger_config_file.h b/ml/dlib/dlib/logger/logger_config_file.h new file mode 100644 index 000000000..b0a030f80 --- /dev/null +++ b/ml/dlib/dlib/logger/logger_config_file.h @@ -0,0 +1,135 @@ +// Copyright (C) 2007 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_LOGGER_CONFIg_FILE_ +#define DLIB_LOGGER_CONFIg_FILE_ + +#include "logger_kernel_abstract.h" +#include "logger_kernel_1.h" +#include <string> +#include "../config_reader.h" + +// ---------------------------------------------------------------------------------------- + +namespace dlib +{ + class logger_config_file_error : public error + { + /*! + WHAT THIS OBJECT REPRESENTS + This is the exception class used by the configure_loggers_from_file() + function defined below. + !*/ + public: + logger_config_file_error(const std::string& s):error(s){} + }; + + void configure_loggers_from_file ( + const std::string& file_name + ); + /*! + ensures + - configures the loggers with the contents of the file_name file + throws + - dlib::logger_config_file_error + this exception is thrown if there is a problem reading the config file + !*/ + + void configure_loggers_from_file ( + const config_reader& cr + ); + /*! + ensures + - configures the loggers with the contents of cr. This function is just like + the above version that reads from a file except that it reads from an in-memory + config_reader instead. + throws + - dlib::logger_config_file_error + this exception is thrown if there is a problem reading the config file + !*/ + +// ---------------------------------------------------------------------------------------- + + /*! + # ----------------------------------------------- + # ------------- EXAMPLE CONFIG FILE ------------- + # ----------------------------------------------- + + # The overall format of the config file is the same as the one defined by + # the config_reader component of this library. + + # This line is a comment line + + # The config file always has a block named logger_config. This is where all the + # config data for the loggers reside. + logger_config + { + # This sets all loggers to the level LINFO since it is just inside the + # logger_config block + logging_level = info + + # Alternatively we could specify a user defined logging level by + # supplying a priority number. The following line would specify + # that only logging levels at or above 100 are printed. (note that + # you would have to comment out the logging_level statement above + # to avoid a conflict). + # logging_level = 100 + + parent_logger + { + # This sets all loggers named "parent_logger" or children of + # loggers with that name to not log at all (i.e. to logging level + # LNONE). + logging_level = none + } + + + parent_logger2 + { + # set loggers named "parent_logger2" and its children loggers + # to write their output to a file named out.txt + output = file out.txt + + child_logger + { + # Set loggers named "parent_logger2.child_logger" and children of loggers + # with this name to logging level LALL + logging_level = all + + # Note that this logger will also log to out.txt because that is what + # its parent does and we haven't overridden it here with something else. + # if we wanted this logger to write to cout instead we could uncomment + # the following line: + # output = cout + } + } + } + + # So in summary, all logger config stuff goes inside a block named logger_config. Then + # inside that block all blocks must be the names of loggers. There are only two keys, + # logging_level and output. + # + # The valid values of logging_level are: + # "LALL", "LNONE", "LTRACE", "LDEBUG", "LINFO", "LWARN", "LERROR", "LFATAL", + # "ALL", "NONE", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", + # "all", "none", "trace", "debug", "info", "warn", "error", "fatal", or + # any integral value + # + # The valid values of output are: + # "cout", "cerr", "clog", or a string of the form "file some_file_name" + # which causes the output to be logged to the specified file. + # + !*/ + + +} + +// ---------------------------------------------------------------------------------------- + +#ifdef NO_MAKEFILE +#include "logger_config_file.cpp" +#endif + +#endif // DLIB_LOGGER_CONFIg_FILE_ + + + diff --git a/ml/dlib/dlib/logger/logger_kernel_1.cpp b/ml/dlib/dlib/logger/logger_kernel_1.cpp new file mode 100644 index 000000000..093cd29a8 --- /dev/null +++ b/ml/dlib/dlib/logger/logger_kernel_1.cpp @@ -0,0 +1,498 @@ +// 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_CPp_ +#define DLIB_LOGGER_KERNEL_1_CPp_ + +#include "logger_kernel_1.h" +#include <iostream> +#include <sstream> + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + void set_all_logging_output_streams ( + std::ostream& out_ + ) + { + 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(out_.rdbuf()); + gd.loggers.element()->hook.clear(); + } + + gd.set_output_stream("",out_); + + // set the default hook to be an empty member function pointer + logger::hook_mfp hook; + gd.set_output_hook("",hook); + } + + void set_all_logging_levels ( + const log_level& new_level + ) + { + logger::global_data& gd = logger::get_global_data(); + auto_mutex M(gd.m); + gd.loggers.reset(); + while (gd.loggers.move_next()) + { + gd.loggers.element()->cur_level = new_level; + } + + gd.set_level("",new_level); + } + + void set_all_logging_headers ( + const print_header_type& new_header + ) + { + logger::global_data& gd = logger::get_global_data(); + auto_mutex M(gd.m); + gd.loggers.reset(); + while (gd.loggers.move_next()) + { + gd.loggers.element()->print_header = new_header; + } + + gd.set_logger_header("",new_header); + } + +// ---------------------------------------------------------------------------------------- + + namespace logger_helper_stuff + { + class helper + { + public: + helper() + { + std::ostringstream sout; + print_default_logger_header(sout,"some_name",LDEBUG,0); + } + }; + // do this to make sure all the static members of print_default_logger_header get + // initialized when the program turns on. + static helper a; + // make a logger to make extra sure the static global_data object gets + // initialized before any threads start up. Also do this so that there is always + // at least one logger so that the global data won't be deleted until the + // program is terminating. + static logger log("dlib"); + } + +// ---------------------------------------------------------------------------------------- + + void print_default_logger_header ( + std::ostream& out, + const std::string& logger_name, + const log_level& l, + const uint64 thread_id + ) + { + using namespace std; + static timestamper ts; + static const uint64 first_time = ts.get_timestamp(); + + const uint64 cur_time = (ts.get_timestamp() - first_time)/1000; + streamsize old_width = out.width(); out.width(5); + out << cur_time << " " << l.name; + out.width(old_width); + + out << " [" << thread_id << "] " << logger_name << ": "; + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// global_data stuff +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + logger::global_data:: + ~global_data ( + ) + { + unregister_thread_end_handler(*this,&global_data::thread_end_handler); + } + +// ---------------------------------------------------------------------------------------- + + logger::global_data:: + global_data( + ) : + next_thread_name(1) + { + // make sure the main program thread always has id 0. Since there is + // a global logger object declared in this file we should expect that + // the global_data object will be initialized in the main program thread + // so if we call get_thread_id() now we should get the main thread id. + thread_id_type main_id = get_thread_id(); + uint64 id_zero = 0; + thread_names.add(main_id,id_zero); + + // set up the defaults + auto_flush_table.val = true; + streambuf_table.val = std::cout.rdbuf(); + header_table.val = print_default_logger_header; + + // also allocate an initial buffer for hook based logging + hookbuf.buffer.reserve(1000); + } + + logger::global_data::level_container:: + level_container ( + ) : val(300,"ERROR") {} + +// ---------------------------------------------------------------------------------------- + + template <typename T> + const T& search_tables ( + const T& c, + const std::string& name + ) + { + if (c.table.size() == 0 || name.size() == 0) + return c; + + const std::string::size_type pos = name.find_first_of("."); + const std::string first = name.substr(0,pos); + std::string last; + if (pos != std::string::npos) + last = name.substr(pos+1); + + if (c.table.is_in_domain(first)) + { + return search_tables(*c.table[first], last); + } + else + { + return c; + } + } + +// ---------------------------------------------------------------------------------------- + + template <typename T, typename U> + void assign_tables ( + T& c, + const std::string& name, + const U& val + ) + { + if (name.size() == 0) + { + c.val = val; + c.table.clear(); + return; + } + + const std::string::size_type pos = name.find_first_of("."); + std::string first = name.substr(0,pos); + std::string last; + if (pos != std::string::npos) + last = name.substr(pos+1); + + if (c.table.is_in_domain(first)) + { + assign_tables(*c.table[first], last, val); + } + else + { + std::unique_ptr<T> temp (new T); + temp->val = c.val; + assign_tables(*temp, last, val); + c.table.add(first,temp); + } + } + +// ---------------------------------------------------------------------------------------- + + const log_level logger::global_data:: + level ( + const std::string& name + ) const + { + auto_mutex M(m); + return search_tables(level_table, name).val; + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + set_level ( + const std::string& name, + const log_level& new_level + ) + { + auto_mutex M(m); + assign_tables(level_table, name, new_level); + } + +// ---------------------------------------------------------------------------------------- + + bool logger::global_data:: + auto_flush ( + const std::string& name + ) const + { + auto_mutex M(m); + return search_tables(auto_flush_table, name).val; + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + set_auto_flush ( + const std::string& name, + bool enabled + ) + { + auto_mutex M(m); + assign_tables(auto_flush_table, name, enabled); + } + +// ---------------------------------------------------------------------------------------- + + std::streambuf* logger::global_data:: + output_streambuf ( + const std::string& name + ) + { + auto_mutex M(m); + return search_tables(streambuf_table, name).val; + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + set_output_stream ( + const std::string& name, + std::ostream& out_ + ) + { + auto_mutex M(m); + assign_tables( streambuf_table, name, out_.rdbuf()); + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + set_output_stream ( + const std::string& name, + std::streambuf& buf + ) + { + auto_mutex M(m); + assign_tables( streambuf_table, name, &buf); + } + +// ---------------------------------------------------------------------------------------- + + logger::hook_mfp logger::global_data:: + output_hook ( + const std::string& name + ) + { + auto_mutex M(m); + return search_tables(hook_table, name).val; + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + set_output_hook ( + const std::string& name, + const hook_mfp& hook + ) + { + auto_mutex M(m); + assign_tables( hook_table, name, hook); + } + +// ---------------------------------------------------------------------------------------- + + print_header_type logger::global_data:: + logger_header ( + const std::string& name + ) + { + auto_mutex M(m); + return search_tables(header_table, name).val; + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + set_logger_header ( + const std::string& name, + print_header_type ph + ) + { + auto_mutex M(m); + assign_tables(header_table, name, ph); + } + +// ---------------------------------------------------------------------------------------- + + logger::global_data& logger::get_global_data() + { + // Allocate the global_data on the heap rather than on the stack because + // we want to guard against the case where this static object would be destroyed + // during program termination BEFORE all logger objects are destroyed. + static global_data* gd = new global_data; + return *gd; + } + +// ---------------------------------------------------------------------------------------- + + void logger::global_data:: + thread_end_handler ( + ) + { + auto_mutex M(m); + thread_id_type id = get_thread_id(); + thread_id_type junkd; + uint64 junkr; + thread_names.remove(id,junkd,junkr); + } + +// ---------------------------------------------------------------------------------------- + + uint64 logger::global_data:: + get_thread_name ( + ) + { + thread_id_type id = get_thread_id(); + uint64 thread_name; + if (thread_names.is_in_domain(id)) + { + thread_name = thread_names[id]; + } + else + { + if (is_dlib_thread(id)) + register_thread_end_handler(*this,&global_data::thread_end_handler); + thread_name = next_thread_name; + thread_names.add(id,thread_name); + thread_name = next_thread_name; + ++next_thread_name; + } + return thread_name; + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// logger_stream stuff +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + void logger::logger_stream:: + print_header_and_stuff ( + ) + { + if (!been_used) + { + log.gd.m.lock(); + + // Check if the output hook is setup. If it isn't then we print the logger + // header like normal. Otherwise we need to remember to clear out the output + // stringstream we always write to. + if (log.hook.is_set() == false) + { + log.logger_header()(log.out,log.name(),l,log.gd.get_thread_name()); + } + else + { + // Make sure the hook buffer doesn't have any old data in it before we start + // logging a new message into it. + log.gd.hookbuf.buffer.resize(0); + } + been_used = true; + } + } + +// ---------------------------------------------------------------------------------------- + + void logger::logger_stream:: + print_end_of_line ( + ) + { + auto_unlock M(log.gd.m); + + if (log.hook.is_set() == false) + { + if (log.auto_flush_enabled) + log.out << std::endl; + else + log.out << "\n"; + } + else + { + // Make sure the buffer is a proper C-string + log.gd.hookbuf.buffer.push_back('\0'); + // call the output hook with all the info regarding this log message. + log.hook(log.name(), l, log.gd.get_thread_name(), &log.gd.hookbuf.buffer[0]); + } + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// logger stuff +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + logger:: + logger ( + const std::string& name_ + ) : + gd(get_global_data()), + logger_name(name_), + out(gd.output_streambuf(logger_name)), + cur_level(gd.level(logger_name)) + { + DLIB_ASSERT(name_[0] != '\0', + "\tlogger::logger()" + << "\n\tYou can't make a logger with an empty name" + << "\n\tthis: " << this + ); + + auto_mutex M(gd.m); + logger* temp = this; + gd.loggers.add(temp); + + // load the appropriate settings + print_header = gd.logger_header(logger_name); + auto_flush_enabled = gd.auto_flush(logger_name); + hook = gd.output_hook(logger_name); + } + +// ---------------------------------------------------------------------------------------- + + logger:: + ~logger ( + ) + { + gd.m.lock(); + gd.loggers.destroy(this); + // if this was the last logger then delete the global data + if (gd.loggers.size() == 0) + { + gd.m.unlock(); + delete &gd; + } + else + { + gd.m.unlock(); + } + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_LOGGER_KERNEL_1_CPp_ + 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_ + diff --git a/ml/dlib/dlib/logger/logger_kernel_abstract.h b/ml/dlib/dlib/logger/logger_kernel_abstract.h new file mode 100644 index 000000000..b6a4367a2 --- /dev/null +++ b/ml/dlib/dlib/logger/logger_kernel_abstract.h @@ -0,0 +1,429 @@ +// Copyright (C) 2006 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_LOGGER_KERNEl_ABSTRACT_ +#ifdef DLIB_LOGGER_KERNEl_ABSTRACT_ + +#include "../threads.h" +#include <limits> +#include <string> +#include <iostream> +#include "../uintn.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + class log_level + { + /*! + WHAT THIS OBJECT REPRESENTS + This object is a simple named level to log at. It contains a numeric + priority and a name to use in the logging messages. + !*/ + public: + log_level( + int priority_, + const char* name_ + ); + /*! + ensures + - #priority = priority_ + - the first 19 characters of name_ are copied into name and name + is null terminated. + !*/ + + 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); + /*! + ensures + - performs out << item.name + - returns 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 + ); + /*! + ensures + - for all loggers L (even loggers not yet constructed): + - #L.output_streambuf() == out.rdbuf() + - Removes any previous output hook from L. So now the logger + L will write all its messages to the given output stream. + throws + - std::bad_alloc + !*/ + +// ---------------------------------------------------------------------------------------- + + 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 + ); + /*! + ensures + - for all loggers L (even loggers not yet constructed): + - #L.logger_header() == new_header + throws + - std::bad_alloc + !*/ + +// ---------------------------------------------------------------------------------------- + + 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) + ); + /*! + ensures + - for all loggers L (even loggers not yet constructed): + - #L.output_streambuf() == 0 + - performs the equivalent to calling L.set_output_hook(object, hook); + (i.e. sets all loggers so that they will use the given hook function) + throws + - std::bad_alloc + !*/ + + template < + typename T + > + void set_all_logging_output_hooks ( + T& object + ); + /*! + ensures + - calls set_all_logging_output_hooks(object, &T::log); + !*/ + +// ---------------------------------------------------------------------------------------- + + void set_all_logging_levels ( + const log_level& new_level + ); + /*! + ensures + - for all loggers L (even loggers not yet constructed): + - #L.level() == new_level + throws + - std::bad_alloc + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + void print_default_logger_header ( + std::ostream& out, + const std::string& logger_name, + const log_level& l, + const uint64 thread_id + ); + /*! + requires + - is not called more than once at a time (i.e. is not called from multiple + threads at the same time). + ensures + - let MS be the number of milliseconds since program start. + - prints a string to out in the form: "MS l.name [thread_id] logger_name:" + !*/ + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + class logger + { + /*! + INITIAL VALUE + - name() == a user supplied value given to the constructor + - The values of level(), output_streambuf(), logger_header(), and + auto_flush() are inherited from the parent of this logger. + + WHAT THIS OBJECT REPRESENTS + This object represents a logging output stream in the style of the log4j + logger available for Java. + + Additionally, the logger doesn't perform any memory allocations during + each logging action. It just writes directly into the user supplied output + stream. Alternatively, if you use a logging output hook no memory allocations + are performed either. Logging just goes straight into a memory buffer + which gets passed to the user supplied logging hook. + + DEFAULTS + If the user hasn't specified values for the four inherited values level(), + output_streambuf(), logger_header(), or auto_flush() then the default + values will be used. The defaults are as follows: + - level() == LERROR + - output_streambuf() == std::cout.rdbuf() (i.e. the default is to log + to standard output). + - logger_header() == print_default_logger_header + - auto_flush() == true + + THREAD SAFETY + All methods of this class are thread safe. Note that it is safe to + chain calls to operator << such as: + log << LINFO << "message " << variable << " more message"; + The logger ensures that the entire statement executes atomically so the + message won't be broken up by other loggers in other threads. + !*/ + + class logger_stream + { + public: + + bool is_enabled ( + ) const; + /*! + ensures + - returns true if this logger stream will print out items + given to it by the << operator. returns false otherwise. + !*/ + + template <typename T> + logger_stream& operator << ( + const T& item + ); + /*! + ensures + - if (is_enabled()) then + - writes item to this output stream + - returns *this + !*/ + }; + + public: + + logger ( + const std::string& name_ + ); + /*! + requires + - name_ != "" + ensures + - #*this is properly initialized + - #name() == name_ + throws + - std::bad_alloc + - dlib::thread_error + !*/ + + virtual ~logger ( + ); + /*! + ensures + - any resources associated with *this have been released + !*/ + + const std::string& name ( + ) const; + /*! + ensures + - returns the name of this logger + !*/ + + logger_stream operator << ( + const log_level& l + ) const; + /*! + ensures + - if (l.priority >= level().priority) then + - returns a logger_stream with is_enabled() == true. I.e. this + returned stream will write its output to the I/O destination + used by this logger object. + - else + - returns a logger stream with is_enabled() == false + throws + - std::bad_alloc + !*/ + + bool is_child_of ( + const logger& log + ) const; + /*! + ensures + - if ( (name().find(log.name() + ".") == 0) || (log.name() == name()) ) then + - returns true + (i.e. if log.name() + "." is a prefix of name() or if both *this and log + have the same name then return true) + - else + - returns false + !*/ + + const log_level level ( + ) const; + /*! + ensures + - returns the current log level of this logger. + !*/ + + void set_level ( + const log_level& new_level + ); + /*! + ensures + - for all loggers L such that L.is_child_of(*this) == true: + - #L.level() == new_level + throws + - std::bad_alloc + !*/ + + bool auto_flush ( + ); + /*! + ensures + - returns true if the output stream is flushed after every logged message. + returns false otherwise. (Note that flushing only does anything if + the logger is set to use an output stream rather than a hook) + !*/ + + void set_auto_flush ( + bool enabled + ); + /*! + ensures + - for all loggers L such that L.is_child_of(*this) == true: + - #L.auto_flush() == enabled + throws + - std::bad_alloc + !*/ + + + 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) + ); + /*! + requires + - hook is a valid pointer to a member function in T + ensures + - for all loggers L such that L.is_child_of(*this) == true: + - #L.output_streambuf() == 0 + - #L will not send its log messages to an ostream object anymore. Instead + it will call the given hook member function (i.e. (object.*hook)(name,l,id,msg) ) + for each message that needs to be logged. + - The arguments to the hook function have the following meanings: + - logger_name == The name of the logger that is printing the log message. + - l == The level of the logger that is printing the log message. + - thread_id == A number that uniquely identifies the thread trying to log + the message. Note that this number is unique among all threads, past and + present. Also note that this id is not the same one returned by + get_thread_id(). + - message_to_log == the actual text of the message the user is giving to + the logger object to log. + - All hook functions will also only be called one at a time. This means + that hook functions don't need to be thread safe. + !*/ + + std::streambuf* output_streambuf ( + ); + /*! + ensures + - if (an output hook isn't set) then + - returns the output stream buffer that this logger writes all + messages to. + - else + - returns 0 + !*/ + + void set_output_stream ( + std::ostream& out + ); + /*! + ensures + - for all loggers L such that L.is_child_of(*this) == true: + - #L.output_streambuf() == out.rdbuf() + - Removes any previous output hook from L. So now the logger + L will write all its messages to the given output stream. + throws + - std::bad_alloc + !*/ + + print_header_type logger_header ( + ) const; + /*! + ensures + - returns the function that is called to print the header information + onto each logged message. The arguments to the function have the following + meanings: + - out == The output stream this function writes the header to. + - logger_name == The name of the logger that is printing the log message. + - l == The level of the logger that is printing the log message. + - thread_id == A number that uniquely identifies the thread trying to log + the message. Note that this number is unique among all threads, past and + present. Also note that this id is not the same one returned by + get_thread_id(). + - This logger_header function will also only be called once at a time. This means + the logger_header function doesn't need to be thread safe. + - the logger_header function is only used when output_streambuf() != 0 + !*/ + + void set_logger_header ( + print_header_type print_header + ); + /*! + ensures + - for all loggers L such that L.is_child_of(*this) == true: + - #L.logger_header() == print_header + throws + - std::bad_alloc + !*/ + + private: + + // restricted functions + logger(const logger&); // copy constructor + logger& operator=(const logger&); // assignment operator + + }; + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_LOGGER_KERNEl_ABSTRACT_ + |