From 575f702f358485abc025924556d9787095b74bee Mon Sep 17 00:00:00 2001 From: devgianlu Date: Tue, 29 Sep 2026 16:03:29 +0200 Subject: [PATCH] feat(log): set Abseil's level from a go-plugin level A host that moves a plugin's level while it runs names one of go-plugin's five levels. A plugin logging through Abseil had to translate that into Abseil's minimum severity and a VLOG verbosity itself, and get debug and trace the same way round as the bridge that reports them. SetAbslLevel does it: debug and trace become the VLOG verbosities InstallAbslBridge's trace_from_verbosity maps them from, warn and error become Abseil's minimum severity, and Write follows as well. Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 4 ++++ include/go_plugin/log_absl.hpp | 10 ++++++++++ src/log_absl.cpp | 21 +++++++++++++++++++++ tests/test_log_absl.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/README.md b/README.md index 1bf3a83..47fd0c6 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,10 @@ go_plugin::log::InstallAbslBridge(); // LOG(WARNING) now reaches the host as a Abseil has no debug or trace severity of its own — they exist only as `VLOG` verbosities — so the bridge maps `VLOG(1)` to debug and `VLOG(2)` and above to trace, and it stops Abseil writing its own copy of each line to standard error. +`go_plugin::log::SetAbslLevel(level)` sets the least severe level a plugin logs at, through Abseil as much as through +`Write`: Abseil's minimum severity, and the `VLOG` verbosity that the same mapping gives debug and trace. It is for a +plugin whose host moves its level while it runs. + To add another backend, translate its records into `go_plugin::log::Submit` and add a target beside the Abseil one; nothing in the core changes. diff --git a/include/go_plugin/log_absl.hpp b/include/go_plugin/log_absl.hpp index 75e7fa7..2205924 100644 --- a/include/go_plugin/log_absl.hpp +++ b/include/go_plugin/log_absl.hpp @@ -1,5 +1,7 @@ #pragma once +#include "go_plugin/log.hpp" + namespace go_plugin::log { struct AbslBridgeOptions { @@ -29,4 +31,12 @@ struct AbslBridgeOptions { */ void InstallAbslBridge(const AbslBridgeOptions& options = {}); +/** + * Sets the least severe level a plugin logs at, through Abseil as much as + * through Write: Abseil's minimum severity, and the VLOG verbosity that debug + * and trace map to under InstallAbslBridge's options. For a plugin whose host + * moves its level while it runs. + */ +void SetAbslLevel(Level min); + } // namespace go_plugin::log diff --git a/src/log_absl.cpp b/src/log_absl.cpp index 827f3d7..842703a 100644 --- a/src/log_absl.cpp +++ b/src/log_absl.cpp @@ -1,5 +1,6 @@ #include "go_plugin/log_absl.hpp" +#include #include #include @@ -15,6 +16,8 @@ namespace go_plugin::log { namespace { +std::atomic g_trace_from_verbosity{AbslBridgeOptions{}.trace_from_verbosity}; + Level LevelFor(const absl::LogEntry& entry, const AbslBridgeOptions& options) { // A VLOG is filed at info severity; its verbosity is the only thing that // separates it from an ordinary info line. @@ -64,6 +67,7 @@ class Bridge final : public absl::LogSink { void InstallAbslBridge(const AbslBridgeOptions& options) { static std::once_flag once; std::call_once(once, [&options] { + g_trace_from_verbosity.store(options.trace_from_verbosity, std::memory_order_relaxed); // Never destroyed: Abseil holds the pointer for the life of the process // and a LOG(FATAL) unwinds nothing. static Bridge bridge(options); @@ -75,4 +79,21 @@ void InstallAbslBridge(const AbslBridgeOptions& options) { }); } +void SetAbslLevel(Level min) { + SetLevel(min); + + const int trace_from = g_trace_from_verbosity.load(std::memory_order_relaxed); + int verbosity = 0; + absl::LogSeverityAtLeast severity = absl::LogSeverityAtLeast::kInfo; + switch (min) { + case Level::Trace: verbosity = trace_from; break; + case Level::Debug: verbosity = trace_from - 1; break; + case Level::Info: break; + case Level::Warn: severity = absl::LogSeverityAtLeast::kWarning; break; + case Level::Error: severity = absl::LogSeverityAtLeast::kError; break; + } + absl::SetGlobalVLogLevel(verbosity); + absl::SetMinLogLevel(severity); +} + } // namespace go_plugin::log diff --git a/tests/test_log_absl.cpp b/tests/test_log_absl.cpp index 6d15411..877e582 100644 --- a/tests/test_log_absl.cpp +++ b/tests/test_log_absl.cpp @@ -72,3 +72,27 @@ TEST_F(AbslBridge, CarriesTheCaller) { ASSERT_EQ(Lines().size(), 1u); EXPECT_NE(Lines()[0].find(R"("caller":"test_log_absl.cpp:)"), std::string::npos) << Lines()[0]; } + +// One level moves Abseil as a whole: its VLOG verbosities by the bridge's own +// mapping of debug and trace, and its severities. +TEST_F(AbslBridge, SetAbslLevelDecidesWhatAbseilLogs) { + go_plugin::log::SetAbslLevel(go_plugin::log::Level::Debug); + VLOG(1) << "debug line"; + VLOG(2) << "trace line"; + ASSERT_EQ(Lines().size(), 1u); + EXPECT_NE(Lines()[0].find("debug line"), std::string::npos); + + Lines().clear(); + go_plugin::log::SetAbslLevel(go_plugin::log::Level::Trace); + VLOG(2) << "trace line"; + EXPECT_EQ(Lines().size(), 1u); + + Lines().clear(); + go_plugin::log::SetAbslLevel(go_plugin::log::Level::Warn); + VLOG(1) << "debug line"; + LOG(INFO) << "info line"; + LOG(WARNING) << "warning line"; + ASSERT_EQ(Lines().size(), 1u); + EXPECT_NE(Lines()[0].find("warning line"), std::string::npos); + EXPECT_EQ(go_plugin::log::GetLevel(), go_plugin::log::Level::Warn); +}