From a478d52fdeed1c3a1342615ecdc30e77ac1d9af9 Mon Sep 17 00:00:00 2001 From: Murali Vilayannur <muralivn@fb.com> Date: Tue, 8 May 2018 01:10:30 -0700 Subject: [PATCH] Add a LOG_IF equivalent to folly's logging library Summary: Allows for conditional logging of messages. Reviewed By: simpkins Differential Revision: D7888834 fbshipit-source-id: d1fe47d82fe8889f6b597062f5542e7eb61ed9e6 --- folly/logging/printf.h | 10 ++++- folly/logging/test/PrintfTest.cpp | 70 ++++++++++++++++++++++++++++++ folly/logging/test/XlogTest.cpp | 72 +++++++++++++++++++++++++++++++ folly/logging/xlog.h | 36 +++++++++++----- 4 files changed, 177 insertions(+), 11 deletions(-) diff --git a/folly/logging/printf.h b/folly/logging/printf.h index 9ba34b290..3a0b6b47c 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 785a85bc9..423c95711 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 89a003d62..2206d9ca2 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 a101a4e45..7080a34e5 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)>{} & \ -- 2.26.2