You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
concurrentqueue/benchmarks/dlib/logger/logger_kernel_1.h

688 lines
20 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_
#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_