aboutsummaryrefslogtreecommitdiff
path: root/absl/log
diff options
context:
space:
mode:
authorDerek Mauro <dmauro@google.com>2023-08-04 13:54:47 -0700
committerCopybara-Service <copybara-worker@google.com>2023-08-04 13:55:54 -0700
commit70172ada858b8739ce07e8c2f1ecd8c11c8768c7 (patch)
treee59640940355eb71082261dd9f5c87ae1aa31e78 /absl/log
parent659b77b713fe5f1f75e6e1bb121c0eed1c8f964a (diff)
downloadabseil-70172ada858b8739ce07e8c2f1ecd8c11c8768c7.tar.gz
abseil-70172ada858b8739ce07e8c2f1ecd8c11c8768c7.tar.bz2
abseil-70172ada858b8739ce07e8c2f1ecd8c11c8768c7.zip
Release the `DFATAL` pseudo-LogSeverity level
`DFATAL` is defined as `FATAL` in debug mode, and as `ERROR` when `NDEBUG` is defined. Closes #1279 PiperOrigin-RevId: 553904244 Change-Id: Iaa207ee65b2a39b4b7f5da241208c3d39cd5da0e
Diffstat (limited to 'absl/log')
-rw-r--r--absl/log/CMakeLists.txt1
-rw-r--r--absl/log/globals_test.cc29
-rw-r--r--absl/log/internal/conditions.h11
-rw-r--r--absl/log/log.h2
-rw-r--r--absl/log/log_streamer.h10
-rw-r--r--absl/log/log_streamer_test.cc51
-rw-r--r--absl/log/stripping_test.cc43
7 files changed, 147 insertions, 0 deletions
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt
index 9320ce59..2fc34d01 100644
--- a/absl/log/CMakeLists.txt
+++ b/absl/log/CMakeLists.txt
@@ -845,6 +845,7 @@ absl_cc_test(
absl::log_internal_test_helpers
absl::log_severity
absl::scoped_mock_log
+ GTest::gmock
GTest::gtest_main
)
diff --git a/absl/log/globals_test.cc b/absl/log/globals_test.cc
index f7af47cd..3d936cd7 100644
--- a/absl/log/globals_test.cc
+++ b/absl/log/globals_test.cc
@@ -101,4 +101,33 @@ TEST(TestGlobals, AndroidLogTag) {
EXPECT_DEATH_IF_SUPPORTED(absl::SetAndroidNativeTag("test_tag_fail"), ".*");
}
+TEST(TestExitOnDFatal, OffTest) {
+ // Turn off...
+ absl::log_internal::SetExitOnDFatal(false);
+ EXPECT_FALSE(absl::log_internal::ExitOnDFatal());
+
+ // We don't die.
+ {
+ absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
+
+ // LOG(DFATAL) has severity FATAL if debugging, but is
+ // downgraded to ERROR if not debugging.
+ EXPECT_CALL(log, Log(absl::kLogDebugFatal, _, "This should not be fatal"));
+
+ log.StartCapturingLogs();
+ LOG(DFATAL) << "This should not be fatal";
+ }
+}
+
+#if GTEST_HAS_DEATH_TEST
+TEST(TestDeathWhileExitOnDFatal, OnTest) {
+ absl::log_internal::SetExitOnDFatal(true);
+ EXPECT_TRUE(absl::log_internal::ExitOnDFatal());
+
+ // Death comes on little cats' feet.
+ EXPECT_DEBUG_DEATH({ LOG(DFATAL) << "This should be fatal in debug mode"; },
+ "This should be fatal in debug mode");
+}
+#endif
+
} // namespace
diff --git a/absl/log/internal/conditions.h b/absl/log/internal/conditions.h
index f576d650..41f67215 100644
--- a/absl/log/internal/conditions.h
+++ b/absl/log/internal/conditions.h
@@ -137,6 +137,15 @@
? true \
: (::absl::log_internal::ExitQuietly(), false)) \
: false))
+#define ABSL_LOG_INTERNAL_CONDITION_DFATAL(type, condition) \
+ ABSL_LOG_INTERNAL_##type##_CONDITION( \
+ (ABSL_ASSUME(absl::kLogDebugFatal == absl::LogSeverity::kError || \
+ absl::kLogDebugFatal == absl::LogSeverity::kFatal), \
+ (condition) && \
+ (::absl::kLogDebugFatal >= \
+ static_cast<::absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) || \
+ (::absl::kLogDebugFatal == ::absl::LogSeverity::kFatal && \
+ (::absl::log_internal::AbortQuietly(), false)))))
#define ABSL_LOG_INTERNAL_CONDITION_LEVEL(severity) \
for (int log_internal_severity_loop = 1; log_internal_severity_loop; \
@@ -163,6 +172,8 @@
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_DFATAL(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) \
diff --git a/absl/log/log.h b/absl/log/log.h
index 602b5acf..99e78ea8 100644
--- a/absl/log/log.h
+++ b/absl/log/log.h
@@ -32,6 +32,8 @@
// * The `QFATAL` pseudo-severity level is equivalent to `FATAL` but triggers
// quieter termination messages, e.g. without a full stack trace, and skips
// running registered error handlers.
+// * The `DFATAL` pseudo-severity level is defined as `FATAL` in debug mode and
+// as `ERROR` otherwise.
// Some preprocessor shenanigans are used to ensure that e.g. `LOG(INFO)` has
// the same meaning even if a local symbol or preprocessor macro named `INFO` is
// defined. To specify a severity level using an expression instead of a
diff --git a/absl/log/log_streamer.h b/absl/log/log_streamer.h
index 2d41a07f..4ed2435d 100644
--- a/absl/log/log_streamer.h
+++ b/absl/log/log_streamer.h
@@ -165,6 +165,16 @@ inline LogStreamer LogFatalStreamer(absl::string_view file, int line) {
return absl::LogStreamer(absl::LogSeverity::kFatal, file, line);
}
+// LogDebugFatalStreamer()
+//
+// Returns a LogStreamer that writes at level LogSeverity::kLogDebugFatal.
+//
+// In debug mode, the program will be terminated when this `LogStreamer` is
+// destroyed, regardless of whether any data were streamed in.
+inline LogStreamer LogDebugFatalStreamer(absl::string_view file, int line) {
+ return absl::LogStreamer(absl::kLogDebugFatal, file, line);
+}
+
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/log/log_streamer_test.cc b/absl/log/log_streamer_test.cc
index 328d70d0..40c7d488 100644
--- a/absl/log/log_streamer_test.cc
+++ b/absl/log/log_streamer_test.cc
@@ -151,6 +151,57 @@ TEST(LogStreamerDeathTest, LogFatalStreamer) {
}
#endif
+#ifdef NDEBUG
+TEST(LogStreamerTest, LogDebugFatalStreamer) {
+ absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)),
+ Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kError)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("WriteToStream: foo")),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value {
+ str: "WriteToStream: foo"
+ })pb")),
+ Stacktrace(IsEmpty()))));
+
+ test_sink.StartCapturingLogs();
+ WriteToStream("foo",
+ &absl::LogDebugFatalStreamer("path/file.cc", 1234).stream());
+}
+#elif GTEST_HAS_DEATH_TEST
+TEST(LogStreamerDeathTest, LogDebugFatalStreamer) {
+ EXPECT_EXIT(
+ {
+ absl::ScopedMockLog test_sink;
+
+ EXPECT_CALL(test_sink, Send)
+ .Times(AnyNumber())
+ .WillRepeatedly(DeathTestUnexpectedLogging());
+
+ EXPECT_CALL(
+ test_sink,
+ Send(AllOf(
+ SourceFilename(Eq("path/file.cc")), SourceLine(Eq(1234)),
+ Prefix(IsTrue()), LogSeverity(Eq(absl::LogSeverity::kFatal)),
+ TimestampInMatchWindow(),
+ ThreadID(Eq(absl::base_internal::GetTID())),
+ TextMessage(Eq("WriteToStream: foo")),
+ ENCODED_MESSAGE(EqualsProto(R"pb(value {
+ str: "WriteToStream: foo"
+ })pb")))))
+ .WillOnce(DeathTestExpectedLogging());
+
+ test_sink.StartCapturingLogs();
+ WriteToStream(
+ "foo", &absl::LogDebugFatalStreamer("path/file.cc", 1234).stream());
+ },
+ DiedOfFatal, DeathTestValidateExpectations());
+}
+#endif
+
TEST(LogStreamerTest, LogStreamer) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
diff --git a/absl/log/stripping_test.cc b/absl/log/stripping_test.cc
index aff91495..35357039 100644
--- a/absl/log/stripping_test.cc
+++ b/absl/log/stripping_test.cc
@@ -322,6 +322,49 @@ TEST_F(StrippingTest, Fatal) {
}
}
+TEST_F(StrippingTest, DFatal) {
+ // We need to load a copy of the needle string into memory (so we can search
+ // for it) without leaving it lying around in plaintext in the executable file
+ // as would happen if we used a literal. We might (or might not) leave it
+ // lying around later; that's what the tests are for!
+ const std::string needle = absl::Base64Escape("StrippingTest.DFatal");
+ // We don't care if the LOG statement is actually executed, we're just
+ // checking that it's stripped.
+ if (kReallyDie) LOG(DFATAL) << "U3RyaXBwaW5nVGVzdC5ERmF0YWw=";
+
+ std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
+ ASSERT_THAT(exe, NotNull());
+ // `DFATAL` can be `ERROR` or `FATAL`, and a compile-time optimizer doesn't
+ // know which, because `absl::kLogDebugFatal` is declared `extern` and defined
+ // in another TU. Link-time optimization might do better. We have six cases:
+ // | `AMLL` is-> | `<=ERROR` | `FATAL` | `>FATAL` |
+ // | ------------------- | --------- | ------- | -------- |
+ // | `DFATAL` is `ERROR` | present | ? | stripped |
+ // | `DFATAL` is `FATAL` | present | present | stripped |
+
+ // These constexpr variables are used to suppress unreachable code warnings
+ // in the if-else statements below.
+
+ // "present" in the table above: `DFATAL` exceeds `ABSL_MIN_LOG_LEVEL`, so
+ // `DFATAL` statements should not be stripped (and they should be logged
+ // when executed, but that's a different testsuite).
+ constexpr bool kExpectPresent = absl::kLogDebugFatal >= kAbslMinLogLevel;
+
+ // "stripped" in the table above: even though the compiler may not know
+ // which value `DFATAL` has, it should be able to strip it since both
+ // possible values ought to be stripped.
+ constexpr bool kExpectStripped = kAbslMinLogLevel > absl::LogSeverity::kFatal;
+
+ if (kExpectPresent) {
+ EXPECT_THAT(exe.get(), FileHasSubstr(needle));
+ } else if (kExpectStripped) {
+ EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
+ } else {
+ // "?" in the table above; may or may not be stripped depending on whether
+ // any link-time optimization is done. Either outcome is ok.
+ }
+}
+
TEST_F(StrippingTest, Level) {
const std::string needle = absl::Base64Escape("StrippingTest.Level");
volatile auto severity = absl::LogSeverity::kWarning;