Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "base/logging.h" |
| 6 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 7 | #include <sys/syscall.h> |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 8 | #include <time.h> |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 9 | #include <errno.h> |
| 10 | #include <pthread.h> |
| 11 | #include <stdio.h> |
| 12 | #include <stdlib.h> |
| 13 | #include <string.h> |
Alex Vakulenko | 674f0eb | 2016-01-20 08:10:48 -0800 | [diff] [blame] | 14 | #include <sys/stat.h> |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 15 | #include <unistd.h> |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 16 | |
| 17 | #include <algorithm> |
| 18 | #include <cstring> |
| 19 | #include <ctime> |
| 20 | #include <iomanip> |
| 21 | #include <ostream> |
| 22 | #include <string> |
| 23 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 24 | #include "base/posix/eintr_wrapper.h" |
| 25 | #include "base/strings/string_piece.h" |
| 26 | #include "base/strings/string_util.h" |
| 27 | #include "base/strings/stringprintf.h" |
Vitaly Buka | 8750b27 | 2015-08-18 18:39:08 -0700 | [diff] [blame] | 28 | #include "base/strings/utf_string_conversion_utils.h" |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 29 | |
| 30 | namespace logging { |
| 31 | |
| 32 | namespace { |
| 33 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 34 | const char* const log_severity_names[LOG_NUM_SEVERITIES] = { |
| 35 | "INFO", "WARNING", "ERROR", "FATAL" }; |
| 36 | |
| 37 | const char* log_severity_name(int severity) { |
| 38 | if (severity >= 0 && severity < LOG_NUM_SEVERITIES) |
| 39 | return log_severity_names[severity]; |
| 40 | return "UNKNOWN"; |
| 41 | } |
| 42 | |
| 43 | int g_min_log_level = 0; |
| 44 | |
| 45 | LoggingDestination g_logging_destination = LOG_DEFAULT; |
| 46 | |
| 47 | // For LOG_ERROR and above, always print to stderr. |
| 48 | const int kAlwaysPrintErrorLevel = LOG_ERROR; |
| 49 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 50 | // What should be prepended to each message? |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 51 | bool g_log_timestamp = true; |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 52 | |
| 53 | // Should we pop up fatal debug messages in a dialog? |
| 54 | bool show_error_dialogs = false; |
| 55 | |
| 56 | // An assert handler override specified by the client to be called instead of |
| 57 | // the debug message dialog and process termination. |
| 58 | LogAssertHandlerFunction log_assert_handler = nullptr; |
| 59 | // A log message handler that gets notified of every log message we process. |
| 60 | LogMessageHandlerFunction log_message_handler = nullptr; |
| 61 | |
| 62 | // Helper functions to wrap platform differences. |
| 63 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 64 | } // namespace |
| 65 | |
| 66 | LoggingSettings::LoggingSettings() |
Vitaly Buka | 8750b27 | 2015-08-18 18:39:08 -0700 | [diff] [blame] | 67 | : logging_dest(LOG_DEFAULT) {} |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 68 | |
| 69 | bool BaseInitLoggingImpl(const LoggingSettings& settings) { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 70 | g_logging_destination = settings.logging_dest; |
| 71 | |
Vitaly Buka | 8750b27 | 2015-08-18 18:39:08 -0700 | [diff] [blame] | 72 | return true; |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | void SetMinLogLevel(int level) { |
| 76 | g_min_log_level = std::min(LOG_FATAL, level); |
| 77 | } |
| 78 | |
| 79 | int GetMinLogLevel() { |
| 80 | return g_min_log_level; |
| 81 | } |
| 82 | |
Alex Vakulenko | 674f0eb | 2016-01-20 08:10:48 -0800 | [diff] [blame] | 83 | bool ShouldCreateLogMessage(int severity) { |
| 84 | if (severity < g_min_log_level) |
| 85 | return false; |
| 86 | |
| 87 | // Return true here unless we know ~LogMessage won't do anything. Note that |
| 88 | // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even |
| 89 | // when g_logging_destination is LOG_NONE. |
| 90 | return g_logging_destination != LOG_NONE || log_message_handler || |
| 91 | severity >= kAlwaysPrintErrorLevel; |
| 92 | } |
| 93 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 94 | int GetVlogVerbosity() { |
| 95 | return std::max(-1, LOG_INFO - GetMinLogLevel()); |
| 96 | } |
| 97 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 98 | void SetLogItems(bool enable_process_id, bool enable_thread_id, |
| 99 | bool enable_timestamp, bool enable_tickcount) { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 100 | g_log_timestamp = enable_timestamp; |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | void SetShowErrorDialogs(bool enable_dialogs) { |
| 104 | show_error_dialogs = enable_dialogs; |
| 105 | } |
| 106 | |
| 107 | void SetLogAssertHandler(LogAssertHandlerFunction handler) { |
| 108 | log_assert_handler = handler; |
| 109 | } |
| 110 | |
| 111 | void SetLogMessageHandler(LogMessageHandlerFunction handler) { |
| 112 | log_message_handler = handler; |
| 113 | } |
| 114 | |
| 115 | LogMessageHandlerFunction GetLogMessageHandler() { |
| 116 | return log_message_handler; |
| 117 | } |
| 118 | |
| 119 | // Explicit instantiations for commonly used comparisons. |
| 120 | template std::string* MakeCheckOpString<int, int>( |
| 121 | const int&, const int&, const char* names); |
| 122 | template std::string* MakeCheckOpString<unsigned long, unsigned long>( |
| 123 | const unsigned long&, const unsigned long&, const char* names); |
| 124 | template std::string* MakeCheckOpString<unsigned long, unsigned int>( |
| 125 | const unsigned long&, const unsigned int&, const char* names); |
| 126 | template std::string* MakeCheckOpString<unsigned int, unsigned long>( |
| 127 | const unsigned int&, const unsigned long&, const char* names); |
| 128 | template std::string* MakeCheckOpString<std::string, std::string>( |
| 129 | const std::string&, const std::string&, const char* name); |
| 130 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 131 | LogMessage::LogMessage(const char* file, int line, LogSeverity severity) |
| 132 | : severity_(severity), file_(file), line_(line) { |
| 133 | Init(file, line); |
| 134 | } |
| 135 | |
Alex Vakulenko | 674f0eb | 2016-01-20 08:10:48 -0800 | [diff] [blame] | 136 | LogMessage::LogMessage(const char* file, int line, const char* condition) |
| 137 | : severity_(LOG_FATAL), file_(file), line_(line) { |
| 138 | Init(file, line); |
| 139 | stream_ << "Check failed: " << condition << ". "; |
| 140 | } |
| 141 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 142 | LogMessage::LogMessage(const char* file, int line, std::string* result) |
| 143 | : severity_(LOG_FATAL), file_(file), line_(line) { |
| 144 | Init(file, line); |
| 145 | stream_ << "Check failed: " << *result; |
| 146 | delete result; |
| 147 | } |
| 148 | |
| 149 | LogMessage::LogMessage(const char* file, int line, LogSeverity severity, |
| 150 | std::string* result) |
| 151 | : severity_(severity), file_(file), line_(line) { |
| 152 | Init(file, line); |
| 153 | stream_ << "Check failed: " << *result; |
| 154 | delete result; |
| 155 | } |
| 156 | |
| 157 | LogMessage::~LogMessage() { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 158 | stream_ << std::endl; |
| 159 | std::string str_newline(stream_.str()); |
| 160 | |
| 161 | // Give any log message handler first dibs on the message. |
| 162 | if (log_message_handler && |
| 163 | log_message_handler(severity_, file_, line_, |
| 164 | message_start_, str_newline)) { |
| 165 | // The handler took care of it, no further processing. |
| 166 | return; |
| 167 | } |
| 168 | |
| 169 | if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 170 | ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); |
| 171 | fflush(stderr); |
| 172 | } else if (severity_ >= kAlwaysPrintErrorLevel) { |
| 173 | // When we're only outputting to a log file, above a certain log level, we |
| 174 | // should still output to stderr so that we can better detect and diagnose |
| 175 | // problems with unit tests, especially on the buildbots. |
| 176 | ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); |
| 177 | fflush(stderr); |
| 178 | } |
| 179 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 180 | if (severity_ == LOG_FATAL) { |
| 181 | // Ensure the first characters of the string are on the stack so they |
| 182 | // are contained in minidumps for diagnostic purposes. |
| 183 | char str_stack[1024]; |
| 184 | str_newline.copy(str_stack, arraysize(str_stack)); |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 185 | |
| 186 | if (log_assert_handler) { |
| 187 | // Make a copy of the string for the handler out of paranoia. |
| 188 | log_assert_handler(std::string(stream_.str())); |
| 189 | } else { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 190 | // Crash the process to generate a dump. |
Vitaly Buka | 8750b27 | 2015-08-18 18:39:08 -0700 | [diff] [blame] | 191 | abort(); |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 192 | } |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // writes the common header info to the stream |
| 197 | void LogMessage::Init(const char* file, int line) { |
| 198 | base::StringPiece filename(file); |
| 199 | size_t last_slash_pos = filename.find_last_of("\\/"); |
| 200 | if (last_slash_pos != base::StringPiece::npos) |
| 201 | filename.remove_prefix(last_slash_pos + 1); |
| 202 | |
| 203 | // TODO(darin): It might be nice if the columns were fixed width. |
| 204 | |
| 205 | stream_ << '['; |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 206 | if (g_log_timestamp) { |
| 207 | time_t t = time(nullptr); |
Alex Vakulenko | 674f0eb | 2016-01-20 08:10:48 -0800 | [diff] [blame] | 208 | struct tm local_time; |
| 209 | memset(&local_time, 0, sizeof(local_time)); |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 210 | #ifdef _MSC_VER |
| 211 | localtime_s(&local_time, &t); |
| 212 | #else |
| 213 | localtime_r(&t, &local_time); |
| 214 | #endif |
| 215 | struct tm* tm_time = &local_time; |
| 216 | stream_ << std::setfill('0') |
| 217 | << std::setw(2) << 1 + tm_time->tm_mon |
| 218 | << std::setw(2) << tm_time->tm_mday |
| 219 | << '/' |
| 220 | << std::setw(2) << tm_time->tm_hour |
| 221 | << std::setw(2) << tm_time->tm_min |
| 222 | << std::setw(2) << tm_time->tm_sec |
| 223 | << ':'; |
| 224 | } |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 225 | if (severity_ >= 0) |
| 226 | stream_ << log_severity_name(severity_); |
| 227 | else |
| 228 | stream_ << "VERBOSE" << -severity_; |
| 229 | |
| 230 | stream_ << ":" << filename << "(" << line << ")] "; |
| 231 | |
| 232 | message_start_ = stream_.str().length(); |
| 233 | } |
| 234 | |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 235 | void RawLog(int level, const char* message) { |
Alex Vakulenko | bf79a9e | 2016-03-28 15:11:43 -0700 | [diff] [blame] | 236 | if (level >= g_min_log_level && message) { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 237 | size_t bytes_written = 0; |
| 238 | const size_t message_len = strlen(message); |
| 239 | int rv; |
| 240 | while (bytes_written < message_len) { |
| 241 | rv = HANDLE_EINTR( |
| 242 | write(STDERR_FILENO, message + bytes_written, |
| 243 | message_len - bytes_written)); |
| 244 | if (rv < 0) { |
| 245 | // Give up, nothing we can do now. |
| 246 | break; |
| 247 | } |
| 248 | bytes_written += rv; |
| 249 | } |
| 250 | |
| 251 | if (message_len > 0 && message[message_len - 1] != '\n') { |
| 252 | do { |
| 253 | rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1)); |
| 254 | if (rv < 0) { |
| 255 | // Give up, nothing we can do now. |
| 256 | break; |
| 257 | } |
| 258 | } while (rv != 1); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | if (level == LOG_FATAL) |
Vitaly Buka | 8750b27 | 2015-08-18 18:39:08 -0700 | [diff] [blame] | 263 | abort(); |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | // This was defined at the beginning of this file. |
| 267 | #undef write |
| 268 | |
Vitaly Buka | 60b8f00 | 2015-08-20 13:47:48 -0700 | [diff] [blame] | 269 | void LogErrorNotReached(const char* file, int line) { |
Vitaly Buka | cbed206 | 2015-08-17 12:54:05 -0700 | [diff] [blame] | 270 | LogMessage(file, line, LOG_ERROR).stream() |
| 271 | << "NOTREACHED() hit."; |
| 272 | } |
| 273 | |
| 274 | } // namespace logging |