-
Notifications
You must be signed in to change notification settings - Fork 21
util: add force_tag overloads to logging utilities
#272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v26.1.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -71,6 +71,14 @@ class logger { | |||||||||||||||||
| #endif | ||||||||||||||||||
|
|
||||||||||||||||||
| public: | ||||||||||||||||||
| /// Tag used to request that a log call bypass the configured level | ||||||||||||||||||
| /// gate. Passed as the first argument to any force-suffixed overload | ||||||||||||||||||
| /// or to the per-level helpers. A distinct type (not bool) avoids | ||||||||||||||||||
| /// ambiguity with the format_info-first overloads: a string literal | ||||||||||||||||||
| /// converts to format_info but not to force_tag. | ||||||||||||||||||
| struct force_tag {}; | ||||||||||||||||||
| static constexpr force_tag force{}; | ||||||||||||||||||
|
|
||||||||||||||||||
| class log_writer { | ||||||||||||||||||
| public: | ||||||||||||||||||
| virtual ~log_writer() = default; | ||||||||||||||||||
|
|
@@ -251,20 +259,13 @@ public: | |||||||||||||||||
| /// | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void log(log_level level, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| if (is_enabled(level)) { | ||||||||||||||||||
| try { | ||||||||||||||||||
| lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) { | ||||||||||||||||||
| #ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT | ||||||||||||||||||
| return fmt::format_to(it, fmt.format, std::forward<Args>(args)...); | ||||||||||||||||||
| #else | ||||||||||||||||||
| return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...); | ||||||||||||||||||
| #endif | ||||||||||||||||||
| }); | ||||||||||||||||||
| do_log(level, writer); | ||||||||||||||||||
| } catch (...) { | ||||||||||||||||||
| failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| do_log_checked(level, false, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void log(log_level level, force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| do_log_checked(level, true, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// logs with a rate limit to desired level if enabled, otherwise we ignore the log line | ||||||||||||||||||
|
|
@@ -283,19 +284,14 @@ public: | |||||||||||||||||
| /// | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void log(log_level level, rate_limit& rl, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| if (is_enabled(level) && rl.check()) { | ||||||||||||||||||
| try { | ||||||||||||||||||
| lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) { | ||||||||||||||||||
| if (rl.has_dropped_messages()) { | ||||||||||||||||||
| it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages()); | ||||||||||||||||||
| } | ||||||||||||||||||
| return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...); | ||||||||||||||||||
| }); | ||||||||||||||||||
| do_log(level, writer); | ||||||||||||||||||
| } catch (...) { | ||||||||||||||||||
| failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| do_log_checked_rl(level, false, rl, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// Force-emit rate-limited variant: bypass the level gate (the rate | ||||||||||||||||||
| /// limit itself still applies). | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void log(log_level level, force_tag, rate_limit& rl, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| do_log_checked_rl(level, true, rl, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// \cond internal | ||||||||||||||||||
|
|
@@ -355,6 +351,11 @@ public: | |||||||||||||||||
| void error(format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::error, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void error(force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::error, force, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Log with warning tag: | ||||||||||||||||||
| /// WARN %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | ||||||||||||||||||
| /// | ||||||||||||||||||
|
|
@@ -366,6 +367,11 @@ public: | |||||||||||||||||
| void warn(format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::warn, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void warn(force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::warn, force, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Log with info tag: | ||||||||||||||||||
| /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | ||||||||||||||||||
| /// | ||||||||||||||||||
|
|
@@ -377,6 +383,11 @@ public: | |||||||||||||||||
| void info(format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::info, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void info(force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::info, force, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Log with info tag on shard zero only: | ||||||||||||||||||
| /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | ||||||||||||||||||
| /// | ||||||||||||||||||
|
|
@@ -390,6 +401,13 @@ public: | |||||||||||||||||
| log(log_level::info, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void info0(force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| if (is_shard_zero()) { | ||||||||||||||||||
| log(log_level::info, force, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Log with debug tag: | ||||||||||||||||||
| /// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | ||||||||||||||||||
| /// | ||||||||||||||||||
|
|
@@ -401,6 +419,11 @@ public: | |||||||||||||||||
| void debug(format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::debug, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void debug(force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::debug, force, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Log with trace tag: | ||||||||||||||||||
| /// TRACE %Y-%m-%d %T,%03d [shard 0] - "your msg" \n | ||||||||||||||||||
| /// | ||||||||||||||||||
|
|
@@ -412,6 +435,11 @@ public: | |||||||||||||||||
| void trace(format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::trace, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
| /// Force-emit variant: bypass the level gate. See \ref force_tag. | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void trace(force_tag, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| log(log_level::trace, force, std::move(fmt), std::forward<Args>(args)...); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// \return name of the logger. Usually one logger per module | ||||||||||||||||||
| /// | ||||||||||||||||||
|
|
@@ -457,6 +485,42 @@ public: | |||||||||||||||||
| /// | ||||||||||||||||||
| /// \note this is a noop if fmtlib's version is less than 6.0 | ||||||||||||||||||
| static void set_with_color(bool enabled) noexcept; | ||||||||||||||||||
|
|
||||||||||||||||||
| private: | ||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void do_log_checked(log_level level, bool bypass, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| if (bypass || is_enabled(level)) { | ||||||||||||||||||
| try { | ||||||||||||||||||
| lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) { | ||||||||||||||||||
| #ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT | ||||||||||||||||||
| return fmt::format_to(it, fmt.format, std::forward<Args>(args)...); | ||||||||||||||||||
| #else | ||||||||||||||||||
| return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...); | ||||||||||||||||||
| #endif | ||||||||||||||||||
| }); | ||||||||||||||||||
| do_log(level, writer); | ||||||||||||||||||
| } catch (...) { | ||||||||||||||||||
| failed_to_log(std::current_exception(), fmt::string_view(fmt.format), fmt.loc); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| template <typename... Args> | ||||||||||||||||||
| void do_log_checked_rl(log_level level, bool bypass, rate_limit& rl, format_info_t<Args...> fmt, Args&&... args) noexcept { | ||||||||||||||||||
| if ((bypass || is_enabled(level)) && rl.check()) { | ||||||||||||||||||
| try { | ||||||||||||||||||
| lambda_log_writer writer([&] (internal::log_buf::inserter_iterator it) { | ||||||||||||||||||
| if (rl.has_dropped_messages()) { | ||||||||||||||||||
| it = fmt::format_to(it, "(rate limiting dropped {} similar messages) ", rl.get_and_reset_dropped_messages()); | ||||||||||||||||||
| } | ||||||||||||||||||
| return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...); | ||||||||||||||||||
|
Comment on lines
+515
to
+516
|
||||||||||||||||||
| } | |
| return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...); | |
| } | |
| #ifdef SEASTAR_LOGGER_COMPILE_TIME_FMT | |
| return fmt::format_to(it, fmt.format, std::forward<Args>(args)...); | |
| #else | |
| return fmt::format_to(it, fmt::runtime(fmt.format), std::forward<Args>(args)...); | |
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /* | ||
| * This file is open source software, licensed to you under the terms | ||
| * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | ||
| * distributed with this work for additional information regarding copyright | ||
| * ownership. You may not use this file except in compliance with the License. | ||
| * | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://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 <seastar/testing/thread_test_case.hh> | ||
| #include <seastar/util/log.hh> | ||
|
|
||
| #include <sstream> | ||
|
|
||
| namespace { | ||
| seastar::logger test_log("test"); | ||
| } | ||
|
|
||
| SEASTAR_THREAD_TEST_CASE(force_bypasses_level_gate_log_overload) { | ||
| std::ostringstream captured; | ||
| seastar::logger::set_ostream(captured); | ||
| seastar::logger::set_ostream_enabled(true); | ||
|
|
||
|
Comment on lines
+29
to
+32
|
||
| test_log.set_level(seastar::log_level::warn); | ||
|
|
||
| // Without force: dropped. | ||
| test_log.log(seastar::log_level::info, "dropped_line"); | ||
| BOOST_REQUIRE(captured.str().find("dropped_line") == std::string::npos); | ||
|
|
||
| // With force: emitted. | ||
| test_log.log(seastar::log_level::info, seastar::logger::force, "forced_line"); | ||
| BOOST_REQUIRE(captured.str().find("forced_line") != std::string::npos); | ||
| } | ||
|
|
||
| SEASTAR_THREAD_TEST_CASE(force_bypasses_level_gate_convenience_methods) { | ||
| std::ostringstream captured; | ||
| seastar::logger::set_ostream(captured); | ||
| seastar::logger::set_ostream_enabled(true); | ||
|
|
||
|
Comment on lines
+45
to
+48
|
||
| test_log.set_level(seastar::log_level::warn); | ||
|
|
||
| test_log.info(seastar::logger::force, "info_forced"); | ||
| test_log.debug(seastar::logger::force, "debug_forced"); | ||
| test_log.trace(seastar::logger::force, "trace_forced"); | ||
| test_log.warn(seastar::logger::force, "warn_forced"); | ||
| test_log.error(seastar::logger::force, "error_forced"); | ||
|
|
||
| const auto out = captured.str(); | ||
| BOOST_REQUIRE(out.find("info_forced") != std::string::npos); | ||
| BOOST_REQUIRE(out.find("debug_forced") != std::string::npos); | ||
| BOOST_REQUIRE(out.find("trace_forced") != std::string::npos); | ||
| BOOST_REQUIRE(out.find("warn_forced") != std::string::npos); | ||
| BOOST_REQUIRE(out.find("error_forced") != std::string::npos); | ||
|
|
||
| // Without force, info/debug/trace dropped under warn level. | ||
| captured.str(""); | ||
| test_log.info("info_dropped"); | ||
| BOOST_REQUIRE(captured.str().find("info_dropped") == std::string::npos); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc comment for
force_tagsays it is "Passed as the first argument" to force overloads, but forlogger::log(log_level, force_tag, ...)the tag is the second argument (afterlog_level). Consider rewording to avoid misleading API consumers (e.g., "passed immediately after the log level" forlog(), and as the first arg for per-level helpers).