| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "absl/log/internal/log_message.h" |
|
|
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
|
|
| #ifndef _WIN32 |
| #include <unistd.h> |
| #endif |
|
|
| #include <algorithm> |
| #include <array> |
| #include <atomic> |
| #include <memory> |
| #include <ostream> |
| #include <string> |
| #include <tuple> |
|
|
| #include "absl/base/attributes.h" |
| #include "absl/base/config.h" |
| #include "absl/base/internal/raw_logging.h" |
| #include "absl/base/internal/strerror.h" |
| #include "absl/base/internal/sysinfo.h" |
| #include "absl/base/log_severity.h" |
| #include "absl/container/inlined_vector.h" |
| #include "absl/debugging/internal/examine_stack.h" |
| #include "absl/log/globals.h" |
| #include "absl/log/internal/append_truncated.h" |
| #include "absl/log/internal/globals.h" |
| #include "absl/log/internal/log_format.h" |
| #include "absl/log/internal/log_sink_set.h" |
| #include "absl/log/internal/proto.h" |
| #include "absl/log/log_entry.h" |
| #include "absl/log/log_sink.h" |
| #include "absl/log/log_sink_registry.h" |
| #include "absl/memory/memory.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/time.h" |
| #include "absl/types/span.h" |
|
|
| extern "C" ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL( |
| AbslInternalOnFatalLogMessage)(const absl::LogEntry&) { |
| |
| } |
|
|
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| namespace log_internal { |
|
|
| namespace { |
| |
| enum EventTag : uint8_t { |
| kValue = 7, |
| }; |
|
|
| |
| enum ValueTag : uint8_t { |
| kString = 1, |
| kStringLiteral = 6, |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| bool PrintValue(absl::Span<char>& dst, absl::Span<const char> buf) { |
| if (dst.size() <= 1) return false; |
| ProtoField field; |
| while (field.DecodeFrom(&buf)) { |
| switch (field.tag()) { |
| case ValueTag::kString: |
| case ValueTag::kStringLiteral: |
| if (field.type() == WireType::kLengthDelimited) |
| if (log_internal::AppendTruncated(field.string_value(), dst) < |
| field.string_value().size()) |
| return false; |
| } |
| } |
| return true; |
| } |
|
|
| absl::string_view Basename(absl::string_view filepath) { |
| #ifdef _WIN32 |
| size_t path = filepath.find_last_of("/\\"); |
| #else |
| size_t path = filepath.find_last_of('/'); |
| #endif |
| if (path != filepath.npos) filepath.remove_prefix(path + 1); |
| return filepath; |
| } |
|
|
| void WriteToString(const char* data, void* str) { |
| reinterpret_cast<std::string*>(str)->append(data); |
| } |
| void WriteToStream(const char* data, void* os) { |
| auto* cast_os = static_cast<std::ostream*>(os); |
| *cast_os << data; |
| } |
| } |
|
|
| struct LogMessage::LogMessageData final { |
| LogMessageData(const char* file, int line, absl::LogSeverity severity, |
| absl::Time timestamp); |
| LogMessageData(const LogMessageData&) = delete; |
| LogMessageData& operator=(const LogMessageData&) = delete; |
|
|
| |
| absl::LogEntry entry; |
|
|
| |
| bool first_fatal; |
| |
| bool fail_quietly; |
| |
| bool is_perror; |
|
|
| |
| absl::InlinedVector<absl::LogSink*, 16> extra_sinks; |
| |
| |
| bool extra_sinks_only; |
|
|
| std::ostream manipulated; |
|
|
| |
| std::array<char, kLogMessageBufferSize> encoded_buf; |
| |
| |
| |
| |
| |
| absl::Span<char> encoded_remaining; |
|
|
| |
| std::array<char, kLogMessageBufferSize> string_buf; |
|
|
| void FinalizeEncodingAndFormat(); |
| }; |
|
|
| LogMessage::LogMessageData::LogMessageData(const char* file, int line, |
| absl::LogSeverity severity, |
| absl::Time timestamp) |
| : extra_sinks_only(false), |
| manipulated(nullptr), |
| |
| encoded_remaining(absl::MakeSpan(encoded_buf)) { |
| |
| manipulated.setf(std::ios_base::showbase | std::ios_base::boolalpha); |
| entry.full_filename_ = file; |
| entry.base_filename_ = Basename(file); |
| entry.line_ = line; |
| entry.prefix_ = absl::ShouldPrependLogPrefix(); |
| entry.severity_ = absl::NormalizeLogSeverity(severity); |
| entry.verbose_level_ = absl::LogEntry::kNoVerbosityLevel; |
| entry.timestamp_ = timestamp; |
| entry.tid_ = absl::base_internal::GetCachedTID(); |
| } |
|
|
| void LogMessage::LogMessageData::FinalizeEncodingAndFormat() { |
| |
| |
| |
| absl::Span<const char> encoded_data( |
| encoded_buf.data(), |
| static_cast<size_t>(encoded_remaining.data() - encoded_buf.data())); |
| |
| |
| absl::Span<char> string_remaining(string_buf); |
| |
| |
| |
| |
| string_remaining.remove_suffix(2); |
| entry.prefix_len_ = |
| entry.prefix() ? log_internal::FormatLogPrefix( |
| entry.log_severity(), entry.timestamp(), entry.tid(), |
| entry.source_basename(), entry.source_line(), |
| log_internal::ThreadIsLoggingToLogSink() |
| ? PrefixFormat::kRaw |
| : PrefixFormat::kNotRaw, |
| string_remaining) |
| : 0; |
| |
| |
| ProtoField field; |
| while (field.DecodeFrom(&encoded_data)) { |
| switch (field.tag()) { |
| case EventTag::kValue: |
| if (field.type() != WireType::kLengthDelimited) continue; |
| if (PrintValue(string_remaining, field.bytes_value())) continue; |
| break; |
| } |
| break; |
| } |
| auto chars_written = |
| static_cast<size_t>(string_remaining.data() - string_buf.data()); |
| string_buf[chars_written++] = '\n'; |
| string_buf[chars_written++] = '\0'; |
| entry.text_message_with_prefix_and_newline_and_nul_ = |
| absl::MakeSpan(string_buf).subspan(0, chars_written); |
| } |
|
|
| LogMessage::LogMessage(const char* file, int line, absl::LogSeverity severity) |
| : data_(absl::make_unique<LogMessageData>(file, line, severity, |
| absl::Now())) { |
| data_->first_fatal = false; |
| data_->is_perror = false; |
| data_->fail_quietly = false; |
|
|
| |
| |
| |
| LogBacktraceIfNeeded(); |
| } |
|
|
| LogMessage::LogMessage(const char* file, int line, InfoTag) |
| : LogMessage(file, line, absl::LogSeverity::kInfo) {} |
| LogMessage::LogMessage(const char* file, int line, WarningTag) |
| : LogMessage(file, line, absl::LogSeverity::kWarning) {} |
| LogMessage::LogMessage(const char* file, int line, ErrorTag) |
| : LogMessage(file, line, absl::LogSeverity::kError) {} |
|
|
| LogMessage::~LogMessage() { |
| #ifdef ABSL_MIN_LOG_LEVEL |
| if (data_->entry.log_severity() < |
| static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) && |
| data_->entry.log_severity() < absl::LogSeverity::kFatal) { |
| return; |
| } |
| #endif |
| Flush(); |
| } |
|
|
| LogMessage& LogMessage::AtLocation(absl::string_view file, int line) { |
| data_->entry.full_filename_ = file; |
| data_->entry.base_filename_ = Basename(file); |
| data_->entry.line_ = line; |
| LogBacktraceIfNeeded(); |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::NoPrefix() { |
| data_->entry.prefix_ = false; |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::WithVerbosity(int verbose_level) { |
| if (verbose_level == absl::LogEntry::kNoVerbosityLevel) { |
| data_->entry.verbose_level_ = absl::LogEntry::kNoVerbosityLevel; |
| } else { |
| data_->entry.verbose_level_ = std::max(0, verbose_level); |
| } |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::WithTimestamp(absl::Time timestamp) { |
| data_->entry.timestamp_ = timestamp; |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::WithThreadID(absl::LogEntry::tid_t tid) { |
| data_->entry.tid_ = tid; |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::WithMetadataFrom(const absl::LogEntry& entry) { |
| data_->entry.full_filename_ = entry.full_filename_; |
| data_->entry.base_filename_ = entry.base_filename_; |
| data_->entry.line_ = entry.line_; |
| data_->entry.prefix_ = entry.prefix_; |
| data_->entry.severity_ = entry.severity_; |
| data_->entry.verbose_level_ = entry.verbose_level_; |
| data_->entry.timestamp_ = entry.timestamp_; |
| data_->entry.tid_ = entry.tid_; |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::WithPerror() { |
| data_->is_perror = true; |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::ToSinkAlso(absl::LogSink* sink) { |
| ABSL_INTERNAL_CHECK(sink, "null LogSink*"); |
| data_->extra_sinks.push_back(sink); |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::ToSinkOnly(absl::LogSink* sink) { |
| ABSL_INTERNAL_CHECK(sink, "null LogSink*"); |
| data_->extra_sinks.clear(); |
| data_->extra_sinks.push_back(sink); |
| data_->extra_sinks_only = true; |
| return *this; |
| } |
|
|
| #ifdef __ELF__ |
| extern "C" void __gcov_dump() ABSL_ATTRIBUTE_WEAK; |
| extern "C" void __gcov_flush() ABSL_ATTRIBUTE_WEAK; |
| #endif |
|
|
| void LogMessage::FailWithoutStackTrace() { |
| |
| log_internal::SetSuppressSigabortTrace(true); |
| #if defined _DEBUG && defined COMPILER_MSVC |
| |
| __debugbreak(); |
| #endif |
|
|
| #ifdef __ELF__ |
| |
| if (&__gcov_dump != nullptr) { |
| __gcov_dump(); |
| } else if (&__gcov_flush != nullptr) { |
| __gcov_flush(); |
| } |
| #endif |
|
|
| abort(); |
| } |
|
|
| void LogMessage::FailQuietly() { |
| |
| |
| |
| |
| |
| |
| |
| |
| _exit(1); |
| } |
|
|
| LogMessage& LogMessage::operator<<(const std::string& v) { |
| CopyToEncodedBuffer<StringType::kNotLiteral>(v); |
| return *this; |
| } |
|
|
| LogMessage& LogMessage::operator<<(absl::string_view v) { |
| CopyToEncodedBuffer<StringType::kNotLiteral>(v); |
| return *this; |
| } |
| LogMessage& LogMessage::operator<<(std::ostream& (*m)(std::ostream& os)) { |
| OstreamView view(*data_); |
| data_->manipulated << m; |
| return *this; |
| } |
| LogMessage& LogMessage::operator<<(std::ios_base& (*m)(std::ios_base& os)) { |
| OstreamView view(*data_); |
| data_->manipulated << m; |
| return *this; |
| } |
| template LogMessage& LogMessage::operator<<(const char& v); |
| template LogMessage& LogMessage::operator<<(const signed char& v); |
| template LogMessage& LogMessage::operator<<(const unsigned char& v); |
| template LogMessage& LogMessage::operator<<(const short& v); |
| template LogMessage& LogMessage::operator<<(const unsigned short& v); |
| template LogMessage& LogMessage::operator<<(const int& v); |
| template LogMessage& LogMessage::operator<<(const unsigned int& v); |
| template LogMessage& LogMessage::operator<<(const long& v); |
| template LogMessage& LogMessage::operator<<(const unsigned long& v); |
| template LogMessage& LogMessage::operator<<(const long long& v); |
| template LogMessage& LogMessage::operator<<( |
| const unsigned long long& v); |
| template LogMessage& LogMessage::operator<<(void* const& v); |
| template LogMessage& LogMessage::operator<<(const void* const& v); |
| template LogMessage& LogMessage::operator<<(const float& v); |
| template LogMessage& LogMessage::operator<<(const double& v); |
| template LogMessage& LogMessage::operator<<(const bool& v); |
|
|
| void LogMessage::Flush() { |
| if (data_->entry.log_severity() < absl::MinLogLevel()) return; |
|
|
| if (data_->is_perror) { |
| InternalStream() << ": " << absl::base_internal::StrError(errno_saver_()) |
| << " [" << errno_saver_() << "]"; |
| } |
|
|
| |
| ABSL_CONST_INIT static std::atomic<bool> seen_fatal(false); |
| if (data_->entry.log_severity() == absl::LogSeverity::kFatal && |
| absl::log_internal::ExitOnDFatal()) { |
| |
| |
| bool expected_seen_fatal = false; |
| if (seen_fatal.compare_exchange_strong(expected_seen_fatal, true, |
| std::memory_order_relaxed)) { |
| data_->first_fatal = true; |
| } |
| } |
|
|
| data_->FinalizeEncodingAndFormat(); |
| data_->entry.encoding_ = |
| absl::string_view(data_->encoded_buf.data(), |
| static_cast<size_t>(data_->encoded_remaining.data() - |
| data_->encoded_buf.data())); |
| SendToLog(); |
| } |
|
|
| void LogMessage::SetFailQuietly() { data_->fail_quietly = true; } |
|
|
| LogMessage::OstreamView::OstreamView(LogMessageData& message_data) |
| : data_(message_data), encoded_remaining_copy_(data_.encoded_remaining) { |
| |
| |
| |
| |
| |
| |
| message_start_ = |
| EncodeMessageStart(EventTag::kValue, encoded_remaining_copy_.size(), |
| &encoded_remaining_copy_); |
| string_start_ = |
| EncodeMessageStart(ValueTag::kString, encoded_remaining_copy_.size(), |
| &encoded_remaining_copy_); |
| setp(encoded_remaining_copy_.data(), |
| encoded_remaining_copy_.data() + encoded_remaining_copy_.size()); |
| data_.manipulated.rdbuf(this); |
| } |
|
|
| LogMessage::OstreamView::~OstreamView() { |
| data_.manipulated.rdbuf(nullptr); |
| if (!string_start_.data()) { |
| |
| |
| |
| data_.encoded_remaining.remove_suffix(data_.encoded_remaining.size()); |
| return; |
| } |
| const absl::Span<const char> contents(pbase(), |
| static_cast<size_t>(pptr() - pbase())); |
| if (contents.empty()) return; |
| encoded_remaining_copy_.remove_prefix(contents.size()); |
| EncodeMessageLength(string_start_, &encoded_remaining_copy_); |
| EncodeMessageLength(message_start_, &encoded_remaining_copy_); |
| data_.encoded_remaining = encoded_remaining_copy_; |
| } |
|
|
| std::ostream& LogMessage::OstreamView::stream() { return data_.manipulated; } |
|
|
| bool LogMessage::IsFatal() const { |
| return data_->entry.log_severity() == absl::LogSeverity::kFatal && |
| absl::log_internal::ExitOnDFatal(); |
| } |
|
|
| void LogMessage::PrepareToDie() { |
| |
| |
| |
| if (data_->first_fatal) { |
| |
| ABSL_INTERNAL_C_SYMBOL(AbslInternalOnFatalLogMessage)(data_->entry); |
| } |
|
|
| if (!data_->fail_quietly) { |
| |
| log_internal::LogToSinks(data_->entry, absl::MakeSpan(data_->extra_sinks), |
| data_->extra_sinks_only); |
|
|
| |
| |
| data_->entry.stacktrace_ = "*** Check failure stack trace: ***\n"; |
| debugging_internal::DumpStackTrace( |
| 0, log_internal::MaxFramesInLogStackTrace(), |
| log_internal::ShouldSymbolizeLogStackTrace(), WriteToString, |
| &data_->entry.stacktrace_); |
| } |
| } |
|
|
| void LogMessage::Die() { |
| absl::FlushLogSinks(); |
|
|
| if (data_->fail_quietly) { |
| FailQuietly(); |
| } else { |
| FailWithoutStackTrace(); |
| } |
| } |
|
|
| void LogMessage::SendToLog() { |
| if (IsFatal()) PrepareToDie(); |
| |
| log_internal::LogToSinks(data_->entry, absl::MakeSpan(data_->extra_sinks), |
| data_->extra_sinks_only); |
| if (IsFatal()) Die(); |
| } |
|
|
| void LogMessage::LogBacktraceIfNeeded() { |
| if (!absl::log_internal::IsInitialized()) return; |
|
|
| if (!absl::log_internal::ShouldLogBacktraceAt(data_->entry.source_basename(), |
| data_->entry.source_line())) |
| return; |
| OstreamView view(*data_); |
| view.stream() << " (stacktrace:\n"; |
| debugging_internal::DumpStackTrace( |
| 1, log_internal::MaxFramesInLogStackTrace(), |
| log_internal::ShouldSymbolizeLogStackTrace(), WriteToStream, |
| &view.stream()); |
| view.stream() << ") "; |
| } |
|
|
| |
| |
| |
| |
| template <LogMessage::StringType str_type> |
| void LogMessage::CopyToEncodedBuffer(absl::string_view str) { |
| auto encoded_remaining_copy = data_->encoded_remaining; |
| auto start = EncodeMessageStart( |
| EventTag::kValue, BufferSizeFor(WireType::kLengthDelimited) + str.size(), |
| &encoded_remaining_copy); |
| |
| |
| |
| if (EncodeStringTruncate(str_type == StringType::kLiteral |
| ? ValueTag::kStringLiteral |
| : ValueTag::kString, |
| str, &encoded_remaining_copy)) { |
| |
| EncodeMessageLength(start, &encoded_remaining_copy); |
| data_->encoded_remaining = encoded_remaining_copy; |
| } else { |
| |
| |
| data_->encoded_remaining.remove_suffix(data_->encoded_remaining.size()); |
| } |
| } |
| template void LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>( |
| absl::string_view str); |
| template void LogMessage::CopyToEncodedBuffer< |
| LogMessage::StringType::kNotLiteral>(absl::string_view str); |
| template <LogMessage::StringType str_type> |
| void LogMessage::CopyToEncodedBuffer(char ch, size_t num) { |
| auto encoded_remaining_copy = data_->encoded_remaining; |
| auto value_start = EncodeMessageStart( |
| EventTag::kValue, BufferSizeFor(WireType::kLengthDelimited) + num, |
| &encoded_remaining_copy); |
| auto str_start = EncodeMessageStart(str_type == StringType::kLiteral |
| ? ValueTag::kStringLiteral |
| : ValueTag::kString, |
| num, &encoded_remaining_copy); |
| if (str_start.data()) { |
| |
| log_internal::AppendTruncated(ch, num, encoded_remaining_copy); |
| EncodeMessageLength(str_start, &encoded_remaining_copy); |
| EncodeMessageLength(value_start, &encoded_remaining_copy); |
| data_->encoded_remaining = encoded_remaining_copy; |
| } else { |
| |
| |
| data_->encoded_remaining.remove_suffix(data_->encoded_remaining.size()); |
| } |
| } |
| template void LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>( |
| char ch, size_t num); |
| template void LogMessage::CopyToEncodedBuffer< |
| LogMessage::StringType::kNotLiteral>(char ch, size_t num); |
|
|
| LogMessageFatal::LogMessageFatal(const char* file, int line) |
| : LogMessage(file, line, absl::LogSeverity::kFatal) {} |
|
|
| LogMessageFatal::LogMessageFatal(const char* file, int line, |
| absl::string_view failure_msg) |
| : LogMessage(file, line, absl::LogSeverity::kFatal) { |
| *this << "Check failed: " << failure_msg << " "; |
| } |
|
|
| |
| |
| #if defined(_MSC_VER) && !defined(__clang__) |
| #pragma warning(push) |
| #pragma warning(disable : 4722) |
| #endif |
| LogMessageFatal::~LogMessageFatal() { |
| Flush(); |
| FailWithoutStackTrace(); |
| } |
| #if defined(_MSC_VER) && !defined(__clang__) |
| #pragma warning(pop) |
| #endif |
|
|
| LogMessageQuietlyFatal::LogMessageQuietlyFatal(const char* file, int line) |
| : LogMessage(file, line, absl::LogSeverity::kFatal) { |
| SetFailQuietly(); |
| } |
|
|
| LogMessageQuietlyFatal::LogMessageQuietlyFatal(const char* file, int line, |
| absl::string_view failure_msg) |
| : LogMessage(file, line, absl::LogSeverity::kFatal) { |
| SetFailQuietly(); |
| *this << "Check failed: " << failure_msg << " "; |
| } |
|
|
| |
| |
| #if defined(_MSC_VER) && !defined(__clang__) |
| #pragma warning(push) |
| #pragma warning(disable : 4722) |
| #endif |
| LogMessageQuietlyFatal::~LogMessageQuietlyFatal() { |
| Flush(); |
| FailQuietly(); |
| } |
| #if defined(_MSC_VER) && !defined(__clang__) |
| #pragma warning(pop) |
| #endif |
|
|
| } |
|
|
| ABSL_NAMESPACE_END |
| } |
|
|