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