| // | |
| // Copyright 2022 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "absl/log/internal/log_message.h" | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <algorithm> | |
| #include <array> | |
| #include <atomic> | |
| #include <ios> | |
| #include <memory> | |
| #include <ostream> | |
| #include <string> | |
| #include <string_view> | |
| #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/base/nullability.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/nullguard.h" | |
| #include "absl/log/internal/proto.h" | |
| #include "absl/log/internal/structured_proto.h" | |
| #include "absl/log/log_entry.h" | |
| #include "absl/log/log_sink.h" | |
| #include "absl/log/log_sink_registry.h" | |
| #include "absl/strings/internal/utf8.h" | |
| #include "absl/strings/string_view.h" | |
| #include "absl/time/clock.h" | |
| #include "absl/time/time.h" | |
| #include "absl/types/span.h" | |
| #ifndef _WIN32 | |
| #include <unistd.h> | |
| #endif | |
| extern "C" ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL( | |
| AbslInternalOnFatalLogMessage)(const absl::LogEntry&) { | |
| // Default - Do nothing | |
| } | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace log_internal { | |
| namespace { | |
| // message `logging.proto.Event` | |
| enum EventTag : uint8_t { | |
| kFileName = 2, | |
| kFileLine = 3, | |
| kTimeNsecs = 4, | |
| kSeverity = 5, | |
| kThreadId = 6, | |
| kValue = 7, | |
| kSequenceNumber = 9, | |
| kThreadName = 10, | |
| }; | |
| // message `logging.proto.Value` | |
| enum ValueTag : uint8_t { | |
| kString = 1, | |
| kStringLiteral = 6, | |
| }; | |
| // Decodes a `logging.proto.Value` from `buf` and writes a string representation | |
| // into `dst`. The string representation will be truncated if `dst` is not | |
| // large enough to hold it. Returns false if `dst` has size zero or one (i.e. | |
| // sufficient only for a nul-terminator) and no decoded data could be written. | |
| // This function may or may not write a nul-terminator into `dst`, and it may or | |
| // may not truncate the data it writes in order to do make space for that nul | |
| // terminator. In any case, `dst` will be advanced to point at the byte where | |
| // subsequent writes should begin. | |
| 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; | |
| } | |
| // See `logging.proto.Severity` | |
| int32_t ProtoSeverity(absl::LogSeverity severity, int verbose_level) { | |
| switch (severity) { | |
| case absl::LogSeverity::kInfo: | |
| if (verbose_level == absl::LogEntry::kNoVerbosityLevel) return 800; | |
| return 600 - verbose_level; | |
| case absl::LogSeverity::kWarning: | |
| return 900; | |
| case absl::LogSeverity::kError: | |
| return 950; | |
| case absl::LogSeverity::kFatal: | |
| return 1100; | |
| default: | |
| return 800; | |
| } | |
| } | |
| 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; | |
| } | |
| } // namespace | |
| struct LogMessage::LogMessageData final { | |
| LogMessageData(absl::string_view file, int line, | |
| absl::LogSeverity severity, absl::Time timestamp); | |
| LogMessageData(const LogMessageData&) = delete; | |
| LogMessageData& operator=(const LogMessageData&) = delete; | |
| // `LogEntry` sent to `LogSink`s; contains metadata. | |
| absl::LogEntry entry; | |
| // true => this was first fatal msg | |
| bool first_fatal; | |
| // true => all failures should be quiet | |
| bool fail_quietly; | |
| // true => PLOG was requested | |
| bool is_perror; | |
| // Extra `LogSink`s to log to, in addition to `global_sinks`. | |
| absl::InlinedVector<absl::LogSink* absl_nonnull, 16> extra_sinks; | |
| // If true, log to `extra_sinks` but not to `global_sinks` or hardcoded | |
| // non-sink targets (e.g. stderr, log files). | |
| bool extra_sinks_only; | |
| std::ostream manipulated; // ostream with IO manipulators applied | |
| // A `logging.proto.Event` proto message is built into `encoded_buf`. | |
| std::array<char, kLogMessageBufferSize> encoded_buf; | |
| // `encoded_remaining()` is the suffix of `encoded_buf` that has not been | |
| // filled yet. If a datum to be encoded does not fit into | |
| // `encoded_remaining()` and cannot be truncated to fit, the size of | |
| // `encoded_remaining()` will be zeroed to prevent encoding of any further | |
| // data. Note that in this case its `data()` pointer will not point past the | |
| // end of `encoded_buf`. | |
| // The first use of `encoded_remaining()` is our chance to record metadata | |
| // after any modifications (e.g. by `AtLocation()`) but before any data have | |
| // been recorded. We want to record metadata before data so that data are | |
| // preferentially truncated if we run out of buffer. | |
| absl::Span<char>& encoded_remaining() { | |
| if (encoded_remaining_actual_do_not_use_directly.data() == nullptr) { | |
| encoded_remaining_actual_do_not_use_directly = | |
| absl::MakeSpan(encoded_buf); | |
| InitializeEncodingAndFormat(); | |
| } | |
| return encoded_remaining_actual_do_not_use_directly; | |
| } | |
| absl::Span<char> encoded_remaining_actual_do_not_use_directly; | |
| // A formatted string message is built in `string_buf`. | |
| std::array<char, kLogMessageBufferSize> string_buf; | |
| void InitializeEncodingAndFormat(); | |
| void FinalizeEncodingAndFormat(); | |
| }; | |
| LogMessage::LogMessageData::LogMessageData(absl::string_view file, | |
| int line, absl::LogSeverity severity, | |
| absl::Time timestamp) | |
| : extra_sinks_only(false), manipulated(nullptr) { | |
| // Legacy defaults for LOG's ostream: | |
| 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::InitializeEncodingAndFormat() { | |
| EncodeStringTruncate(EventTag::kFileName, entry.source_filename(), | |
| &encoded_remaining()); | |
| EncodeVarint(EventTag::kFileLine, static_cast<int32_t>(entry.source_line()), | |
| &encoded_remaining()); | |
| EncodeVarint(EventTag::kTimeNsecs, absl::ToUnixNanos(entry.timestamp()), | |
| &encoded_remaining()); | |
| EncodeVarint(EventTag::kSeverity, | |
| ProtoSeverity(entry.log_severity(), entry.verbosity()), | |
| &encoded_remaining()); | |
| EncodeVarint(EventTag::kThreadId, static_cast<uint64_t>(entry.tid()), | |
| &encoded_remaining()); | |
| } | |
| void LogMessage::LogMessageData::FinalizeEncodingAndFormat() { | |
| // Note that `encoded_remaining()` may have zero size without pointing past | |
| // the end of `encoded_buf`, so the difference between `data()` pointers is | |
| // used to compute the size of `encoded_data`. | |
| absl::Span<const char> encoded_data( | |
| encoded_buf.data(), | |
| static_cast<size_t>(encoded_remaining().data() - encoded_buf.data())); | |
| // `string_remaining` is the suffix of `string_buf` that has not been filled | |
| // yet. | |
| absl::Span<char> string_remaining(string_buf); | |
| // We may need to write a newline and nul-terminator at the end of the decoded | |
| // string data. Rather than worry about whether those should overwrite the | |
| // end of the string (if the buffer is full) or be appended, we avoid writing | |
| // into the last two bytes so we always have space to append. | |
| 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; | |
| // Decode data from `encoded_buf` until we run out of data or we run out of | |
| // `string_remaining`. | |
| 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; | |
| } | |
| } | |
| 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* absl_nonnull file, int line, | |
| absl::LogSeverity severity) | |
| : LogMessage(absl::string_view(file), line, severity) {} | |
| LogMessage::LogMessage(absl::string_view file, int line, | |
| absl::LogSeverity severity) | |
| : data_( | |
| std::make_unique<LogMessageData>(file, line, severity, absl::Now())) { | |
| data_->first_fatal = false; | |
| data_->is_perror = false; | |
| data_->fail_quietly = false; | |
| // This logs a backtrace even if the location is subsequently changed using | |
| // AtLocation. This quirk, and the behavior when AtLocation is called twice, | |
| // are fixable but probably not worth fixing. | |
| LogBacktraceIfNeeded(); | |
| } | |
| LogMessage::LogMessage(const char* absl_nonnull file, int line, InfoTag) | |
| : LogMessage(file, line, absl::LogSeverity::kInfo) {} | |
| LogMessage::LogMessage(const char* absl_nonnull file, int line, WarningTag) | |
| : LogMessage(file, line, absl::LogSeverity::kWarning) {} | |
| LogMessage::LogMessage(const char* absl_nonnull file, int line, ErrorTag) | |
| : LogMessage(file, line, absl::LogSeverity::kError) {} | |
| // This cannot go in the header since LogMessageData is defined in this file. | |
| LogMessage::~LogMessage() = default; | |
| 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* absl_nonnull sink) { | |
| ABSL_INTERNAL_CHECK(sink, "null LogSink*"); | |
| data_->extra_sinks.push_back(sink); | |
| return *this; | |
| } | |
| LogMessage& LogMessage::ToSinkOnly(absl::LogSink* absl_nonnull 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() { | |
| // Now suppress repeated trace logging: | |
| log_internal::SetSuppressSigabortTrace(true); | |
| #if defined _DEBUG && defined COMPILER_MSVC | |
| // When debugging on windows, avoid the obnoxious dialog. | |
| __debugbreak(); | |
| #endif | |
| #ifdef __ELF__ | |
| // For b/8737634, flush coverage if we are in coverage mode. | |
| if (&__gcov_dump != nullptr) { | |
| __gcov_dump(); | |
| } else if (&__gcov_flush != nullptr) { | |
| __gcov_flush(); | |
| } | |
| #endif | |
| abort(); | |
| } | |
| void LogMessage::FailQuietly() { | |
| // _exit. Calling abort() would trigger all sorts of death signal handlers | |
| // and a detailed stack trace. Calling exit() would trigger the onexit | |
| // handlers, including the heap-leak checker, which is guaranteed to fail in | |
| // this case: we probably just new'ed the std::string that we logged. | |
| // Anyway, if you're calling Fail or FailQuietly, you're trying to bail out | |
| // of the program quickly, and it doesn't make much sense for FailQuietly to | |
| // offer different guarantees about exit behavior than Fail does. (And as a | |
| // consequence for QCHECK and CHECK to offer different exit behaviors) | |
| _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<<(const std::wstring& v) { | |
| CopyToEncodedBuffer<StringType::kNotLiteral>(v); | |
| return *this; | |
| } | |
| LogMessage& LogMessage::operator<<(std::wstring_view v) { | |
| CopyToEncodedBuffer<StringType::kNotLiteral>(v); | |
| return *this; | |
| } | |
| template <> | |
| LogMessage& LogMessage::operator<< <const wchar_t*>( | |
| const wchar_t* absl_nullable const& v) { | |
| if (v == nullptr) { | |
| CopyToEncodedBuffer<StringType::kNotLiteral>( | |
| absl::string_view(kCharNull.data(), kCharNull.size() - 1)); | |
| } else { | |
| CopyToEncodedBuffer<StringType::kNotLiteral>(v); | |
| } | |
| return *this; | |
| } | |
| LogMessage& LogMessage::operator<<(wchar_t v) { | |
| CopyToEncodedBuffer<StringType::kNotLiteral>(std::wstring_view(&v, 1)); | |
| 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; | |
| } | |
| // NOLINTBEGIN(runtime/int) | |
| // NOLINTBEGIN(google-runtime-int) | |
| 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); | |
| // NOLINTEND(google-runtime-int) | |
| // NOLINTEND(runtime/int) | |
| void LogMessage::Flush() { | |
| if (data_->entry.log_severity() < absl::MinLogLevel()) return; | |
| if (data_->is_perror) { | |
| InternalStream() << ": " << absl::base_internal::StrError(errno_saver_()) | |
| << " [" << errno_saver_() << "]"; | |
| } | |
| // Have we already seen a fatal message? | |
| ABSL_CONST_INIT static std::atomic<bool> seen_fatal(false); | |
| if (data_->entry.log_severity() == absl::LogSeverity::kFatal && | |
| absl::log_internal::ExitOnDFatal()) { | |
| // Exactly one LOG(FATAL) message is responsible for aborting the process, | |
| // even if multiple threads LOG(FATAL) concurrently. | |
| 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()) { | |
| // This constructor sets the `streambuf` up so that streaming into an attached | |
| // ostream encodes string data in-place. To do that, we write appropriate | |
| // headers into the buffer using a copy of the buffer view so that we can | |
| // decide not to keep them later if nothing is ever streamed in. We don't | |
| // know how much data we'll get, but we can use the size of the remaining | |
| // buffer as an upper bound and fill in the right size once we know it. | |
| 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()) { | |
| // The second field header didn't fit. Whether the first one did or not, we | |
| // shouldn't commit `encoded_remaining_copy_`, and we also need to zero the | |
| // size of `data_->encoded_remaining()` so that no more data are encoded. | |
| 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 we log a FATAL message, flush all the log destinations, then toss | |
| // a signal for others to catch. We leave the logs in a state that | |
| // someone else can use them (as long as they flush afterwards) | |
| if (data_->first_fatal) { | |
| // Notify observers about the upcoming fatal error. | |
| ABSL_INTERNAL_C_SYMBOL(AbslInternalOnFatalLogMessage)(data_->entry); | |
| } | |
| if (!data_->fail_quietly) { | |
| // Log the message first before we start collecting stack trace. | |
| log_internal::LogToSinks(data_->entry, absl::MakeSpan(data_->extra_sinks), | |
| data_->extra_sinks_only); | |
| // `DumpStackTrace` generates an empty string under MSVC. | |
| // Adding the constant prefix here simplifies testing. | |
| 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(); | |
| // Also log to all registered sinks, even if OnlyLogToStderr() is set. | |
| 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() << ") "; | |
| } | |
| // Encodes into `data_->encoded_remaining()` a partial `logging.proto.Event` | |
| // containing the specified string data using a `Value` field appropriate to | |
| // `str_type`. Truncates `str` if necessary, but emits nothing and marks the | |
| // buffer full if even the field headers do not fit. | |
| template <LogMessage::StringType str_type> | |
| ABSL_ATTRIBUTE_NOINLINE void LogMessage::CopyToEncodedBuffer( | |
| absl::string_view str) { | |
| auto encoded_remaining_copy = data_->encoded_remaining(); | |
| constexpr uint8_t tag_value = str_type == StringType::kLiteral | |
| ? ValueTag::kStringLiteral | |
| : ValueTag::kString; | |
| auto start = EncodeMessageStart( | |
| EventTag::kValue, | |
| BufferSizeFor(tag_value, WireType::kLengthDelimited) + str.size(), | |
| &encoded_remaining_copy); | |
| // If the `logging.proto.Event.value` field header did not fit, | |
| // `EncodeMessageStart` will have zeroed `encoded_remaining_copy`'s size and | |
| // `EncodeStringTruncate` will fail too. | |
| if (EncodeStringTruncate(tag_value, str, &encoded_remaining_copy)) { | |
| // The string may have been truncated, but the field header fit. | |
| EncodeMessageLength(start, &encoded_remaining_copy); | |
| data_->encoded_remaining() = encoded_remaining_copy; | |
| } else { | |
| // The field header(s) did not fit; zero `encoded_remaining()` so we don't | |
| // write anything else later. | |
| 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> | |
| ABSL_ATTRIBUTE_NOINLINE void LogMessage::CopyToEncodedBuffer(char ch, | |
| size_t num) { | |
| auto encoded_remaining_copy = data_->encoded_remaining(); | |
| constexpr uint8_t tag_value = str_type == StringType::kLiteral | |
| ? ValueTag::kStringLiteral | |
| : ValueTag::kString; | |
| auto value_start = EncodeMessageStart( | |
| EventTag::kValue, | |
| BufferSizeFor(tag_value, WireType::kLengthDelimited) + num, | |
| &encoded_remaining_copy); | |
| auto str_start = EncodeMessageStart(tag_value, num, &encoded_remaining_copy); | |
| if (str_start.data()) { | |
| // The field headers fit. | |
| 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 { | |
| // The field header(s) did not fit; zero `encoded_remaining()` so we don't | |
| // write anything else later. | |
| 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); | |
| template <LogMessage::StringType str_type> | |
| ABSL_ATTRIBUTE_NOINLINE void LogMessage::CopyToEncodedBuffer( | |
| std::wstring_view str) { | |
| auto encoded_remaining_copy = data_->encoded_remaining(); | |
| constexpr uint8_t tag_value = str_type == StringType::kLiteral | |
| ? ValueTag::kStringLiteral | |
| : ValueTag::kString; | |
| size_t max_str_byte_length = | |
| absl::strings_internal::kMaxEncodedUTF8Size * str.length(); | |
| auto value_start = | |
| EncodeMessageStart(EventTag::kValue, | |
| BufferSizeFor(tag_value, WireType::kLengthDelimited) + | |
| max_str_byte_length, | |
| &encoded_remaining_copy); | |
| auto str_start = EncodeMessageStart(tag_value, max_str_byte_length, | |
| &encoded_remaining_copy); | |
| if (str_start.data()) { | |
| log_internal::AppendTruncated(str, encoded_remaining_copy); | |
| EncodeMessageLength(str_start, &encoded_remaining_copy); | |
| EncodeMessageLength(value_start, &encoded_remaining_copy); | |
| data_->encoded_remaining() = encoded_remaining_copy; | |
| } else { | |
| // The field header(s) did not fit; zero `encoded_remaining()` so we don't | |
| // write anything else later. | |
| data_->encoded_remaining().remove_suffix(data_->encoded_remaining().size()); | |
| } | |
| } | |
| template void LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>( | |
| std::wstring_view str); | |
| template void LogMessage::CopyToEncodedBuffer< | |
| LogMessage::StringType::kNotLiteral>(std::wstring_view str); | |
| template void LogMessage::CopyToEncodedBufferWithStructuredProtoField< | |
| LogMessage::StringType::kLiteral>(StructuredProtoField field, | |
| absl::string_view str); | |
| template void LogMessage::CopyToEncodedBufferWithStructuredProtoField< | |
| LogMessage::StringType::kNotLiteral>(StructuredProtoField field, | |
| absl::string_view str); | |
| template <LogMessage::StringType str_type> | |
| ABSL_ATTRIBUTE_NOINLINE void | |
| LogMessage::CopyToEncodedBufferWithStructuredProtoField( | |
| StructuredProtoField field, absl::string_view str) { | |
| auto encoded_remaining_copy = data_->encoded_remaining(); | |
| size_t encoded_field_size = BufferSizeForStructuredProtoField(field); | |
| constexpr uint8_t tag_value = str_type == StringType::kLiteral | |
| ? ValueTag::kStringLiteral | |
| : ValueTag::kString; | |
| auto start = EncodeMessageStart( | |
| EventTag::kValue, | |
| encoded_field_size + | |
| BufferSizeFor(tag_value, WireType::kLengthDelimited) + str.size(), | |
| &encoded_remaining_copy); | |
| // Write the encoded proto field. | |
| if (!EncodeStructuredProtoField(field, encoded_remaining_copy)) { | |
| // The header / field will not fit; zero `encoded_remaining()` so we | |
| // don't write anything else later. | |
| data_->encoded_remaining().remove_suffix(data_->encoded_remaining().size()); | |
| return; | |
| } | |
| // Write the string, truncating if necessary. | |
| if (!EncodeStringTruncate(tag_value, str, &encoded_remaining_copy)) { | |
| // The length of the string itself did not fit; zero `encoded_remaining()` | |
| // so the value is not encoded at all. | |
| data_->encoded_remaining().remove_suffix(data_->encoded_remaining().size()); | |
| return; | |
| } | |
| EncodeMessageLength(start, &encoded_remaining_copy); | |
| data_->encoded_remaining() = encoded_remaining_copy; | |
| } | |
| // We intentionally don't return from these destructors. Disable MSVC's warning | |
| // about the destructor never returning as we do so intentionally here. | |
| #if defined(_MSC_VER) && !defined(__clang__) | |
| #pragma warning(push) | |
| #pragma warning(disable : 4722) | |
| #endif | |
| LogMessageFatal::LogMessageFatal(const char* absl_nonnull file, int line) | |
| : LogMessage(file, line, absl::LogSeverity::kFatal) {} | |
| LogMessageFatal::LogMessageFatal(const char* absl_nonnull file, int line, | |
| const char* absl_nonnull failure_msg) | |
| : LogMessage(file, line, absl::LogSeverity::kFatal) { | |
| *this << "Check failed: " << failure_msg << " "; | |
| } | |
| LogMessageFatal::~LogMessageFatal() { FailWithoutStackTrace(); } | |
| LogMessageDebugFatal::LogMessageDebugFatal(const char* absl_nonnull file, | |
| int line) | |
| : LogMessage(file, line, absl::LogSeverity::kFatal) {} | |
| LogMessageDebugFatal::~LogMessageDebugFatal() { FailWithoutStackTrace(); } | |
| LogMessageQuietlyDebugFatal::LogMessageQuietlyDebugFatal( | |
| const char* absl_nonnull file, int line) | |
| : LogMessage(file, line, absl::LogSeverity::kFatal) { | |
| SetFailQuietly(); | |
| } | |
| LogMessageQuietlyDebugFatal::~LogMessageQuietlyDebugFatal() { FailQuietly(); } | |
| LogMessageQuietlyFatal::LogMessageQuietlyFatal(const char* absl_nonnull file, | |
| int line) | |
| : LogMessage(file, line, absl::LogSeverity::kFatal) { | |
| SetFailQuietly(); | |
| } | |
| LogMessageQuietlyFatal::LogMessageQuietlyFatal( | |
| const char* absl_nonnull file, int line, | |
| const char* absl_nonnull failure_msg) | |
| : LogMessageQuietlyFatal(file, line) { | |
| *this << "Check failed: " << failure_msg << " "; | |
| } | |
| LogMessageQuietlyFatal::~LogMessageQuietlyFatal() { FailQuietly(); } | |
| #if defined(_MSC_VER) && !defined(__clang__) | |
| #pragma warning(pop) | |
| #endif | |
| } // namespace log_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl |