// loglite library logging.hpp header file ---------------------------------/ // (C) Copyright Jean-Daniel Michaud, Ilya Pokolev & Igor Solovyov 2007-2008. // Permission to copy, use, modify, sell and distribute this software is // granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied warranty, // and with no claim as to its suitability for any purpose. // See http://www.boost.org/LICENSE_1_0.txt for licensing. // See http://code.google.com/p/loglite/ for library home page. #ifndef LOGLITE_HPP #define LOGLITE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BOOST_CONFIG_HPP # include #endif #if defined(BOOST_HAS_THREADS) && !defined(LOGLITE_NO_THREAD) # include # include # include #endif // BOOST_HAS_THREADS && !LOGLITE_NO_THREAD #include #include #include #include #include #include #ifdef _MSC_VER # define __func__ __FUNCTION__ # define __PRETTY_FUNCTION__ __FUNCSIG__ // Code copied from redhat website #elif __STDC_VERSION__ < 199901L # if __GNUC__ >= 2 # define __func__ __FUNCTION__ # else # define __func__ "" # endif #endif // !Code copied from redhat website #ifndef LOGLITE_NO_CODE_GENERATION_FOR_LOG #define LOGLITE_INIT( format ) \ { \ loglite::logger_p l = loglite::logger::get_instance(); \ l->add_format(format); \ } #define LOGLITE_ADD_OUTPUT_STREAM( sink ) \ { \ loglite::logger_p l = loglite::logger::get_instance(); \ l->add_sink(sink); \ } #define LOGLITE_LOG(mask, qualifier, _trace) \ { \ loglite::logger_p _loglite__l_ = loglite::logger::get_instance(); \ if (_loglite__l_->get_global_log_mask() & mask) \ { \ std::stringstream string_stream; \ string_stream << _trace; \ loglite::log_param_t log_param = { mask, \ &qualifier, \ string_stream.str(), \ __FILE__, __LINE__, \ __func__, __PRETTY_FUNCTION__, NULL }; \ _loglite__l_->trace(log_param); \ } \ } #define LOGLITE_LOG_( mask, _trace ) \ { LOGLITE_LOG(mask, loglite::log, _trace) } #define LOGLITE_LOG_UNFORMATTED(mask, qualifier, _trace) \ { \ loglite::logger_p l = loglite::logger::get_instance(); \ if (l->get_global_log_mask() & mask) \ { \ std::stringstream string_stream; \ string_stream << _trace; \ loglite::log_param_t log_param = { mask, \ &qualifier, \ string_stream.str(), \ __FILE__, __LINE__, \ __func__, __PRETTY_FUNCTION__, NULL } ; \ l->unformatted_trace(log_param); \ } \ } #define LOGLITE_LOG_SCOPE( mask, scope_object ) \ loglite::scope_item scope_object \ ( \ mask, \ &loglite::scope, \ __FILE__, __LINE__, \ __func__, __PRETTY_FUNCTION__ \ ); #else // !LOGLITE_NO_CODE_GENERATION_FOR_LOG #define LOGLITE_INIT( format ) #define LOGLITE_ADD_OUTPUT_STREAM( sink ) #define LOGLITE_LOG(mask, qualifier, _trace) #define LOGLITE_LOG_( mask, _trace ) #define LOGLITE_LOG_UNFORMATTED(mask, qualifier, _trace) #endif // LOGLITE_NO_CODE_GENERATION_FOR_LOG #define LOGLITE_MAX_LINE_STR_SIZE 20 // log(2^64) #define LOGLITE_MASK_UP_LIMIT 65535 // 2^sizeof (mask_t) - 1 #define LOGLITE_MASK_LEVEL_1 1 // 0001 #define LOGLITE_MASK_LEVEL_2 3 // 0011 #define LOGLITE_MASK_LEVEL_3 7 // 0111 #define LOGLITE_MASK_LEVEL_4 15 // 1111 #define LOGLITE_LEVEL_1 1 #define LOGLITE_LEVEL_2 2 #define LOGLITE_LEVEL_3 4 #define LOGLITE_LEVEL_4 8 #define LOGLITE_LOG_SEPARATOR '\f' #define LOGLITE_LOG_L1( _trace ) LOGLITE_LOG_( LOGLITE_LEVEL_1, _trace ) #define LOGLITE_LOG_L2( _trace ) LOGLITE_LOG_( LOGLITE_LEVEL_2, _trace ) #define LOGLITE_LOG_L3( _trace ) LOGLITE_LOG_( LOGLITE_LEVEL_3, _trace ) #define LOGLITE_LOG_L4( _trace ) LOGLITE_LOG_( LOGLITE_LEVEL_4, _trace ) namespace loglite { // Logging forward declarations ---------------------------------------------// class log_element; class mask_element; class qualifier; class trace_element; class format; class sink; class logger; class scope_item; // Logging typedefs declarations --------------------------------------------// typedef enum { SINK = 0, FORMAT } sink_format_assoc_e; typedef std::list > element_list_t; typedef std::list > stream_list_t; typedef unsigned short mask_t; typedef std::vector scope_stack_t; typedef struct { mask_t m_mask; const qualifier *m_qualifier; std::string m_trace; std::string m_filename; unsigned int m_line; std::string m_func_name; std::string m_func_sig; scope_stack_t *m_scope; } log_param_t; typedef std::list format_list_t; typedef boost::tuple sink_format_assoc_t; typedef std::list sink_format_assoc_list_t; typedef std::list qualifier_list_t; typedef boost::shared_ptr logger_p; // Used for shared_ptr() on statically allocated log_element ----------------// struct null_deleter { void operator()(void const *) const {} }; // Qualifier class declaration ----------------------------------------------// class qualifier { public: qualifier() {} virtual ~qualifier() {} inline std::string to_string() const { return m_identifier; } virtual bool operator==(const qualifier &q) { return false; } protected: std::string m_identifier; }; class log_qualifier : public qualifier { public: log_qualifier() { m_identifier = "log"; } bool operator==(const qualifier &q) { return (dynamic_cast(&q) != NULL); } }; class notice_qualifier : public qualifier { public: notice_qualifier() { m_identifier = "notice"; } bool operator==(const qualifier &q) { return (dynamic_cast(&q) != NULL); } }; class scope_qualifier : public qualifier { public: scope_qualifier() { m_identifier = "scope"; } bool operator==(const qualifier &q) { return (dynamic_cast(&q) != NULL); } }; class warning_qualifier : public qualifier { public: warning_qualifier() { m_identifier = "warning"; } bool operator==(const qualifier &q) { return (dynamic_cast(&q) != NULL); } }; class error_qualifier : public qualifier { public: error_qualifier() { m_identifier = "error"; } bool operator==(const qualifier &q) { return (dynamic_cast(&q) != NULL); } }; // Element classes declaration ---------------------------------------------// class log_element { public: virtual ~log_element() {} virtual std::string to_string() { assert(0); return ""; }; virtual std::string visit(format &f, const log_param_t &log_param); }; class mask_element : public log_element { public: std::string to_string(mask_t l) { return str(boost::format("%i") % l); }; std::string visit(format &f, const log_param_t &log_param); }; class filename_element : public log_element { public: typedef enum { full_path, filename_only } display_format_e; filename_element() : m_display_format(full_path) {} filename_element(display_format_e d) : m_display_format(d) {} std::string to_string(const std::string &f) { if (m_display_format == full_path) return f; boost::filesystem::path p(f); return p.leaf(); } std::string visit(format &f, const log_param_t &log_param); private: display_format_e m_display_format; }; class line_element : public log_element { public: std::string to_string(unsigned int l) { return str(boost::format("%i") % l); } std::string visit(format &f, const log_param_t &log_param); }; class date_element : public log_element { public: date_element() { m_output_facet = new boost::gregorian::date_facet(); m_output_facet->set_iso_extended_format(); m_ss.reset(new std::stringstream()); m_ss->imbue(std::locale(std::locale::classic(), m_output_facet)); } date_element(const std::string &format) { m_output_facet = new boost::gregorian::date_facet(); m_output_facet->format(format.c_str()); m_ss.reset(new std::stringstream()); m_ss->imbue(std::locale(std::locale::classic(), m_output_facet)); } std::string to_string() { boost::gregorian::date d(boost::gregorian::day_clock::local_day()); m_ss->str(""); (*m_ss) << d; return m_ss->str(); } private: boost::shared_ptr m_ss; // no shared ptr. the stringstream take ownership of the facet // once imbue called. boost::gregorian::date_facet *m_output_facet; }; class time_element : public log_element { public: time_element() { m_output_facet = new boost::posix_time::time_facet(); m_output_facet->set_iso_extended_format(); m_ss.reset(new std::stringstream()); m_ss->imbue(std::locale(std::locale::classic(), m_output_facet)); } time_element(const std::string &format) { m_output_facet = new boost::posix_time::time_facet(); m_output_facet->format(format.c_str()); m_ss.reset(new std::stringstream()); m_ss->imbue(std::locale(std::locale::classic(), m_output_facet)); } std::string to_string() { boost::posix_time::ptime t(boost::posix_time::microsec_clock::local_time()); m_ss->str(""); (*m_ss) << t; return m_ss->str(); }; private: boost::shared_ptr m_ss; // no shared ptr. the stringstream take ownership of the facet // once imbue called. boost::posix_time::time_facet *m_output_facet; }; class trace_element : public log_element { public: std::string to_string(const std::string& s) { return s; }; std::string visit(format &f, const log_param_t &log_param); }; class eol_element : public log_element { public: std::string to_string() { return "\n"; }; }; class literal_element : public log_element { public: explicit literal_element(std::string l) : m_literal(l) {} std::string to_string() { return m_literal; }; private: std::string m_literal; }; class qualifier_element : public log_element { public: qualifier_element(const qualifier &lq) { m_qualifier_identifier = lq.to_string(); } std::string to_string() { return m_qualifier_identifier; }; private: std::string m_qualifier_identifier; }; // end-of-trace qualifier // Used to delimite a formatted trace when transmitted through network // or shared_memory streams. class eot_element : public log_element { public: std::string to_string() { return "\f"; }; }; class function_name_element : public log_element //function name qualifier { public: std::string to_string(const std::string &n) { return n; } std::string visit(format &f, const log_param_t &log_param); }; // pretty function name qualifier class pretty_function_name_element : public log_element { public: std::string to_string(const std::string &n) { return n; } std::string visit(format &f, const log_param_t &log_param); }; // scope qualifier // Dump a string containing the stack scope maintained by the // creation of scope_item object or the call to macro LOGLITE_LOG_SCOPE class scope_element : public log_element { public: std::string to_string(scope_stack_t *s); std::string visit(format &f, const log_param_t &log_param); }; // Format class declaration ------------------------------------------------// class format { public: format(log_element &e) : m_identifier("unnamed") { boost::shared_ptr p(&e, null_deleter()); m_element_list.push_back(p); } format(log_element &e, const std::string &identifier) : m_identifier(identifier) { boost::shared_ptr p(&e, null_deleter()); m_element_list.push_back(p); } format(element_list_t e) : m_element_list(e), m_identifier("unnamed") {} format(element_list_t e, const std::string &identifier) : m_element_list(e), m_identifier(identifier) {} std::string produce_trace(const log_param_t &log_param) { element_list_t::iterator e_it = m_element_list.begin(); std::stringstream str_stream; for (; e_it != m_element_list.end(); ++e_it) { str_stream << (*e_it)->visit(*this, log_param); } return str_stream.str(); } // Visitors for the log elements std::string accept(log_element &e) { return e.to_string(); } std::string accept(mask_element &e, mask_t l) { return e.to_string(l); } std::string accept(trace_element &e, const std::string& s) { return e.to_string(s); } std::string accept(filename_element &e, const std::string& s) { return e.to_string(s); } std::string accept(line_element &e, unsigned int l) { return e.to_string(l); } std::string accept(function_name_element &e, const std::string& s) { return e.to_string(s); } std::string accept(pretty_function_name_element &e, const std::string& s) { return e.to_string(s); } std::string accept(scope_element &e, scope_stack_t *s) { return e.to_string(s); } private: element_list_t m_element_list; std::string m_identifier; }; // Sink class declaration --------------------------------------------------// class sink { public: sink(std::ostream *s, mask_t log_mask = 1) { if (s) if (*s == std::cout || *s == std::cerr || *s == std::clog) m_output_stream.reset(s, null_deleter()); else m_output_stream.reset(s); set_log_mask(log_mask); } void set_log_mask(mask_t log_mask) { m_log_mask = log_mask; } inline mask_t get_log_mask() const { return m_log_mask; } void consume_trace(format &f, const log_param_t &log_param) { /* make here check to avoid producing a useless trace */ if (log_param.m_mask > m_log_mask) return ; qualifier_list_t::const_iterator it = m_qualifier_list.begin(); bool qualifier_present = false; for ( ; !qualifier_present && it != m_qualifier_list.end(); ++it) qualifier_present = (**it == *log_param.m_qualifier); if (!qualifier_present) return ; *m_output_stream << f.produce_trace(log_param) << std::flush; } void attach_qualifier(qualifier &q) { m_qualifier_list.push_back(&q); } private: mask_t m_log_mask; boost::shared_ptr m_output_stream; qualifier_list_t m_qualifier_list; }; // Element static instantiations --------------------------------------------// static mask_element mask = mask_element(); static filename_element filename = filename_element(); static line_element line = line_element(); static date_element date = date_element(); static time_element time = time_element(); static trace_element trace = trace_element(); static eol_element eol = eol_element(); static eot_element eot = eot_element(); static function_name_element function_name = function_name_element(); static pretty_function_name_element function_signature = pretty_function_name_element(); static scope_element scope_stack = scope_element(); static log_qualifier log = log_qualifier(); static notice_qualifier notice = notice_qualifier(); static scope_qualifier scope = scope_qualifier(); static warning_qualifier warning = warning_qualifier(); static error_qualifier error = error_qualifier(); // Logger class declaration ------------------------------------------------// static logger_p g__logger; class logger { public: logger() : m_global_log_mask(0) {} static void create_instance() { g__logger.reset(new logger()); } static logger_p &get_instance() { #if defined(BOOST_HAS_THREADS) && !defined (LOGLITE_NO_THREAD) static boost::once_flag once = BOOST_ONCE_INIT; boost::call_once(logger::create_instance, once); #else static bool once = true; if (once) { create_instance(); once = false; } #endif return g__logger; } void add_format(format f) { m_format_list.push_back(f); } void add_sink(sink s) { if (m_format_list.begin() == m_format_list.end()) throw "no format defined"; // Updating global_mask used for full lazy evaluation m_global_log_mask |= s.get_log_mask(); m_sink_format_assoc.push_back ( sink_format_assoc_t(s, *m_format_list.begin()) ); } void add_sink(sink s, format f) { // Updating global_mask used for full lazy evaluation m_global_log_mask |= s.get_log_mask(); m_sink_format_assoc.push_back(sink_format_assoc_t(s, f)); } inline mask_t get_global_log_mask() { return m_global_log_mask; } void trace(mask_t m, const qualifier &q, const std::string &s, const std::string &f = "", unsigned int l = 0) { log_param_t log_param = { m, &q, s, f, l, "", "", NULL }; trace(log_param); } void unformatted_trace(mask_t m, const qualifier &q, const std::string &s) { log_param_t log_param = { m, &q, s, "", 0, "", "", NULL }; unformatted_trace(log_param); } void trace(log_param_t &log_param) { #if defined(BOOST_HAS_THREADS) && !defined(LOGLITE_NO_THREAD) boost::mutex::scoped_lock scoped_lock(m_mutex); #endif // BOOST_HAS_THREADS log_param.m_scope = &m_scope_stack; // Attach the scope stack here sink_format_assoc_list_t::iterator s_it = m_sink_format_assoc.begin(); for (; s_it != m_sink_format_assoc.end(); ++s_it) { boost::get(*s_it).consume_trace (boost::get(*s_it), log_param); } } void unformatted_trace(log_param_t &log_param); void push_scope(scope_item *s) { m_scope_stack.push_back(s); } void pop_scope() { m_scope_stack.pop_back(); } private: format_list_t m_format_list; sink_format_assoc_list_t m_sink_format_assoc; // The global log mask is the highest log mask on all the sink // added to the logger. If no sink as a log mask matching the global // mask for a trace, the trace does not need to be evaluated. mask_t m_global_log_mask; // The scope stack. Can be access using the scope_stack element scope_stack_t m_scope_stack; #if defined(BOOST_HAS_THREADS) && !defined(LOGLITE_NO_THREAD) boost::mutex m_mutex; #endif // BOOST_HAS_THREADS }; // logger // Scope Logger class declaration -------------------------------------------// class scope_item { public: scope_item(const std::string &scope_identifier, mask_t mask = LOGLITE_LEVEL_1) : m_scope_identifier(scope_identifier), m_mask(mask) { log_param_t lp = { m_mask, &scope, "Entering " + m_scope_identifier, "", 0, "", "", NULL }; m_log_param = lp; g__logger->trace(m_log_param); g__logger->push_scope(this); } scope_item(mask_t m, qualifier *q, const std::string &f, unsigned int l, const std::string &func_name, const std::string &func_sig) : m_scope_identifier(func_name), m_mask(m) { log_param_t lp = { m, q, "Entering " + func_name, f, l, func_name, func_sig, NULL }; m_log_param = lp; g__logger->trace(m_log_param); g__logger->push_scope(this); } ~scope_item() { m_log_param.m_trace = "Exiting " + m_scope_identifier; g__logger->trace(m_log_param); g__logger->pop_scope(); } std::string get_id() { return m_scope_identifier; } private: std::string m_scope_identifier; mask_t m_mask; log_param_t m_log_param; }; // Element functions definition ---------------------------------------------// inline std::string log_element::visit(format &f, const log_param_t &log_param) { return f.accept(*this); } inline std::string mask_element::visit(format &f, const log_param_t &log_param) { return f.accept(*this, log_param.m_mask); } inline std::string trace_element::visit(format &f, const log_param_t &log_param) { return f.accept(*this, log_param.m_trace); } inline std::string filename_element::visit(format &f, const log_param_t &log_param) { return f.accept(*this, log_param.m_filename); } inline std::string line_element::visit(format &f, const log_param_t &log_param) { return f.accept(*this, log_param.m_line); } inline std::string function_name_element::visit ( format &f, const log_param_t &log_param ) { return f.accept(*this, log_param.m_func_name); } inline std::string pretty_function_name_element::visit ( format &f, const log_param_t &log_param ) { return f.accept(*this, log_param.m_func_sig); } inline std::string scope_element::visit(format &f, const log_param_t &log_param) { return f.accept(*this, log_param.m_scope); } } // !namespace loglite // Element global operators -------------------------------------------------// inline loglite::element_list_t operator>>(loglite::log_element &lhs, loglite::log_element &rhs) { loglite::element_list_t l; l.push_back(boost::shared_ptr (&lhs, loglite::null_deleter())); l.push_back(boost::shared_ptr (&rhs, loglite::null_deleter())); return l; } inline loglite::element_list_t operator>>(loglite::element_list_t lhs, loglite::log_element &rhs) { lhs.push_back(boost::shared_ptr (&rhs, loglite::null_deleter())); return lhs; } inline loglite::element_list_t operator>>(const std::string &s, loglite::log_element &rhs) { loglite::element_list_t l; boost::shared_ptr p(new loglite::literal_element(s)); l.push_back(p); l.push_back(boost::shared_ptr (&rhs, loglite::null_deleter())); return l; } inline loglite::element_list_t operator>>(loglite::element_list_t lhs, const std::string &s) { boost::shared_ptr p(new loglite::literal_element(s)); lhs.push_back(p); return lhs; } inline void loglite::logger::unformatted_trace(log_param_t &log_param) { #if defined(BOOST_HAS_THREADS) && !defined(LOGLITE_NO_THREAD) boost::mutex::scoped_lock scoped_lock(m_mutex); #endif // BOOST_HAS_THREADS log_param.m_scope = &m_scope_stack; // Attach the scope stack here sink_format_assoc_list_t::iterator s_it = m_sink_format_assoc.begin(); for (; s_it != m_sink_format_assoc.end(); ++s_it) { loglite::format f(loglite::trace); boost::get(*s_it).consume_trace(f, log_param); } } inline std::string loglite::scope_element::to_string(scope_stack_t *s) { scope_stack_t::iterator it = s->begin(); std::string scope_str; for (; it != s->end(); ++it) { if (it == s->begin()) scope_str += (*it)->get_id(); else scope_str += " -> " + (*it)->get_id(); } return scope_str; }; #endif // !LOGLITE_HPP