/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Based on chromium's base/logging.cc, with a lot of simplifications. #include "base/logging.h" #include "base/immediate_crash.h" #include "base/posix/safe_strerror.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #if BUILDFLAG(IS_WIN) #include #endif #include namespace logging { static int g_min_log_level = 0; // A log message handler that gets notified of every log message we process. LogMessageHandlerFunction g_log_message_handler = nullptr; void SetLogMessageHandler(LogMessageHandlerFunction handler) { g_log_message_handler = handler; } LogMessageHandlerFunction GetLogMessageHandler() { return g_log_message_handler; } bool ShouldCreateLogMessage(int severity) { return severity >= g_min_log_level; } #if BUILDFLAG(IS_WIN) // This has already been defined in the header, but defining it again as DWORD // ensures that the type used in the header is equivalent to DWORD. If not, // the redefinition is a compile error. typedef DWORD SystemErrorCode; #endif SystemErrorCode GetLastSystemErrorCode() { #if BUILDFLAG(IS_WIN) return ::GetLastError(); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) return errno; #endif } // For LOGGING_ERROR and above, always print to stderr. const int kAlwaysPrintErrorLevel = LOGGING_ERROR; static bool ShouldLogToStderr(int severity) { return severity >= kAlwaysPrintErrorLevel; } const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"}; static_assert(LOGGING_NUM_SEVERITIES == std::size(log_severity_names), "Incorrect number of log_severity_names"); const char* log_severity_name(int severity) { if (severity >= 0 && severity < LOGGING_NUM_SEVERITIES) { return log_severity_names[severity]; } return "UNKNOWN"; } LogMessage::LogMessage(const char* file, int line, LogSeverity severity) : severity_(severity), file_(file), line_(line) { // Don't let actions from this method affect the system error after returning. base::ScopedClearLastError scoped_clear_last_error; // Most logging initializes `file` from __FILE__. This isn't true for // base::Location::Current() which already does the stripping (and is used // for some logging, especially CHECKs). // Note(moz): Upstream code mitigates this with a hack, but for our mozilla // vendoring we don't, hence the long filenames shown in logs // except for CHECKs. { stream_ << "[zucchini:"; if (severity_ >= 0) { stream_ << log_severity_name(severity_); } else { stream_ << "VERBOSE" << -severity_; } stream_ << ":" << file << ":" << line << "] "; } message_start_ = stream_.str().length(); } LogMessage::~LogMessage() { // Don't let actions from this method affect the system error after returning. base::ScopedClearLastError scoped_clear_last_error; // Note(moz): Contrary to upstream code, we do not add an extra newline // before forwarding the message. This is the format expected by // the updater's log function. std::string str(stream_.str()); // Give any log message handler first dibs on the message. bool handled = g_log_message_handler && g_log_message_handler(severity_, file_, line_, message_start_, str); if (!handled) { if (ShouldLogToStderr(severity_)) { fputs(str.c_str(), stderr); fputc('\n', stderr); } else { // Contrary to fputs(), puts() already writes a newline character puts(str.c_str()); } } if (severity_ == LOGGING_FATAL) { base::ImmediateCrash(); } } BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { #if BUILDFLAG(IS_WIN) LPWSTR msgbuf = nullptr; DWORD len = ::FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, 0, reinterpret_cast(&msgbuf), 0, nullptr); if (len) { std::u16string message = base::WideToUTF16(msgbuf); ::LocalFree(msgbuf); msgbuf = nullptr; // Messages returned by system end with line breaks. return base::UTF16ToUTF8(base::CollapseWhitespace(message, true)) + base::StringPrintf(" (0x%lX)", error_code); } return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)", GetLastError(), error_code); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) return base::safe_strerror(error_code) + base::StringPrintf(" (%d)", error_code); #endif // BUILDFLAG(IS_WIN) } #if BUILDFLAG(IS_WIN) Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, int line, LogSeverity severity, SystemErrorCode err) : LogMessage(file, line, severity), err_(err) {} Win32ErrorLogMessage::~Win32ErrorLogMessage() { // Don't let actions from this method affect the system error after returning. base::ScopedClearLastError scoped_clear_last_error; stream() << ": " << SystemErrorCodeToString(err_); } #elif BUILDFLAG(IS_POSIX) ErrnoLogMessage::ErrnoLogMessage(const char* file, int line, LogSeverity severity, SystemErrorCode err) : LogMessage(file, line, severity), err_(err) {} ErrnoLogMessage::~ErrnoLogMessage() { // Don't let actions from this method affect the system error after returning. base::ScopedClearLastError scoped_clear_last_error; stream() << ": " << SystemErrorCodeToString(err_); } #endif // BUILDFLAG(IS_WIN) } // namespace logging