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