Note: Goby version 1 (shown here) is now considered obsolete. Please use version 2 for new projects, and consider upgrading old projects.

Goby Underwater Autonomy Project  Series: 1.1, revision: 163, released on 2013-02-06 14:23:27 -0500
util/liblogger/flex_ostreambuf.cpp
00001 // copyright 2009 t. schneider tes@mit.edu
00002 // 
00003 // this file is part of flex-cout, a terminal display library
00004 // that extends the functionality of std::cout
00005 //
00006 // This program is free software: you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation, either version 3 of the License, or
00009 // (at your option) any later version.
00010 //
00011 // This software is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License
00017 // along with this software.  If not, see <http://www.gnu.org/licenses/>.
00018 
00019 #include <iostream>
00020 #include <sstream>
00021 #include <limits>
00022 #include <iomanip>
00023 
00024 #include <boost/assign.hpp>
00025 #include <boost/foreach.hpp>
00026 #include <boost/bind.hpp>
00027 #include <boost/date_time.hpp>
00028 
00029 //#ifdef HAS_NCURSES
00030 #include "flex_ncurses.h"
00031 //#endif
00032 
00033 #include "flex_ostreambuf.h"
00034 #include "goby/util/sci.h"
00035 #include "goby/util/time.h"
00036 #include "goby/core/libcore/exception.h"
00037 #include "logger_manipulators.h"
00038 
00039 
00040 
00041 boost::mutex curses_mutex;
00042 
00043 boost::mutex goby::util::Logger::mutex;
00044 
00045 goby::util::FlexOStreamBuf::FlexOStreamBuf(): buffer_(1),
00046                                               name_("no name"),
00047                                               die_flag_(false),
00048                                               warn_flag_(false),
00049                                               debug_flag_(false),
00050 #ifdef HAS_NCURSES
00051                                               curses_(0),
00052 #endif
00053                                               start_time_(goby_time()),
00054                                               is_quiet_(true),
00055                                               is_gui_(false)
00056                                               
00057 {
00058     Group no_group("", "Ungrouped messages");
00059     groups_[""] = no_group;    
00060 }
00061 
00062 goby::util::FlexOStreamBuf::~FlexOStreamBuf()
00063 {
00064 #ifdef HAS_NCURSES    
00065     if(curses_) delete curses_;
00066 #endif
00067 }
00068 
00069 void goby::util::FlexOStreamBuf::add_stream(Logger::Verbosity verbosity, std::ostream* os)
00070 {
00071     //check that this stream doesn't exist
00072     // if so, update its verbosity and return
00073     bool stream_exists = false;
00074     BOOST_FOREACH(StreamConfig& sc, streams_)
00075     {
00076         if(sc.os() == os)
00077         {
00078             sc.set_verbosity(verbosity);
00079             stream_exists = true;
00080         }
00081     }
00082 
00083     if(!stream_exists)
00084         streams_.push_back(StreamConfig(os, verbosity));
00085     
00086 
00087     if(verbosity == Logger::gui)
00088     {
00089 #ifdef HAS_NCURSES    
00090         if(is_gui_) return;
00091         
00092         is_gui_ = true;
00093         
00094         curses_ = new FlexNCurses;
00095 
00096         boost::mutex::scoped_lock lock(curses_mutex);
00097 
00098         curses_->startup();
00099 
00100         // add any groups already on the screen as ncurses windows
00101         typedef std::pair<std::string, Group> P;
00102         BOOST_FOREACH(const P&p, groups_)
00103             curses_->add_win(&groups_[p.first]);
00104         
00105         curses_->recalculate_win();
00106 
00107         input_thread_ = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&FlexNCurses::run_input, curses_)));
00108 #else
00109         throw(goby::Exception("Tried to use verbosity==gui without compiling against NCurses. Install NCurses and recompile goby or use a different verbosity"));
00110 #endif
00111     }
00112     else if(verbosity != Logger::quiet)
00113     {
00114         is_quiet_ = false;
00115     }
00116 }
00117 
00118 void goby::util::FlexOStreamBuf::add_group(const std::string & name, Group g)
00119 {
00120     if(groups_.count(name)) return;
00121     
00122     groups_[name] = g;
00123 
00124 #ifdef HAS_NCURSES    
00125     if(is_gui_)
00126     {
00127         boost::mutex::scoped_lock lock(curses_mutex);
00128         curses_->add_win(&groups_[name]);
00129     }
00130 #endif
00131 }
00132 
00133 int goby::util::FlexOStreamBuf::overflow(int c /*= EOF*/)
00134 {
00135     if(c == EOF)
00136         return c;
00137     else if(c == '\n')
00138         buffer_.push_back(std::string());
00139     else
00140         buffer_.back().push_back(c);
00141     
00142     return c;
00143 }
00144 
00145 // called when flush() or std::endl
00146 int goby::util::FlexOStreamBuf::sync()
00147 {
00148     // all but last one
00149     while(buffer_.size() > 1)
00150     {
00151         display(buffer_.front());
00152         buffer_.pop_front();
00153     }
00154     
00155     group_name_.erase();
00156 
00157 
00158     debug_flag_ = false;
00159     warn_flag_ = false;
00160     
00161     if(die_flag_) exit(EXIT_FAILURE);
00162     return 0;
00163 }
00164 
00165 void goby::util::FlexOStreamBuf::display(std::string & s)
00166 {
00167 
00168 #ifdef HAS_NCURSES    
00169     if(is_gui_)
00170     {
00171         if(!die_flag_)
00172         {
00173             boost::mutex::scoped_lock lock(curses_mutex);
00174             std::stringstream line;
00175             boost::posix_time::time_duration time_of_day = goby_time().time_of_day();
00176             line << "\n"
00177                  << std::setfill('0') << std::setw(2) << time_of_day.hours() << ":"  
00178                  << std::setw(2) << time_of_day.minutes() << ":" 
00179                  << std::setw(2) << time_of_day.seconds() <<
00180                 TermColor::esc_code_from_col(groups_[group_name_].color()) << " | "  << esc_nocolor
00181                  <<  s;
00182             
00183             curses_->insert(goby_time(), line.str(), &groups_[group_name_]);
00184         }
00185         else
00186         {
00187             curses_->alive(false);
00188             input_thread_->join();
00189             curses_->cleanup();
00190             
00191             std::cout << TermColor::esc_code_from_col(groups_[group_name_].color()) << name_ << esc_nocolor << ": " << s << esc_nocolor << std::endl;
00192         }           
00193     }
00194 #endif
00195     
00196     BOOST_FOREACH(const StreamConfig& cfg, streams_)
00197     {
00198         if(cfg.os() == &std::cout || cfg.os() == &std::cerr || cfg.os() == &std::clog)
00199         {
00200             switch(cfg.verbosity())
00201             {
00202                 default:
00203                 case Logger::quiet:
00204                     break;
00205                 case Logger::warn:
00206                     if(!warn_flag_) break;
00207                 case Logger::verbose:
00208                     if(debug_flag_) break;
00209                 case Logger::debug:
00210                     *cfg.os() << TermColor::esc_code_from_col(groups_[group_name_].color()) << name_ << esc_nocolor << " (" << boost::posix_time::to_iso_string(goby_time()) << "): " << s << esc_nocolor << std::endl;
00211                     break;        
00212             }
00213         }
00214         else if(cfg.os())
00215         {
00216             switch(cfg.verbosity())
00217             {
00218                 default:
00219                 case Logger::quiet:
00220                     break;
00221                 case Logger::warn:
00222                     if(!warn_flag_) break;
00223                 case Logger::verbose:
00224                     if(debug_flag_) break;
00225                 case Logger::debug:
00226                     basic_log_header(*cfg.os(), group_name_);
00227                     strip_escapes(s);
00228                     *cfg.os() << s << std::endl;
00229                     break;        
00230             }
00231         }
00232     }
00233 }
00234 
00235 void goby::util::FlexOStreamBuf::refresh()
00236 {
00237 #ifdef HAS_NCURSES
00238     if(is_gui_)
00239     {
00240         boost::mutex::scoped_lock lock(curses_mutex);
00241         curses_->recalculate_win();
00242     }
00243 #endif
00244 }
00245 
00246 // clean out any escape codes for non terminal streams
00247 void goby::util::FlexOStreamBuf::strip_escapes(std::string& s)
00248 {
00249     static const std::string esc = "\33[";
00250     static const std::string m = "m";
00251     
00252     size_t esc_pos, m_pos;
00253     while((esc_pos = s.find(esc)) != std::string::npos
00254           && (m_pos = s.find(m, esc_pos)) != std::string::npos)
00255         s.erase(esc_pos, m_pos-esc_pos+1);
00256     
00257 }
00258 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends