| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/logging.h" | |
| #include <sys/syscall.h> | |
| #include <time.h> | |
| #include <errno.h> | |
| #include <pthread.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #include <algorithm> | |
| #include <cstring> | |
| #include <ctime> | |
| #include <iomanip> | |
| #include <ostream> | |
| #include <string> | |
| #include "base/posix/eintr_wrapper.h" | |
| #include "base/strings/string_piece.h" | |
| #include "base/strings/string_util.h" | |
| #include "base/strings/stringprintf.h" | |
| #include "base/strings/utf_string_conversion_utils.h" | |
| namespace logging { | |
| namespace { | |
| const char* const log_severity_names[LOG_NUM_SEVERITIES] = { | |
| "INFO", "WARNING", "ERROR", "FATAL" }; | |
| const char* log_severity_name(int severity) { | |
| if (severity >= 0 && severity < LOG_NUM_SEVERITIES) | |
| return log_severity_names[severity]; | |
| return "UNKNOWN"; | |
| } | |
| int g_min_log_level = 0; | |
| LoggingDestination g_logging_destination = LOG_DEFAULT; | |
| // For LOG_ERROR and above, always print to stderr. | |
| const int kAlwaysPrintErrorLevel = LOG_ERROR; | |
| // What should be prepended to each message? | |
| bool g_log_timestamp = true; | |
| // Should we pop up fatal debug messages in a dialog? | |
| bool show_error_dialogs = false; | |
| // An assert handler override specified by the client to be called instead of | |
| // the debug message dialog and process termination. | |
| LogAssertHandlerFunction log_assert_handler = nullptr; | |
| // A log message handler that gets notified of every log message we process. | |
| LogMessageHandlerFunction log_message_handler = nullptr; | |
| // Helper functions to wrap platform differences. | |
| } // namespace | |
| LoggingSettings::LoggingSettings() | |
| : logging_dest(LOG_DEFAULT) {} | |
| bool BaseInitLoggingImpl(const LoggingSettings& settings) { | |
| g_logging_destination = settings.logging_dest; | |
| return true; | |
| } | |
| void SetMinLogLevel(int level) { | |
| g_min_log_level = std::min(LOG_FATAL, level); | |
| } | |
| int GetMinLogLevel() { | |
| return g_min_log_level; | |
| } | |
| bool ShouldCreateLogMessage(int severity) { | |
| if (severity < g_min_log_level) | |
| return false; | |
| // Return true here unless we know ~LogMessage won't do anything. Note that | |
| // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even | |
| // when g_logging_destination is LOG_NONE. | |
| return g_logging_destination != LOG_NONE || log_message_handler || | |
| severity >= kAlwaysPrintErrorLevel; | |
| } | |
| int GetVlogVerbosity() { | |
| return std::max(-1, LOG_INFO - GetMinLogLevel()); | |
| } | |
| void SetLogItems(bool enable_process_id, bool enable_thread_id, | |
| bool enable_timestamp, bool enable_tickcount) { | |
| g_log_timestamp = enable_timestamp; | |
| } | |
| void SetShowErrorDialogs(bool enable_dialogs) { | |
| show_error_dialogs = enable_dialogs; | |
| } | |
| void SetLogAssertHandler(LogAssertHandlerFunction handler) { | |
| log_assert_handler = handler; | |
| } | |
| void SetLogMessageHandler(LogMessageHandlerFunction handler) { | |
| log_message_handler = handler; | |
| } | |
| LogMessageHandlerFunction GetLogMessageHandler() { | |
| return log_message_handler; | |
| } | |
| // Explicit instantiations for commonly used comparisons. | |
| template std::string* MakeCheckOpString<int, int>( | |
| const int&, const int&, const char* names); | |
| template std::string* MakeCheckOpString<unsigned long, unsigned long>( | |
| const unsigned long&, const unsigned long&, const char* names); | |
| template std::string* MakeCheckOpString<unsigned long, unsigned int>( | |
| const unsigned long&, const unsigned int&, const char* names); | |
| template std::string* MakeCheckOpString<unsigned int, unsigned long>( | |
| const unsigned int&, const unsigned long&, const char* names); | |
| template std::string* MakeCheckOpString<std::string, std::string>( | |
| const std::string&, const std::string&, const char* name); | |
| void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) { | |
| (*os) << "nullptr"; | |
| } | |
| LogMessage::LogMessage(const char* file, int line, LogSeverity severity) | |
| : severity_(severity), file_(file), line_(line) { | |
| Init(file, line); | |
| } | |
| LogMessage::LogMessage(const char* file, int line, const char* condition) | |
| : severity_(LOG_FATAL), file_(file), line_(line) { | |
| Init(file, line); | |
| stream_ << "Check failed: " << condition << ". "; | |
| } | |
| LogMessage::LogMessage(const char* file, int line, std::string* result) | |
| : severity_(LOG_FATAL), file_(file), line_(line) { | |
| Init(file, line); | |
| stream_ << "Check failed: " << *result; | |
| delete result; | |
| } | |
| LogMessage::LogMessage(const char* file, int line, LogSeverity severity, | |
| std::string* result) | |
| : severity_(severity), file_(file), line_(line) { | |
| Init(file, line); | |
| stream_ << "Check failed: " << *result; | |
| delete result; | |
| } | |
| LogMessage::~LogMessage() { | |
| stream_ << std::endl; | |
| std::string str_newline(stream_.str()); | |
| // Give any log message handler first dibs on the message. | |
| if (log_message_handler && | |
| log_message_handler(severity_, file_, line_, | |
| message_start_, str_newline)) { | |
| // The handler took care of it, no further processing. | |
| return; | |
| } | |
| if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { | |
| ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); | |
| fflush(stderr); | |
| } else if (severity_ >= kAlwaysPrintErrorLevel) { | |
| // When we're only outputting to a log file, above a certain log level, we | |
| // should still output to stderr so that we can better detect and diagnose | |
| // problems with unit tests, especially on the buildbots. | |
| ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); | |
| fflush(stderr); | |
| } | |
| if (severity_ == LOG_FATAL) { | |
| // Ensure the first characters of the string are on the stack so they | |
| // are contained in minidumps for diagnostic purposes. | |
| char str_stack[1024]; | |
| str_newline.copy(str_stack, arraysize(str_stack)); | |
| if (log_assert_handler) { | |
| // Make a copy of the string for the handler out of paranoia. | |
| log_assert_handler(std::string(stream_.str())); | |
| } else { | |
| // Crash the process to generate a dump. | |
| abort(); | |
| } | |
| } | |
| } | |
| // writes the common header info to the stream | |
| void LogMessage::Init(const char* file, int line) { | |
| base::StringPiece filename(file); | |
| size_t last_slash_pos = filename.find_last_of("\\/"); | |
| if (last_slash_pos != base::StringPiece::npos) | |
| filename.remove_prefix(last_slash_pos + 1); | |
| // TODO(darin): It might be nice if the columns were fixed width. | |
| stream_ << '['; | |
| if (g_log_timestamp) { | |
| time_t t = time(nullptr); | |
| struct tm local_time; | |
| memset(&local_time, 0, sizeof(local_time)); | |
| #ifdef _MSC_VER | |
| localtime_s(&local_time, &t); | |
| #else | |
| localtime_r(&t, &local_time); | |
| #endif | |
| struct tm* tm_time = &local_time; | |
| stream_ << std::setfill('0') | |
| << std::setw(2) << 1 + tm_time->tm_mon | |
| << std::setw(2) << tm_time->tm_mday | |
| << '/' | |
| << std::setw(2) << tm_time->tm_hour | |
| << std::setw(2) << tm_time->tm_min | |
| << std::setw(2) << tm_time->tm_sec | |
| << ':'; | |
| } | |
| if (severity_ >= 0) | |
| stream_ << log_severity_name(severity_); | |
| else | |
| stream_ << "VERBOSE" << -severity_; | |
| stream_ << ":" << filename << "(" << line << ")] "; | |
| message_start_ = stream_.str().length(); | |
| } | |
| void RawLog(int level, const char* message) { | |
| if (level >= g_min_log_level && message) { | |
| size_t bytes_written = 0; | |
| const size_t message_len = strlen(message); | |
| int rv; | |
| while (bytes_written < message_len) { | |
| rv = HANDLE_EINTR( | |
| write(STDERR_FILENO, message + bytes_written, | |
| message_len - bytes_written)); | |
| if (rv < 0) { | |
| // Give up, nothing we can do now. | |
| break; | |
| } | |
| bytes_written += rv; | |
| } | |
| if (message_len > 0 && message[message_len - 1] != '\n') { | |
| do { | |
| rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1)); | |
| if (rv < 0) { | |
| // Give up, nothing we can do now. | |
| break; | |
| } | |
| } while (rv != 1); | |
| } | |
| } | |
| if (level == LOG_FATAL) | |
| abort(); | |
| } | |
| // This was defined at the beginning of this file. | |
| #undef write | |
| void LogErrorNotReached(const char* file, int line) { | |
| LogMessage(file, line, LOG_ERROR).stream() | |
| << "NOTREACHED() hit."; | |
| } | |
| } // namespace logging |