nux-1.14.0
|
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