Commit a31096bd authored by Davis King's avatar Davis King

Modified the logger's hook implementation so that it uses a special stream

buffer instead of an std::ostringstream.  This way logging doesn't cause
memory allocations.  This breaks backwards compatibility with the previous
hook function API but only slightly.  The new hook functions must take a
const char* instead of std::string.

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%403395
parent cea9d020
...@@ -123,6 +123,9 @@ namespace dlib ...@@ -123,6 +123,9 @@ namespace dlib
auto_flush_table.val = true; auto_flush_table.val = true;
streambuf_table.val = std::cout.rdbuf(); streambuf_table.val = std::cout.rdbuf();
header_table.val = print_default_logger_header; 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:: logger::global_data::level_container::
...@@ -260,6 +263,18 @@ namespace dlib ...@@ -260,6 +263,18 @@ namespace dlib
assign_tables( streambuf_table, name, out_.rdbuf()); 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:: logger::hook_mfp logger::global_data::
...@@ -377,9 +392,9 @@ namespace dlib ...@@ -377,9 +392,9 @@ namespace dlib
} }
else else
{ {
// use the empty_string in hopes that it is generally faster than calling // Make sure the hook buffer doesn't have any old data in it before we start
// str("") since this avoids the construction of a new std::string object. // logging a new message into it.
log.gd.sout.str(log.gd.empty_string); log.gd.hookbuf.buffer.resize(0);
} }
been_used = true; been_used = true;
} }
...@@ -402,8 +417,10 @@ namespace dlib ...@@ -402,8 +417,10 @@ namespace dlib
} }
else 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. // call the output hook with all the info regarding this log message.
log.hook(log.name(), l, log.gd.get_thread_name(), log.gd.sout.str()); log.hook(log.name(), l, log.gd.get_thread_name(), &log.gd.hookbuf.buffer[0]);
} }
} }
......
...@@ -9,13 +9,14 @@ ...@@ -9,13 +9,14 @@
#include "logger_kernel_abstract.h" #include "logger_kernel_abstract.h"
#include <limits> #include <limits>
#include <cstring> #include <cstring>
#include <sstream>
#include "../algs.h" #include "../algs.h"
#include "../assert.h" #include "../assert.h"
#include "../uintn.h" #include "../uintn.h"
#include "../map.h" #include "../map.h"
#include "../smart_pointers.h" #include "../smart_pointers.h"
#include "../member_function_pointer.h" #include "../member_function_pointer.h"
#include <streambuf>
#include <vector>
namespace dlib namespace dlib
{ {
...@@ -84,7 +85,7 @@ namespace dlib ...@@ -84,7 +85,7 @@ namespace dlib
- if (hook.is_set() == false) then - if (hook.is_set() == false) then
- out.rdbuf() == output_streambuf() - out.rdbuf() == output_streambuf()
- else - else
- out.rdbuf() == gd.sout.rdbuf() - out.rdbuf() == &gd.hookbuf
- output_streambuf() == 0 - output_streambuf() == 0
- cur_level == level() - cur_level == level()
...@@ -98,6 +99,8 @@ namespace dlib ...@@ -98,6 +99,8 @@ namespace dlib
to a thread when we find that it isn't already in thread_names. to a thread when we find that it isn't already in thread_names.
!*/ !*/
// ------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------
class logger_stream class logger_stream
{ {
...@@ -180,13 +183,16 @@ namespace dlib ...@@ -180,13 +183,16 @@ namespace dlib
logger& log; logger& log;
bool been_used; bool been_used;
const bool enabled; const bool enabled;
}; }; // end of class logger_stream
// ------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------
friend class logger_stream; friend class logger_stream;
public: public:
typedef member_function_pointer<const std::string&, const log_level&, typedef member_function_pointer<const std::string&, const log_level&,
const uint64, const std::string&>::kernel_1a_c hook_mfp; const uint64, const char*>::kernel_1a_c hook_mfp;
logger ( logger (
const char* name_ const char* name_
...@@ -273,7 +279,7 @@ namespace dlib ...@@ -273,7 +279,7 @@ namespace dlib
void (T::*hook_)(const std::string& logger_name, void (T::*hook_)(const std::string& logger_name,
const log_level& l, const log_level& l,
const uint64 thread_id, const uint64 thread_id,
const std::string& message_to_log) const char* message_to_log)
) )
{ {
auto_mutex M(gd.m); auto_mutex M(gd.m);
...@@ -284,13 +290,13 @@ namespace dlib ...@@ -284,13 +290,13 @@ namespace dlib
{ {
if (gd.loggers.element()->is_child_of(*this)) if (gd.loggers.element()->is_child_of(*this))
{ {
gd.loggers.element()->out.rdbuf(gd.sout.rdbuf()); gd.loggers.element()->out.rdbuf(&gd.hookbuf);
gd.loggers.element()->hook = hook; gd.loggers.element()->hook = hook;
} }
} }
gd.set_output_hook(logger_name, hook); gd.set_output_hook(logger_name, hook);
gd.set_output_stream(logger_name, gd.sout); gd.set_output_stream(logger_name, gd.hookbuf);
} }
void set_output_stream ( void set_output_stream (
...@@ -341,6 +347,8 @@ namespace dlib ...@@ -341,6 +347,8 @@ namespace dlib
private: private:
// ------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------
struct global_data struct global_data
{ {
...@@ -348,8 +356,30 @@ namespace dlib ...@@ -348,8 +356,30 @@ namespace dlib
set<logger*>::kernel_1b loggers; set<logger*>::kernel_1b loggers;
map<thread_id_type,uint64>::kernel_1b thread_names; map<thread_id_type,uint64>::kernel_1b thread_names;
uint64 next_thread_name; uint64 next_thread_name;
std::ostringstream sout;
const std::string empty_string; // 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 (
); );
...@@ -460,6 +490,19 @@ namespace dlib ...@@ -460,6 +490,19 @@ namespace dlib
- #output_streambuf(L) == out_.rdbuf() - #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 struct output_hook_container
{ {
hook_mfp val; hook_mfp val;
...@@ -520,6 +563,7 @@ namespace dlib ...@@ -520,6 +563,7 @@ namespace dlib
static global_data& get_global_data(); static global_data& get_global_data();
// ------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------
friend void set_all_logging_levels ( friend void set_all_logging_levels (
...@@ -538,7 +582,7 @@ namespace dlib ...@@ -538,7 +582,7 @@ namespace dlib
void (T::*hook_)(const std::string& logger_name, void (T::*hook_)(const std::string& logger_name,
const log_level& l, const log_level& l,
const uint64 thread_id, const uint64 thread_id,
const std::string& message_to_log) const char* message_to_log)
) )
{ {
logger::hook_mfp hook; logger::hook_mfp hook;
...@@ -549,11 +593,11 @@ namespace dlib ...@@ -549,11 +593,11 @@ namespace dlib
gd.loggers.reset(); gd.loggers.reset();
while (gd.loggers.move_next()) while (gd.loggers.move_next())
{ {
gd.loggers.element()->out.rdbuf(gd.sout.rdbuf()); gd.loggers.element()->out.rdbuf(&gd.hookbuf);
gd.loggers.element()->hook = hook; gd.loggers.element()->hook = hook;
} }
gd.set_output_stream("",gd.sout); gd.set_output_stream("",gd.hookbuf);
gd.set_output_hook("",hook); gd.set_output_hook("",hook);
} }
......
...@@ -77,7 +77,7 @@ namespace dlib ...@@ -77,7 +77,7 @@ namespace dlib
void (T::*hook)(const std::string& logger_name, void (T::*hook)(const std::string& logger_name,
const log_level& l, const log_level& l,
const uint64 thread_id, const uint64 thread_id,
const std::string& message_to_log) const char* message_to_log)
); );
/*! /*!
ensures ensures
...@@ -141,6 +141,12 @@ namespace dlib ...@@ -141,6 +141,12 @@ namespace dlib
one implementation of this object at a time. Thus, to create instances one implementation of this object at a time. Thus, to create instances
of the logger you would simply write logger my_logger("some_name"); of the logger you would simply write logger my_logger("some_name");
Finally, note that 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 DEFAULTS
If the user hasn't specified values for the four inherited values level(), If the user hasn't specified values for the four inherited values level(),
output_streambuf(), logger_header(), or auto_flush() then the default output_streambuf(), logger_header(), or auto_flush() then the default
...@@ -288,7 +294,7 @@ namespace dlib ...@@ -288,7 +294,7 @@ namespace dlib
void (T::*hook)(const std::string& logger_name, void (T::*hook)(const std::string& logger_name,
const log_level& l, const log_level& l,
const uint64 thread_id, const uint64 thread_id,
const std::string& message_to_log) const char* message_to_log)
); );
/*! /*!
requires requires
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment