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   if(!file_stream->good()) {
00174 
00175     std::ostringstream s;
00176     s << "Cannot open output log file '" << path << "' directory does not exist or is write protected.";
00177 
00178     throw std::runtime_error(s.str());
00179 
00180   }
00181 
00182 
00183   file_stream_map[name] = new LogStream(path, file_stream, file_stream);
00184 }
00185 
00186 
00187 void
00188 close_log_file(
00189   const std::string &   name)
00190 {
00191   LogStreamMap &file_stream_map = get_file_stream_map();
00192 
00193   LogStreamMap::iterator it = file_stream_map.find(name);
00194 
00195   if (it != file_stream_map.end()) {
00196     delete (*it).second;
00197     file_stream_map.erase(it);
00198   }
00199 }
00200 
00201 
00202 void
00203 register_log_ostream(
00204   std::ostream &        os,
00205   const std::string &   name)
00206 {
00207   LogStreamMap &file_stream_map = get_file_stream_map();
00208 
00209   LogStreamMap::iterator it = file_stream_map.find(name);
00210 
00211   if (it != file_stream_map.end()) {
00212     std::ostringstream s;
00213     s << "Log ostream " << name << " has already been registered";
00214 
00215     //Do we really want to throw if a stream is registered multiple times?
00216     //I don't think so... commenting this out.
00217     //throw std::runtime_error(s.str());
00218   }
00219   else {
00220     file_stream_map[name] = new LogStream(name, &os, 0);
00221   }
00222 }
00223 
00224 
00225 void
00226 unregister_log_ostream(
00227   std::ostream &        os)
00228 {
00229   LogStreamMap &file_stream_map = get_file_stream_map();
00230 
00231   for (LogStreamMap::iterator it = file_stream_map.begin(); it != file_stream_map.end(); ++it) {
00232     if ((*it).second->m_ostream == &os) {
00233       delete (*it).second;
00234       file_stream_map.erase(it);
00235       break;
00236     }
00237   }
00238 
00239   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00240 
00241   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it)
00242     (*it).second->m_teeStreambuf->remove(&os);
00243 }
00244 
00245 
00246 const std::string &
00247 get_log_path(
00248   const std::string &   name)
00249 {
00250   static std::string not_found = "";
00251 
00252   LogStreamMap &file_stream_map = get_file_stream_map();
00253 
00254   LogStreamMap::iterator it = file_stream_map.find(name);
00255 
00256   return it == file_stream_map.end() ? not_found : (*it).second->m_path;
00257 }
00258 
00259 
00260 std::ostream *
00261 get_log_ostream(
00262   const std::string &   name)
00263 {
00264   LogStreamMap &file_stream_map = get_file_stream_map();
00265 
00266   LogStreamMap::iterator it = file_stream_map.find(name);
00267 
00268   return it == file_stream_map.end() ? 0 : (*it).second->m_ostream;
00269 }
00270 
00271 
00272 void
00273 register_ostream(
00274   std::ostream &        os,
00275   const std::string &   name)
00276 {
00277   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00278 
00279   unregister_ostream(os);
00280 
00281   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00282 
00283   if (it != ostream_tee_streambuf_map.end()) {
00284 //     delete (*it).second;
00285 //     ostream_tee_streambuf_map.erase(it);
00286 //   }
00287     std::ostringstream s;
00288     s << "Output stream " << name << " has already been registered";
00289 
00290     throw std::runtime_error(s.str());
00291   }
00292 
00293   ostream_tee_streambuf_map[name] = new OStreamTeeStreambuf(os);
00294 }
00295 
00296 
00297 void
00298 unregister_ostream(
00299   std::ostream &        os)
00300 {
00301   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00302 
00303   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it)
00304     (*it).second->m_teeStreambuf->remove(&os);
00305 
00306   for (OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.begin(); it != ostream_tee_streambuf_map.end(); ++it) {
00307     if ((*it).second->m_ostream == &os) {
00308       delete (*it).second;
00309       ostream_tee_streambuf_map.erase(it);
00310       break;
00311     }
00312   }
00313 
00314 }
00315 
00316 
00317 std::ostream *
00318 get_ostream_ostream(
00319   const std::string &   name)
00320 {
00321   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00322 
00323   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00324 
00325   return it == ostream_tee_streambuf_map.end() ? 0 : (*it).second->m_ostream;
00326 }
00327 
00328 
00329 tee_streambuf *
00330 get_ostream_tee_streambuf(
00331   const std::string &   name)
00332 {
00333   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00334 
00335   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00336 
00337   return it == ostream_tee_streambuf_map.end() ? 0 : (*it).second->m_teeStreambuf;
00338 }
00339 
00340 
00341 std::ostream *
00342 get_ostream_tee_ostream(
00343   const std::string &   name)
00344 {
00345   OStreamTeeStreambufMap &ostream_tee_streambuf_map = get_ostream_tee_streambuf_map();
00346 
00347   OStreamTeeStreambufMap::iterator it = ostream_tee_streambuf_map.find(name);
00348 
00349   return it == ostream_tee_streambuf_map.end() ? 0 : (*it).second->m_ostream;
00350 }
00351 
00352 
00353 bool
00354 is_registered_ostream(
00355   const std::string &   name)
00356 {
00357   return get_ostream_ostream(name) != 0;
00358 }
00359 
00360 
00361 namespace {
00362 
00363 struct Command
00364 {
00365   virtual ~Command()
00366   {}
00367 
00368   virtual void execute() = 0;
00369 };
00370 
00371 #ifdef __INTEL_COMPILER
00372 #pragma warning(push)
00373 #pragma warning(disable: 444)
00374 #endif
00375 struct CommandList : public std::list<Command *>
00376 {
00377   CommandList()
00378     : std::list<Command *>()
00379   {}
00380 
00381   ~CommandList()
00382   {
00383     for (std::list<Command *>::iterator it = begin(); it != end(); ++it)
00384       delete (*it);
00385   }
00386 };
00387 #ifdef __INTEL_COMPILER
00388 #pragma warning(pop)
00389 #endif
00390 
00391 namespace {
00392 
00393 tee_streambuf &
00394 parse_tee_streambuf(
00395   const std::string &   tee_ostream_name)
00396 {
00397   tee_streambuf *osb = get_ostream_tee_streambuf(tee_ostream_name);
00398 
00399   if (!osb) {
00400     std::ostringstream s;
00401 
00402     s << "Output stream " << tee_ostream_name << " has not been registered for output logging";
00403     throw std::runtime_error(s.str());
00404   }
00405 
00406   return *osb;
00407 }
00408 
00409 
00410 std::ostream *
00411 parse_ostream(
00412   const std::string &   ostream_name)
00413 {
00414   std::ostream *os = get_log_ostream(ostream_name);
00415 
00416   if (!os)
00417     os = get_ostream_tee_ostream(ostream_name);
00418 
00419   if (!os) {
00420     std::ostringstream s;
00421 
00422     s << "Log file '" << ostream_name << "' has not been registered";
00423     throw std::runtime_error(s.str());
00424   }
00425 
00426   return os;
00427 }
00428 
00429 
00430 struct OpenLog : public Command
00431 {
00432   OpenLog(
00433     const std::string &name,
00434     const std::string &path)
00435     : m_name(name),
00436       m_path(path)
00437   {}
00438 
00439   virtual ~OpenLog()
00440   {}
00441 
00442   virtual void execute() {
00443     create_log_file(m_name, m_path);
00444   }
00445 
00446   std::string        m_name;
00447   std::string        m_path;
00448 };
00449 
00450 
00451 struct CloseLog : public Command
00452 {
00453   CloseLog(
00454     const std::string &name)
00455     : m_name(name)
00456   {}
00457 
00458   virtual ~CloseLog()
00459   {}
00460 
00461   virtual void execute() {
00462     close_log_file(m_name);
00463   }
00464 
00465   std::string        m_name;
00466 };
00467 
00468 
00469 struct ClearTeeOStream : public Command
00470 {
00471   ClearTeeOStream(
00472     const std::string & tee_ostream_name)
00473     : m_teeOStreamName(tee_ostream_name)
00474   {}
00475 
00476   virtual ~ClearTeeOStream()
00477   {}
00478 
00479   virtual void execute() {
00480     parse_tee_streambuf(m_teeOStreamName).clear();
00481   }
00482 
00483   std::string        m_teeOStreamName;
00484 };
00485 
00486 
00487 struct AddTeeOStream : public Command
00488 {
00489   AddTeeOStream(
00490     const std::string & tee_ostream_name,
00491     const std::string & ostream_name)
00492     : m_teeOStreamName(tee_ostream_name),
00493       m_ostreamName(ostream_name)
00494   {}
00495 
00496   virtual ~AddTeeOStream()
00497   {}
00498 
00499   virtual void execute() {
00500     if (m_ostreamName != "null")
00501       parse_tee_streambuf(m_teeOStreamName).add(parse_ostream(m_ostreamName));
00502   }
00503 
00504   std::string        m_teeOStreamName;
00505   std::string        m_ostreamName;
00506 };
00507 
00508 
00509 struct RemoveTeeOStream : public Command
00510 {
00511   RemoveTeeOStream(
00512     const std::string & tee_ostream_name,
00513     const std::string & ostream_name)
00514     : m_teeOStreamName(tee_ostream_name),
00515       m_ostreamName(ostream_name)
00516   {}
00517 
00518   virtual ~RemoveTeeOStream()
00519   {}
00520 
00521   virtual void execute() {
00522     parse_tee_streambuf(m_teeOStreamName).remove(parse_ostream(m_ostreamName));
00523   }
00524 
00525   std::string        m_teeOStreamName;
00526   std::string        m_ostreamName;
00527 };
00528 
00529 } // namespace <empty>
00530 
00531 
00532 /*
00533  * Startup:     out > cout pout > cout dout > cout
00534  * Normal:      out > log-path+pout pout > null dout > out
00535  * Diagnostic:  out > out-path+pout pout > pout-path dout > out
00536  *
00537  * Modify:      out > +pout
00538  *              out > -pout
00539  */
00540 void
00541 parse_output_description(
00542   const std::string &   output_description,
00543   CommandList &         command_list)
00544 {
00545   typedef std::pair<const char *, const char *>  Token;
00546   typedef std::list<Token> TokenList;
00547 
00548   command_list.clear();
00549 
00550   TokenList tokens;
00551 
00552   for (const char *c = output_description.c_str(); *c; ) {
00553     if (std::isspace(*c))
00554       ++c;
00555 
00556     else if (*c == '>' || *c == '+' || *c == '-' || *c == '=') {
00557       tokens.push_back(Token(c, c + 1));
00558       ++c;
00559     }
00560 
00561     else if (*c == '\"') {
00562       const char *d = c + 1;
00563       while (*d && *d != '\"')
00564         ++d;
00565       tokens.push_back(Token(c + 1, d));
00566       c = d + 1;
00567     }
00568 
00569     else {
00570       const char *d = c;
00571       while (std::isgraph(*d) && *d != '+' && *d != '-' &&*d != '=' && *d != '>')
00572         ++d;
00573       tokens.push_back(Token(c, d));
00574       c = d;
00575     }
00576   }
00577 
00578   for (TokenList::iterator it = tokens.begin(); it != tokens.end(); ) {
00579     std::string name((*it).first, (*it).second);
00580 
00581     ++it; if (it == tokens.end()) break;
00582     std::string operation((*it).first, (*it).second);
00583 
00584     if (operation == "=") {
00585       ++it;  if (it == tokens.end()) break;
00586       std::string path((*it).first, (*it).second);
00587       if (!path.empty())
00588         command_list.push_back(new OpenLog(name, path));
00589       else
00590         command_list.push_back(new CloseLog(name));
00591       ++it;  if (it == tokens.end()) break;
00592     }
00593 
00594     else if (operation == ">") {
00595       parse_tee_streambuf(name);
00596 
00597       ++it;  if (it == tokens.end()) break;
00598       std::string token(std::string((*it).first, (*it).second));
00599       if (token != "+" && token != "-") {
00600         std::string ostream_name(std::string((*it).first, (*it).second));
00601 
00602         command_list.push_back(new ClearTeeOStream(name));
00603         command_list.push_back(new AddTeeOStream(name, ostream_name));
00604         ++it;  if (it == tokens.end()) break;
00605       }
00606 
00607       while (it != tokens.end()) {
00608         token = std::string((*it).first, (*it).second);
00609         if (token == "+") {
00610           ++it;  if (it == tokens.end()) break;
00611           std::string ostream_name(std::string((*it).first, (*it).second));
00612 
00613           command_list.push_back(new AddTeeOStream(name, ostream_name));
00614           ++it;  if (it == tokens.end()) break;
00615         }
00616 
00617         else if (token == "-") {
00618           ++it;  if (it == tokens.end()) break;
00619           std::string ostream_name(std::string((*it).first, (*it).second));
00620 
00621           command_list.push_back(new RemoveTeeOStream(name, ostream_name));
00622           ++it;  if (it == tokens.end()) break;
00623         }
00624         else
00625           break;
00626       }
00627     }
00628   }
00629 }
00630 
00631 void
00632 execute(
00633   const CommandList &   command_list)
00634 {
00635   for (CommandList::const_iterator it = command_list.begin(); it != command_list.end(); ++it)
00636     (*it)->execute();
00637 }
00638 
00639 } // namespace <empty>
00640 
00641 void
00642 bind_output_streams(
00643   const std::string &   output_description)
00644 {
00645   stk::CommandList command_list;
00646 
00647   parse_output_description(output_description, command_list);
00648   execute(command_list);
00649 }
00650 
00651 } // namespace stk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends