nux-1.16.0
Logger.cpp
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 #include "Logger.h"
00024 #include "LoggingWriter.h"
00025 
00026 #include <execinfo.h>
00027 
00028 #include <map>
00029 #include <sstream>
00030 #include <vector>
00031 #include <boost/algorithm/string.hpp>
00032 #include <boost/utility.hpp>
00033 
00034 namespace nux {
00035 namespace logging {
00036 
00037 namespace {
00038 char const* str_level(Level severity);
00039 }
00040 
00041 class LoggerModule
00042 {
00043 public:
00044   LoggerModule(std::string const& module, LoggerModulePtr const& parent);
00045 
00046   std::string const& module() const;
00047 
00048   bool IsErrorEnabled() const;
00049   bool IsWarningEnabled() const;
00050   bool IsInfoEnabled() const;
00051   bool IsDebugEnabled() const;
00052   bool IsTraceEnabled() const;
00053 
00054   void SetLogLevel(Level level);
00055   Level GetLogLevel() const;
00056   Level GetEffectiveLogLevel() const;
00057 
00058 private:
00059   std::string module_;
00060   Level level_;
00061   LoggerModulePtr parent_;
00062   // An attempt to make sure the writer is around for as long as the loggers.
00063   Writer& writer_;
00064 };
00065 
00066 class LoggerModules : boost::noncopyable
00067 {
00068 public:
00069   static LoggerModules& Instance();
00070 
00071   LoggerModulePtr const& GetModule(std::string const& module);
00072 
00073   void reset();
00074   std::string dump_logging_levels(std::string const& prefix);
00075 
00076 private:
00077   LoggerModules();
00078 
00079 private:
00080   typedef std::map<std::string, LoggerModulePtr> ModuleMap;
00081   ModuleMap modules_;
00082   LoggerModulePtr root_;
00083 };
00084 
00085 
00086 inline std::string const& LoggerModule::module() const
00087 {
00088   return module_;
00089 }
00090 
00091 inline bool LoggerModule::IsErrorEnabled() const
00092 {
00093   return GetEffectiveLogLevel() <= Error;
00094 }
00095 
00096 inline bool LoggerModule::IsWarningEnabled() const
00097 {
00098   return GetEffectiveLogLevel() <= Warning;
00099 }
00100 
00101 inline bool LoggerModule::IsInfoEnabled() const
00102 {
00103   return GetEffectiveLogLevel() <= Info;
00104 }
00105 
00106 inline bool LoggerModule::IsDebugEnabled() const
00107 {
00108   return GetEffectiveLogLevel() <= Debug;
00109 }
00110 
00111 inline bool LoggerModule::IsTraceEnabled() const
00112 {
00113   return GetEffectiveLogLevel() <= Trace;
00114 }
00115 
00116 inline void LoggerModule::SetLogLevel(Level level)
00117 {
00118   // The root module can't be unspecified.
00119   if (module_ == "" && level == NotSpecified)
00120     level = Warning;
00121   level_ = level;
00122 }
00123 
00124 inline Level LoggerModule::GetLogLevel() const
00125 {
00126   return level_;
00127 }
00128 
00129 inline Level LoggerModule::GetEffectiveLogLevel() const
00130 {
00131   if (level_ == NotSpecified && parent_)
00132     return parent_->GetEffectiveLogLevel();
00133   else
00134     return level_;
00135 }
00136 
00137 
00138 Logger::Logger(std::string const& module)
00139   : pimpl(LoggerModules::Instance().GetModule(module))
00140 {
00141 }
00142 
00143 std::string const& Logger::module() const
00144 {
00145   return pimpl->module();
00146 }
00147 
00148 bool Logger::IsErrorEnabled() const
00149 {
00150   return pimpl->IsErrorEnabled();
00151 }
00152 
00153 bool Logger::IsWarningEnabled() const
00154 {
00155   return pimpl->IsWarningEnabled();
00156 }
00157 
00158 bool Logger::IsInfoEnabled() const
00159 {
00160   return pimpl->IsInfoEnabled();
00161 }
00162 
00163 bool Logger::IsDebugEnabled() const
00164 {
00165   return pimpl->IsDebugEnabled();
00166 }
00167 
00168 bool Logger::IsTraceEnabled() const
00169 {
00170   return pimpl->IsTraceEnabled();
00171 }
00172 
00173 void Logger::SetLogLevel(Level level)
00174 {
00175   pimpl->SetLogLevel(level);
00176 }
00177 
00178 Level Logger::GetLogLevel() const
00179 {
00180   return pimpl->GetLogLevel();
00181 }
00182 
00183 Level Logger::GetEffectiveLogLevel() const
00184 {
00185   return pimpl->GetEffectiveLogLevel();
00186 }
00187 
00188 
00189 LoggerModule::LoggerModule(std::string const& module,
00190                            LoggerModulePtr const& parent)
00191   : module_(module)
00192   , level_(NotSpecified)
00193   , parent_(parent)
00194   , writer_(Writer::Instance())
00195 {
00196 }
00197 
00198 LoggerModules::LoggerModules()
00199   : root_(new LoggerModule("", LoggerModulePtr()))
00200 {
00201   // Make sure we have the root logger available.
00202   root_->SetLogLevel(Warning);
00203   modules_.insert(ModuleMap::value_type("", root_));
00204 }
00205 
00206 LoggerModules& LoggerModules::Instance()
00207 {
00208   static LoggerModules instance;
00209   return instance;
00210 }
00211 
00212 LoggerModulePtr const& LoggerModules::GetModule(std::string const& module)
00213 {
00214   std::string lower_module = boost::to_lower_copy(module);
00215   ModuleMap::iterator i = modules_.find(lower_module);
00216   if (i != modules_.end())
00217     return i->second;
00218 
00219   // Make the new LoggerModule and its parents.
00220   // Split on '.'
00221   std::string::size_type idx = lower_module.rfind(".");
00222   LoggerModulePtr parent = root_;
00223   if (idx != std::string::npos) {
00224     parent = GetModule(lower_module.substr(0, idx));
00225   }
00226   LoggerModulePtr logger(new LoggerModule(lower_module, parent));
00227   // std::map insert method returns a pair<iterator, bool> which seems
00228   // overly annoying to make a temporary of, so just return the const
00229   // reference pointed to by the interator.
00230   return modules_.insert(ModuleMap::value_type(lower_module, logger)).first->second;
00231 }
00232 
00233 void LoggerModules::reset()
00234 {
00235   for (ModuleMap::iterator i = modules_.begin(), end = modules_.end(); i != end; ++i)
00236   {
00237     i->second->SetLogLevel(NotSpecified);
00238   }
00239 }
00240 
00241 std::string LoggerModules::dump_logging_levels(std::string const& prefix)
00242 {
00243   std::ostringstream sout;
00244   bool first = true;
00245   for (ModuleMap::iterator i = modules_.begin(), end = modules_.end(); i != end; ++i)
00246   {
00247     std::string const& module_name = i->first;
00248     LoggerModulePtr const& module = i->second;
00249     Level severity = module->GetLogLevel();
00250     if (severity == NotSpecified)
00251       continue; // Don't write out unspecified ones.
00252     if (first)
00253       first = false;
00254     else
00255       sout << "\n";
00256     sout << prefix;
00257     if (module_name == "")
00258       sout << "<root>";
00259     else
00260       sout << module_name;
00261     sout << " " << str_level(severity);
00262   }
00263   return sout.str();
00264 }
00265 
00266 
00267 class LogStreamBuffer : public std::stringbuf
00268 {
00269 public:
00270   LogStreamBuffer(Level severity,
00271                   std::string const& module,
00272                   std::string const& filename,
00273                   int line_number);
00274 protected:
00275   virtual int sync();
00276 private:
00277   Level severity_;
00278   std::string module_;
00279   std::string filename_;
00280   int line_number_;
00281   std::time_t timestamp_;
00282 };
00283 
00284 LogStream::LogStream(Level severity,
00285                      std::string const& module,
00286                      std::string const& filename,
00287                      int line_number)
00288   : std::ostream(new LogStreamBuffer(severity, module,
00289                                      filename, line_number))
00290 {
00291 }
00292 
00293 LogStream::~LogStream()
00294 {
00295   rdbuf()->pubsync();
00296   std::streambuf* buff = rdbuf(0);
00297   delete buff;
00298 }
00299 
00300 
00301 LogStreamBuffer::LogStreamBuffer(Level severity,
00302                                  std::string const& module,
00303                                  std::string const& filename,
00304                                  int line_number)
00305   : std::stringbuf(std::ios_base::out)
00306   , severity_(severity)
00307   , module_(module)
00308   , filename_(filename)
00309   , line_number_(line_number)
00310   , timestamp_(std::time(0))
00311 {
00312 }
00313 
00314 int LogStreamBuffer::sync()
00315 {
00316   std::string message = str();
00317   // reset the stream
00318   str("");
00319   // Only log the message if there is something there.
00320   if (!message.empty())
00321     Writer::Instance().WriteMessage(severity_, module_,
00322                                     filename_, line_number_,
00323                                     timestamp_, message);
00324   return 0; // success
00325 }
00326 
00333 void reset_logging()
00334 {
00335   LoggerModules::Instance().reset();
00336 }
00337 
00338 std::string dump_logging_levels(std::string const& prefix)
00339 {
00340   return LoggerModules::Instance().dump_logging_levels(prefix);
00341 }
00342 
00343 void configure_logging(const char* config_string)
00344 {
00345   if (!config_string)
00346     return;
00347   std::vector<std::string> values;
00348   boost::split(values, config_string, boost::is_any_of(";:"));
00349   for (std::vector<std::string>::iterator i = values.begin(), end = values.end();
00350        i != end; ++i)
00351   {
00352     std::string& value = *i;
00353     std::string::size_type pos = value.find("=");
00354     if (pos != std::string::npos)
00355     {
00356       std::string name = value.substr(0, pos);
00357       std::string level = value.substr(pos+1);
00358       if (name == "<root>")
00359         name = "";
00360       Logger(name).SetLogLevel(get_logging_level(level));
00361     }
00362   }
00363 }
00364 
00365 Level get_logging_level(std::string level)
00366 {
00367   boost::to_upper(level);
00368   if (level == "TRACE")
00369     return Trace;
00370   if (level == "DEBUG")
00371     return Debug;
00372   if (level == "INFO")
00373     return Info;
00374   if (level == "WARN" || level == "WARNING")
00375     return Warning;
00376   if (level == "ERROR")
00377     return Error;
00378   return Warning;
00379 }
00380 
00381 std::string backtrace(int levels)
00382 {
00383   std::ostringstream sout;
00384   void* trace[256];
00385   int n = ::backtrace(trace, 256);
00386   if (!n) {
00387     return sout.str();
00388   }
00389 
00390   char** strings = ::backtrace_symbols(trace, n);
00391 
00392   if (levels != -1) {
00393     n = std::min(n, levels);
00394   }
00395 
00396   for (int i = 0; i < n; ++i) {
00397     sout << i << ": " << strings[i] << '\n';
00398   }
00399   if (strings) {
00400     free (strings);
00401   }
00402 
00403   return sout.str();
00404 }
00405 
00406 
00407 BlockTracer::BlockTracer(Logger& logger,
00408                          Level level,
00409                          std::string const& function_name,
00410                          std::string const& filename,
00411                          int line_number)
00412   : logger_(logger)
00413   , level_(level)
00414   , function_name_(function_name)
00415   , filename_(filename)
00416   , line_number_(line_number)
00417 {
00418   if (logger_.GetEffectiveLogLevel() <= level_)
00419   {
00420     LogStream(level_, logger_.module(), filename_, line_number_).stream()
00421       << "+" << function_name_;
00422   }
00423 }
00424 
00425 BlockTracer::~BlockTracer()
00426 {
00427   if (logger_.GetEffectiveLogLevel() <= level_)
00428   {
00429     LogStream(level_, logger_.module(), filename_, line_number_).stream()
00430       << "-" << function_name_;
00431   }
00432 }
00433 
00434 
00435 namespace {
00436 char const* str_level(Level severity)
00437 {
00438   switch (severity)
00439   {
00440   case NotSpecified:
00441     return "NOT_SPECIFIED";
00442   case Trace:
00443     return "TRACE";
00444   case Debug:
00445     return "DEBUG";
00446   case Info:
00447     return "INFO";
00448   case Warning:
00449     return "WARNING";
00450   case Error:
00451     return "ERROR";
00452   case Critical:
00453     return "CRITICAL";
00454   }
00455   return "<unknown>";
00456 }
00457 
00458 }
00459 
00460 } // namespace logging
00461 } // namespace nux
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends