LogCabin
Core/Debug.cc
Go to the documentation of this file.
00001 /* Copyright (c) 2011-2012 Stanford University
00002  * Copyright (c) 2014-2015 Diego Ongaro
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
00011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 #include <cassert>
00018 #include <cstdarg>
00019 #include <cstdio>
00020 #include <cstring>
00021 #include <ctime>
00022 #include <mutex>
00023 #include <strings.h>
00024 #include <sys/types.h>
00025 #include <unistd.h>
00026 #include <unordered_map>
00027 
00028 #include "Core/Debug.h"
00029 #include "Core/StringUtil.h"
00030 #include "Core/ThreadId.h"
00031 #include "include/LogCabin/Debug.h"
00032 
00033 namespace LogCabin {
00034 namespace Core {
00035 namespace Debug {
00036 
00037 DebugMessage::DebugMessage()
00038     : filename()
00039     , linenum()
00040     , function()
00041     , logLevel()
00042     , logLevelString()
00043     , processName()
00044     , threadName()
00045     , message()
00046 {
00047 }
00048 
00049 DebugMessage::DebugMessage(const DebugMessage& other)
00050     : filename(other.filename)
00051     , linenum(other.linenum)
00052     , function(other.function)
00053     , logLevel(other.logLevel)
00054     , logLevelString(other.logLevelString)
00055     , processName(other.processName)
00056     , threadName(other.threadName)
00057     , message(other.message)
00058 {
00059 }
00060 
00061 DebugMessage::DebugMessage(DebugMessage&& other)
00062     : filename(other.filename)
00063     , linenum(other.linenum)
00064     , function(other.function)
00065     , logLevel(other.logLevel)
00066     , logLevelString(other.logLevelString)
00067     , processName(std::move(other.processName))
00068     , threadName(std::move(other.threadName))
00069     , message(std::move(other.message))
00070 {
00071 }
00072 
00073 DebugMessage::~DebugMessage()
00074 {
00075 }
00076 
00077 DebugMessage&
00078 DebugMessage::operator=(const DebugMessage& other)
00079 {
00080     filename = other.filename;
00081     linenum = other.linenum;
00082     function = other.function;
00083     logLevel = other.logLevel;
00084     logLevelString = other.logLevelString;
00085     processName = processName;
00086     threadName = threadName;
00087     message = other.message;
00088     return *this;
00089 }
00090 
00091 DebugMessage&
00092 DebugMessage::operator=(DebugMessage&& other)
00093 {
00094     filename = other.filename;
00095     linenum = other.linenum;
00096     function = other.function;
00097     logLevel = other.logLevel;
00098     logLevelString = other.logLevelString;
00099     processName = std::move(other.processName);
00100     threadName = std::move(other.threadName);
00101     message = std::move(other.message);
00102     return *this;
00103 }
00104 
00105 
00106 std::string processName = Core::StringUtil::format("%u", getpid());
00107 
00108 namespace Internal {
00109 
00110 /**
00111  * Protects #logPolicy, #isLoggingCache, and #logFilename.
00112  */
00113 std::mutex mutex;
00114 
00115 /**
00116  * Specifies the log messages that should be displayed for each filename.
00117  * This first component is a pattern; the second is a log level.
00118  * A filename is matched against each pattern in order: if the filename starts
00119  * with or ends with the pattern, the corresponding log level defines the most
00120  * verbose messages that are to be displayed for the file. If a filename
00121  * matches no pattern, its log level will default to NOTICE.
00122  *
00123  * Protected by #mutex.
00124  */
00125 std::vector<std::pair<std::string, std::string>> logPolicy;
00126 
00127 /**
00128  * A cache of the results of getLogLevel(), since that function is slow.
00129  * This needs to be cleared when the logPolicy changes.
00130  * The key to the map is a pointer to the absolute filename, which should be a
00131  * string literal.
00132  *
00133  * Protected by #mutex.
00134  */
00135 std::unordered_map<const char*, LogLevel> isLoggingCache;
00136 
00137 /**
00138  * Filename of currently open stream, if known.
00139  * Protected by #mutex.
00140  */
00141 std::string logFilename;
00142 
00143 /**
00144  * Where log messages go (unless logHandler is set).
00145  */
00146 FILE* stream = stderr;
00147 
00148 /**
00149  * If set, a callback that takes log messages instead of the normal log file
00150  * (stream). This is exposed to client applications so they can integrate with
00151  * their own logging mechanism.
00152  */
00153 std::function<void(DebugMessage)> logHandler;
00154 
00155 /**
00156  * Convert a log level to a (static) string.
00157  * PANICs if the string is not a valid log level (case insensitive).
00158  */
00159 const char*
00160 logLevelToString(LogLevel level)
00161 {
00162     switch (level) {
00163         case LogLevel::SILENT:  return "SILENT";
00164         case LogLevel::ERROR:   return "ERROR";
00165         case LogLevel::WARNING: return "WARNING";
00166         case LogLevel::NOTICE:  return "NOTICE";
00167         case LogLevel::VERBOSE: return "VERBOSE";
00168     }
00169     log(LogLevel::ERROR, __FILE__, __LINE__, __FUNCTION__,
00170         Core::StringUtil::format("%d is not a valid log level",
00171                                  static_cast<int>(level)).c_str());
00172     abort();
00173 }
00174 
00175 /**
00176  * Convert a string to a log level.
00177  * PANICs if the string is not a valid log level (case insensitive).
00178  */
00179 LogLevel
00180 logLevelFromString(const std::string& level)
00181 {
00182     if (strcasecmp(level.c_str(), "SILENT")  == 0)  return LogLevel::SILENT;
00183     if (strcasecmp(level.c_str(), "ERROR")   == 0)   return LogLevel::ERROR;
00184     if (strcasecmp(level.c_str(), "WARNING") == 0) return LogLevel::WARNING;
00185     if (strcasecmp(level.c_str(), "NOTICE")  == 0)  return LogLevel::NOTICE;
00186     if (strcasecmp(level.c_str(), "VERBOSE") == 0) return LogLevel::VERBOSE;
00187     log(LogLevel::ERROR, __FILE__, __LINE__, __FUNCTION__,
00188         Core::StringUtil::format("'%s' is not a valid log level",
00189                                  level.c_str()).c_str());
00190     abort();
00191 }
00192 
00193 /**
00194  * From the policy, calculate the most verbose log level that should be
00195  * displayed for this file.
00196  * This is slow, so isLogging() caches the results in isLoggingCache.
00197  *
00198  * Must be called with #mutex held.
00199  *
00200  * \param fileName
00201  *      Relative filename.
00202  */
00203 LogLevel
00204 getLogLevel(const char* fileName)
00205 {
00206     for (auto it = logPolicy.begin(); it != logPolicy.end(); ++it) {
00207         const std::string& pattern = it->first;
00208         const std::string& logLevel = it->second;
00209         if (Core::StringUtil::startsWith(fileName, pattern) ||
00210             Core::StringUtil::endsWith(fileName, pattern)) {
00211             return logLevelFromString(logLevel);
00212         }
00213     }
00214     return LogLevel::NOTICE;
00215 }
00216 
00217 /**
00218  * Return the number of characters of __FILE__ that make up the path prefix.
00219  * That is, __FILE__ plus this value will be the relative path from the top
00220  * directory of the code tree.
00221  */
00222 size_t
00223 calculateLengthFilePrefix()
00224 {
00225     const char* start = __FILE__;
00226     const char* match = strstr(__FILE__, "Core/Debug.cc");
00227     assert(match != NULL);
00228     return size_t(match - start);
00229 }
00230 
00231 /// Stores result of calculateLengthFilePrefix().
00232 const size_t lengthFilePrefix = calculateLengthFilePrefix();
00233 
00234 /**
00235  * Strip out the common prefix of a filename to get a path from the project's
00236  * root directory.
00237  * \param fileName
00238  *      An absolute or relative filename, usually the value of __FILE__.
00239  * \return
00240  *      A nicer way to show display 'fileName' to the user.
00241  *      For example, this file would yield "Core/Debug.cc".
00242  */
00243 const char*
00244 relativeFileName(const char* fileName)
00245 {
00246     // Remove the prefix only if it matches that of __FILE__. This check is
00247     // needed in case someone compiles different files using different paths.
00248     if (strncmp(fileName, __FILE__, lengthFilePrefix) == 0)
00249         return fileName + lengthFilePrefix;
00250     else
00251         return fileName;
00252 }
00253 
00254 } // namespace Internal
00255 using namespace Internal; // NOLINT
00256 
00257 std::string
00258 getLogFilename()
00259 {
00260     std::lock_guard<std::mutex> lockGuard(mutex);
00261     return logFilename;
00262 }
00263 
00264 std::string
00265 setLogFilename(const std::string& filename)
00266 {
00267     FILE* next = fopen(filename.c_str(), "a");
00268     if (next == NULL) {
00269         return Core::StringUtil::format(
00270             "Could not open %s for writing debug log messages: %s",
00271             filename.c_str(),
00272             strerror(errno));
00273     }
00274     FILE* old;
00275     {
00276         std::lock_guard<std::mutex> lockGuard(mutex);
00277         old = stream;
00278         stream = next;
00279         logFilename = filename;
00280     }
00281     if (old != stdout && old != stderr) {
00282         if (fclose(old) != 0) {
00283             WARNING("Failed to close previous debug log file descriptor: %s",
00284                     strerror(errno));
00285         }
00286     }
00287     return {};
00288 }
00289 
00290 std::string
00291 reopenLogFromFilename()
00292 {
00293     std::string filename = getLogFilename();
00294     if (filename.empty())
00295         return {};
00296     else
00297         return setLogFilename(filename);
00298 }
00299 
00300 FILE*
00301 setLogFile(FILE* newFile)
00302 {
00303     std::lock_guard<std::mutex> lockGuard(mutex);
00304     logFilename.clear();
00305     FILE* old = stream;
00306     stream = newFile;
00307     return old;
00308 }
00309 
00310 std::function<void(DebugMessage)>
00311 setLogHandler(std::function<void(DebugMessage)> handler)
00312 {
00313     std::lock_guard<std::mutex> lockGuard(mutex);
00314     std::function<void(DebugMessage)> old = logHandler;
00315     logHandler = handler;
00316     return old;
00317 }
00318 
00319 std::vector<std::pair<std::string, std::string>>
00320 getLogPolicy()
00321 {
00322     std::lock_guard<std::mutex> lockGuard(mutex);
00323     return logPolicy;
00324 }
00325 
00326 void
00327 setLogPolicy(const std::vector<std::pair<std::string,
00328                                          std::string>>& newPolicy)
00329 {
00330     std::lock_guard<std::mutex> lockGuard(mutex);
00331     logPolicy = newPolicy;
00332     isLoggingCache.clear();
00333 }
00334 
00335 void
00336 setLogPolicy(const std::initializer_list<std::pair<std::string,
00337                                          std::string>>& newPolicy)
00338 {
00339     setLogPolicy(std::vector<std::pair<std::string,
00340                                          std::string>>(newPolicy));
00341 }
00342 
00343 std::vector<std::pair<std::string, std::string>>
00344 logPolicyFromString(const std::string& in)
00345 {
00346     std::vector<std::pair<std::string, std::string>> policy;
00347     std::vector<std::string> rules = Core::StringUtil::split(in, ',');
00348     for (auto it = rules.begin(); it != rules.end(); ++it) {
00349         std::vector<std::string> pair = Core::StringUtil::split(*it, '@');
00350         if (pair.size() == 1) {
00351             policy.emplace_back("", pair.at(0));
00352         } else if (pair.size() == 2) {
00353             policy.emplace_back(pair.at(0), pair.at(1));
00354         } else {
00355             // someone put an @ in their pattern? odd
00356             assert(pair.size() >= 3);
00357             std::string level = pair.back();
00358             pair.pop_back();
00359             policy.emplace_back(Core::StringUtil::join(pair, "@"),
00360                                 level);
00361         }
00362     }
00363     return policy;
00364 }
00365 
00366 std::string
00367 logPolicyToString(const std::vector<std::pair<std::string,
00368                                               std::string>>& policy)
00369 {
00370     std::stringstream ss;
00371     if (policy.empty()) {
00372         ss << LogLevel::NOTICE;
00373     } else {
00374         bool lastPatternEmpty = false;
00375         for (uint64_t i = 0; i < policy.size(); ++i) {
00376             if (policy.at(i).first.empty()) {
00377                 lastPatternEmpty = true;
00378             } else {
00379                 lastPatternEmpty = false;
00380                 ss << policy.at(i).first;
00381                 ss << "@";
00382             }
00383             ss << policy.at(i).second;
00384             if (i < policy.size() - 1)
00385                 ss << ",";
00386         }
00387         if (!lastPatternEmpty) {
00388             ss << ",";
00389             ss << LogLevel::NOTICE;
00390         }
00391     }
00392     return ss.str();
00393 }
00394 
00395 std::ostream&
00396 operator<<(std::ostream& ostream, LogLevel level)
00397 {
00398     ostream << logLevelToString(level);
00399     return ostream;
00400 }
00401 
00402 bool
00403 isLogging(LogLevel level, const char* fileName)
00404 {
00405     std::lock_guard<std::mutex> lockGuard(mutex);
00406     LogLevel verbosity;
00407     auto it = isLoggingCache.find(fileName);
00408     if (it == isLoggingCache.end()) {
00409         verbosity = getLogLevel(relativeFileName(fileName));
00410         isLoggingCache[fileName] = verbosity;
00411     } else {
00412         verbosity = it->second;
00413     }
00414     return uint32_t(level) <= uint32_t(verbosity);
00415 }
00416 
00417 void
00418 log(LogLevel level,
00419     const char* fileName, uint32_t lineNum, const char* functionName,
00420     const char* message)
00421 {
00422     if (logHandler) {
00423         DebugMessage d;
00424         d.filename = relativeFileName(fileName);
00425         d.linenum = int(lineNum);
00426         d.function = functionName;
00427         d.logLevel = int(level);
00428         d.logLevelString = logLevelToString(level);
00429         d.processName = processName;
00430         d.threadName = ThreadId::getName();
00431         d.message = message;
00432         (logHandler)(d);
00433         return;
00434     }
00435 
00436     // Don't use Core::Time here since it could potentially call PANIC.
00437     struct timespec now;
00438     clock_gettime(CLOCK_REALTIME, &now);
00439 
00440     // Failures are a little annoying here, since we can't exactly log
00441     // errors that come up.
00442     char formattedSeconds[64]; // a human-readable string now.tv_sec
00443     bool ok = false;
00444     { // First, try gmtime and strftime.
00445         struct tm calendarTime;
00446         if (gmtime_r(&now.tv_sec, &calendarTime) != NULL) {
00447             ok = (strftime(formattedSeconds,
00448                            sizeof(formattedSeconds),
00449                            "%F %T",
00450                            &calendarTime) > 0);
00451         }
00452     }
00453     if (!ok) { // If that failed, use the raw number.
00454         snprintf(formattedSeconds,
00455                  sizeof(formattedSeconds),
00456                  "%010lu",
00457                  now.tv_sec);
00458         formattedSeconds[sizeof(formattedSeconds) - 1] = '\0';
00459     }
00460 
00461     fprintf(stream, "%s.%06lu %s:%d in %s() %s[%s:%s]: %s\n",
00462             formattedSeconds, now.tv_nsec / 1000,
00463             relativeFileName(fileName), lineNum, functionName,
00464             logLevelToString(level),
00465             processName.c_str(), ThreadId::getName().c_str(),
00466             message);
00467 
00468     fflush(stream);
00469 }
00470 
00471 } // namespace LogCabin::Core::Debug
00472 } // namespace LogCabin::Core
00473 } // namespace LogCabin
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines