From 0b934194ba96170b945f28c9ed296e43e6788b8d Mon Sep 17 00:00:00 2001 From: Stefan Larimore Date: Wed, 15 Jan 2025 19:32:22 -0800 Subject: [PATCH] Introduce conditional XLOGF_EVERY_<>_OR macros Summary: Continuing where garrettd in D48244354 left off. My project has a need for this new behavior (see reference to this diff in D67725319). I decided to "refactor" some comments for the XX_EVERY_N macros to de-dup a bit. Reviewed By: yfeldblum Differential Revision: D67739158 fbshipit-source-id: bc94414177de6cf2df4c4e23b7f33bfb43ae8c49 --- folly/logging/test/XlogTest.cpp | 25 +++++++++++++++++++++++ folly/logging/xlog.h | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/folly/logging/test/XlogTest.cpp b/folly/logging/test/XlogTest.cpp index af308a2809a..7e74d1986dd 100644 --- a/folly/logging/test/XlogTest.cpp +++ b/folly/logging/test/XlogTest.cpp @@ -400,6 +400,24 @@ TEST_F(XlogTest, rateLimiting) { "msg 0", "msg 8", "msg 16", "msg 24", "msg 32", "msg 40", "msg 48")); handler->clearMessages(); + // Test XLOGF_EVERY_N_OR + for (size_t n = 0; n < 10; ++n) { + bool shouldLog = n % 2 == 0; + XLOGF_EVERY_N_OR(DBG1, shouldLog, 2, "msg {}", n); + } + EXPECT_THAT( + handler->getMessageValues(), + ElementsAre( + "msg 0", + "msg 1", + "msg 2", + "msg 4", + "msg 5", + "msg 6", + "msg 8", + "msg 9")); + handler->clearMessages(); + // Test XLOG_EVERY_N_EXACT for (size_t n = 0; n < 50; ++n) { XLOG_EVERY_N_EXACT(DBG1, 7, "msg ", n); @@ -481,6 +499,7 @@ TEST_F(XlogTest, rateLimiting) { XLOG_EVERY_MS_IF(DBG1, shouldLog, 100, "int arg conditional ", n); XLOGF_EVERY_MS_IF(DBG1, shouldLog, 100, "int fmt arg conditional {}", n); XLOG_EVERY_MS_OR(DBG1, shouldLog, 100, "int arg conditional or ", n); + XLOGF_EVERY_MS_OR(DBG1, shouldLog, 100, "int fmt arg conditional or {}", n); // Sleep for 100ms between iterations 5 and 6 if (n == 5) { @@ -502,13 +521,16 @@ TEST_F(XlogTest, rateLimiting) { "1x ms arg 0", "3x s arg 0", "int arg conditional or 0", + "int fmt arg conditional or 0", "2x int arg 1", "3x s arg 1", "3x s arg 2", "int arg conditional 2", "int fmt arg conditional 2", "int arg conditional or 2", + "int fmt arg conditional or 2", "int arg conditional or 4", + "int fmt arg conditional or 4", "int arg 6", "ms arg 6", "fmt arg 6", @@ -520,9 +542,12 @@ TEST_F(XlogTest, rateLimiting) { "int arg conditional 6", "int fmt arg conditional 6", "int arg conditional or 6", + "int fmt arg conditional or 6", "2x int arg 7", "int arg conditional or 7", + "int fmt arg conditional or 7", "int arg conditional or 8", + "int fmt arg conditional or 8", })); handler->clearMessages(); diff --git a/folly/logging/xlog.h b/folly/logging/xlog.h index fdf3b63db15..c4f28d64e44 100644 --- a/folly/logging/xlog.h +++ b/folly/logging/xlog.h @@ -205,6 +205,25 @@ static_assert( #define XLOGF_EVERY_MS(level, ms, fmt, ...) \ XLOGF_EVERY_MS_IF(level, true, ms, fmt, ##__VA_ARGS__) +/** + * Similar to XLOGF(...) except log a message if the specified condition + * predicate evaluates to true or every @param ms milliseconds + * + * Note that this is threadsafe. + */ +#define XLOGF_EVERY_MS_OR(level, cond, ms, fmt, ...) \ + XLOGF_IF( \ + level, \ + (cond) || \ + [__folly_detail_xlog_ms = ms] { \ + static ::folly::logging::IntervalRateLimiter \ + folly_detail_xlog_limiter( \ + 1, ::std::chrono::milliseconds(__folly_detail_xlog_ms)); \ + return folly_detail_xlog_limiter.check(); \ + }(), \ + fmt, \ + ##__VA_ARGS__) + namespace folly { namespace detail { @@ -305,6 +324,23 @@ FOLLY_EXPORT FOLLY_ALWAYS_INLINE bool xlogEveryNImpl(size_t n) { fmt, \ ##__VA_ARGS__) +/** + * Similar to XLOGF(...) except it logs a message if the condition predicate + * evalutes to true or approximately every @param n invocations + * + * See concurrency discussion for XLOG_EVERY_N which applies here as well. + */ +#define XLOGF_EVERY_N_OR(level, cond, n, fmt, ...) \ + XLOGF_IF( \ + level, \ + (cond) || \ + [&] { \ + struct folly_detail_xlog_tag {}; \ + return ::folly::detail::xlogEveryNImpl(n); \ + }(), \ + fmt, \ + ##__VA_ARGS__) + namespace folly { namespace detail {