499 lines
14 KiB
C++
499 lines
14 KiB
C++
// 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_
|
|
|