diff --git a/folly/logging/printf.h b/folly/logging/printf.h index 9ba34b2900b82bbf090464972748da73d5a63fef..3a0b6b47c388ab307e6b4c7846eb46e814abb093 100644 --- a/folly/logging/printf.h +++ b/folly/logging/printf.h @@ -47,8 +47,16 @@ std::string loggingFormatPrintf( * Log a message to the file's default log category using a printf-style format * string. */ -#define XLOGC(level, fmt, ...) \ +#define XLOGC(level, fmt, ...) XLOGC_IF(level, true, fmt, ##__VA_ARGS__) + +/** + * Log a message using a printf-style format string if and only if the + * specified condition predicate evaluates to true. Note that the condition + * is *only* evaluated if the log-level check passes. + */ +#define XLOGC_IF(level, cond, fmt, ...) \ XLOG_IMPL( \ ::folly::LogLevel::level, \ + cond, \ ::folly::LogStreamProcessor::APPEND, \ ::folly::loggingFormatPrintf(fmt, ##__VA_ARGS__)) diff --git a/folly/logging/test/PrintfTest.cpp b/folly/logging/test/PrintfTest.cpp index 785a85bc945dbd9d56b4fd4c02651202f1ef5283..423c95711f7d7a2b3d0bd2c9df29d9b880956a80 100644 --- a/folly/logging/test/PrintfTest.cpp +++ b/folly/logging/test/PrintfTest.cpp @@ -90,6 +90,76 @@ TEST(PrintfTest, printfStyleMacros) { EXPECT_EQ("no xlog format arguments", messages[0].first.getMessage()); messages.clear(); + XLOGC_IF(DBG1, false, "no xlog format arguments"); + ASSERT_EQ(0, messages.size()); + XLOGC_IF(DBG1, true, "xlog format arguments"); + ASSERT_EQ(1, messages.size()); + messages.clear(); + + argumentEvaluated = false; + XLOGC_IF(DBG1, true, "xlog format string %d", getValue()); + ASSERT_EQ(1, messages.size()); + EXPECT_TRUE(argumentEvaluated); + messages.clear(); + + argumentEvaluated = false; + XLOGC_IF(DBG1, false, "xlog format string %d", getValue()); + ASSERT_EQ(0, messages.size()); + EXPECT_FALSE(argumentEvaluated); + messages.clear(); + + // more complex conditional expressions + std::array<bool, 2> conds = {false, true}; + for (unsigned i = 0; i < conds.size(); i++) { + for (unsigned j = 0; j < conds.size(); j++) { + argumentEvaluated = false; + XLOGC_IF( + DBG1, conds[i] && conds[j], "testing conditional %d", getValue()); + EXPECT_EQ((conds[i] && conds[j]) ? 1 : 0, messages.size()); + messages.clear(); + if (conds[i] && conds[j]) { + EXPECT_TRUE(argumentEvaluated); + } else { + EXPECT_FALSE(argumentEvaluated); + } + + argumentEvaluated = false; + XLOGC_IF( + DBG1, conds[i] || conds[j], "testing conditional %d", getValue()); + EXPECT_EQ((conds[i] || conds[j]) ? 1 : 0, messages.size()); + messages.clear(); + if (conds[i] || conds[j]) { + EXPECT_TRUE(argumentEvaluated); + } else { + EXPECT_FALSE(argumentEvaluated); + } + } + } + + XLOGC_IF(DBG1, 0x6 & 0x2, "More conditional 1"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOGC_IF(DBG1, 0x6 | 0x2, "More conditional 2"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOGC_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOGC_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOGC_IF(DBG1, 0x3 & 0x4 ? true : false, "More conditional 4"); + EXPECT_EQ(0, messages.size()); + messages.clear(); + + XLOGC_IF(DBG1, false ? true : false, "More conditional 5"); + EXPECT_EQ(0, messages.size()); + messages.clear(); + // Errors attempting to format the message should not throw FB_LOGC(footest1234, ERR, "width overflow: %999999999999999999999d", 5); ASSERT_EQ(1, messages.size()); diff --git a/folly/logging/test/XlogTest.cpp b/folly/logging/test/XlogTest.cpp index 89a003d6264ac942b188d0edec5d08a9bd5154e4..2206d9ca27e86ad5da53738537eb91261a8dbae8 100644 --- a/folly/logging/test/XlogTest.cpp +++ b/folly/logging/test/XlogTest.cpp @@ -43,6 +43,78 @@ TEST(Xlog, xlogName) { EXPECT_EQ("xlog_test.main_file", XLOG_GET_CATEGORY()->getName()); } +TEST(Xlog, xlogIf) { + auto handler = make_shared<TestLogHandler>(); + LoggerDB::get().getCategory("xlog_test")->addHandler(handler); + auto& messages = handler->getMessages(); + + // info messages are not enabled initially. + EXPECT_FALSE(XLOG_IS_ON(INFO)); + EXPECT_TRUE(XLOG_IS_ON(ERR)); + XLOG_IF(INFO, false, "testing 1"); + EXPECT_EQ(0, messages.size()); + messages.clear(); + + XLOG_IF(INFO, true, "testing 1"); + EXPECT_EQ(0, messages.size()); + messages.clear(); + + // Increase the log level, then log a message. + LoggerDB::get().setLevel("xlog_test.main_file", LogLevel::DBG1); + XLOG_IF(DBG1, false, "testing: ", 1, 2, 3); + ASSERT_EQ(0, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, true, "testing: ", 1, 2, 3); + ASSERT_EQ(1, messages.size()); + messages.clear(); + + // more complex conditional expressions + std::array<bool, 2> conds = {false, true}; + for (unsigned i = 0; i < conds.size(); i++) { + for (unsigned j = 0; j < conds.size(); j++) { + XLOG_IF(DBG1, conds[i] && conds[j], "testing conditional"); + EXPECT_EQ((conds[i] && conds[j]) ? 1 : 0, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, conds[i] || conds[j], "testing conditional"); + EXPECT_EQ((conds[i] || conds[j]) ? 1 : 0, messages.size()); + messages.clear(); + } + } + + XLOG_IF(DBG1, 0x6 & 0x2, "More conditional 1"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, 0x6 | 0x2, "More conditional 2"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3"); + EXPECT_EQ(1, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, 0x3 & 0x4 ? true : false, "More conditional 4"); + EXPECT_EQ(0, messages.size()); + messages.clear(); + + XLOG_IF(DBG1, false ? true : false, "More conditional 5"); + EXPECT_EQ(0, messages.size()); + messages.clear(); + + XLOGF_IF(DBG1, false, "number: {:>3d}; string: {}", 12, "foo"); + ASSERT_EQ(0, messages.size()); + messages.clear(); + XLOGF_IF(DBG1, true, "number: {:>3d}; string: {}", 12, "foo"); + ASSERT_EQ(1, messages.size()); + messages.clear(); +} + TEST(Xlog, xlog) { auto handler = make_shared<TestLogHandler>(); LoggerDB::get().getCategory("xlog_test")->addHandler(handler); diff --git a/folly/logging/xlog.h b/folly/logging/xlog.h index a101a4e45ae2997f681294c64b2caec8de7481b9..7080a34e58a15e62b2863c168cf6cf528198ac09 100644 --- a/folly/logging/xlog.h +++ b/folly/logging/xlog.h @@ -53,21 +53,37 @@ * best if you always invoke the compiler from the root directory of your * project repository. */ -#define XLOG(level, ...) \ +#define XLOG(level, ...) XLOG_IF(level, true, ##__VA_ARGS__) + +/** + * Log a message if and only if the specified condition predicate evaluates + * to true. Note that the condition is *only* evaluated if the log-level check + * passes. + */ +#define XLOG_IF(level, cond, ...) \ XLOG_IMPL( \ ::folly::LogLevel::level, \ + cond, \ ::folly::LogStreamProcessor::APPEND, \ ##__VA_ARGS__) - /** * Log a message to this file's default log category, using a format string. */ -#define XLOGF(level, fmt, arg1, ...) \ - XLOG_IMPL( \ - ::folly::LogLevel::level, \ - ::folly::LogStreamProcessor::FORMAT, \ - fmt, \ - arg1, \ +#define XLOGF(level, fmt, arg1, ...) \ + XLOGF_IF(level, true, fmt, arg1, ##__VA_ARGS__) + +/** + * Log a message using a format string if and only if the specified condition + * predicate evaluates to true. Note that the condition is *only* evaluated + * if the log-level check passes. + */ +#define XLOGF_IF(level, cond, fmt, arg1, ...) \ + XLOG_IMPL( \ + ::folly::LogLevel::level, \ + cond, \ + ::folly::LogStreamProcessor::FORMAT, \ + fmt, \ + arg1, \ ##__VA_ARGS__) /** @@ -132,8 +148,8 @@ * skipped with just a single check of the LogLevel. */ /* clang-format off */ -#define XLOG_IMPL(level, type, ...) \ - (!XLOG_IS_ON_IMPL(level)) \ +#define XLOG_IMPL(level, cond, type, ...) \ + (!XLOG_IS_ON_IMPL(level) || !(cond)) \ ? ::folly::logDisabledHelper( \ std::integral_constant<bool, ::folly::isLogLevelFatal(level)>{}) \ : ::folly::LogStreamVoidify< ::folly::isLogLevelFatal(level)>{} & \