// 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_