Back to index

nux  3.0.0
Logger.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright 2011 Inalogic® Inc.
00004  *
00005  * This program is free software: you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License, as
00007  * published by the  Free Software Foundation; either version 2.1 or 3.0
00008  * of the License.
00009  *
00010  * This program is distributed in the hope that it will be useful, but
00011  * WITHOUT ANY WARRANTY; without even the implied warranties of
00012  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00013  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00014  * License for more details.
00015  *
00016  * You should have received a copy of both the GNU Lesser General Public
00017  * License along with this program. If not, see <http://www.gnu.org/licenses/>
00018  *
00019  * Authored by: Tim Penhey <tim.penhey@canonical.com>
00020  *
00021  */
00022 
00023 #pragma warning(disable: 4996)  // 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
00024                                 // 'std::_Equal1': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
00025 
00026 #include "NuxCore.h"
00027 #include "Logger.h"
00028 #include "LoggingWriter.h"
00029 
00030 #if defined(NUX_OS_LINUX)
00031 #include <execinfo.h>
00032 #endif
00033 
00034 #include <map>
00035 #include <sstream>
00036 #include <vector>
00037 #include <boost/algorithm/string.hpp>
00038 #include <boost/utility.hpp>
00039 
00040 namespace nux {
00041 namespace logging {
00042 
00043 namespace {
00044 char const* str_level(Level severity);
00045 }
00046 
00047 class LoggerModule
00048 {
00049 public:
00050   LoggerModule(std::string const& module, LoggerModulePtr const& parent);
00051 
00052   std::string const& module() const;
00053 
00054   bool IsErrorEnabled() const;
00055   bool IsWarningEnabled() const;
00056   bool IsInfoEnabled() const;
00057   bool IsDebugEnabled() const;
00058   bool IsTraceEnabled() const;
00059 
00060   void SetLogLevel(Level level);
00061   Level GetLogLevel() const;
00062   Level GetEffectiveLogLevel() const;
00063 
00064 private:
00065   std::string module_;
00066   Level level_;
00067   LoggerModulePtr parent_;
00068   // An attempt to make sure the writer is around for as long as the loggers.
00069   Writer& writer_;
00070 };
00071 
00072 class LoggerModules : boost::noncopyable
00073 {
00074 public:
00075   static LoggerModules& Instance();
00076 
00077   LoggerModulePtr const& GetModule(std::string const& module);
00078 
00079   void reset();
00080   std::string dump_logging_levels(std::string const& prefix);
00081 
00082 private:
00083   LoggerModules();
00084 
00085 private:
00086   typedef std::map<std::string, LoggerModulePtr> ModuleMap;
00087   ModuleMap modules_;
00088   LoggerModulePtr root_;
00089 };
00090 
00091 
00092 inline std::string const& LoggerModule::module() const
00093 {
00094   return module_;
00095 }
00096 
00097 inline bool LoggerModule::IsErrorEnabled() const
00098 {
00099   return GetEffectiveLogLevel() <= Error;
00100 }
00101 
00102 inline bool LoggerModule::IsWarningEnabled() const
00103 {
00104   return GetEffectiveLogLevel() <= Warning;
00105 }
00106 
00107 inline bool LoggerModule::IsInfoEnabled() const
00108 {
00109   return GetEffectiveLogLevel() <= Info;
00110 }
00111 
00112 inline bool LoggerModule::IsDebugEnabled() const
00113 {
00114   return GetEffectiveLogLevel() <= Debug;
00115 }
00116 
00117 inline bool LoggerModule::IsTraceEnabled() const
00118 {
00119   return GetEffectiveLogLevel() <= Trace;
00120 }
00121 
00122 inline void LoggerModule::SetLogLevel(Level level)
00123 {
00124   // The root module can't be unspecified.
00125   if (module_ == "" && level == NotSpecified)
00126     level = Warning;
00127   level_ = level;
00128 }
00129 
00130 inline Level LoggerModule::GetLogLevel() const
00131 {
00132   return level_;
00133 }
00134 
00135 inline Level LoggerModule::GetEffectiveLogLevel() const
00136 {
00137   if (level_ == NotSpecified && parent_)
00138     return parent_->GetEffectiveLogLevel();
00139   else
00140     return level_;
00141 }
00142 
00143 
00144 Logger::Logger(std::string const& module)
00145   : pimpl(LoggerModules::Instance().GetModule(module))
00146 {
00147 }
00148 
00149 std::string const& Logger::module() const
00150 {
00151   return pimpl->module();
00152 }
00153 
00154 bool Logger::IsErrorEnabled() const
00155 {
00156   return pimpl->IsErrorEnabled();
00157 }
00158 
00159 bool Logger::IsWarningEnabled() const
00160 {
00161   return pimpl->IsWarningEnabled();
00162 }
00163 
00164 bool Logger::IsInfoEnabled() const
00165 {
00166   return pimpl->IsInfoEnabled();
00167 }
00168 
00169 bool Logger::IsDebugEnabled() const
00170 {
00171   return pimpl->IsDebugEnabled();
00172 }
00173 
00174 bool Logger::IsTraceEnabled() const
00175 {
00176   return pimpl->IsTraceEnabled();
00177 }
00178 
00179 void Logger::SetLogLevel(Level level)
00180 {
00181   pimpl->SetLogLevel(level);
00182 }
00183 
00184 Level Logger::GetLogLevel() const
00185 {
00186   return pimpl->GetLogLevel();
00187 }
00188 
00189 Level Logger::GetEffectiveLogLevel() const
00190 {
00191   return pimpl->GetEffectiveLogLevel();
00192 }
00193 
00194 
00195 LoggerModule::LoggerModule(std::string const& module,
00196                            LoggerModulePtr const& parent)
00197   : module_(module)
00198   , level_(NotSpecified)
00199   , parent_(parent)
00200   , writer_(Writer::Instance())
00201 {
00202 }
00203 
00204 LoggerModules::LoggerModules()
00205   : root_(new LoggerModule("", LoggerModulePtr()))
00206 {
00207   // Make sure we have the root logger available.
00208   root_->SetLogLevel(Warning);
00209   modules_.insert(ModuleMap::value_type("", root_));
00210 }
00211 
00212 LoggerModules& LoggerModules::Instance()
00213 {
00214   static LoggerModules instance;
00215   return instance;
00216 }
00217 
00218 LoggerModulePtr const& LoggerModules::GetModule(std::string const& module)
00219 {
00220   std::string lower_module = boost::to_lower_copy(module);
00221   ModuleMap::iterator i = modules_.find(lower_module);
00222   if (i != modules_.end())
00223     return i->second;
00224 
00225   // Make the new LoggerModule and its parents.
00226   // Split on '.'
00227   std::string::size_type idx = lower_module.rfind(".");
00228   LoggerModulePtr parent = root_;
00229   if (idx != std::string::npos) {
00230     parent = GetModule(lower_module.substr(0, idx));
00231   }
00232   LoggerModulePtr logger(new LoggerModule(lower_module, parent));
00233   // std::map insert method returns a pair<iterator, bool> which seems
00234   // overly annoying to make a temporary of, so just return the const
00235   // reference pointed to by the interator.
00236   return modules_.insert(ModuleMap::value_type(lower_module, logger)).first->second;
00237 }
00238 
00239 void LoggerModules::reset()
00240 {
00241   for (ModuleMap::iterator i = modules_.begin(), end = modules_.end(); i != end; ++i)
00242   {
00243     i->second->SetLogLevel(NotSpecified);
00244   }
00245 }
00246 
00247 std::string LoggerModules::dump_logging_levels(std::string const& prefix)
00248 {
00249   std::ostringstream sout;
00250   bool first = true;
00251   for (ModuleMap::iterator i = modules_.begin(), end = modules_.end(); i != end; ++i)
00252   {
00253     std::string const& module_name = i->first;
00254     LoggerModulePtr const& module = i->second;
00255     Level severity = module->GetLogLevel();
00256     if (severity == NotSpecified)
00257       continue; // Don't write out unspecified ones.
00258     if (first)
00259       first = false;
00260     else
00261       sout << "\n";
00262     sout << prefix;
00263     if (module_name == "")
00264       sout << "<root>";
00265     else
00266       sout << module_name;
00267     sout << " " << str_level(severity);
00268   }
00269   return sout.str();
00270 }
00271 
00272 
00273 class LogStreamBuffer : public std::stringbuf
00274 {
00275 public:
00276   LogStreamBuffer(Level severity,
00277                   std::string const& module,
00278                   std::string const& filename,
00279                   int line_number);
00280 protected:
00281   virtual int sync();
00282 private:
00283   Level severity_;
00284   std::string module_;
00285   std::string filename_;
00286   int line_number_;
00287   std::time_t timestamp_;
00288 };
00289 
00290 LogStream::LogStream(Level severity,
00291                      std::string const& module,
00292                      std::string const& filename,
00293                      int line_number)
00294   : std::ostream(new LogStreamBuffer(severity, module,
00295                                      filename, line_number))
00296 {
00297 }
00298 
00299 LogStream::~LogStream()
00300 {
00301   rdbuf()->pubsync();
00302   std::streambuf* buff = rdbuf(0);
00303   delete buff;
00304 }
00305 
00306 
00307 LogStreamBuffer::LogStreamBuffer(Level severity,
00308                                  std::string const& module,
00309                                  std::string const& filename,
00310                                  int line_number)
00311   : std::stringbuf(std::ios_base::out)
00312   , severity_(severity)
00313   , module_(module)
00314   , filename_(filename)
00315   , line_number_(line_number)
00316   , timestamp_(std::time(0))
00317 {
00318 }
00319 
00320 int LogStreamBuffer::sync()
00321 {
00322   std::string message = str();
00323   // reset the stream
00324   str("");
00325   // Only log the message if there is something there.
00326   if (!message.empty())
00327     Writer::Instance().WriteMessage(severity_, module_,
00328                                     filename_, line_number_,
00329                                     timestamp_, message);
00330   return 0; // success
00331 }
00332 
00339 void reset_logging()
00340 {
00341   LoggerModules::Instance().reset();
00342 }
00343 
00344 std::string dump_logging_levels(std::string const& prefix)
00345 {
00346   return LoggerModules::Instance().dump_logging_levels(prefix);
00347 }
00348 
00349 void configure_logging(const char* config_string)
00350 {
00351   if (!config_string)
00352     return;
00353   std::vector<std::string> values;
00354   boost::split(values, config_string, boost::is_any_of(";:"));
00355   for (std::vector<std::string>::iterator i = values.begin(), end = values.end();
00356        i != end; ++i)
00357   {
00358     std::string& value = *i;
00359     std::string::size_type pos = value.find("=");
00360     if (pos != std::string::npos)
00361     {
00362       std::string name = value.substr(0, pos);
00363       std::string level = value.substr(pos+1);
00364       if (name == "<root>")
00365         name = "";
00366       Logger(name).SetLogLevel(get_logging_level(level));
00367     }
00368   }
00369 }
00370 
00371 Level get_logging_level(std::string level)
00372 {
00373   boost::to_upper(level);
00374   if (level == "TRACE")
00375     return Trace;
00376   if (level == "DEBUG")
00377     return Debug;
00378   if (level == "INFO")
00379     return Info;
00380   if (level == "WARN" || level == "WARNING")
00381     return Warning;
00382   if (level == "ERROR")
00383     return Error;
00384   return Warning;
00385 }
00386 
00387 #if defined(NUX_OS_LINUX)
00388 std::string Backtrace(int levels)
00389 {
00390   std::ostringstream sout;
00391   void* trace[256];
00392   int n = ::backtrace(trace, 256);
00393   if (!n) {
00394     return sout.str();
00395   }
00396 
00397   char** strings = ::backtrace_symbols(trace, n);
00398 
00399   if (levels != -1) {
00400     n = std::min(n, levels);
00401   }
00402 
00403   for (int i = 0; i < n; ++i) {
00404     sout << i << ": " << strings[i] << '\n';
00405   }
00406   if (strings) {
00407     free (strings);
00408   }
00409 
00410   return sout.str();
00411 }
00412 #endif
00413 
00414 BlockTracer::BlockTracer(Logger& logger,
00415                          Level level,
00416                          std::string const& function_name,
00417                          std::string const& filename,
00418                          int line_number)
00419   : logger_(logger)
00420   , level_(level)
00421   , function_name_(function_name)
00422   , filename_(filename)
00423   , line_number_(line_number)
00424 {
00425   if (logger_.GetEffectiveLogLevel() <= level_)
00426   {
00427     LogStream(level_, logger_.module(), filename_, line_number_).stream()
00428       << "+" << function_name_;
00429   }
00430 }
00431 
00432 BlockTracer::~BlockTracer()
00433 {
00434   if (logger_.GetEffectiveLogLevel() <= level_)
00435   {
00436     LogStream(level_, logger_.module(), filename_, line_number_).stream()
00437       << "-" << function_name_;
00438   }
00439 }
00440 
00441 
00442 namespace {
00443 char const* str_level(Level severity)
00444 {
00445   switch (severity)
00446   {
00447   case NotSpecified:
00448     return "NOT_SPECIFIED";
00449   case Trace:
00450     return "TRACE";
00451   case Debug:
00452     return "DEBUG";
00453   case Info:
00454     return "INFO";
00455   case Warning:
00456     return "WARNING";
00457   case Error:
00458     return "ERROR";
00459   case Critical:
00460     return "CRITICAL";
00461   }
00462   return "<unknown>";
00463 }
00464 
00465 }
00466 
00467 } // namespace logging
00468 } // namespace nux