logger.cc

Go to the documentation of this file.
00001 /*
00002    The lestes compiler suite
00003    Copyright (C) 2002, 2003, 2004, 2005 Miroslav Tichy
00004    Copyright (C) 2002, 2003, 2004, 2005 Petr Zika
00005    Copyright (C) 2002, 2003, 2004, 2005 Vojtech Hala
00006    Copyright (C) 2002, 2003, 2004, 2005 Jiri Kosina
00007    Copyright (C) 2002, 2003, 2004, 2005 Pavel Sanda
00008    Copyright (C) 2002, 2003, 2004, 2005 Jan Zouhar
00009    Copyright (C) 2002, 2003, 2004, 2005 Rudolf Thomas
00010 
00011    This program is free software; you can redistribute it and/or modify
00012    it under the terms of the GNU General Public License as published by
00013    the Free Software Foundation; version 2 of the License.
00014 
00015    This program is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018    GNU General Public License for more details.
00019 
00020    See the full text of the GNU General Public License version 2, and
00021    the limitations in the file doc/LICENSE.
00022 
00023    By accepting the license the licensee waives any and all claims
00024    against the copyright holder(s) related in whole or in part to the
00025    work, its use, and/or the inability to use it.
00026  
00027  */
00028 #include <lestes/msg/logger.hh>
00029 #include <lestes/msg/logger_util.hh>
00030 #include <lestes/std/objectize_macros.hh>
00031 #include <stack>
00032 #include <iostream>
00033 #include <fstream>
00034 
00035 #include <libxml/xmlmemory.h>
00036 #include <libxml/parser.h>
00037 
00038 package(lestes);
00039 package(msg);
00040 
00041 lstring logger2fullname( const ptr<logger> &l );
00042 
00043 /* initialization to itself is needed, as the initialization may actually be
00044  * called after an initializer in other translation unit has called
00045  * root_instance(), which would have changed the value of the_root_instance.
00046  * if that is the case, the changed value is not lost, just copied.
00047  * if it is not the case, null value is copied as from the zero-initialized
00048  * object [8.5/6]
00049  */
00050 ptr<logger> logger::the_root_instance = the_root_instance;
00051 /*
00052  * null_stream, unlike other static fields, has to be initialized even when
00053  * logger::init() is not called. the loggers must work (have non-null
00054  * null_ostream at hand) even when logger::init() was never called, as in that
00055  * case, logging is off
00056  */
00057 ptr<ostream_wrapper> logger::null_ostream =
00058                 ostream_wrapper::create( new ::std::ofstream(), true );
00059 // does not have to be initialized here, but is
00060 ptr<ostream_wrapper> logger::cerr_wrapper =
00061                 ostream_wrapper::create( &::std::cerr, false );
00062 ptr<logger::files_map_type> logger::files_map = files_map_type::create();
00063 
00064 ptr<logger> logger::create( const lstring & a_name, const ptr<logger> & a_parent )
00065 {
00066         return new logger( a_name, a_parent );
00067 }
00068 
00069 ptr<logger> logger::root_instance()
00070 {
00071         if (!the_root_instance)
00072                 the_root_instance = new logger();
00073         return the_root_instance;
00074 }
00075 
00076 ptr<logger> logger::parent_get() const
00077 {
00078         return parent;
00079 }
00080 lstring logger::name_get() const
00081 {
00082         return name;
00083 }
00084 ptr<logger::children_map_type> logger::children_get() const
00085 {
00086         return children;
00087 }
00088 
00089 ptr < logger_formatter > logger::formatter_get() const
00090 {
00091         return formatter;
00092 }
00093 void logger::formatter_set(ptr < logger_formatter > x)
00094 {
00095         lassert(x);
00096         formatter = x;
00097 }
00098 #ifdef ALL_LOGGER_GETTERS_NEEDED
00099 bool logger::logging_get() const
00100 {
00101         return logging;
00102 }
00103 bool logger::logging_changed_get() const
00104 {
00105         return logging_changed;
00106 }
00107 #endif
00108 
00109 logger::logger()
00110         : logging(false), logging_changed(false), ostr(NULL),
00111         parent(this), children( children_map_type::create() ),
00112         formatter( plain_formatter::instance() )
00113 {}
00114 
00115 logger::logger( const lstring & a_name, const ptr<logger> a_parent )
00116         : logging(false), logging_changed(false), ostr(NULL), name(a_name),
00117         parent( checked(a_parent) ), children( children_map_type::create() ),
00118         formatter( plain_formatter::instance() )
00119 {
00120         bool not_inserted_yet = parent->children->insert(
00121                 *pair< lstring, srp<logger> >::create(name,this) ).second;
00122         lassert2( not_inserted_yet, "Trying to add a child logger with an already taken name." );
00123 }
00124 
00125 #define cast_xml( str ) ((const xmlChar *)(str))
00126 #define cast_c( str )   ((const char *)(str))
00127 
00128 static bool attr2bool( const xmlChar * value )
00129 {
00130         if (!strcmp(cast_c(value),"on"))
00131                 return true;
00132         if (!strcmp(cast_c(value),"off"))
00133                 return false;
00134 
00135         ::std::cerr << "Invalid on/off value '" << value << "'. Using 'off'." << ::std::endl;
00136 
00137         return false;
00138 }
00139 
00140 //! A friend of the logger class, see comments for its 'configure' method.
00141 class logger_configurator {
00142         // Only allow the method to be called from logger methods.
00143         friend class logger;
00144         //! Configures children of given logger according to given xml node chain.
00145         static void configure( xmlNode *node, bool inherited,
00146                 const ptr<logger> & parent_logger,
00147                 const ptr<ostream_wrapper> & parent_stream );
00148 };
00149 
00150 /*!
00151  * This method is not part of the logger class to avoid dependency on libxml headers.
00152  * However, as it touches logger's private fields so it has to be in a friend
00153  * class. Friend method would not help, as that would require parameter types
00154  * to be known in the header (where the logger class is declared).
00155  */
00156 void logger_configurator::configure( xmlNode *node, bool inherited,
00157                         const ptr<logger> & parent_logger,
00158                         const ptr<ostream_wrapper> & parent_stream )
00159 {
00160         lassert( node );
00161         lassert( parent_stream );
00162         
00163         // "full path" to the parent logger; used in error output
00164         lstring path = logger2fullname(parent_logger);
00165         if (path != "/")
00166                 path += "/";
00167 
00168         xmlChar *self_prop = NULL;
00169         xmlChar *children_prop = NULL;
00170         xmlChar *name_prop = NULL;
00171         xmlChar *file_prop = NULL;
00172         xmlChar * formatter_prop = NULL;
00173         xmlChar * formatter_param_prop = NULL;
00174 
00175         for ( ; node; node = node->next ) {
00176 
00177                 // skip text and other irrelevant nodes
00178                 if (node->type != XML_ELEMENT_NODE)
00179                         continue;
00180 
00181                 if (strcmp( cast_c(node->name), "logger" )) {
00182                         ::std::cerr << "Ignoring unrecognized element '" <<
00183                                 path << node->name << "'." << ::std::endl;
00184                         continue;
00185                 }
00186 
00187                 // cleanup after previous iteration; freeing NULLs in the first one will not hurt
00188                 xmlFree( self_prop );
00189                 xmlFree( children_prop );
00190                 xmlFree( name_prop );
00191                 xmlFree( file_prop );
00192                 xmlFree( formatter_prop );
00193                 xmlFree( formatter_param_prop );
00194                 name_prop = xmlGetProp( node, cast_xml("name") );
00195                 self_prop = xmlGetProp( node, cast_xml("self") );
00196                 children_prop = xmlGetProp( node, cast_xml("children") );
00197                 file_prop = xmlGetProp( node, cast_xml("file") );
00198                 formatter_prop = xmlGetProp( node, cast_xml("formatter") );
00199                 formatter_param_prop = xmlGetProp( node, cast_xml("parameter") );
00200 
00201                 if (!name_prop) {
00202                         ::std::cerr << "Name attribute missing in element under '" <<
00203                                 path << "'." << ::std::endl;
00204                         continue;
00205                 }
00206                 lstring name_attr(cast_c(name_prop));
00207 
00208                 bool self;
00209                 if (!self_prop)
00210                         self = inherited;
00211                 else
00212                         self = attr2bool(self_prop);
00213 
00214                 bool children;
00215                 if (!children_prop)
00216                         children = inherited;
00217                 else
00218                         children = attr2bool(children_prop);
00219 
00220                 if (parent_logger->children->find(name_attr)
00221                                 == parent_logger->children->end()) {
00222                         ::std::cerr << "Trying to configure non-existing logger '" <<
00223                                 path << name_attr << "'." << ::std::endl;
00224                         continue;
00225                 }
00226 
00227                 ptr<ostream_wrapper> stream;
00228                 if (file_prop && file_prop[0]) {
00229                         lstring fn(cast_c(file_prop));
00230                         if (logger::files_map->find(fn) == logger::files_map->end()) {
00231                                 stream = (*logger::files_map)[fn] =
00232                                         ostream_wrapper::create(
00233                                                 new ::std::ofstream(fn.c_str()), true );
00234                         } else
00235                                 stream = (*logger::files_map)[fn];
00236                 } else if (file_prop) {
00237                         // empty value, use cerr
00238                         stream = logger::cerr_wrapper;
00239                 } else {
00240                         // attribute missing, use stream from parent
00241                         stream = parent_stream;
00242                 }
00243 
00244                 const srp<logger> & this_logger = (*(parent_logger->children))[name_attr];
00245                 if (this_logger->logging_changed) {
00246                         ::std::cerr << "Trying to configure logger '" <<
00247                                 path << name_attr <<
00248                                 "' for the second time, ignoring." << ::std::endl;
00249                         continue;
00250                 }
00251                 this_logger->logging = self;
00252                 this_logger->logging_changed = true;
00253                 this_logger->ostr = stream;
00254 
00255                 this_logger->formatter = formatter_factory::create_formatter(
00256                                 formatter_prop ? cast_c(formatter_prop) : "",
00257                                 formatter_param_prop ? cast_c(formatter_param_prop) : "");
00258 
00259                 // call self recursively
00260                 if (node->children)
00261                         configure( node->children, children, this_logger, stream );
00262         }
00263         
00264         // check that all the child loggers were configured
00265         ptr<logger::children_map_type> ch_map = parent_logger->children;
00266         for (logger::children_map_type::const_iterator it = ch_map->begin();
00267                         it != ch_map->end(); ++it )
00268                 if (!(it->second->logging_changed))
00269                         ::std::cerr << "Logger '" << path << it->second->name <<
00270                                 "' not configured." << ::std::endl;
00271 
00272         xmlFree( self_prop );
00273         xmlFree( children_prop );
00274         xmlFree( name_prop );
00275         xmlFree( file_prop );
00276 }
00277 
00278 /*!
00279  * Tries to parse filename of given name and apply settings in it to the logger tree.
00280  * After returning true, should not be called again (violation is detected and not fatal).
00281  * When not called at all, all loggers are off.
00282  *
00283  * \return  false when file could not be read or parsed, when the root element is invalid, or when the root element did not contain the required attributes
00284  * \return  true otherwise; this includes some non-fatal errors which are reported to the user
00285  */
00286 bool logger::init( const lstring & filename )
00287 {
00288         if (root_instance()->logging_changed) {
00289                 ::std::cerr << "Trying to initialize loggers again, ignoring." << ::std::endl;
00290                 return false;
00291         }
00292 
00293         /*
00294          * this initializes the library and checks potential ABI mismatches
00295          * between the version it was compiled for and the actual shared
00296          * library used.
00297          */
00298         LIBXML_TEST_VERSION
00299 
00300         xmlDoc *doc = NULL;
00301         xmlNode *root_element = NULL;
00302         xmlChar *self_prop = NULL;
00303         xmlChar *children_prop = NULL;
00304         xmlChar *name_prop = NULL;
00305         xmlChar *file_prop = NULL;
00306 
00307         ptr<ostream_wrapper> stream;
00308 
00309         bool result = false;
00310         bool self;
00311         bool children;
00312 
00313         /* parse the file and get the DOM */
00314         doc = xmlParseFile( filename.c_str() );
00315         if (!doc) {
00316                 ::std::cerr << "Could not parse configuration file." << ::std::endl;
00317                 goto err_out;
00318         }
00319 
00320         root_element = xmlDocGetRootElement(doc);
00321         if (!root_element) {
00322                 ::std::cerr << "Root element missing." << ::std::endl;
00323                 goto err_out;
00324         }
00325         if (strcmp( cast_c(root_element->name), "logger" )) {
00326                 ::std::cerr << "Invalid root element '" << root_element->name <<
00327                         "'. Expected 'logger'." << ::std::endl;
00328                 goto err_out;
00329         }
00330 
00331         self_prop = xmlGetProp( root_element, cast_xml("self") );
00332         children_prop = xmlGetProp( root_element, cast_xml("children") );
00333 
00334         if (!self_prop || !children_prop) {
00335                 ::std::cerr << "Both 'self' and 'children' attributes have"
00336                         " to be set in the root logger element." << ::std::endl;
00337                 goto err_out;
00338         }
00339 
00340         name_prop = xmlGetProp( root_element, cast_xml("name") );
00341         if (name_prop)
00342                 ::std::cerr << "Name attribute ignored in the root logger element." << ::std::endl;
00343         file_prop = xmlGetProp( root_element, cast_xml("file") );
00344 
00345         self = attr2bool(self_prop);
00346         children = attr2bool(children_prop);
00347         // empty or non-existing 'file' attribute results in cerr being used
00348         if (file_prop && file_prop[0]) {
00349                 lstring fn(cast_c(file_prop));
00350                 stream = (*files_map)[fn] = ostream_wrapper::create(
00351                                 new ::std::ofstream(fn.c_str()), true );
00352         } else
00353                 stream = cerr_wrapper;
00354 
00355         root_instance()->logging = self;
00356         root_instance()->logging_changed = true;
00357         root_instance()->ostr = stream;
00358 
00359         if (root_element->children)
00360                 logger_configurator::configure( root_element->children, children,
00361                                                 root_instance(), stream );
00362 
00363         result = true;
00364 err_out:
00365         xmlFree( self_prop );
00366         xmlFree( children_prop );
00367         xmlFree( name_prop );
00368         xmlFree( file_prop );
00369         xmlFreeDoc( doc );
00370         xmlCleanupParser();
00371         return result;
00372 }
00373 
00374 /*!
00375  * Can be called without logger::init() being called previously.
00376  */
00377 void logger::finish()
00378 {
00379         for ( files_map_type::iterator it = files_map->begin(); it != files_map->end(); ++it )
00380                 it->second->release();  // this destructs the ofstream; the file is closed
00381 }
00382 
00383 void logger::subtree_dump( const ptr<logger> & l, ::std::ostream & os )
00384 {
00385         children_map_type::const_iterator it = l->children->begin();
00386         children_map_type::const_iterator end_it = l->children->end();
00387 
00388         for ( ; it != end_it; ++it ) {
00389                 os << "<logger name=\"" << it->second->name << '"';
00390                 if (it->second->children->empty()) {
00391                         os << " />" << ::std::endl;
00392                 } else {
00393                         os << '>' << ::std::endl;
00394                         subtree_dump( it->second, os );
00395                         os << "</logger>" << ::std::endl;
00396                 }
00397         }
00398 }
00399 
00400 ::std::ostream & logger::dump_skeleton( ::std::ostream & os )
00401 {
00402         os <<   "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
00403                 "<logger self=\"off\" children=\"off\""
00404                         " xmlns=\"http://lestes.jikos.cz/schemas/log-conf\">" << ::std::endl;
00405         
00406         subtree_dump( root_instance(), os );
00407         // the root element, unlike the other ones, is never shortened to <logger ... />
00408         os << "</logger>" << ::std::endl;
00409         return os;
00410 }
00411 
00412 ::std::ostream & logger::operator<<( const ptr<logger_formatter> & lf )
00413 {
00414         return lf->format( this, *((logging ? *ostr : *null_ostream).stream_get()) );
00415 }
00416 
00417 void logger::gc_mark()
00418 {
00419         ostr.gc_mark();
00420         parent.gc_mark();
00421         children.gc_mark();
00422         formatter.gc_mark();
00423         object::gc_mark();
00424 }
00425 
00426 end_package(msg);
00427 end_package(lestes);
00428 

Generated on Mon Feb 12 18:22:38 2007 for lestes by doxygen 1.5.1-20070107