#ifndef MY_LIBS___TLOG__TLOG_HPP #define MY_LIBS___TLOG__TLOG_HPP #include #include #include #include #include #include namespace tlog { class xml_formatter { public: std::string encode( const std::string& in ) { if ( must_be_encoded( in ) ) return cdata_begin() + in + cdata_end(); else return in; } std::string decode( const std::string& in ) { if ( in.length() >= cdata_begin().length() + cdata_end().length() && in.substr( 0, cdata_begin().length() ) == cdata_begin() && in.substr( in.length() - cdata_end().length() ) == cdata_end() ) return in.substr( cdata_begin().length(), in.length() - cdata_begin().length() - cdata_end().length() ); else return in; } bool must_be_encoded( const std::string& in ) { return std::find_if( in.begin(), in.end(), has_xml_entity ) != in.end(); } std::string prepare( const std::string& in ) { std::string r; bool prepend = false; if ( in.empty() ) prepend = true; if ( ! prepend && ! isalpha( in[ 0 ] ) ) prepend = true; if ( ! prepend && in.size() >= 3 && iequal( in.substr( 0, 3 ), "xml" ) ) prepend = true; if ( prepend ) { r.resize( 1 + in.size() ); r[ 0 ] = 'n'; } else r.resize( in.size() ); std::transform( in.begin(), in.end(), r.begin() + ( prepend ? 1 : 0 ), prepare_char ); return r; } static xml_formatter& get_instance() { static xml_formatter instance; return instance; } protected: static bool iequal( const std::string& s1, const std::string& s2 ) { if ( s1.length() != s2.length() ) return false; return std::equal( s1.begin(), s1.end(), s2.begin(), equal_ignore_case ); } static const std::string& cdata_begin() { static std::string s = ""; return s; } static bool has_xml_entity( char c ) { return c == '<' || c == '>' || c == '&' || c == '\'' || c == '\"'; } static char prepare_char( char c ) { if ( isalnum( c ) ) return c; else return '_'; } static bool equal_ignore_case( char c1, char c2 ) { return tolower( c1 ) == tolower( c2 ); } private: xml_formatter() {} xml_formatter( const xml_formatter& ); }; struct attribute_properties { std::string name; int value_max_width; attribute_properties( const std::string& name_, int value_max_width_ ) : name( name_ ), value_max_width( value_max_width_ ) {} int get_whole_width() const { return 1 + name.length() + 1 + 1 + value_max_width + 1; } std::string get_empty_string() const { return " " + name + "=\"\"" + std::string( value_max_width, ' ' ); } }; struct node_attributes { typedef std::vector< attribute_properties > attributes_t; attributes_t attributes; const attribute_properties* get_attribute( const std::string& attr_name ) const { attributes_t::const_iterator it; for ( it = attributes.begin(); it != attributes.end(); ++it ) { if ( it->name == attr_name ) return &(*it); } return 0; } int get_attribute_offset( const std::string& attr_name ) const { attributes_t::const_iterator it; int offset = 0; for ( it = attributes.begin(); it != attributes.end(); ++it ) { if ( it->name != attr_name ) offset += it->get_whole_width(); else break; } if ( it == attributes.end() ) offset = -1; return offset; } int get_whole_length() const { int l = 0; attributes_t::const_iterator it; for ( it = attributes.begin(); it != attributes.end(); ++it ) l += it->get_whole_width(); return l; } std::string get_empty_string() const { std::string e; attributes_t::const_iterator it; for ( it = attributes.begin(); it != attributes.end(); ++it ) e.append( it->get_empty_string() ); return e; } }; struct character_writer { public: char ch; unsigned int count; character_writer( char ch_, unsigned int count_ ) : ch( ch_ ), count( count_ ) {} }; std::ostream& operator<<( std::ostream& ostr, const character_writer& t ) { for ( unsigned int i = 0; i < t.count; ++i ) ostr.put( t.ch ); return ostr; } class xml_writer { public: struct node_handle { xml_writer* writer; std::string node_name; unsigned int offset; const node_attributes* attributes; void write_attribute( const std::string& name, const std::string& value ) { int attr_offset = attributes->get_attribute_offset( name ); if ( attr_offset == -1 ) return; const attribute_properties* attr = attributes->get_attribute( name ); assert( attr != 0 ); unsigned int p = offset + 1 + node_name.length() + attr_offset; p += 1 + name.length() + 1 + 1; int len = std::min( (int)value.length(), attr->value_max_width ); writer->out_stream->seekp( p ); (*writer->out_stream) << value.substr( 0, len ) << "\"" << character_writer( ' ', attr->value_max_width - len ); writer->out_stream->seekp( 0, std::ios_base::end ); } }; xml_writer( std::ostream* ostr ) : out_stream( ostr ) { } node_handle write_node( const std::string& name, const node_attributes* attributes ) { node_handle n; n.writer = this; n.attributes = attributes; std::string pname = xml_formatter::get_instance().prepare( name ); n.node_name = pname; n.offset = (unsigned int)out_stream->tellp() + node_stack.size(); (*out_stream) << character_writer( '\t', node_stack.size() ) << '<' << pname << attributes->get_empty_string() << '>' << std::endl; node_stack.push( pname ); return n; } node_handle write_text_node( const std::string& name, const node_attributes* attributes, const std::string& text ) { node_handle n; n.writer = this; n.attributes = attributes; std::string pname = xml_formatter::get_instance().prepare( name ); n.node_name = pname; n.offset = (unsigned int)out_stream->tellp() + node_stack.size(); std::string etext = xml_formatter::get_instance().encode( text ); (*out_stream) << character_writer( '\t', node_stack.size() ) << '<' << pname << attributes->get_empty_string() << '>' << etext << "' << std::endl; return n; } void write_end_node() { if ( node_stack.empty() ) return; (*out_stream) << character_writer( '\t', node_stack.size() - 1 ) << "' << std::endl; node_stack.pop(); } protected: std::ostream* out_stream; std::stack< std::string > node_stack; friend struct node_handle; private: xml_writer( const xml_writer& ); xml_writer& operator=( const xml_writer& ); }; class node_attributes_collection { public: unsigned int register_node_attributes( const node_attributes& ap ) { collection.push_back( ap ); return collection.size() - 1; } const node_attributes& get_node_attributes( unsigned int index ) const { return collection[ index ]; } static node_attributes_collection& get_instance() { static node_attributes_collection instance; return instance; } protected: std::vector< node_attributes > collection; private: node_attributes_collection() { initialize(); } node_attributes_collection( const node_attributes_collection& ); void initialize() { collection.clear(); { node_attributes a; a.attributes.push_back( attribute_properties( "p", 7 ) ); collection.push_back( a ); } } }; class logger { public: logger( std::ostream* ostr ) { writer = new xml_writer( ostr ); enter_node( "tlog" ); } ~logger() { while ( ! node_stack.empty() ) leave_node(); delete writer; } enum message_priority { PRIORITY_INFORMATION = 1, PRIORITY_WARNING, PRIORITY_ERROR }; void enter_node( const std::string& node_name ) { xml_writer::node_handle n; n = writer->write_node( node_name, get_node_attributes() ); node_stack.push( n ); priority_stack.push( 0 ); } void leave_node() { if ( node_stack.empty() ) return; assert( ! priority_stack.empty() ); int pr = priority_stack.top(); node_stack.top().write_attribute( "p", get_priority_name( (message_priority)pr ) ); node_stack.pop(); priority_stack.pop(); writer->write_end_node(); if ( ! priority_stack.empty() ) priority_stack.top() = std::max( priority_stack.top(), pr ); } void information( const std::string& text ) { log( text, PRIORITY_INFORMATION ); } void warning( const std::string& text ) { log( text, PRIORITY_WARNING ); } void error( const std::string& text ) { log( text, PRIORITY_ERROR ); } void log( const std::string& text, message_priority pr ) { if ( node_stack.empty() ) return; assert( ! priority_stack.empty() ); xml_writer::node_handle n; n = writer->write_text_node( "log", get_text_node_attributes(), text ); n.write_attribute( "p", get_priority_name( pr ) ); priority_stack.top() = std::max( priority_stack.top(), (int)pr ); } struct scoped_node_keeper { scoped_node_keeper( logger& l, const std::string& name ) : logger_ptr( &l ) { logger_ptr->enter_node( name ); } ~scoped_node_keeper() { logger_ptr->leave_node(); } private: logger* logger_ptr; scoped_node_keeper( const scoped_node_keeper& ); scoped_node_keeper& operator=( const scoped_node_keeper& ); }; static std::string get_priority_name( message_priority p ) { if ( p == PRIORITY_INFORMATION ) return "info"; else if ( p == PRIORITY_WARNING ) return "warning"; else if ( p == PRIORITY_ERROR ) return "error"; else return "unknown"; } protected: const node_attributes* get_node_attributes() const { return &node_attributes_collection::get_instance().get_node_attributes( 0 ); } const node_attributes* get_text_node_attributes() const { return &node_attributes_collection::get_instance().get_node_attributes( 0 ); } std::stack< xml_writer::node_handle > node_stack; std::stack< int > priority_stack; xml_writer* writer; private: logger( const logger& ); logger& operator=( const logger& ); }; } #define TLOG_TOKEN_PASTE_(x, y) x##y #define TLOG_TOKEN_PASTE(x, y) TLOG_TOKEN_PASTE_(x, y) #define TLOG_SCOPED_NODE( Logger, NodeName ) \ tlog::logger::scoped_node_keeper TLOG_TOKEN_PASTE( TLOG__LOGGER__SCOPED_NODE_KEEPER_, __LINE__ ) ( (Logger), (NodeName) ) #define TLOG_INFO( Logger, Text ) \ (Logger).information( Text ) #define TLOG_WARNING( Logger, Text ) \ (Logger).warning( Text ) #define TLOG_ERROR( Logger, Text ) \ (Logger).error( Text ) #endif // MY_LIBS___TLOG__TLOG_HPP