diff options
Diffstat (limited to 'absl/log/internal')
26 files changed, 3819 insertions, 0 deletions
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel new file mode 100644 index 00000000..c674f288 --- /dev/null +++ b/absl/log/internal/BUILD.bazel @@ -0,0 +1,302 @@ +# +# 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. +# + +load( + "//absl:copts/configure_copts.bzl", + "ABSL_DEFAULT_COPTS", + "ABSL_DEFAULT_LINKOPTS", + "ABSL_TEST_COPTS", +) + +package(default_visibility = [ + "//absl/log:__pkg__", +]) + +licenses(["notice"]) + +cc_library( + name = "check_op", + srcs = ["check_op.cc"], + hdrs = ["check_op.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/log:__pkg__", + ], + deps = [ + ":nullguard", + ":nullstream", + ":strip", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/strings", + ], +) + +cc_library( + name = "conditions", + srcs = ["conditions.cc"], + hdrs = ["conditions.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":voidify", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_library( + name = "config", + hdrs = ["config.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/log:__pkg__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + ], +) + +cc_library( + name = "flags", + hdrs = ["flags.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/flags:flag", + ], +) + +cc_library( + name = "format", + srcs = ["log_format.cc"], + hdrs = ["log_format.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":globals", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:log_severity", + "//absl/strings", + "//absl/strings:str_format", + "//absl/time", + "//absl/types:span", + ], +) + +cc_library( + name = "globals", + srcs = ["globals.cc"], + hdrs = ["globals.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/log:__pkg__", + ], + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:log_severity", + "//absl/base:raw_logging_internal", + "//absl/strings", + "//absl/time", + ], +) + +cc_library( + name = "log_message", + srcs = ["log_message.cc"], + hdrs = ["log_message.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + "//absl/log:__pkg__", + ], + deps = [ + ":config", + ":format", + ":globals", + ":log_sink_set", + ":nullguard", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:errno_saver", + "//absl/base:log_severity", + "//absl/base:raw_logging_internal", + "//absl/base:strerror", + "//absl/container:inlined_vector", + "//absl/debugging:examine_stack", + "//absl/log:globals", + "//absl/log:log_entry", + "//absl/log:log_sink", + "//absl/log:log_sink_registry", + "//absl/memory", + "//absl/strings", + "//absl/strings:str_format", + "//absl/time", + "//absl/types:span", + ], +) + +cc_library( + name = "log_sink_set", + srcs = ["log_sink_set.cc"], + hdrs = ["log_sink_set.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":globals", + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:log_severity", + "//absl/base:raw_logging_internal", + "//absl/cleanup", + "//absl/log:globals", + "//absl/log:log_entry", + "//absl/log:log_sink", + "//absl/strings", + "//absl/synchronization", + "//absl/types:span", + ], +) + +cc_library( + name = "nullguard", + hdrs = ["nullguard.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + ], +) + +cc_library( + name = "nullstream", + hdrs = ["nullstream.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + "//absl/base:config", + "//absl/base:core_headers", + "//absl/base:log_severity", + "//absl/strings", + ], +) + +cc_library( + name = "strip", + hdrs = ["strip.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":log_message", + ":nullstream", + "//absl/base:log_severity", + ], +) + +cc_library( + name = "test_actions", + testonly = True, + srcs = ["test_actions.cc"], + hdrs = ["test_actions.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + "//absl/base:config", + "//absl/base:log_severity", + "//absl/log:log_entry", + "//absl/strings", + "//absl/time", + ], +) + +cc_library( + name = "test_helpers", + testonly = True, + srcs = ["test_helpers.cc"], + hdrs = ["test_helpers.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":globals", + "//absl/base:config", + "//absl/base:log_severity", + "//absl/log:globals", + "//absl/log:initialize", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "test_matchers", + testonly = True, + srcs = ["test_matchers.cc"], + hdrs = ["test_matchers.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = [ + ":config", + ":test_helpers", + "//absl/base:config", + "//absl/base:log_severity", + "//absl/log:log_entry", + "//absl/strings", + "//absl/time", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "voidify", + hdrs = ["voidify.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + deps = ["//absl/base:config"], +) + +# Test targets +cc_test( + name = "stderr_log_sink_test", + size = "small", + srcs = ["stderr_log_sink_test.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "no_test_android", + "no_test_darwin_x86_64", + "no_test_ios", + "no_test_wasm", + ], + deps = [ + ":test_helpers", + "//absl/base:core_headers", + "//absl/base:log_severity", + "//absl/log", + "//absl/log:globals", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/absl/log/internal/check_op.cc b/absl/log/internal/check_op.cc new file mode 100644 index 00000000..f4b67647 --- /dev/null +++ b/absl/log/internal/check_op.cc @@ -0,0 +1,118 @@ +// 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/check_op.h" + +#include <string.h> + +#ifdef _MSC_VER +#define strcasecmp _stricmp +#else +#include <strings.h> // for strcasecmp, but msvc does not have this header +#endif + +#include <sstream> +#include <string> + +#include "absl/base/config.h" +#include "absl/strings/str_cat.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +#define ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(x) \ + template std::string* MakeCheckOpString(x, x, const char*) +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(bool); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(int64_t); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(uint64_t); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(float); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(double); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(char); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(unsigned char); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const std::string&); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const absl::string_view&); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const char*); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const signed char*); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const unsigned char*); +ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING(const void*); +#undef ABSL_LOGGING_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING + +CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext) { + stream_ << exprtext << " ("; +} + +std::ostream& CheckOpMessageBuilder::ForVar2() { + stream_ << " vs. "; + return stream_; +} + +std::string* CheckOpMessageBuilder::NewString() { + stream_ << ")"; + return new std::string(stream_.str()); +} + +void MakeCheckOpValueString(std::ostream& os, const char v) { + if (v >= 32 && v <= 126) { + os << "'" << v << "'"; + } else { + os << "char value " << int{v}; + } +} + +void MakeCheckOpValueString(std::ostream& os, const signed char v) { + if (v >= 32 && v <= 126) { + os << "'" << v << "'"; + } else { + os << "signed char value " << int{v}; + } +} + +void MakeCheckOpValueString(std::ostream& os, const unsigned char v) { + if (v >= 32 && v <= 126) { + os << "'" << v << "'"; + } else { + os << "unsigned char value " << int{v}; + } +} + +void MakeCheckOpValueString(std::ostream& os, const void* p) { + if (p == nullptr) { + os << "(null)"; + } else { + os << p; + } +} + +// Helper functions for string comparisons. +#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \ + std::string* Check##func##expected##Impl(const char* s1, const char* s2, \ + const char* exprtext) { \ + bool equal = s1 == s2 || (s1 && s2 && !func(s1, s2)); \ + if (equal == expected) { \ + return nullptr; \ + } else { \ + return new std::string( \ + absl::StrCat(exprtext, " (", s1, " vs. ", s2, ")")); \ + } \ + } +DEFINE_CHECK_STROP_IMPL(CHECK_STREQ, strcmp, true) +DEFINE_CHECK_STROP_IMPL(CHECK_STRNE, strcmp, false) +DEFINE_CHECK_STROP_IMPL(CHECK_STRCASEEQ, strcasecmp, true) +DEFINE_CHECK_STROP_IMPL(CHECK_STRCASENE, strcasecmp, false) +#undef DEFINE_CHECK_STROP_IMPL + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h new file mode 100644 index 00000000..559e5afc --- /dev/null +++ b/absl/log/internal/check_op.h @@ -0,0 +1,385 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/check_op.h +// ----------------------------------------------------------------------------- +// +// This file declares helpers routines and macros used to implement `CHECK` +// macros. + +#ifndef ABSL_LOG_INTERNAL_CHECK_OP_H_ +#define ABSL_LOG_INTERNAL_CHECK_OP_H_ + +#include <stdint.h> + +#include <ostream> +#include <sstream> +#include <string> +#include <utility> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/log/internal/nullguard.h" +#include "absl/log/internal/nullstream.h" +#include "absl/log/internal/strip.h" + +// `ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL` wraps string literals that +// should be stripped when `ABSL_MIN_LOG_LEVEL` exceeds `kFatal`. +#ifdef ABSL_MIN_LOG_LEVEL +#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) \ + (::absl::LogSeverity::kFatal >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \ + ? (literal) \ + : "") +#else +#define ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(literal) (literal) +#endif + +#ifdef NDEBUG +// `NDEBUG` is defined, so `DCHECK_EQ(x, y)` and so on do nothing. However, we +// still want the compiler to parse `x` and `y`, because we don't want to lose +// potentially useful errors and warnings. +#define ABSL_LOG_INTERNAL_DCHECK_NOP(x, y) \ + while (false && ((void)(x), (void)(y), 0)) \ + ::absl::log_internal::NullStream().InternalStream() +#endif + +#define ABSL_LOG_INTERNAL_CHECK_OP(name, op, val1, val2) \ + while ( \ + ::std::string* absl_log_internal_check_op_result ABSL_ATTRIBUTE_UNUSED = \ + ::absl::log_internal::name##Impl( \ + ::absl::log_internal::GetReferenceableValue(val1), \ + ::absl::log_internal::GetReferenceableValue(val2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val1 " " #op \ + " " #val2))) \ + ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_op_result).InternalStream() +#define ABSL_LOG_INTERNAL_QCHECK_OP(name, op, val1, val2) \ + while (::std::string* absl_log_internal_qcheck_op_result = \ + ::absl::log_internal::name##Impl( \ + ::absl::log_internal::GetReferenceableValue(val1), \ + ::absl::log_internal::GetReferenceableValue(val2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val1 " " #op \ + " " #val2))) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_op_result).InternalStream() +#define ABSL_LOG_INTERNAL_CHECK_STROP(func, op, expected, s1, s2) \ + while (::std::string* absl_log_internal_check_strop_result = \ + ::absl::log_internal::Check##func##expected##Impl( \ + (s1), (s2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#s1 " " #op " " #s2))) \ + ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_strop_result) \ + .InternalStream() +#define ABSL_LOG_INTERNAL_QCHECK_STROP(func, op, expected, s1, s2) \ + while (::std::string* absl_log_internal_qcheck_strop_result = \ + ::absl::log_internal::Check##func##expected##Impl( \ + (s1), (s2), \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#s1 " " #op " " #s2))) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_qcheck_strop_result) \ + .InternalStream() +// This one is tricky: +// * We must evaluate `val` exactly once, yet we need to do two things with it: +// evaluate `.ok()` and (sometimes) `.ToString()`. +// * `val` might be an `absl::Status` or some `absl::StatusOr<T>`. +// * `val` might be e.g. `ATemporary().GetStatus()`, which may return a +// reference to a member of `ATemporary` that is only valid until the end of +// the full expression. +// * We don't want this file to depend on `absl::Status` `#include`s or linkage, +// nor do we want to move the definition to status and introduce a dependency +// in the other direction. We can be assured that callers must already have a +// `Status` and the necessary `#include`s and linkage. +// * Callsites should be small and fast (at least when `val.ok()`): one branch, +// minimal stack footprint. +// * In particular, the string concat stuff should be out-of-line and emitted +// in only one TU to save linker input size +// * We want the `val.ok()` check inline so static analyzers and optimizers can +// see it. +// * As usual, no braces so we can stream into the expansion with `operator<<`. +// * Also as usual, it must expand to a single (partial) statement with no +// ambiguous-else problems. +#define ABSL_LOG_INTERNAL_CHECK_OK(val) \ + for (::std::pair<const ::absl::Status*, ::std::string*> \ + absl_log_internal_check_ok_goo; \ + absl_log_internal_check_ok_goo.first = \ + ::absl::log_internal::AsStatus(val), \ + absl_log_internal_check_ok_goo.second = \ + ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \ + ? nullptr \ + : ::absl::status_internal::MakeCheckFailString( \ + absl_log_internal_check_ok_goo.first, \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \ + !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \ + ABSL_LOG_INTERNAL_CHECK(*absl_log_internal_check_ok_goo.second) \ + .InternalStream() +#define ABSL_LOG_INTERNAL_QCHECK_OK(val) \ + for (::std::pair<const ::absl::Status*, ::std::string*> \ + absl_log_internal_check_ok_goo; \ + absl_log_internal_check_ok_goo.first = \ + ::absl::log_internal::AsStatus(val), \ + absl_log_internal_check_ok_goo.second = \ + ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok()) \ + ? nullptr \ + : ::absl::status_internal::MakeCheckFailString( \ + absl_log_internal_check_ok_goo.first, \ + ABSL_LOG_INTERNAL_STRIP_STRING_LITERAL(#val " is OK")), \ + !ABSL_PREDICT_TRUE(absl_log_internal_check_ok_goo.first->ok());) \ + ABSL_LOG_INTERNAL_QCHECK(*absl_log_internal_check_ok_goo.second) \ + .InternalStream() + +namespace absl { +ABSL_NAMESPACE_BEGIN + +class Status; +template <typename T> +class StatusOr; + +namespace status_internal { +std::string* MakeCheckFailString(const absl::Status* status, + const char* prefix); +} // namespace status_internal + +namespace log_internal { + +// Convert a Status or a StatusOr to its underlying status value. +// +// (This implementation does not require a dep on absl::Status to work.) +inline const absl::Status* AsStatus(const absl::Status& s) { return &s; } +template <typename T> +const absl::Status* AsStatus(const absl::StatusOr<T>& s) { + return &s.status(); +} + +// A helper class for formatting `expr (V1 vs. V2)` in a `CHECK_XX` statement. +// See `MakeCheckOpString` for sample usage. +class CheckOpMessageBuilder final { + public: + // Inserts `exprtext` and ` (` to the stream. + explicit CheckOpMessageBuilder(const char* exprtext); + ~CheckOpMessageBuilder() = default; + // For inserting the first variable. + std::ostream& ForVar1() { return stream_; } + // For inserting the second variable (adds an intermediate ` vs. `). + std::ostream& ForVar2(); + // Get the result (inserts the closing `)`). + std::string* NewString(); + + private: + std::ostringstream stream_; +}; + +// This formats a value for a failing `CHECK_XX` statement. Ordinarily, it uses +// the definition for `operator<<`, with a few special cases below. +template <typename T> +inline void MakeCheckOpValueString(std::ostream& os, const T& v) { + os << log_internal::NullGuard<T>::Guard(v); +} + +// Overloads for char types provide readable values for unprintable characters. +void MakeCheckOpValueString(std::ostream& os, char v); +void MakeCheckOpValueString(std::ostream& os, signed char v); +void MakeCheckOpValueString(std::ostream& os, unsigned char v); +void MakeCheckOpValueString(std::ostream& os, const void* p); + +namespace detect_specialization { + +// MakeCheckOpString is being specialized for every T and U pair that is being +// passed to the CHECK_op macros. However, there is a lot of redundancy in these +// specializations that creates unnecessary library and binary bloat. +// The number of instantiations tends to be O(n^2) because we have two +// independent inputs. This technique works by reducing `n`. +// +// Most user-defined types being passed to CHECK_op end up being printed as a +// builtin type. For example, enums tend to be implicitly converted to its +// underlying type when calling operator<<, and pointers are printed with the +// `const void*` overload. +// To reduce the number of instantiations we coerce these values before calling +// MakeCheckOpString instead of inside it. +// +// To detect if this coercion is needed, we duplicate all the relevant +// operator<< overloads as specified in the standard, just in a different +// namespace. If the call to `stream << value` becomes ambiguous, it means that +// one of these overloads is the one selected by overload resolution. We then +// do overload resolution again just with our overload set to see which one gets +// selected. That tells us which type to coerce to. +// If the augmented call was not ambiguous, it means that none of these were +// selected and we can't coerce the input. +// +// As a secondary step to reduce code duplication, we promote integral types to +// their 64-bit variant. This does not change the printed value, but reduces the +// number of instantiations even further. Promoting an integer is very cheap at +// the call site. +int64_t operator<<(std::ostream&, short value); // NOLINT +int64_t operator<<(std::ostream&, unsigned short value); // NOLINT +int64_t operator<<(std::ostream&, int value); +int64_t operator<<(std::ostream&, unsigned int value); +int64_t operator<<(std::ostream&, long value); // NOLINT +uint64_t operator<<(std::ostream&, unsigned long value); // NOLINT +int64_t operator<<(std::ostream&, long long value); // NOLINT +uint64_t operator<<(std::ostream&, unsigned long long value); // NOLINT +float operator<<(std::ostream&, float value); +double operator<<(std::ostream&, double value); +long double operator<<(std::ostream&, long double value); +bool operator<<(std::ostream&, bool value); +const void* operator<<(std::ostream&, const void* value); +const void* operator<<(std::ostream&, std::nullptr_t); + +// These `char` overloads are specified like this in the standard, so we have to +// write them exactly the same to ensure the call is ambiguous. +// If we wrote it in a different way (eg taking std::ostream instead of the +// template) then one call might have a higher rank than the other and it would +// not be ambiguous. +template <typename Traits> +char operator<<(std::basic_ostream<char, Traits>&, char); +template <typename Traits> +signed char operator<<(std::basic_ostream<char, Traits>&, signed char); +template <typename Traits> +unsigned char operator<<(std::basic_ostream<char, Traits>&, unsigned char); +template <typename Traits> +const char* operator<<(std::basic_ostream<char, Traits>&, const char*); +template <typename Traits> +const signed char* operator<<(std::basic_ostream<char, Traits>&, + const signed char*); +template <typename Traits> +const unsigned char* operator<<(std::basic_ostream<char, Traits>&, + const unsigned char*); + +// This overload triggers when the call is not ambiguous. +// It means that T is being printed with some overload not on this list. +// We keep the value as `const T&`. +template <typename T, typename = decltype(std::declval<std::ostream&>() + << std::declval<const T&>())> +const T& Detect(int); + +// This overload triggers when the call is ambiguous. +// It means that T is either one from this list or printed as one from this +// list. Eg an enum that decays to `int` for printing. +// We ask the overload set to give us the type we want to convert it to. +template <typename T> +decltype(detect_specialization::operator<<(std::declval<std::ostream&>(), + std::declval<const T&>())) +Detect(char); + +} // namespace detect_specialization + +template <typename T> +using CheckOpStreamType = decltype(detect_specialization::Detect<T>(0)); + +// Build the error message string. Specify no inlining for code size. +template <typename T1, typename T2> +ABSL_ATTRIBUTE_RETURNS_NONNULL std::string* MakeCheckOpString( + T1 v1, T2 v2, const char* exprtext) ABSL_ATTRIBUTE_NOINLINE; + +template <typename T1, typename T2> +std::string* MakeCheckOpString(T1 v1, T2 v2, const char* exprtext) { + CheckOpMessageBuilder comb(exprtext); + MakeCheckOpValueString(comb.ForVar1(), v1); + MakeCheckOpValueString(comb.ForVar2(), v2); + return comb.NewString(); +} + +// Add a few commonly used instantiations as extern to reduce size of objects +// files. +#define ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(x) \ + extern template std::string* MakeCheckOpString(x, x, const char*) +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(bool); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(int64_t); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(uint64_t); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(float); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(double); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(char); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(unsigned char); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const std::string&); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const absl::string_view&); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char*); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const signed char*); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const unsigned char*); +ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*); +#undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN + +// Helper functions for `ABSL_LOG_INTERNAL_CHECK_OP` macro family. The +// `(int, int)` override works around the issue that the compiler will not +// instantiate the template version of the function on values of unnamed enum +// type. +#define ABSL_LOG_INTERNAL_CHECK_OP_IMPL(name, op) \ + template <typename T1, typename T2> \ + inline constexpr ::std::string* name##Impl(const T1& v1, const T2& v2, \ + const char* exprtext) { \ + using U1 = CheckOpStreamType<T1>; \ + using U2 = CheckOpStreamType<T2>; \ + return ABSL_PREDICT_TRUE(v1 op v2) \ + ? nullptr \ + : MakeCheckOpString<U1, U2>(v1, v2, exprtext); \ + } \ + inline constexpr ::std::string* name##Impl(int v1, int v2, \ + const char* exprtext) { \ + return name##Impl<int, int>(v1, v2, exprtext); \ + } + +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_EQ, ==) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_NE, !=) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LE, <=) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_LT, <) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GE, >=) +ABSL_LOG_INTERNAL_CHECK_OP_IMPL(Check_GT, >) +#undef ABSL_LOG_INTERNAL_CHECK_OP_IMPL + +std::string* CheckstrcmptrueImpl(const char* s1, const char* s2, + const char* exprtext); +std::string* CheckstrcmpfalseImpl(const char* s1, const char* s2, + const char* exprtext); +std::string* CheckstrcasecmptrueImpl(const char* s1, const char* s2, + const char* exprtext); +std::string* CheckstrcasecmpfalseImpl(const char* s1, const char* s2, + const char* exprtext); + +// `CHECK_EQ` and friends want to pass their arguments by reference, however +// this winds up exposing lots of cases where people have defined and +// initialized static const data members but never declared them (i.e. in a .cc +// file), meaning they are not referenceable. This function avoids that problem +// for integers (the most common cases) by overloading for every primitive +// integer type, even the ones we discourage, and returning them by value. +template <typename T> +inline constexpr const T& GetReferenceableValue(const T& t) { + return t; +} +inline constexpr char GetReferenceableValue(char t) { return t; } +inline constexpr unsigned char GetReferenceableValue(unsigned char t) { + return t; +} +inline constexpr signed char GetReferenceableValue(signed char t) { return t; } +inline constexpr short GetReferenceableValue(short t) { return t; } // NOLINT +inline constexpr unsigned short GetReferenceableValue( // NOLINT + unsigned short t) { // NOLINT + return t; +} +inline constexpr int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline constexpr long GetReferenceableValue(long t) { return t; } // NOLINT +inline constexpr unsigned long GetReferenceableValue( // NOLINT + unsigned long t) { // NOLINT + return t; +} +inline constexpr long long GetReferenceableValue(long long t) { // NOLINT + return t; +} +inline constexpr unsigned long long GetReferenceableValue( // NOLINT + unsigned long long t) { // NOLINT + return t; +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_CHECK_OP_H_ diff --git a/absl/log/internal/conditions.cc b/absl/log/internal/conditions.cc new file mode 100644 index 00000000..70f2acef --- /dev/null +++ b/absl/log/internal/conditions.cc @@ -0,0 +1,83 @@ +// 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/conditions.h" + +#include <atomic> +#include <cstdint> + +#include "absl/base/config.h" +#include "absl/base/internal/cycleclock.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { +namespace { + +// The following code behaves like AtomicStatsCounter::LossyAdd() for +// speed since it is fine to lose occasional updates. +// Returns old value of *counter. +uint32_t LossyIncrement(std::atomic<uint32_t>* counter) { + const uint32_t value = counter->load(std::memory_order_relaxed); + counter->store(value + 1, std::memory_order_relaxed); + return value; +} + +} // namespace + +bool LogEveryNState::ShouldLog(int n) { + return n != 0 && (LossyIncrement(&counter_) % n) == 0; +} + +bool LogFirstNState::ShouldLog(int n) { + const uint32_t counter_value = counter_.load(std::memory_order_relaxed); + if (static_cast<int64_t>(counter_value) < n) { + counter_.store(counter_value + 1, std::memory_order_relaxed); + return true; + } + return false; +} + +bool LogEveryPow2State::ShouldLog() { + const uint32_t new_value = LossyIncrement(&counter_) + 1; + return (new_value & (new_value - 1)) == 0; +} + +bool LogEveryNSecState::ShouldLog(double seconds) { + using absl::base_internal::CycleClock; + LossyIncrement(&counter_); + const int64_t now_cycles = CycleClock::Now(); + int64_t next_cycles = next_log_time_cycles_.load(std::memory_order_relaxed); +#if defined(__myriad2__) + // myriad2 does not have 8-byte compare and exchange. Use a racy version that + // is "good enough" but will over-log in the face of concurrent logging. + if (now_cycles > next_cycles) { + next_log_time_cycles_.store(now_cycles + seconds * CycleClock::Frequency(), + std::memory_order_relaxed); + return true; + } + return false; +#else + do { + if (now_cycles <= next_cycles) return false; + } while (!next_log_time_cycles_.compare_exchange_weak( + next_cycles, now_cycles + seconds * CycleClock::Frequency(), + std::memory_order_relaxed, std::memory_order_relaxed)); + return true; +#endif +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/conditions.h b/absl/log/internal/conditions.h new file mode 100644 index 00000000..b89f1dfd --- /dev/null +++ b/absl/log/internal/conditions.h @@ -0,0 +1,222 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/conditions.h +// ----------------------------------------------------------------------------- +// +// This file contains implementation of conditional log statements, like LOG_IF +// including all the ABSL_LOG_INTERNAL_..._CONDITION_... macros and +// various condition classes like LogEveryNState. + +#ifndef ABSL_LOG_INTERNAL_CONDITIONS_H_ +#define ABSL_LOG_INTERNAL_CONDITIONS_H_ + +#ifdef _WIN32 +#include <cstdlib> +#else +#include <unistd.h> +#endif +#include <stdlib.h> + +#include <atomic> +#include <cstdint> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/log/internal/voidify.h" + +// `ABSL_LOG_INTERNAL_CONDITION` prefixes another macro that expands to a +// temporary `LogMessage` instantiation followed by zero or more streamed +// expressions. This definition is tricky to read correctly. It evaluates to +// either +// +// (void)0; +// +// or +// +// ::absl::log_internal::Voidify() && +// ::absl::log_internal::LogMessage(...) << "the user's message"; +// +// If the condition is evaluable at compile time, as is often the case, it +// compiles away to just one side or the other. +// +// Although this is not used anywhere a statement (e.g. `if`) could not go, +// the ternary expression does a better job avoiding spurious diagnostics +// (dangling else, missing switch case) and preserving noreturn semantics (e.g. +// on `LOG(FATAL)`) without requiring braces. +#define ABSL_LOG_INTERNAL_STATELESS_CONDITION(condition) \ + switch (0) \ + case 0: \ + !(condition) ? (void)0 : ::absl::log_internal::Voidify()&& + +// `ABSL_LOG_INTERNAL_STATEFUL_CONDITION` applies a condition like +// `ABSL_LOG_INTERNAL_CONDITION` but adds to that a series of variable +// declarations, including a local static object which stores the state needed +// to implement the stateful macros like `LOG_EVERY_N`. +// +// `for`-loops are used to declare scoped variables without braces (to permit +// streaming into the macro's expansion) and without the dangling-`else` +// problems/diagnostics that come with `if`. +// +// Two more variables are declared in separate `for`-loops: +// +// * `COUNTER` implements a streamable token whose value when streamed is the +// number of times execution has passed through the macro. +// * A boolean flag is used to prevent any of the `for`-loops from ever actually +// looping. +#define ABSL_LOG_INTERNAL_STATEFUL_CONDITION(condition) \ + for (bool absl_log_internal_stateful_condition_do_log(condition); \ + absl_log_internal_stateful_condition_do_log; \ + absl_log_internal_stateful_condition_do_log = false) \ + ABSL_LOG_INTERNAL_STATEFUL_CONDITION_IMPL +#define ABSL_LOG_INTERNAL_STATEFUL_CONDITION_IMPL(kind, ...) \ + for (static ::absl::log_internal::Log##kind##State \ + absl_log_internal_stateful_condition_state; \ + absl_log_internal_stateful_condition_do_log && \ + absl_log_internal_stateful_condition_state.ShouldLog(__VA_ARGS__); \ + absl_log_internal_stateful_condition_do_log = false) \ + for (const uint32_t COUNTER ABSL_ATTRIBUTE_UNUSED = \ + absl_log_internal_stateful_condition_state.counter(); \ + absl_log_internal_stateful_condition_do_log; \ + absl_log_internal_stateful_condition_do_log = false) + +// `ABSL_LOG_INTERNAL_CONDITION_*` serve to combine any conditions from the +// macro (e.g. `LOG_IF` or `VLOG`) with inherent conditions (e.g. +// `ABSL_MIN_LOG_LEVEL`) into a single boolean expression. We could chain +// ternary operators instead, however some versions of Clang sometimes issue +// spurious diagnostics after such expressions due to a control flow analysis +// bug. +#ifdef ABSL_MIN_LOG_LEVEL +#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION( \ + (condition) && ::absl::LogSeverity::kInfo >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) +#define ABSL_LOG_INTERNAL_CONDITION_WARNING(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION( \ + (condition) && ::absl::LogSeverity::kWarning >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) +#define ABSL_LOG_INTERNAL_CONDITION_ERROR(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION( \ + (condition) && ::absl::LogSeverity::kError >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL)) +// NOTE: Use ternary operators instead of short-circuiting to mitigate +// https://bugs.llvm.org/show_bug.cgi?id=51928. +#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION( \ + ((condition) \ + ? (::absl::LogSeverity::kFatal >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \ + ? true \ + : (::absl::log_internal::AbortQuietly(), false)) \ + : false)) +// NOTE: Use ternary operators instead of short-circuiting to mitigate +// https://bugs.llvm.org/show_bug.cgi?id=51928. +#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION( \ + ((condition) \ + ? (::absl::LogSeverity::kFatal >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) \ + ? true \ + : (::absl::log_internal::ExitQuietly(), false)) \ + : false)) + +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ + for (int log_internal_severity_loop = 1; log_internal_severity_loop; \ + log_internal_severity_loop = 0) \ + for (const absl::LogSeverity log_internal_severity = \ + ::absl::NormalizeLogSeverity(severity); \ + log_internal_severity_loop; log_internal_severity_loop = 0) \ + ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION( \ + (condition) && \ + (log_internal_severity >= \ + static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \ + (log_internal_severity == ::absl::LogSeverity::kFatal && \ + (::absl::log_internal::AbortQuietly(), false)))) +#else // ndef ABSL_MIN_LOG_LEVEL +#define ABSL_LOG_INTERNAL_CONDITION_INFO(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(condition) +#define ABSL_LOG_INTERNAL_CONDITION_WARNING(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(condition) +#define ABSL_LOG_INTERNAL_CONDITION_ERROR(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(condition) +#define ABSL_LOG_INTERNAL_CONDITION_FATAL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(condition) +#define ABSL_LOG_INTERNAL_CONDITION_QFATAL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(condition) +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \ + for (int log_internal_severity_loop = 1; log_internal_severity_loop; \ + log_internal_severity_loop = 0) \ + for (const absl::LogSeverity log_internal_severity = \ + ::absl::NormalizeLogSeverity(severity); \ + log_internal_severity_loop; log_internal_severity_loop = 0) \ + ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL +#define ABSL_LOG_INTERNAL_CONDITION_LEVEL_IMPL(type, condition) \ + ABSL_LOG_INTERNAL_##type##_CONDITION(condition) +#endif // ndef ABSL_MIN_LOG_LEVEL + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// Stateful condition class name should be "Log" + name + "State". +class LogEveryNState final { + public: + bool ShouldLog(int n); + uint32_t counter() { return counter_.load(std::memory_order_relaxed); } + + private: + std::atomic<uint32_t> counter_{0}; +}; + +class LogFirstNState final { + public: + bool ShouldLog(int n); + uint32_t counter() { return counter_.load(std::memory_order_relaxed); } + + private: + std::atomic<uint32_t> counter_{0}; +}; + +class LogEveryPow2State final { + public: + bool ShouldLog(); + uint32_t counter() { return counter_.load(std::memory_order_relaxed); } + + private: + std::atomic<uint32_t> counter_{0}; +}; + +class LogEveryNSecState final { + public: + bool ShouldLog(double seconds); + uint32_t counter() { return counter_.load(std::memory_order_relaxed); } + + private: + std::atomic<uint32_t> counter_{0}; + // Cycle count according to CycleClock that we should next log at. + std::atomic<int64_t> next_log_time_cycles_{0}; +}; + +// Helper routines to abort the application quietly + +ABSL_ATTRIBUTE_NORETURN inline void AbortQuietly() { abort(); } +ABSL_ATTRIBUTE_NORETURN inline void ExitQuietly() { _exit(1); } +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_CONDITIONS_H_ diff --git a/absl/log/internal/config.h b/absl/log/internal/config.h new file mode 100644 index 00000000..379e9ab9 --- /dev/null +++ b/absl/log/internal/config.h @@ -0,0 +1,45 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/config.h +// ----------------------------------------------------------------------------- +// + +#ifndef ABSL_LOG_INTERNAL_CONFIG_H_ +#define ABSL_LOG_INTERNAL_CONFIG_H_ + +#include "absl/base/config.h" + +#ifdef _WIN32 +#include <cstdint> +#else +#include <sys/types.h> +#endif + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +#ifdef _WIN32 +using Tid = uint32_t; +#else +using Tid = pid_t; +#endif + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_CONFIG_H_ diff --git a/absl/log/internal/flags.h b/absl/log/internal/flags.h new file mode 100644 index 00000000..0c5e81ed --- /dev/null +++ b/absl/log/internal/flags.h @@ -0,0 +1,53 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/log_flags.h +// ----------------------------------------------------------------------------- +// +// This header declares set of flags which can be used to configure Abseil +// Logging library behaviour at runtime. + +#ifndef ABSL_LOG_INTERNAL_FLAGS_H_ +#define ABSL_LOG_INTERNAL_FLAGS_H_ + +#include <string> + +#include "absl/flags/declare.h" + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// These flags should not be used in C++ code to access logging library +// configuration knobs. Use interfaces defined in absl/log/globals.h +// instead. It is still ok to use these flags on a command line. +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +// Log messages at this severity or above are sent to stderr in *addition* to +// logfiles. Defaults to `ERROR`. See log_severity.h for numeric values of +// severity levels. +ABSL_DECLARE_FLAG(int, stderrthreshold); + +// Log messages at this severity or above are logged; others are discarded. +// Defaults to `INFO`, i.e. log all severities. See log_severity.h for numeric +// values of severity levels. +ABSL_DECLARE_FLAG(int, minloglevel); + +// If specified in the form file:linenum, any messages logged from a matching +// location will also include a backtrace. +ABSL_DECLARE_FLAG(std::string, log_backtrace_at); + +// If true, the log prefix (severity, date, time, PID, etc.) is prepended to +// each message logged. Defaults to true. +ABSL_DECLARE_FLAG(bool, log_prefix); + +#endif // ABSL_LOG_INTERNAL_FLAGS_H_ diff --git a/absl/log/internal/globals.cc b/absl/log/internal/globals.cc new file mode 100644 index 00000000..863b047f --- /dev/null +++ b/absl/log/internal/globals.cc @@ -0,0 +1,125 @@ +// 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/globals.h" + +#include <atomic> +#include <cstdio> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/log_severity.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +namespace { +// Keeps track of whether Logging initialization is finalized. +// Log messages generated before that will go to stderr. +ABSL_CONST_INIT std::atomic<bool> logging_initialized(false); + +// The TimeZone used for logging. This may only be set once. +ABSL_CONST_INIT std::atomic<absl::TimeZone*> timezone_ptr{nullptr}; + +// If true, the logging library will symbolize stack in fatal messages +ABSL_CONST_INIT std::atomic<bool> symbolize_stack_trace(true); + +// Specifies maximum number of stack frames to report in fatal messages. +ABSL_CONST_INIT std::atomic<int> max_frames_in_stack_trace(64); + +ABSL_CONST_INIT std::atomic<bool> exit_on_dfatal(true); +ABSL_CONST_INIT std::atomic<bool> suppress_sigabort_trace(false); +} // namespace + +bool IsInitialized() { + return logging_initialized.load(std::memory_order_acquire); +} + +void SetInitialized() { + logging_initialized.store(true, std::memory_order_release); +} + +void WriteToStderr(absl::string_view message, absl::LogSeverity severity) { + // Avoid using std::cerr from this module since we may get called during + // exit code, and cerr may be partially or fully destroyed by then. + std::fwrite(message.data(), message.size(), 1, stderr); + +#if defined(_WIN64) || defined(_WIN32) || defined(_WIN16) + // C99 requires stderr to not be fully-buffered by default (7.19.3.7), but + // MS CRT buffers it anyway, so we must `fflush` to ensure the string hits + // the console/file before the program dies (and takes the libc buffers + // with it). + // https://docs.microsoft.com/en-us/cpp/c-runtime-library/stream-i-o + if (severity >= absl::LogSeverity::kWarning) { + std::fflush(stderr); + } +#else + // Avoid unused parameter warning in this branch. + (void)severity; +#endif +} + +void SetTimeZone(absl::TimeZone tz) { + absl::TimeZone* expected = nullptr; + absl::TimeZone* new_tz = new absl::TimeZone(tz); + // timezone_ptr can only be set once, otherwise new_tz is leaked. + if (!timezone_ptr.compare_exchange_strong(expected, new_tz, + std::memory_order_release, + std::memory_order_relaxed)) { + ABSL_RAW_LOG(FATAL, + "absl::log_internal::SetTimeZone() has already been called"); + } +} + +const absl::TimeZone* TimeZone() { + return timezone_ptr.load(std::memory_order_acquire); +} + +bool ShouldSymbolizeLogStackTrace() { + return symbolize_stack_trace.load(std::memory_order_acquire); +} + +void EnableSymbolizeLogStackTrace(bool on_off) { + symbolize_stack_trace.store(on_off, std::memory_order_release); +} + +int MaxFramesInLogStackTrace() { + return max_frames_in_stack_trace.load(std::memory_order_acquire); +} + +void SetMaxFramesInLogStackTrace(int max_num_frames) { + max_frames_in_stack_trace.store(max_num_frames, std::memory_order_release); +} + +bool ExitOnDFatal() { return exit_on_dfatal.load(std::memory_order_acquire); } + +void SetExitOnDFatal(bool on_off) { + exit_on_dfatal.store(on_off, std::memory_order_release); +} + +bool SuppressSigabortTrace() { + return suppress_sigabort_trace.load(std::memory_order_acquire); +} + +bool SetSuppressSigabortTrace(bool on_off) { + return suppress_sigabort_trace.exchange(on_off); +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/globals.h b/absl/log/internal/globals.h new file mode 100644 index 00000000..27bc0d09 --- /dev/null +++ b/absl/log/internal/globals.h @@ -0,0 +1,101 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/globals.h +// ----------------------------------------------------------------------------- +// +// This header file contains various global objects and static helper routines +// use in logging implementation. + +#ifndef ABSL_LOG_INTERNAL_GLOBALS_H_ +#define ABSL_LOG_INTERNAL_GLOBALS_H_ + +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// IsInitialized returns true if the logging library is initialized. +// This function is async-signal-safe +bool IsInitialized(); + +// SetLoggingInitialized is called once after logging initialization is done. +void SetInitialized(); + +// Unconditionally write a `message` to stderr. If `severity` exceeds kInfo +// we also flush the stderr stream. +void WriteToStderr(absl::string_view message, absl::LogSeverity severity); + +// Set the TimeZone used for human-friendly times (for example, the log message +// prefix) printed by the logging library. This may only be called once. +void SetTimeZone(absl::TimeZone tz); + +// Returns the TimeZone used for human-friendly times (for example, the log +// message prefix) printed by the logging library Returns nullptr prior to +// initialization. +const absl::TimeZone* TimeZone(); + +// Returns true if stack traces emitted by the logging library should be +// symbolized. This function is async-signal-safe. +bool ShouldSymbolizeLogStackTrace(); + +// Enables or disables symbolization of stack traces emitted by the +// logging library. This function is async-signal-safe. +void EnableSymbolizeLogStackTrace(bool on_off); + +// Returns the maximum number of frames that appear in stack traces +// emitted by the logging library. This function is async-signal-safe. +int MaxFramesInLogStackTrace(); + +// Sets the maximum number of frames that appear in stack traces emitted by +// the logging library. This function is async-signal-safe. +void SetMaxFramesInLogStackTrace(int max_num_frames); + +// Determines whether we exit the program for a LOG(DFATAL) message in +// debug mode. It does this by skipping the call to Fail/FailQuietly. +// This is intended for testing only. +// +// This can have some effects on LOG(FATAL) as well. Failure messages +// are always allocated (rather than sharing a buffer), the crash +// reason is not recorded, the "gwq" status message is not updated, +// and the stack trace is not recorded. The LOG(FATAL) *will* still +// exit the program. Since this function is used only in testing, +// these differences are acceptable. +// +// Additionally, LOG(LEVEL(FATAL)) is indistinguishable from LOG(DFATAL) and +// will not terminate the program if SetExitOnDFatal(false) has been called. +bool ExitOnDFatal(); + +// SetExitOnDFatal() sets the ExitOnDFatal() status +void SetExitOnDFatal(bool on_off); + +// Determines if the logging library should suppress logging of stacktraces in +// the `SIGABRT` handler, typically because we just logged a stacktrace as part +// of `LOG(FATAL)` and are about to send ourselves a `SIGABRT` to end the +// program. +bool SuppressSigabortTrace(); + +// Sets the SuppressSigabortTrace() status and returns the previous state. +bool SetSuppressSigabortTrace(bool on_off); + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_GLOBALS_H_ diff --git a/absl/log/internal/log_format.cc b/absl/log/internal/log_format.cc new file mode 100644 index 00000000..b10a656b --- /dev/null +++ b/absl/log/internal/log_format.cc @@ -0,0 +1,189 @@ +// +// 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_format.h" + +#include <string.h> + +#ifdef _MSC_VER +#include <winsock2.h> // For timeval +#else +#include <sys/time.h> +#endif + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <string> +#include <type_traits> + +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/base/optimization.h" +#include "absl/log/internal/config.h" +#include "absl/log/internal/globals.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "absl/time/civil_time.h" +#include "absl/time/time.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { +namespace { + +// The fields before the filename are all fixed-width except for the thread ID, +// which is of bounded width. +size_t FormatBoundedFields(absl::LogSeverity severity, absl::Time timestamp, + log_internal::Tid tid, absl::Span<char>& buf) { + constexpr size_t kBoundedFieldsMaxLen = + sizeof("SMMDD HH:MM:SS.NNNNNN ") + + (1 + std::numeric_limits<log_internal::Tid>::digits10 + 1) - sizeof(""); + if (ABSL_PREDICT_FALSE(buf.size() < kBoundedFieldsMaxLen)) { + // We don't bother trying to truncate these fields if the buffer is too + // short (or almost too short) because it would require doing a lot more + // length checking (slow) and it should never happen. A 15kB buffer should + // be enough for anyone. Instead we mark `buf` full without writing + // anything. + buf.remove_suffix(buf.size()); + return 0; + } + + // We can't call absl::LocalTime(), localtime_r(), or anything else here that + // isn't async-signal-safe. We can only use the time zone if it has already + // been loaded. + const absl::TimeZone* tz = absl::log_internal::TimeZone(); + if (ABSL_PREDICT_FALSE(tz == nullptr)) { + // If a time zone hasn't been set yet because we are logging before the + // logging library has been initialized, we fallback to a simpler, slower + // method. Just report the raw Unix time in seconds. We cram this into the + // normal time format for the benefit of parsers. + auto tv = absl::ToTimeval(timestamp); + int snprintf_result = absl::SNPrintF( + buf.data(), buf.size(), "%c0000 00:00:%02d.%06d %7d ", + absl::LogSeverityName(severity)[0], static_cast<int>(tv.tv_sec), + static_cast<int>(tv.tv_usec), static_cast<int>(tid)); + if (snprintf_result >= 0) { + buf.remove_prefix(snprintf_result); + return static_cast<size_t>(snprintf_result); + } + return 0; + } + + char* p = buf.data(); + *p++ = absl::LogSeverityName(severity)[0]; + const absl::TimeZone::CivilInfo ci = tz->At(timestamp); + absl::numbers_internal::PutTwoDigits(ci.cs.month(), p); + p += 2; + absl::numbers_internal::PutTwoDigits(ci.cs.day(), p); + p += 2; + *p++ = ' '; + absl::numbers_internal::PutTwoDigits(ci.cs.hour(), p); + p += 2; + *p++ = ':'; + absl::numbers_internal::PutTwoDigits(ci.cs.minute(), p); + p += 2; + *p++ = ':'; + absl::numbers_internal::PutTwoDigits(ci.cs.second(), p); + p += 2; + *p++ = '.'; + const int64_t usecs = absl::ToInt64Microseconds(ci.subsecond); + absl::numbers_internal::PutTwoDigits(usecs / 10000, p); + p += 2; + absl::numbers_internal::PutTwoDigits(usecs / 100 % 100, p); + p += 2; + absl::numbers_internal::PutTwoDigits(usecs % 100, p); + p += 2; + *p++ = ' '; + constexpr bool unsigned_tid_t = !std::is_signed<log_internal::Tid>::value; + if ((unsigned_tid_t || tid >= 0) && tid < 10) *p++ = ' '; + if ((unsigned_tid_t || tid > -10) && tid < 100) *p++ = ' '; + if ((unsigned_tid_t || tid > -100) && tid < 1000) *p++ = ' '; + if ((unsigned_tid_t || tid > -1000) && tid < 10000) *p++ = ' '; + if ((unsigned_tid_t || tid > -10000) && tid < 100000) *p++ = ' '; + if ((unsigned_tid_t || tid > -100000) && tid < 1000000) *p++ = ' '; + p = absl::numbers_internal::FastIntToBuffer(tid, p); + *p++ = ' '; + const size_t bytes_formatted = p - buf.data(); + buf.remove_prefix(bytes_formatted); + return bytes_formatted; +} + +// Copies into `dst` as many bytes of `src` as will fit, then advances `dst` +// past the copied bytes and returns the number of bytes written. +size_t AppendTruncated(absl::string_view src, absl::Span<char>& dst) { + if (src.size() > dst.size()) src = src.substr(0, dst.size()); + memcpy(dst.data(), src.data(), src.size()); + dst.remove_prefix(src.size()); + return src.size(); +} + +size_t FormatLineNumber(int line, absl::Span<char>& buf) { + constexpr size_t kLineFieldMaxLen = + sizeof(":] ") + (1 + std::numeric_limits<int>::digits10 + 1) - sizeof(""); + if (ABSL_PREDICT_FALSE(buf.size() < kLineFieldMaxLen)) { + // As above, we don't bother trying to truncate this if the buffer is too + // short and it should never happen. + buf.remove_suffix(buf.size()); + return 0; + } + char* p = buf.data(); + *p++ = ':'; + p = absl::numbers_internal::FastIntToBuffer(line, p); + *p++ = ']'; + *p++ = ' '; + const size_t bytes_formatted = p - buf.data(); + buf.remove_prefix(bytes_formatted); + return bytes_formatted; +} + +} // namespace + +std::string FormatLogMessage(absl::LogSeverity severity, + absl::CivilSecond civil_second, + absl::Duration subsecond, log_internal::Tid tid, + absl::string_view basename, int line, + absl::string_view message) { + return absl::StrFormat( + "%c%02d%02d %02d:%02d:%02d.%06d %7d %s:%d] %s", + absl::LogSeverityName(severity)[0], civil_second.month(), + civil_second.day(), civil_second.hour(), civil_second.minute(), + civil_second.second(), absl::ToInt64Microseconds(subsecond), tid, + basename, line, message); +} + +// This method is fairly hot, and the library always passes a huge `buf`, so we +// save some bounds-checking cycles by not trying to do precise truncation. +// Truncating at a field boundary is probably a better UX anyway. +// +// The prefix is written in three parts, each of which does a single +// bounds-check and truncation: +// 1. severity, timestamp, and thread ID +// 2. filename +// 3. line number and bracket +size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp, + log_internal::Tid tid, absl::string_view basename, + int line, absl::Span<char>& buf) { + auto prefix_size = FormatBoundedFields(severity, timestamp, tid, buf); + prefix_size += AppendTruncated(basename, buf); + prefix_size += FormatLineNumber(line, buf); + return prefix_size; +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/log_format.h b/absl/log/internal/log_format.h new file mode 100644 index 00000000..a016328f --- /dev/null +++ b/absl/log/internal/log_format.h @@ -0,0 +1,73 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/log_format.h +// ----------------------------------------------------------------------------- +// +// This file declares routines implementing formatting of log message and log +// prefix. + +#ifndef ABSL_LOG_INTERNAL_LOG_FORMAT_H_ +#define ABSL_LOG_INTERNAL_LOG_FORMAT_H_ + +#include <stddef.h> + +#include <string> + +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/log/internal/config.h" +#include "absl/strings/string_view.h" +#include "absl/time/civil_time.h" +#include "absl/time/time.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// Formats log message based on provided data. +std::string FormatLogMessage(absl::LogSeverity severity, + absl::CivilSecond civil_second, + absl::Duration subsecond, log_internal::Tid tid, + absl::string_view basename, int line, + absl::string_view message); + +// Formats various entry metadata into a text string meant for use as a +// prefix on a log message string. Writes into `buf`, advances `buf` to point +// at the remainder of the buffer (i.e. past any written bytes), and returns the +// number of bytes written. +// +// In addition to calling `buf->remove_prefix()` (or the equivalent), this +// function may also do `buf->remove_suffix(buf->size())` in cases where no more +// bytes (i.e. no message data) should be written into the buffer. For example, +// if the prefix ought to be: +// I0926 09:00:00.000000 1234567 foo.cc:123] +// `buf` is too small, the function might fill the whole buffer: +// I0926 09:00:00.000000 1234 +// (note the apparrently incorrect thread ID), or it might write less: +// I0926 09:00:00.000000 +// In this case, it might also empty `buf` prior to returning to prevent +// message data from being written into the space where a reader would expect to +// see a thread ID. +size_t FormatLogPrefix(absl::LogSeverity severity, absl::Time timestamp, + log_internal::Tid tid, absl::string_view basename, + int line, absl::Span<char>& buf); + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_LOG_FORMAT_H_ diff --git a/absl/log/internal/log_message.cc b/absl/log/internal/log_message.cc new file mode 100644 index 00000000..9ef0c29e --- /dev/null +++ b/absl/log/internal/log_message.cc @@ -0,0 +1,508 @@ +// +// 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> + +#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/config.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/log_entry.h" +#include "absl/log/log_sink.h" +#include "absl/log/log_sink_registry.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_format.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&) { + // Default - Do nothing +} + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +namespace { +// Copies into `dst` as many bytes of `src` as will fit, then truncates the +// copied bytes from the front of `dst` and returns the number of bytes written. +size_t AppendTruncated(absl::string_view src, absl::Span<char>* dst) { + if (src.size() > dst->size()) src = src.substr(0, dst->size()); + memcpy(dst->data(), src.data(), src.size()); + dst->remove_prefix(src.size()); + return src.size(); +} + +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 + +// A write-only `std::streambuf` that writes into an `absl::Span<char>`. +// +// This class is responsible for writing a metadata prefix just before the first +// data are streamed in. The metadata are subject to change (cf. +// `LogMessage::AtLocation`) until then, so we wait as long as possible. +// +// This class is also responsible for reserving space for a trailing newline +// so that one can be added later by `Finalize` no matter how many data are +// streamed in. +class LogEntryStreambuf final : public std::streambuf { + public: + explicit LogEntryStreambuf(absl::Span<char> buf, const absl::LogEntry& entry) + : buf_(buf), entry_(entry), prefix_len_(0), finalized_(false) { + // To detect when data are first written, we leave the put area null, + // override `overflow`, and check ourselves in `xsputn`. + } + + LogEntryStreambuf(LogEntryStreambuf&&) = delete; + LogEntryStreambuf& operator=(LogEntryStreambuf&&) = delete; + + absl::Span<const char> Finalize() { + assert(!finalized_); + // If no data were ever streamed in, this is where we must write the prefix. + if (pbase() == nullptr) Initialize(); + // Here we reclaim the two bytes we reserved. + size_t idx = pptr() - pbase(); + setp(buf_.data(), buf_.data() + buf_.size()); + pbump(idx); + sputc('\n'); + sputc('\0'); + finalized_ = true; + return absl::Span<const char>(pbase(), pptr() - pbase()); + } + size_t prefix_len() const { return prefix_len_; } + + protected: + std::streamsize xsputn(const char* s, std::streamsize n) override { + if (pbase() == nullptr) Initialize(); + return Append(absl::string_view(s, n)); + } + + int overflow(int ch = EOF) override { + if (pbase() == nullptr) Initialize(); + if (ch == EOF) return 0; + if (pptr() == epptr()) return EOF; + *pptr() = static_cast<char>(ch); + pbump(1); + return 1; + } + + private: + void Initialize() { + // Here we reserve two bytes in our buffer to guarantee `Finalize` space to + // add a trailing "\n\0". + assert(buf_.size() >= 2); + setp(buf_.data(), buf_.data() + buf_.size() - 2); + if (entry_.prefix()) { + absl::Span<char> remaining = buf_; + prefix_len_ = log_internal::FormatLogPrefix( + entry_.log_severity(), entry_.timestamp(), entry_.tid(), + entry_.source_basename(), entry_.source_line(), remaining); + pbump(prefix_len_); + } + } + + size_t Append(absl::string_view data) { + absl::Span<char> remaining(pptr(), epptr() - pptr()); + const size_t written = AppendTruncated(data, &remaining); + pbump(written); + return written; + } + + const absl::Span<char> buf_; + const absl::LogEntry& entry_; + size_t prefix_len_; + bool finalized_; +}; + +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; + + // `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*, 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; + + // A formatted string message is built in `string_buf`. + std::array<char, kLogMessageBufferSize> string_buf; + + // A `std::streambuf` that stores into `string_buf`. + LogEntryStreambuf streambuf_; +}; + +LogMessage::LogMessageData::LogMessageData(const char* file, int line, + absl::LogSeverity severity, + absl::Time timestamp) + : extra_sinks_only(false), + streambuf_(absl::MakeSpan(string_buf), entry) { + 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(); +} + +LogMessage::LogMessage(const char* file, int line, absl::LogSeverity severity) + : data_( + absl::make_unique<LogMessageData>(file, line, severity, absl::Now())) + , + stream_(&data_->streambuf_) +{ + data_->first_fatal = false; + data_->is_perror = false; + data_->fail_quietly = false; + + // Legacy defaults for LOG's ostream: + stream_.setf(std::ios_base::showbase | std::ios_base::boolalpha); + // `fill('0')` is omitted here because its effects are very different without + // structured logging. Resolution is tracked in b/111310488. + + // 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() { +#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() { + // 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); +} + +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); // NOLINT +template LogMessage& LogMessage::operator<<(const unsigned short& v); // NOLINT +template LogMessage& LogMessage::operator<<(const int& v); +template LogMessage& LogMessage::operator<<(const unsigned int& v); +template LogMessage& LogMessage::operator<<(const long& v); // NOLINT +template LogMessage& LogMessage::operator<<(const unsigned long& v); // NOLINT +template LogMessage& LogMessage::operator<<(const long long& v); // NOLINT +template LogMessage& LogMessage::operator<<( + const unsigned long long& v); // NOLINT +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); +template LogMessage& LogMessage::operator<<(const std::string& v); +template LogMessage& LogMessage::operator<<(const absl::string_view& 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_() << "]"; + } + + // Have we already seen a fatal message? + ABSL_CONST_INIT static std::atomic_flag seen_fatal = ATOMIC_FLAG_INIT; + 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. + data_->first_fatal = !seen_fatal.test_and_set(std::memory_order_relaxed); + } + + data_->entry.text_message_with_prefix_and_newline_and_nul_ = + data_->streambuf_.Finalize(); + data_->entry.prefix_len_ = data_->streambuf_.prefix_len(); + SendToLog(); +} + +void LogMessage::SetFailQuietly() { data_->fail_quietly = true; } + +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; + stream_ << " (stacktrace:\n"; + debugging_internal::DumpStackTrace( + 1, log_internal::MaxFramesInLogStackTrace(), + log_internal::ShouldSymbolizeLogStackTrace(), WriteToStream, &stream_); + stream_ << ") "; +} + +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 << " "; +} + +// ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so +// disable msvc's warning about the d'tor never returning. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4722) +#endif +LogMessageFatal::~LogMessageFatal() { + Flush(); + FailWithoutStackTrace(); +} +#ifdef _MSC_VER +#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 << " "; +} + +// ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so +// disable msvc's warning about the d'tor never returning. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4722) +#endif +LogMessageQuietlyFatal::~LogMessageQuietlyFatal() { + Flush(); + FailQuietly(); +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +} // namespace log_internal + +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h new file mode 100644 index 00000000..37a267c0 --- /dev/null +++ b/absl/log/internal/log_message.h @@ -0,0 +1,287 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/log_message.h +// ----------------------------------------------------------------------------- +// +// This file declares `class absl::log_internal::LogMessage`. This class more or +// less represents a particular log message. LOG/CHECK macros create a +// temporary instance of `LogMessage` and then stream values to it. At the end +// of the LOG/CHECK statement, LogMessage instance goes out of scope and +// `~LogMessage` directs the message to the registered log sinks. +// Heap-allocation of `LogMessage` is unsupported. Construction outside of a +// `LOG` macro is unsupported. + +#ifndef ABSL_LOG_INTERNAL_LOG_MESSAGE_H_ +#define ABSL_LOG_INTERNAL_LOG_MESSAGE_H_ + +#include <ios> +#include <memory> +#include <ostream> +#include <streambuf> +#include <string> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/internal/errno_saver.h" +#include "absl/base/log_severity.h" +#include "absl/log/internal/config.h" +#include "absl/log/internal/nullguard.h" +#include "absl/log/log_entry.h" +#include "absl/log/log_sink.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +constexpr int kLogMessageBufferSize = 15000; + +class LogMessage { + public: + // Used for `LOG`. + LogMessage(const char* file, int line, + absl::LogSeverity severity) ABSL_ATTRIBUTE_COLD; + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; + ~LogMessage() ABSL_ATTRIBUTE_COLD; + + // Overrides the location inferred from the callsite. The string pointed to + // by `file` must be valid until the end of the statement. + LogMessage& AtLocation(absl::string_view file, int line); + // Omits the prefix from this line. The prefix includes metadata about the + // logged data such as source code location and timestamp. + LogMessage& NoPrefix(); + // Sets the verbosity field of the logged message as if it was logged by + // `VLOG(verbose_level)`. Unlike `VLOG`, this method does not affect + // evaluation of the statement when the specified `verbose_level` has been + // disabled. The only effect is on `absl::LogSink` implementations which + // make use of the `absl::LogSink::verbosity()` value. The value + // `absl::LogEntry::kNoVerbosityLevel` can be specified to mark the message + // not verbose. + LogMessage& WithVerbosity(int verbose_level); + // Uses the specified timestamp instead of one collected in the constructor. + LogMessage& WithTimestamp(absl::Time timestamp); + // Uses the specified thread ID instead of one collected in the constructor. + LogMessage& WithThreadID(absl::LogEntry::tid_t tid); + // Copies all metadata (but no data) from the specified `absl::LogEntry`. + LogMessage& WithMetadataFrom(const absl::LogEntry& entry); + // Appends to the logged message a colon, a space, a textual description of + // the current value of `errno` (as by strerror(3)), and the numerical value + // of `errno`. + LogMessage& WithPerror(); + // Sends this message to `*sink` in addition to whatever other sinks it would + // otherwise have been sent to. `sink` must not be null. + LogMessage& ToSinkAlso(absl::LogSink* sink); + // Sends this message to `*sink` and no others. `sink` must not be null. + LogMessage& ToSinkOnly(absl::LogSink* sink); + + // Don't call this method from outside this library. + LogMessage& InternalStream() { return *this; } + + // By-value overloads for small, common types let us overlook common failures + // to define globals and static data members (i.e. in a .cc file). + // clang-format off + // The CUDA toolchain cannot handle these <<<'s: + LogMessage& operator<<(char v) { return operator<< <char>(v); } + LogMessage& operator<<(signed char v) { return operator<< <signed char>(v); } + LogMessage& operator<<(unsigned char v) { + return operator<< <unsigned char>(v); + } + LogMessage& operator<<(signed short v) { // NOLINT + return operator<< <signed short>(v); // NOLINT + } + LogMessage& operator<<(signed int v) { return operator<< <signed int>(v); } + LogMessage& operator<<(signed long v) { // NOLINT + return operator<< <signed long>(v); // NOLINT + } + LogMessage& operator<<(signed long long v) { // NOLINT + return operator<< <signed long long>(v); // NOLINT + } + LogMessage& operator<<(unsigned short v) { // NOLINT + return operator<< <unsigned short>(v); // NOLINT + } + LogMessage& operator<<(unsigned int v) { + return operator<< <unsigned int>(v); + } + LogMessage& operator<<(unsigned long v) { // NOLINT + return operator<< <unsigned long>(v); // NOLINT + } + LogMessage& operator<<(unsigned long long v) { // NOLINT + return operator<< <unsigned long long>(v); // NOLINT + } + LogMessage& operator<<(void* v) { return operator<< <void*>(v); } + LogMessage& operator<<(const void* v) { return operator<< <const void*>(v); } + LogMessage& operator<<(float v) { return operator<< <float>(v); } + LogMessage& operator<<(double v) { return operator<< <double>(v); } + LogMessage& operator<<(bool v) { return operator<< <bool>(v); } + // clang-format on + + // Handle stream manipulators e.g. std::endl. + LogMessage& operator<<(std::ostream& (*m)(std::ostream& os)); + LogMessage& operator<<(std::ios_base& (*m)(std::ios_base& os)); + + // Literal strings. This allows us to record C string literals as literals in + // the logging.proto.Value. + // + // Allow this overload to be inlined to prevent generating instantiations of + // this template for every value of `SIZE` encountered in each source code + // file. That significantly increases linker input sizes. Inlining is cheap + // because the argument to this overload is almost always a string literal so + // the call to `strlen` can be replaced at compile time. The overload for + // `char[]` below should not be inlined. The compiler typically does not have + // the string at compile time and cannot replace the call to `strlen` so + // inlining it increases the binary size. See the discussion on + // cl/107527369. + template <int SIZE> + LogMessage& operator<<(const char (&buf)[SIZE]); + + // This prevents non-const `char[]` arrays from looking like literals. + template <int SIZE> + LogMessage& operator<<(char (&buf)[SIZE]) ABSL_ATTRIBUTE_NOINLINE; + + // Default: uses `ostream` logging to convert `v` to a string. + template <typename T> + LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE; + + // Note: We explicitly do not support `operator<<` for non-const references + // because it breaks logging of non-integer bitfield types (i.e., enums). + + protected: + // Call `abort()` or similar to perform `LOG(FATAL)` crash. It is assumed + // that the caller has already generated and written the trace as appropriate. + ABSL_ATTRIBUTE_NORETURN static void FailWithoutStackTrace(); + + // Similar to `FailWithoutStackTrace()`, but without `abort()`. Terminates + // the process with an error exit code. + ABSL_ATTRIBUTE_NORETURN static void FailQuietly(); + + // Dispatches the completed `absl::LogEntry` to applicable `absl::LogSink`s. + // This might as well be inlined into `~LogMessage` except that + // `~LogMessageFatal` needs to call it early. + void Flush(); + + // After this is called, failures are done as quiet as possible for this log + // message. + void SetFailQuietly(); + + private: + struct LogMessageData; // Opaque type containing message state + + // Returns `true` if the message is fatal or enabled debug-fatal. + bool IsFatal() const; + + // Records some tombstone-type data in anticipation of `Die`. + void PrepareToDie(); + void Die(); + + void SendToLog(); + + // Checks `FLAGS_log_backtrace_at` and appends a backtrace if appropriate. + void LogBacktraceIfNeeded(); + + // This should be the first data member so that its initializer captures errno + // before any other initializers alter it (e.g. with calls to new) and so that + // no other destructors run afterward an alter it (e.g. with calls to delete). + absl::base_internal::ErrnoSaver errno_saver_; + + // We keep the data in a separate struct so that each instance of `LogMessage` + // uses less stack space. + std::unique_ptr<LogMessageData> data_; + + std::ostream stream_; +}; + +// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE` +template <typename T> +LogMessage& LogMessage::operator<<(const T& v) { + stream_ << log_internal::NullGuard<T>().Guard(v); + return *this; +} +inline LogMessage& LogMessage::operator<<( + std::ostream& (*m)(std::ostream& os)) { + stream_ << m; + return *this; +} +inline LogMessage& LogMessage::operator<<( + std::ios_base& (*m)(std::ios_base& os)) { + stream_ << m; + return *this; +} +template <int SIZE> +LogMessage& LogMessage::operator<<(const char (&buf)[SIZE]) { + stream_ << buf; + return *this; +} +// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE` +template <int SIZE> +LogMessage& LogMessage::operator<<(char (&buf)[SIZE]) { + stream_ << buf; + return *this; +} +// We instantiate these specializations in the library's TU to save space in +// other TUs. Since the template is marked `ABSL_ATTRIBUTE_NOINLINE` we will be +// emitting a function call either way. +extern template LogMessage& LogMessage::operator<<(const char& v); +extern template LogMessage& LogMessage::operator<<(const signed char& v); +extern template LogMessage& LogMessage::operator<<(const unsigned char& v); +extern template LogMessage& LogMessage::operator<<(const short& v); // NOLINT +extern template LogMessage& LogMessage::operator<<( + const unsigned short& v); // NOLINT +extern template LogMessage& LogMessage::operator<<(const int& v); +extern template LogMessage& LogMessage::operator<<( + const unsigned int& v); // NOLINT +extern template LogMessage& LogMessage::operator<<(const long& v); // NOLINT +extern template LogMessage& LogMessage::operator<<( + const unsigned long& v); // NOLINT +extern template LogMessage& LogMessage::operator<<( + const long long& v); // NOLINT +extern template LogMessage& LogMessage::operator<<( + const unsigned long long& v); // NOLINT +extern template LogMessage& LogMessage::operator<<(void* const& v); +extern template LogMessage& LogMessage::operator<<(const void* const& v); +extern template LogMessage& LogMessage::operator<<(const float& v); +extern template LogMessage& LogMessage::operator<<(const double& v); +extern template LogMessage& LogMessage::operator<<(const bool& v); +extern template LogMessage& LogMessage::operator<<(const std::string& v); +extern template LogMessage& LogMessage::operator<<(const absl::string_view& v); + +// `LogMessageFatal` ensures the process will exit in failure after logging this +// message. +class LogMessageFatal final : public LogMessage { + public: + LogMessageFatal(const char* file, int line) ABSL_ATTRIBUTE_COLD; + LogMessageFatal(const char* file, int line, + absl::string_view failure_msg) ABSL_ATTRIBUTE_COLD; + ABSL_ATTRIBUTE_NORETURN ~LogMessageFatal(); +}; + +class LogMessageQuietlyFatal final : public LogMessage { + public: + LogMessageQuietlyFatal(const char* file, int line) ABSL_ATTRIBUTE_COLD; + LogMessageQuietlyFatal(const char* file, int line, + absl::string_view failure_msg) ABSL_ATTRIBUTE_COLD; + ABSL_ATTRIBUTE_NORETURN ~LogMessageQuietlyFatal(); +}; + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +extern "C" ABSL_ATTRIBUTE_WEAK void ABSL_INTERNAL_C_SYMBOL( + AbslInternalOnFatalLogMessage)(const absl::LogEntry&); + +#endif // ABSL_LOG_INTERNAL_LOG_MESSAGE_H_ diff --git a/absl/log/internal/log_sink_set.cc b/absl/log/internal/log_sink_set.cc new file mode 100644 index 00000000..4fe301c6 --- /dev/null +++ b/absl/log/internal/log_sink_set.cc @@ -0,0 +1,295 @@ +// +// 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_sink_set.h" + +#ifndef ABSL_HAVE_THREAD_LOCAL +#include <pthread.h> +#endif + +#ifdef __ANDROID__ +#include <android/log.h> +#endif + +#ifdef _WIN32 +#include <windows.h> +#endif + +#include <algorithm> +#include <vector> + +#include "absl/base/attributes.h" +#include "absl/base/call_once.h" +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/log_severity.h" +#include "absl/base/thread_annotations.h" +#include "absl/cleanup/cleanup.h" +#include "absl/log/globals.h" +#include "absl/log/internal/config.h" +#include "absl/log/internal/globals.h" +#include "absl/log/log_entry.h" +#include "absl/log/log_sink.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { +namespace { + +// Returns a mutable reference to a thread-local variable that should be true if +// a globally-registered `LogSink`'s `Send()` is currently being invoked on this +// thread. +bool& ThreadIsLoggingStatus() { +#ifdef ABSL_HAVE_THREAD_LOCAL + ABSL_CONST_INIT thread_local bool thread_is_logging = false; + return thread_is_logging; +#else + ABSL_CONST_INIT static pthread_key_t thread_is_logging_key; + static const bool unused = [] { + if (pthread_key_create(&thread_is_logging_key, [](void* data) { + delete reinterpret_cast<bool*>(data); + })) { + perror("pthread_key_create failed!"); + abort(); + } + return true; + }(); + bool* thread_is_logging_ptr = + reinterpret_cast<bool*>(pthread_getspecific(thread_is_logging_key)); + + if (ABSL_PREDICT_FALSE(!thread_is_logging_ptr)) { + thread_is_logging_ptr = new bool{false}; + if (pthread_setspecific(thread_is_logging_key, thread_is_logging_ptr)) { + perror("pthread_setspecific failed"); + abort(); + } + } + return *thread_is_logging_ptr; +#endif +} + +class StderrLogSink final : public LogSink { + public: + ~StderrLogSink() override = default; + + void Send(const absl::LogEntry& entry) override { + if (entry.log_severity() < absl::StderrThreshold() && + absl::log_internal::IsInitialized()) { + return; + } + + ABSL_CONST_INIT static absl::once_flag warn_if_not_initialized; + absl::call_once(warn_if_not_initialized, []() { + if (absl::log_internal::IsInitialized()) return; + const char w[] = + "WARNING: All log messages before absl::InitializeLog() is called" + " are written to STDERR\n"; + absl::log_internal::WriteToStderr(w, absl::LogSeverity::kWarning); + }); + + if (!entry.stacktrace().empty()) { + absl::log_internal::WriteToStderr(entry.stacktrace(), + entry.log_severity()); + } else { + // TODO(b/226937039): do this outside else condition once we avoid + // ReprintFatalMessage + absl::log_internal::WriteToStderr( + entry.text_message_with_prefix_and_newline(), entry.log_severity()); + } + } +}; + +#if defined(__ANDROID__) +class AndroidLogSink final : public LogSink { + public: + ~AndroidLogSink() override = default; + + void Send(const absl::LogEntry& entry) override { + const int level = AndroidLogLevel(entry); + // TODO(b/37587197): make the tag ("native") configurable. + __android_log_write(level, "native", + entry.text_message_with_prefix_and_newline_c_str()); + if (entry.log_severity() == absl::LogSeverity::kFatal) + __android_log_write(ANDROID_LOG_FATAL, "native", "terminating.\n"); + } + + private: + static int AndroidLogLevel(const absl::LogEntry& entry) { + switch (entry.log_severity()) { + case absl::LogSeverity::kFatal: + return ANDROID_LOG_FATAL; + case absl::LogSeverity::kError: + return ANDROID_LOG_ERROR; + case absl::LogSeverity::kWarning: + return ANDROID_LOG_WARN; + default: + if (entry.verbosity() >= 2) return ANDROID_LOG_VERBOSE; + if (entry.verbosity() == 1) return ANDROID_LOG_DEBUG; + return ANDROID_LOG_INFO; + } + } +}; +#endif // !defined(__ANDROID__) + +#if defined(_WIN32) +class WindowsDebuggerLogSink final : public LogSink { + public: + ~WindowsDebuggerLogSink() override = default; + + void Send(const absl::LogEntry& entry) override { + if (entry.log_severity() < absl::StderrThreshold() && + absl::log_internal::IsInitialized()) { + return; + } + ::OutputDebugStringA(entry.text_message_with_prefix_and_newline_c_str()); + } +}; +#endif // !defined(_WIN32) + +class GlobalLogSinkSet final { + public: + GlobalLogSinkSet() { +#if defined(__myriad2__) || defined(__Fuchsia__) + // myriad2 and Fuchsia do not log to stderr by default. +#else + static StderrLogSink* stderr_log_sink = new StderrLogSink; + AddLogSink(stderr_log_sink); +#endif +#ifdef __ANDROID__ + static AndroidLogSink* android_log_sink = new AndroidLogSink; + AddLogSink(android_log_sink); +#endif +#if defined(_WIN32) + static WindowsDebuggerLogSink* debugger_log_sink = + new WindowsDebuggerLogSink; + AddLogSink(debugger_log_sink); +#endif // !defined(_WIN32) + } + + void LogToSinks(const absl::LogEntry& entry, + absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) + ABSL_LOCKS_EXCLUDED(guard_) { + SendToSinks(entry, extra_sinks); + + if (!extra_sinks_only) { + if (ThreadIsLoggingToLogSink()) { + absl::log_internal::WriteToStderr( + entry.text_message_with_prefix_and_newline(), entry.log_severity()); + } else { + absl::ReaderMutexLock global_sinks_lock(&guard_); + ThreadIsLoggingStatus() = true; + // Ensure the "thread is logging" status is reverted upon leaving the + // scope even in case of exceptions. + auto status_cleanup = + absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; }); + SendToSinks(entry, absl::MakeSpan(sinks_)); + } + } + } + + void AddLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) { + { + absl::WriterMutexLock global_sinks_lock(&guard_); + auto pos = std::find(sinks_.begin(), sinks_.end(), sink); + if (pos == sinks_.end()) { + sinks_.push_back(sink); + return; + } + } + ABSL_INTERNAL_LOG(FATAL, "Duplicate log sinks are not supported"); + } + + void RemoveLogSink(absl::LogSink* sink) ABSL_LOCKS_EXCLUDED(guard_) { + { + absl::WriterMutexLock global_sinks_lock(&guard_); + auto pos = std::find(sinks_.begin(), sinks_.end(), sink); + if (pos != sinks_.end()) { + sinks_.erase(pos); + return; + } + } + ABSL_INTERNAL_LOG(FATAL, "Mismatched log sink being removed"); + } + + void FlushLogSinks() ABSL_LOCKS_EXCLUDED(guard_) { + if (ThreadIsLoggingToLogSink()) { + // The thread_local condition demonstrates that we're already holding the + // lock in order to iterate over `sinks_` for dispatch. The thread-safety + // annotations don't know this, so we use `ABSL_NO_THREAD_SAFETY_ANALYSIS` + guard_.AssertReaderHeld(); + FlushLogSinksLocked(); + } else { + absl::ReaderMutexLock global_sinks_lock(&guard_); + // In case if LogSink::Flush overload decides to log + ThreadIsLoggingStatus() = true; + // Ensure the "thread is logging" status is reverted upon leaving the + // scope even in case of exceptions. + auto status_cleanup = + absl::MakeCleanup([] { ThreadIsLoggingStatus() = false; }); + FlushLogSinksLocked(); + } + } + + private: + void FlushLogSinksLocked() ABSL_SHARED_LOCKS_REQUIRED(guard_) { + for (absl::LogSink* sink : sinks_) { + sink->Flush(); + } + } + + // Helper routine for LogToSinks. + static void SendToSinks(const absl::LogEntry& entry, + absl::Span<absl::LogSink*> sinks) { + for (absl::LogSink* sink : sinks) { + sink->Send(entry); + } + } + + using LogSinksSet = std::vector<absl::LogSink*>; + absl::Mutex guard_; + LogSinksSet sinks_ ABSL_GUARDED_BY(guard_); +}; + +// Returns reference to the global LogSinks set. +GlobalLogSinkSet& GlobalSinks() { + static GlobalLogSinkSet* global_sinks = new GlobalLogSinkSet; + return *global_sinks; +} + +} // namespace + +bool ThreadIsLoggingToLogSink() { return ThreadIsLoggingStatus(); } + +void LogToSinks(const absl::LogEntry& entry, + absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only) { + log_internal::GlobalSinks().LogToSinks(entry, extra_sinks, extra_sinks_only); +} + +void AddLogSink(absl::LogSink* sink) { + log_internal::GlobalSinks().AddLogSink(sink); +} + +void RemoveLogSink(absl::LogSink* sink) { + log_internal::GlobalSinks().RemoveLogSink(sink); +} + +void FlushLogSinks() { log_internal::GlobalSinks().FlushLogSinks(); } + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/log_sink_set.h b/absl/log/internal/log_sink_set.h new file mode 100644 index 00000000..88ab073b --- /dev/null +++ b/absl/log/internal/log_sink_set.h @@ -0,0 +1,54 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/log_sink_set.h +// ----------------------------------------------------------------------------- + +#ifndef ABSL_LOG_INTERNAL_LOG_SINK_SET_H_ +#define ABSL_LOG_INTERNAL_LOG_SINK_SET_H_ + +#include "absl/base/config.h" +#include "absl/log/log_entry.h" +#include "absl/log/log_sink.h" +#include "absl/types/span.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// Returns true if a globally-registered `LogSink`'s `Send()` is currently +// being invoked on this thread. +bool ThreadIsLoggingToLogSink(); + +// This function may log to two sets of sinks: +// +// * If `extra_sinks_only` is true, it will dispatch only to `extra_sinks`. +// `LogMessage::ToSinkAlso` and `LogMessage::ToSinkOnly` are used to attach +// extra sinks to the entry. +// * Otherwise it will also log to the global sinks set. This set is managed +// by `absl::AddLogSink` and `absl::RemoveLogSink`. +void LogToSinks(const absl::LogEntry& entry, + absl::Span<absl::LogSink*> extra_sinks, bool extra_sinks_only); + +// Implementation for operations with log sink set. +void AddLogSink(absl::LogSink* sink); +void RemoveLogSink(absl::LogSink* sink); +void FlushLogSinks(); + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_LOG_SINK_SET_H_ diff --git a/absl/log/internal/nullguard.h b/absl/log/internal/nullguard.h new file mode 100644 index 00000000..147ca814 --- /dev/null +++ b/absl/log/internal/nullguard.h @@ -0,0 +1,56 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/nullguard.h +// ----------------------------------------------------------------------------- +// +// NullGuard exists such that NullGuard<T>::Guard(v) returns v, unless passed a +// nullptr_t, or a null char* or const char*, in which case it returns "(null)". +// This allows streaming NullGuard<T>::Guard(v) to an output stream without +// hitting undefined behavior for null values. + +#ifndef ABSL_LOG_INTERNAL_NULLGUARD_H_ +#define ABSL_LOG_INTERNAL_NULLGUARD_H_ + +#include <cstddef> + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +template <typename T> +struct NullGuard final { + static const T& Guard(const T& v) { return v; } +}; +template <> +struct NullGuard<char*> final { + static const char* Guard(const char* v) { return v ? v : "(null)"; } +}; +template <> +struct NullGuard<const char*> final { + static const char* Guard(const char* v) { return v ? v : "(null)"; } +}; +template <> +struct NullGuard<std::nullptr_t> final { + static const char* Guard(const std::nullptr_t&) { return "(null)"; } +}; + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_NULLGUARD_H_ diff --git a/absl/log/internal/nullstream.h b/absl/log/internal/nullstream.h new file mode 100644 index 00000000..80c62c9e --- /dev/null +++ b/absl/log/internal/nullstream.h @@ -0,0 +1,134 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/nullstream.h +// ----------------------------------------------------------------------------- +// +// Classes `NullStream`, `NullStreamMaybeFatal ` and `NullStreamFatal` +// implement a subset of the `LogMessage` API and are used instead when logging +// of messages has been disabled. + +#ifndef ABSL_LOG_INTERNAL_NULLSTREAM_H_ +#define ABSL_LOG_INTERNAL_NULLSTREAM_H_ + +#ifdef _WIN32 +#include <cstdlib> +#else +#include <unistd.h> +#endif +#include <ios> +#include <ostream> + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// A `NullStream` implements the API of `LogMessage` (a few methods and +// `operator<<`) but does nothing. All methods are defined inline so the +// compiler can eliminate the whole instance and discard anything that's +// streamed in. +class NullStream { + public: + NullStream& AtLocation(absl::string_view, int) { return *this; } + template <typename SourceLocationType> + NullStream& AtLocation(SourceLocationType) { + return *this; + } + NullStream& NoPrefix() { return *this; } + NullStream& WithVerbosity(int) { return *this; } + template <typename TimeType> + NullStream& WithTimestamp(TimeType) { + return *this; + } + template <typename Tid> + NullStream& WithThreadID(Tid) { + return *this; + } + template <typename LogEntryType> + NullStream& WithMetadataFrom(const LogEntryType&) { + return *this; + } + NullStream& WithPerror() { return *this; } + template <typename LogSinkType> + NullStream& ToSinkAlso(LogSinkType*) { + return *this; + } + template <typename LogSinkType> + NullStream& ToSinkOnly(LogSinkType*) { + return *this; + } + template <typename LogSinkType> + NullStream& OutputToSink(LogSinkType*, bool) { + return *this; + } + NullStream& InternalStream() { return *this; } +}; +template <typename T> +inline NullStream& operator<<(NullStream& str, const T&) { + return str; +} +inline NullStream& operator<<(NullStream& str, + std::ostream& (*)(std::ostream& os)) { + return str; +} +inline NullStream& operator<<(NullStream& str, + std::ios_base& (*)(std::ios_base& os)) { + return str; +} + +// `NullStreamMaybeFatal` implements the process termination semantics of +// `LogMessage`, which is used for `DFATAL` severity and expression-defined +// severity e.g. `LOG(LEVEL(HowBadIsIt()))`. Like `LogMessage`, it terminates +// the process when destroyed if the passed-in severity equals `FATAL`. +class NullStreamMaybeFatal final : public NullStream { + public: + explicit NullStreamMaybeFatal(absl::LogSeverity severity) + : fatal_(severity == absl::LogSeverity::kFatal) {} + ~NullStreamMaybeFatal() { + if (fatal_) _exit(1); + } + + private: + bool fatal_; +}; + +// `NullStreamFatal` implements the process termination semantics of +// `LogMessageFatal`, which means it always terminates the process. `DFATAL` +// and expression-defined severity use `NullStreamMaybeFatal` above. +class NullStreamFatal final : public NullStream { + public: + NullStreamFatal() {} + // ABSL_ATTRIBUTE_NORETURN doesn't seem to work on destructors with msvc, so + // disable msvc's warning about the d'tor never returning. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4722) +#endif + ABSL_ATTRIBUTE_NORETURN ~NullStreamFatal() { _exit(1); } +#ifdef _MSC_VER +#pragma warning(pop) +#endif +}; + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_GLOBALS_H_ diff --git a/absl/log/internal/stderr_log_sink_test.cc b/absl/log/internal/stderr_log_sink_test.cc new file mode 100644 index 00000000..763690d1 --- /dev/null +++ b/absl/log/internal/stderr_log_sink_test.cc @@ -0,0 +1,105 @@ +// +// 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 <stdlib.h> + +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/attributes.h" +#include "absl/base/log_severity.h" +#include "absl/log/globals.h" +#include "absl/log/internal/test_helpers.h" +#include "absl/log/log.h" + +namespace { +using ::testing::AllOf; +using ::testing::HasSubstr; + +auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment( + new absl::log_internal::LogTestEnvironment); + +MATCHER_P2(HasSubstrTimes, substr, expected_count, "") { + int count = 0; + std::string::size_type pos = 0; + std::string needle(substr); + while ((pos = arg.find(needle, pos)) != std::string::npos) { + ++count; + pos += needle.size(); + } + + return count == expected_count; +} + +TEST(StderrLogSinkDeathTest, InfoMessagesInStderr) { + EXPECT_DEATH_IF_SUPPORTED( + { + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); + LOG(INFO) << "INFO message"; + exit(1); + }, + "INFO message"); +} + +TEST(StderrLogSinkDeathTest, WarningMessagesInStderr) { + EXPECT_DEATH_IF_SUPPORTED( + { + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); + LOG(WARNING) << "WARNING message"; + exit(1); + }, + "WARNING message"); +} + +TEST(StderrLogSinkDeathTest, ErrorMessagesInStderr) { + EXPECT_DEATH_IF_SUPPORTED( + { + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); + LOG(ERROR) << "ERROR message"; + exit(1); + }, + "ERROR message"); +} + +TEST(StderrLogSinkDeathTest, FatalMessagesInStderr) { + char message[] = "FATAL message"; + char stacktrace[] = "*** Check failure stack trace: ***"; + + int expected_count = 1; + + EXPECT_DEATH_IF_SUPPORTED( + { + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); + LOG(FATAL) << message; + }, + AllOf(HasSubstrTimes(message, expected_count), HasSubstr(stacktrace))); +} + +TEST(StderrLogSinkDeathTest, SecondaryFatalMessagesInStderr) { + auto MessageGen = []() -> std::string { + LOG(FATAL) << "Internal failure"; + return "External failure"; + }; + + EXPECT_DEATH_IF_SUPPORTED( + { + absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); + LOG(FATAL) << MessageGen(); + }, + "Internal failure"); +} + +} // namespace diff --git a/absl/log/internal/strip.h b/absl/log/internal/strip.h new file mode 100644 index 00000000..848c3867 --- /dev/null +++ b/absl/log/internal/strip.h @@ -0,0 +1,71 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/strip.h +// ----------------------------------------------------------------------------- +// + +#ifndef ABSL_LOG_INTERNAL_STRIP_H_ +#define ABSL_LOG_INTERNAL_STRIP_H_ + +#include "absl/base/log_severity.h" +#include "absl/log/internal/log_message.h" +#include "absl/log/internal/nullstream.h" + +// `ABSL_LOGGING_INTERNAL_LOG_*` evaluates to a temporary `LogMessage` object or +// to a related object with a compatible API but different behavior. This set +// of defines comes in three flavors: vanilla, plus two variants that strip some +// logging in subtly different ways for subtly different reasons (see below). +#if defined(STRIP_LOG) && STRIP_LOG +#define ABSL_LOGGING_INTERNAL_LOG_INFO ::absl::log_internal::NullStream() +#define ABSL_LOGGING_INTERNAL_LOG_WARNING ::absl::log_internal::NullStream() +#define ABSL_LOGGING_INTERNAL_LOG_ERROR ::absl::log_internal::NullStream() +#define ABSL_LOGGING_INTERNAL_LOG_FATAL ::absl::log_internal::NullStreamFatal() +#define ABSL_LOGGING_INTERNAL_LOG_QFATAL ::absl::log_internal::NullStreamFatal() +#define ABSL_LOGGING_INTERNAL_LOG_DFATAL \ + ::absl::log_internal::NullStreamMaybeFatal(::absl::kLogDebugFatal) +#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ + ::absl::log_internal::NullStreamMaybeFatal(log_internal_severity) +#define ABSL_LOG_INTERNAL_CHECK(failure_message) ABSL_LOGGING_INTERNAL_LOG_FATAL +#define ABSL_LOG_INTERNAL_QCHECK(failure_message) \ + ABSL_LOGGING_INTERNAL_LOG_QFATAL +#else // !defined(STRIP_LOG) || !STRIP_LOG +#define ABSL_LOGGING_INTERNAL_LOG_INFO \ + ::absl::log_internal::LogMessage(__FILE__, __LINE__, \ + ::absl::LogSeverity::kInfo) +#define ABSL_LOGGING_INTERNAL_LOG_WARNING \ + ::absl::log_internal::LogMessage(__FILE__, __LINE__, \ + ::absl::LogSeverity::kWarning) +#define ABSL_LOGGING_INTERNAL_LOG_ERROR \ + ::absl::log_internal::LogMessage(__FILE__, __LINE__, \ + ::absl::LogSeverity::kError) +#define ABSL_LOGGING_INTERNAL_LOG_FATAL \ + ::absl::log_internal::LogMessageFatal(__FILE__, __LINE__) +#define ABSL_LOGGING_INTERNAL_LOG_QFATAL \ + ::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__) +#define ABSL_LOGGING_INTERNAL_LOG_DFATAL \ + ::absl::log_internal::LogMessage(__FILE__, __LINE__, ::absl::kLogDebugFatal) +#define ABSL_LOGGING_INTERNAL_LOG_LEVEL(severity) \ + ::absl::log_internal::LogMessage(__FILE__, __LINE__, log_internal_severity) +// These special cases dispatch to special-case constructors that allow us to +// avoid an extra function call and shrink non-LTO binaries by a percent or so. +#define ABSL_LOG_INTERNAL_CHECK(failure_message) \ + ::absl::log_internal::LogMessageFatal(__FILE__, __LINE__, failure_message) +#define ABSL_LOG_INTERNAL_QCHECK(failure_message) \ + ::absl::log_internal::LogMessageQuietlyFatal(__FILE__, __LINE__, \ + failure_message) +#endif // !defined(STRIP_LOG) || !STRIP_LOG + +#endif // ABSL_LOG_INTERNAL_STRIP_H_ diff --git a/absl/log/internal/test_actions.cc b/absl/log/internal/test_actions.cc new file mode 100644 index 00000000..41ca9887 --- /dev/null +++ b/absl/log/internal/test_actions.cc @@ -0,0 +1,68 @@ +// +// 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/test_actions.h" + +#include <cassert> +#include <iostream> +#include <string> + +#include "absl/base/config.h" +#include "absl/log/internal/config.h" +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +void WriteToStderrWithFilename::operator()(const absl::LogEntry& entry) const { + std::cerr << message << " (file: " << entry.source_filename() << ")" + << std::endl; +} + +void WriteEntryToStderr::operator()(const absl::LogEntry& entry) const { + if (!message.empty()) std::cerr << message << std::endl; + + std::cerr << "LogEntry{\n" + << " source_filename: \"" + << absl::CHexEscape(entry.source_filename()) << "\"\n" + << " source_basename: \"" + << absl::CHexEscape(entry.source_basename()) << "\"\n" + << " source_line: " << entry.source_line() << "\n" + << " prefix: " << (entry.prefix() ? "true\n" : "false\n") + << " log_severity: " << entry.log_severity() << "\n" + << " timestamp: " << entry.timestamp() << "\n" + << " text_message: \"" << absl::CHexEscape(entry.text_message()) + << "\"\n verbosity: " << entry.verbosity() << "\n" + << "}" << std::endl; +} + +void WriteEntryToStderr::operator()(absl::LogSeverity severity, + absl::string_view filename, + absl::string_view log_message) const { + if (!message.empty()) std::cerr << message << std::endl; + + std::cerr << "LogEntry{\n" + << " source_filename: \"" << absl::CHexEscape(filename) << "\"\n" + << " log_severity: " << severity << "\n" + << " text_message: \"" << absl::CHexEscape(log_message) << "}" + << std::endl; +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/test_actions.h b/absl/log/internal/test_actions.h new file mode 100644 index 00000000..649a0505 --- /dev/null +++ b/absl/log/internal/test_actions.h @@ -0,0 +1,90 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/test_actions.h +// ----------------------------------------------------------------------------- +// +// This file declares Googletest's actions used in the Abseil Logging library +// unit tests. + +#ifndef ABSL_LOG_INTERNAL_TEST_ACTIONS_H_ +#define ABSL_LOG_INTERNAL_TEST_ACTIONS_H_ + +#include <iostream> +#include <ostream> +#include <string> + +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/log/log_entry.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// These actions are used by the child process in a death test. +// +// Expectations set in the child cannot cause test failure in the parent +// directly. Instead, the child can use these actions with +// `EXPECT_CALL`/`WillOnce` and `ON_CALL`/`WillByDefault` (for unexpected calls) +// to write messages to stderr that the parent can match against. +struct WriteToStderr final { + explicit WriteToStderr(absl::string_view m) : message(m) {} + std::string message; + + template <typename... Args> + void operator()(const Args&...) const { + std::cerr << message << std::endl; + } +}; + +struct WriteToStderrWithFilename final { + explicit WriteToStderrWithFilename(absl::string_view m) : message(m) {} + + std::string message; + + void operator()(const absl::LogEntry& entry) const; +}; + +struct WriteEntryToStderr final { + explicit WriteEntryToStderr(absl::string_view m) : message(m) {} + + std::string message = ""; + + void operator()(const absl::LogEntry& entry) const; + void operator()(absl::LogSeverity, absl::string_view, + absl::string_view) const; +}; + +// See the documentation for `DeathTestValidateExpectations` above. +// `DeathTestExpectedLogging` should be used once in a given death test, and the +// applicable severity level is the one that should be passed to +// `DeathTestValidateExpectations`. +inline WriteEntryToStderr DeathTestExpectedLogging() { + return WriteEntryToStderr{"Mock received expected entry:"}; +} + +// `DeathTestUnexpectedLogging` should be used zero or more times to mark +// messages that should not hit the logs as the process dies. +inline WriteEntryToStderr DeathTestUnexpectedLogging() { + return WriteEntryToStderr{"Mock received unexpected entry:"}; +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_TEST_ACTIONS_H_ diff --git a/absl/log/internal/test_helpers.cc b/absl/log/internal/test_helpers.cc new file mode 100644 index 00000000..bff5cc17 --- /dev/null +++ b/absl/log/internal/test_helpers.cc @@ -0,0 +1,82 @@ +// 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/test_helpers.h" + +#ifdef __Fuchsia__ +#include <zircon/syscalls.h> +#endif + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/log/globals.h" +#include "absl/log/initialize.h" +#include "absl/log/internal/globals.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// Returns false if the specified severity level is disabled by +// `ABSL_MIN_LOG_LEVEL` or `absl::MinLogLevel()`. +bool LoggingEnabledAt(absl::LogSeverity severity) { + return severity >= kAbslMinLogLevel && severity >= absl::MinLogLevel(); +} + +// ----------------------------------------------------------------------------- +// Googletest Death Test Predicates +// ----------------------------------------------------------------------------- + +#if GTEST_HAS_DEATH_TEST + +bool DiedOfFatal(int exit_status) { +#if defined(_WIN32) + // Depending on NDEBUG and (configuration?) MSVC's abort either results + // in error code 3 (SIGABRT) or error code 0x80000003 (breakpoint + // triggered). + return ::testing::ExitedWithCode(3)(exit_status & ~0x80000000); +#elif defined(__Fuchsia__) + // The Fuchsia death test implementation kill()'s the process when it detects + // an exception, so it should exit with the corresponding code. See + // FuchsiaDeathTest::Wait(). + return ::testing::ExitedWithCode(ZX_TASK_RETCODE_SYSCALL_KILL)(exit_status); +#elif defined(__ANDROID__) && defined(__aarch64__) + // These are all run under a qemu config that eats died-due-to-signal exit + // statuses. + return true; +#else + return ::testing::KilledBySignal(SIGABRT)(exit_status); +#endif +} + +bool DiedOfQFatal(int exit_status) { + return ::testing::ExitedWithCode(1)(exit_status); +} + +#endif + +// ----------------------------------------------------------------------------- +// Helper for Log inititalization in test +// ----------------------------------------------------------------------------- + +void LogTestEnvironment::SetUp() { + if (!absl::log_internal::IsInitialized()) { + absl::InitializeLog(); + } +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/test_helpers.h b/absl/log/internal/test_helpers.h new file mode 100644 index 00000000..fd06e295 --- /dev/null +++ b/absl/log/internal/test_helpers.h @@ -0,0 +1,71 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/test_helpers.h +// ----------------------------------------------------------------------------- +// +// This file declares testing helpers for the logging library. + +#ifndef ABSL_LOG_INTERNAL_TEST_HELPERS_H_ +#define ABSL_LOG_INTERNAL_TEST_HELPERS_H_ + +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/log/globals.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// `ABSL_MIN_LOG_LEVEL` can't be used directly since it is not always defined. +constexpr auto kAbslMinLogLevel = +#ifdef ABSL_MIN_LOG_LEVEL + static_cast<absl::LogSeverityAtLeast>(ABSL_MIN_LOG_LEVEL); +#else + absl::LogSeverityAtLeast::kInfo; +#endif + +// Returns false if the specified severity level is disabled by +// `ABSL_MIN_LOG_LEVEL` or `absl::MinLogLevel()`. +bool LoggingEnabledAt(absl::LogSeverity severity); + +// ----------------------------------------------------------------------------- +// Googletest Death Test Predicates +// ----------------------------------------------------------------------------- + +#if GTEST_HAS_DEATH_TEST + +bool DiedOfFatal(int exit_status); +bool DiedOfQFatal(int exit_status); + +#endif + +// ----------------------------------------------------------------------------- +// Helper for Log inititalization in test +// ----------------------------------------------------------------------------- + +class LogTestEnvironment : public ::testing::Environment { + public: + ~LogTestEnvironment() override = default; + + void SetUp() override; +}; + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_TEST_HELPERS_H_ diff --git a/absl/log/internal/test_matchers.cc b/absl/log/internal/test_matchers.cc new file mode 100644 index 00000000..ee32617b --- /dev/null +++ b/absl/log/internal/test_matchers.cc @@ -0,0 +1,168 @@ +// +// 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/test_matchers.h" + +#include <sstream> +#include <string> +#include <utility> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/log/internal/config.h" +#include "absl/log/internal/test_helpers.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +::testing::Matcher<const absl::LogEntry&> SourceFilename( + const ::testing::Matcher<absl::string_view>& source_filename) { + return Property("source_filename", &absl::LogEntry::source_filename, + source_filename); +} + +::testing::Matcher<const absl::LogEntry&> SourceBasename( + const ::testing::Matcher<absl::string_view>& source_basename) { + return Property("source_basename", &absl::LogEntry::source_basename, + source_basename); +} + +::testing::Matcher<const absl::LogEntry&> SourceLine( + const ::testing::Matcher<int>& source_line) { + return Property("source_line", &absl::LogEntry::source_line, source_line); +} + +::testing::Matcher<const absl::LogEntry&> Prefix( + const ::testing::Matcher<bool>& prefix) { + return Property("prefix", &absl::LogEntry::prefix, prefix); +} + +::testing::Matcher<const absl::LogEntry&> LogSeverity( + const ::testing::Matcher<absl::LogSeverity>& log_severity) { + return Property("log_severity", &absl::LogEntry::log_severity, log_severity); +} + +::testing::Matcher<const absl::LogEntry&> Timestamp( + const ::testing::Matcher<absl::Time>& timestamp) { + return Property("timestamp", &absl::LogEntry::timestamp, timestamp); +} + +::testing::Matcher<const absl::LogEntry&> TimestampInMatchWindow() { + return Property("timestamp", &absl::LogEntry::timestamp, + ::testing::AllOf(::testing::Ge(absl::Now()), + ::testing::Truly([](absl::Time arg) { + return arg <= absl::Now(); + }))); +} + +::testing::Matcher<const absl::LogEntry&> ThreadID( + const ::testing::Matcher<absl::LogEntry::tid_t>& tid) { + return Property("tid", &absl::LogEntry::tid, tid); +} + +::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefixAndNewline( + const ::testing::Matcher<absl::string_view>& + text_message_with_prefix_and_newline) { + return Property("text_message_with_prefix_and_newline", + &absl::LogEntry::text_message_with_prefix_and_newline, + text_message_with_prefix_and_newline); +} + +::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefix( + const ::testing::Matcher<absl::string_view>& text_message_with_prefix) { + return Property("text_message_with_prefix", + &absl::LogEntry::text_message_with_prefix, + text_message_with_prefix); +} + +::testing::Matcher<const absl::LogEntry&> TextMessage( + const ::testing::Matcher<absl::string_view>& text_message) { + return Property("text_message", &absl::LogEntry::text_message, text_message); +} + +::testing::Matcher<const absl::LogEntry&> TextPrefix( + const ::testing::Matcher<absl::string_view>& text_prefix) { + return ResultOf( + [](const absl::LogEntry& entry) { + absl::string_view msg = entry.text_message_with_prefix(); + msg.remove_suffix(entry.text_message().size()); + return msg; + }, + text_prefix); +} + +::testing::Matcher<const absl::LogEntry&> Verbosity( + const ::testing::Matcher<int>& verbosity) { + return Property("verbosity", &absl::LogEntry::verbosity, verbosity); +} + +::testing::Matcher<const absl::LogEntry&> Stacktrace( + const ::testing::Matcher<absl::string_view>& stacktrace) { + return Property("stacktrace", &absl::LogEntry::stacktrace, stacktrace); +} + +class MatchesOstreamImpl final + : public ::testing::MatcherInterface<absl::string_view> { + public: + explicit MatchesOstreamImpl(std::string expected) + : expected_(std::move(expected)) {} + bool MatchAndExplain(absl::string_view actual, + ::testing::MatchResultListener*) const override { + return actual == expected_; + } + void DescribeTo(std::ostream* os) const override { + *os << "matches the contents of the ostringstream, which are \"" + << expected_ << "\""; + } + + void DescribeNegationTo(std::ostream* os) const override { + *os << "does not match the contents of the ostringstream, which are \"" + << expected_ << "\""; + } + + private: + const std::string expected_; +}; +::testing::Matcher<absl::string_view> MatchesOstream( + const std::ostringstream& stream) { + return ::testing::MakeMatcher(new MatchesOstreamImpl(stream.str())); +} + +// We need to validate what is and isn't logged as the process dies due to +// `FATAL`, `QFATAL`, `CHECK`, etc., but assertions inside a death test +// subprocess don't directly affect the pass/fail status of the parent process. +// Instead, we use the mock actions `DeathTestExpectedLogging` and +// `DeathTestUnexpectedLogging` to write specific phrases to `stderr` that we +// can validate in the parent process using this matcher. +::testing::Matcher<const std::string&> DeathTestValidateExpectations() { + if (log_internal::LoggingEnabledAt(absl::LogSeverity::kFatal)) { + return ::testing::Matcher<const std::string&>(::testing::AllOf( + ::testing::HasSubstr("Mock received expected entry"), + Not(::testing::HasSubstr("Mock received unexpected entry")))); + } + // If `FATAL` logging is disabled, neither message should have been written. + return ::testing::Matcher<const std::string&>(::testing::AllOf( + Not(::testing::HasSubstr("Mock received expected entry")), + Not(::testing::HasSubstr("Mock received unexpected entry")))); +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/test_matchers.h b/absl/log/internal/test_matchers.h new file mode 100644 index 00000000..b8179ccc --- /dev/null +++ b/absl/log/internal/test_matchers.h @@ -0,0 +1,90 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/test_matchers.h +// ----------------------------------------------------------------------------- +// +// This file declares Googletest's matchers used in the Abseil Logging library +// unit tests. + +#ifndef ABSL_LOG_INTERNAL_TEST_MATCHERS_H_ +#define ABSL_LOG_INTERNAL_TEST_MATCHERS_H_ + +#include <iosfwd> +#include <sstream> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/base/config.h" +#include "absl/base/log_severity.h" +#include "absl/log/internal/config.h" +#include "absl/log/internal/test_helpers.h" +#include "absl/log/log_entry.h" +#include "absl/strings/string_view.h" +#include "absl/time/time.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +// These matchers correspond to the components of `absl::LogEntry`. +::testing::Matcher<const absl::LogEntry&> SourceFilename( + const ::testing::Matcher<absl::string_view>& source_filename); +::testing::Matcher<const absl::LogEntry&> SourceBasename( + const ::testing::Matcher<absl::string_view>& source_basename); +// Be careful with this one; multi-line statements using `__LINE__` evaluate +// differently on different platforms. In particular, the MSVC implementation +// of `EXPECT_DEATH` returns the line number of the macro expansion to all lines +// within the code block that's expected to die. +::testing::Matcher<const absl::LogEntry&> SourceLine( + const ::testing::Matcher<int>& source_line); +::testing::Matcher<const absl::LogEntry&> Prefix( + const ::testing::Matcher<bool>& prefix); +::testing::Matcher<const absl::LogEntry&> LogSeverity( + const ::testing::Matcher<absl::LogSeverity>& log_severity); +::testing::Matcher<const absl::LogEntry&> Timestamp( + const ::testing::Matcher<absl::Time>& timestamp); +// Matches if the `LogEntry`'s timestamp falls after the instantiation of this +// matcher and before its execution, as is normal when used with EXPECT_CALL. +::testing::Matcher<const absl::LogEntry&> TimestampInMatchWindow(); +::testing::Matcher<const absl::LogEntry&> ThreadID( + const ::testing::Matcher<absl::LogEntry::tid_t>&); +::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefixAndNewline( + const ::testing::Matcher<absl::string_view>& + text_message_with_prefix_and_newline); +::testing::Matcher<const absl::LogEntry&> TextMessageWithPrefix( + const ::testing::Matcher<absl::string_view>& text_message_with_prefix); +::testing::Matcher<const absl::LogEntry&> TextMessage( + const ::testing::Matcher<absl::string_view>& text_message); +::testing::Matcher<const absl::LogEntry&> TextPrefix( + const ::testing::Matcher<absl::string_view>& text_prefix); +::testing::Matcher<const absl::LogEntry&> Verbosity( + const ::testing::Matcher<int>& verbosity); +::testing::Matcher<const absl::LogEntry&> Stacktrace( + const ::testing::Matcher<absl::string_view>& stacktrace); +// Behaves as `Eq(stream.str())`, but produces better failure messages. +::testing::Matcher<absl::string_view> MatchesOstream( + const std::ostringstream& stream); +::testing::Matcher<const std::string&> DeathTestValidateExpectations(); + +// This feature coming soon =). +#define ENCODED_MESSAGE(message_matcher) ::testing::_ + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_TEST_MATCHERS_H_ diff --git a/absl/log/internal/voidify.h b/absl/log/internal/voidify.h new file mode 100644 index 00000000..8f62da20 --- /dev/null +++ b/absl/log/internal/voidify.h @@ -0,0 +1,44 @@ +// 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. +// +// ----------------------------------------------------------------------------- +// File: log/internal/voidify.h +// ----------------------------------------------------------------------------- +// +// This class is used to explicitly ignore values in the conditional logging +// macros. This avoids compiler warnings like "value computed is not used" and +// "statement has no effect". + +#ifndef ABSL_LOG_INTERNAL_VOIDIFY_H_ +#define ABSL_LOG_INTERNAL_VOIDIFY_H_ + +#include "absl/base/config.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +class Voidify final { + public: + // This has to be an operator with a precedence lower than << but higher than + // ?: + template <typename T> + void operator&&(const T&) const&& {} +}; + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_VOIDIFY_H_ |