Sierra Toolkit Version of the Day
OutputLog.cpp
00001 /*------------------------------------------------------------------------*/
00002 /*                 Copyright 2010 Sandia Corporation.                     */
00003 /*  Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive   */
00004 /*  license for use of this work by or on behalf of the U.S. Government.  */
00005 /*  Export of this program may require a license from the                 */
00006 /*  United States Government.                                             */
00007 /*------------------------------------------------------------------------*/
00008 
00009 #include <stk_util/environment/OutputLog.hpp>
00010 #include <stk_util/util/TeeStreambuf.hpp>
00011 
00012 
00013 #include <map>
00014 #include <list>
00015 #include <string>
00016 #include <iostream>
00017 #include <sstream>
00018 #include <fstream>
00019 #include <stdexcept>
00020 #include <cctype>
00021 
00022 namespace stk {
00023 
00024 namespace {
00025 
00026 struct LogStream
00027 {
00028   LogStream(const std::string &path, std::ostream *output_stream, std::ofstream *file_stream)
00029     : m_path(path),
00030       m_ostream(output_stream),
00031       m_ofstream(file_stream)
00032   {}
00033 
00034   ~LogStream();
00035 
00036   std::string           m_path;
00037   std::ostream *        m_ostream;
00038   std::ofstream *       m_ofstream;
00039 
00040   private:
00041   LogStream(const LogStream &);
00042   void operator = (const LogStream &);
00043 };
00044 
00045 #ifdef __INTEL_COMPILER
00046 #pragma warning(push)
00047 #pragma warning(disable: 444)
00048 #endif
00049 struct LogStreamMap : public std::map<std::string, LogStream *>
00050 {
00051   LogStreamMap()
00052   {}
00053 
00054   ~LogStreamMap() {
00055     while (!empty()) {
00056       LogStream *log_stream = (*begin()).second;
00057       erase(begin());
00058       delete log_stream;
00059     }
00060   }
00061 };
00062 #ifdef __INTEL_COMPILER
00063 #pragma warning(pop)
00064 #endif
00065 
00066 struct OStreamTeeStreambuf
00067 {
00068   OStreamTeeStreambuf(std::ostream &output_stream)
00069     : m_ostream(&output_stream),
00070       m_origRdbuf(output_stream.rdbuf()),
00071       m_teeStreambuf(new tee_streambuf(&output_stream))
00072   {
00073     m_ostream->rdbuf(m_teeStreambuf);
00074   }
00075 
00076   ~OStreamTeeStreambuf();
00077 
00078   std::ostream *        m_ostream;
00079   std::streambuf *      m_origRdbuf;
00080   tee_streambuf *       m_teeStreambuf;
00081 
00082   private:
00083   OStreamTeeStreambuf(const OStreamTeeStreambuf &);
00084   void operator = (const OStreamTeeStreambuf &);
00085 };
00086 
00087 #ifdef __INTEL_COMPILER
00088 #pragma warning(push)
00089 #pragma warning(disable: 444)
00090 #endif
00091 struct OStreamTeeStreambufMap : public std::map<std::string, OStreamTeeStreambuf *>
00092 {
00093   OStreamTeeStreambufMap()
00094   {}
00095 
00096   ~OStreamTeeStreambufMap() {
00097     while (!empty()) {
00098       OStreamTeeStreambuf *tee_streambuf = (*begin()).second;
00099       erase(begin());
00100       delete tee_streambuf;
00101     }
00102   }
00103 };
00104 #ifdef __INTEL_COMPILER
00105 #pragma warning(pop)
00106 #endif
00107 
00108 LogStreamMap &
00109 get_file_stream_map()
00110 {
00111   static LogStreamMap s_logFileStreamMap;
00112 
00113   return s_logFileStreamMap;
00114 }
00115 
00116 
00117 OStreamTeeStreambufMap &
00118 get_ostream_tee_streambuf_map()
00119 {
00120   static OStreamTeeStreambufMap s_ostreamTeeStreambufMap;
00121 
00122   return s_ostreamTeeStreambufMap;
00123 }
00124 
00125 
00126 LogStream::~LogStream()
00127 {
00128   m_ostream->flush();
00129 
00130   // If the output stream was created internally (via bind_output_stream), be sure to remove it from
00131   // all OStreamTeeStreamBuf's
00132   if (m_ofstream) {
00133     OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00134 
00135     for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it)
00136       (*it).second->m_teeStreambuf->remove(m_ofstream);
00137 
00138     delete m_ofstream;
00139   }
00140 }
00141 
00142 
00143 OStreamTeeStreambuf::~OStreamTeeStreambuf()
00144 {
00145   if (m_ostream) {
00146     m_ostream->flush();
00147     m_ostream->rdbuf(m_origRdbuf);
00148   }
00149 
00150   // Be sure to remove this from all OStreamTeeStreamBuf's
00151   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00152 
00153   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it)
00154     (*it).second->m_teeStreambuf->remove(m_ostream);
00155 
00156   delete m_teeStreambuf;
00157 }
00158 
00159 } // namespace <empty>
00160 
00161 
00162 void
00163 create_log_file(
00164   const std::string &   name,
00165   const std::string &   path)
00166 {
00167   LogStreamMap &file_stream_map = get_file_stream_map();
00168 
00169   close_log_file(name);
00170 
00171   std::ofstream *file_stream = new std::ofstream(path.c_str());
00172 
00173   file_stream_map[name] = new LogStream(path, file_stream, file_stream);
00174 }
00175 
00176 
00177 void
00178 close_log_file(
00179   const std::string &   name)
00180 {
00181   LogStreamMap &file_stream_map = get_file_stream_map();
00182 
00183   LogStreamMap::iterator it = file_stream_map.find(name);
00184 
00185   if (it != file_stream_map.end()) {
00186     delete (*it).second;
00187     file_stream_map.erase(it);
00188   }
00189 }
00190 
00191 
00192 void
00193 register_log_ostream(
00194   std::ostream &        os,
00195   const std::string &   name)
00196 {
00197   LogStreamMap &file_stream_map = get_file_stream_map();
00198 
00199   LogStreamMap::iterator it = file_stream_map.find(name);
00200 
00201   if (it != file_stream_map.end()) {
00202     std::ostringstream s;
00203     s << "Log ostream " << name << " has already been registered";
00204 
00205     //Do we really want to throw if a stream is registered multiple times?
00206     //I don't think so... commenting this out.
00207     //throw std::runtime_error(s.str());
00208   }
00209   else {
00210     file_stream_map[name] = new LogStream(name, &os, 0);
00211   }
00212 }
00213 
00214 
00215 void
00216 unregister_log_ostream(
00217   std::ostream &        os)
00218 {
00219   LogStreamMap &file_stream_map = get_file_stream_map();
00220 
00221   for (LogStreamMap::iterator it = file_stream_map.begin(); it != file_stream_map.end(); ++it) {
00222     if ((*it).second->m_ostream == &os) {
00223       delete (*it).second;
00224       file_stream_map.erase(it);
00225       break;
00226     }
00227   }
00228 
00229   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00230 
00231   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it)
00232     (*it).second->m_teeStreambuf->remove(&os);
00233 }
00234 
00235 
00236 const std::string &
00237 get_log_path(
00238   const std::string &   name)
00239 {
00240   static std::string not_found = "";
00241 
00242   LogStreamMap &file_stream_map = get_file_stream_map();
00243 
00244   LogStreamMap::iterator it = file_stream_map.find(name);
00245 
00246   return it == file_stream_map.end() ? not_found : (*it).second->m_path;
00247 }
00248 
00249 
00250 std::ostream *
00251 get_log_ostream(
00252   const std::string &   name)
00253 {
00254   LogStreamMap &file_stream_map = get_file_stream_map();
00255 
00256   LogStreamMap::iterator it = file_stream_map.find(name);
00257 
00258   return it == file_stream_map.end() ? 0 : (*it).second->m_ostream;
00259 }
00260 
00261 
00262 void
00263 register_ostream(
00264   std::ostream &        os,
00265   const std::string &   name)
00266 {
00267   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00268 
00269   unregister_ostream(os);
00270 
00271   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00272 
00273   if (it != ostream_tee_streambuf_map.end()) {
00274 //     delete (*it).second;
00275 //     ostream_tee_streambuf_map.erase(it);
00276 //   }
00277     std::ostringstream s;
00278     s << "Output stream " << name << " has already been registered";
00279 
00280     throw std::runtime_error(s.str());
00281   }
00282 
00283   ostream_tee_streambuf_map[name] = new OStreamTeeStreambuf(os);
00284 }
00285 
00286 
00287 void
00288 unregister_ostream(
00289   std::ostream &        os)
00290 {
00291   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00292 
00293   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it)
00294     (*it).second->m_teeStreambuf->remove(&os);
00295 
00296   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it) {
00297     if ((*it).second->m_ostream == &os) {
00298       delete (*it).second;
00299       ostream_tee_streambuf_map.erase(it);
00300       break;
00301     }
00302   }
00303 
00304 }
00305 
00306 
00307 std::ostream *
00308 get_ostream_ostream(
00309   const std::string &   name)
00310 {
00311   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00312 
00313   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00314 
00315   return it == ostream_tee_streambuf_map.end() ? 0 : (*it).second->m_ostream;
00316 }
00317 
00318 
00319 tee_streambuf *
00320 get_ostream_tee_streambuf(
00321   const std::string &   name)
00322 {
00323   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00324 
00325   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00326 
00327   return it == ostream_tee_streambuf_map.end() ? 0 : (*it).second->m_teeStreambuf;
00328 }
00329 
00330 
00331 std::ostream *
00332 get_ostream_tee_ostream(
00333   const std::string &   name)
00334 {
00335   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00336 
00337   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00338 
00339   return it == ostream_tee_streambuf_map.end() ? 0 : (*it).second->m_ostream;
00340 }
00341 
00342 
00343 bool
00344 is_registered_ostream(
00345   const std::string &   name)
00346 {
00347   return get_ostream_ostream(name) != 0;
00348 }
00349 
00350 
00351 namespace {
00352 
00353 struct Command
00354 {
00355   virtual ~Command()
00356   {}
00357 
00358   virtual void execute() = 0;
00359 };
00360 
00361 #ifdef __INTEL_COMPILER
00362 #pragma warning(push)
00363 #pragma warning(disable: 444)
00364 #endif
00365 struct CommandList : public std::list<Command *>
00366 {
00367   CommandList()
00368     : std::list<Command *>()
00369   {}
00370 
00371   ~CommandList()
00372   {
00373     for (std::list<Command *>::iterator it = begin(); it != end(); ++it)
00374       delete (*it);
00375   }
00376 };
00377 #ifdef __INTEL_COMPILER
00378 #pragma warning(pop)
00379 #endif
00380 
00381 namespace {
00382 
00383 tee_streambuf &
00384 parse_tee_streambuf(
00385   const std::string &   tee_ostream_name)
00386 {
00387   tee_streambuf *osb = get_ostream_tee_streambuf(tee_ostream_name);
00388 
00389   if (!osb) {
00390     std::ostringstream s;
00391 
00392     s << "Output stream " << tee_ostream_name << " has not been registered for output logging";
00393     throw std::runtime_error(s.str());
00394   }
00395 
00396   return *osb;
00397 }
00398 
00399 
00400 std::ostream *
00401 parse_ostream(
00402   const std::string &   ostream_name)
00403 {
00404   std::ostream *os = get_log_ostream(ostream_name);
00405 
00406   if (!os)
00407     os = get_ostream_tee_ostream(ostream_name);
00408 
00409   if (!os) {
00410     std::ostringstream s;
00411 
00412     s << "Log file '" << ostream_name << "' has not been registered";
00413     throw std::runtime_error(s.str());
00414   }
00415 
00416   return os;
00417 }
00418 
00419 
00420 struct OpenLog : public Command
00421 {
00422   OpenLog(
00423     const std::string &name,
00424     const std::string &path)
00425     : m_name(name),
00426       m_path(path)
00427   {}
00428 
00429   virtual ~OpenLog()
00430   {}
00431 
00432   virtual void execute() {
00433     create_log_file(m_name, m_path);
00434   }
00435 
00436   std::string        m_name;
00437   std::string        m_path;
00438 };
00439 
00440 
00441 struct CloseLog : public Command
00442 {
00443   CloseLog(
00444     const std::string &name)
00445     : m_name(name)
00446   {}
00447 
00448   virtual ~CloseLog()
00449   {}
00450 
00451   virtual void execute() {
00452     close_log_file(m_name);
00453   }
00454 
00455   std::string        m_name;
00456 };
00457 
00458 
00459 struct ClearTeeOStream : public Command
00460 {
00461   ClearTeeOStream(
00462     const std::string & tee_ostream_name)
00463     : m_teeOStreamName(tee_ostream_name)
00464   {}
00465 
00466   virtual ~ClearTeeOStream()
00467   {}
00468 
00469   virtual void execute() {
00470     parse_tee_streambuf(m_teeOStreamName).clear();
00471   }
00472 
00473   std::string        m_teeOStreamName;
00474 };
00475 
00476 
00477 struct AddTeeOStream : public Command
00478 {
00479   AddTeeOStream(
00480     const std::string & tee_ostream_name,
00481     const std::string & ostream_name)
00482     : m_teeOStreamName(tee_ostream_name),
00483       m_ostreamName(ostream_name)
00484   {}
00485 
00486   virtual ~AddTeeOStream()
00487   {}
00488 
00489   virtual void execute() {
00490     if (m_ostreamName != "null")
00491       parse_tee_streambuf(m_teeOStreamName).add(parse_ostream(m_ostreamName));
00492   }
00493 
00494   std::string        m_teeOStreamName;
00495   std::string        m_ostreamName;
00496 };
00497 
00498 
00499 struct RemoveTeeOStream : public Command
00500 {
00501   RemoveTeeOStream(
00502     const std::string & tee_ostream_name,
00503     const std::string & ostream_name)
00504     : m_teeOStreamName(tee_ostream_name),
00505       m_ostreamName(ostream_name)
00506   {}
00507 
00508   virtual ~RemoveTeeOStream()
00509   {}
00510 
00511   virtual void execute() {
00512     parse_tee_streambuf(m_teeOStreamName).remove(parse_ostream(m_ostreamName));
00513   }
00514 
00515   std::string        m_teeOStreamName;
00516   std::string        m_ostreamName;
00517 };
00518 
00519 } // namespace <empty>
00520 
00521 
00522 /*
00523  * Startup:     out > cout pout > cout dout > cout
00524  * Normal:      out > log-path+pout pout > null dout > out
00525  * Diagnostic:  out > out-path+pout pout > pout-path dout > out
00526  *
00527  * Modify:      out > +pout
00528  *              out > -pout
00529  */
00530 void
00531 parse_output_description(
00532   const std::string &   output_description,
00533   CommandList &         command_list)
00534 {
00535   typedef std::pair<const char *, const char *>  Token;
00536   typedef std::list<Token> TokenList;
00537 
00538   command_list.clear();
00539 
00540   TokenList tokens;
00541 
00542   for (const char *c = output_description.c_str(); *c; ) {
00543     if (std::isspace(*c))
00544       ++c;
00545 
00546     else if (*c == '>' || *c == '+' || *c == '-' || *c == '=') {
00547       tokens.push_back(Token(c, c + 1));
00548       ++c;
00549     }
00550 
00551     else if (*c == '\"') {
00552       const char *d = c + 1;
00553       while (*d && *d != '\"')
00554         ++d;
00555       tokens.push_back(Token(c + 1, d));
00556       c = d + 1;
00557     }
00558 
00559     else {
00560       const char *d = c;
00561       while (std::isgraph(*d) && *d != '+' && *d != '-' &&*d != '=' && *d != '>')
00562         ++d;
00563       tokens.push_back(Token(c, d));
00564       c = d;
00565     }
00566   }
00567 
00568   for (TokenList::iterator it = tokens.begin(); it != tokens.end(); ) {
00569     std::string name((*it).first, (*it).second);
00570 
00571     ++it; if (it == tokens.end()) break;
00572     std::string operation((*it).first, (*it).second);
00573 
00574     if (operation == "=") {
00575       ++it;  if (it == tokens.end()) break;
00576       std::string path((*it).first, (*it).second);
00577       if (!path.empty())
00578         command_list.push_back(new OpenLog(name, path));
00579       else
00580         command_list.push_back(new CloseLog(name));
00581       ++it;  if (it == tokens.end()) break;
00582     }
00583 
00584     else if (operation == ">") {
00585       parse_tee_streambuf(name);
00586 
00587       ++it;  if (it == tokens.end()) break;
00588       std::string token(std::string((*it).first, (*it).second));
00589       if (token != "+" && token != "-") {
00590         std::string ostream_name(std::string((*it).first, (*it).second));
00591 
00592         command_list.push_back(new ClearTeeOStream(name));
00593         command_list.push_back(new AddTeeOStream(name, ostream_name));
00594         ++it;  if (it == tokens.end()) break;
00595       }
00596 
00597       while (it != tokens.end()) {
00598         token = std::string((*it).first, (*it).second);
00599         if (token == "+") {
00600           ++it;  if (it == tokens.end()) break;
00601           std::string ostream_name(std::string((*it).first, (*it).second));
00602 
00603           command_list.push_back(new AddTeeOStream(name, ostream_name));
00604           ++it;  if (it == tokens.end()) break;
00605         }
00606 
00607         else if (token == "-") {
00608           ++it;  if (it == tokens.end()) break;
00609           std::string ostream_name(std::string((*it).first, (*it).second));
00610 
00611           command_list.push_back(new RemoveTeeOStream(name, ostream_name));
00612           ++it;  if (it == tokens.end()) break;
00613         }
00614         else
00615           break;
00616       }
00617     }
00618   }
00619 }
00620 
00621 void
00622 execute(
00623   const CommandList &   command_list)
00624 {
00625   for (CommandList::const_iterator it = command_list.begin(); it != command_list.end(); ++it)
00626     (*it)->execute();
00627 }
00628 
00629 } // namespace <empty>
00630 
00631 void
00632 bind_output_streams(
00633   const std::string &   output_description)
00634 {
00635   stk::CommandList command_list;
00636 
00637   parse_output_description(output_description, command_list);
00638   execute(command_list);
00639 }
00640 
00641 } // namespace stk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends