LogCabin
|
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