// LogHandler destructors.
}
+std::vector<std::shared_ptr<LogHandler>> LogCategory::getHandlers() const {
+ return *(handlers_.rlock());
+}
+
void LogCategory::setLevel(LogLevel level, bool inherit) {
// We have to set the level through LoggerDB, since we require holding
// the LoggerDB lock to iterate through our children in case our effective
*/
void clearHandlers();
+ /**
+ * Get the list of LogHandlers attached to this category.
+ */
+ std::vector<std::shared_ptr<LogHandler>> getHandlers() const;
+
/* Internal methods for use by other parts of the logging library code */
/**
*/
#include <folly/experimental/logging/LoggerDB.h>
+#include <set>
+
#include <folly/Conv.h>
#include <folly/FileUtil.h>
#include <folly/String.h>
#include <folly/experimental/logging/LogCategory.h>
+#include <folly/experimental/logging/LogHandler.h>
#include <folly/experimental/logging/LogLevel.h>
#include <folly/experimental/logging/Logger.h>
#include <folly/experimental/logging/RateLimiter.h>
//
// However, we do call db_->cleanupHandlers() to destroy any registered
// LogHandler objects. The LogHandlers can be user-defined objects and may
- // hold resources that should be cleaned up.
+ // hold resources that should be cleaned up. This also ensures that the
+ // LogHandlers flush all outstanding messages before we exit.
db_->cleanupHandlers();
}
}
}
+void LoggerDB::flushAllHandlers() {
+ // Build a set of all LogHandlers. We use a set to avoid calling flush()
+ // more than once on the same handler if it is registered on multiple
+ // different categories.
+ std::set<std::shared_ptr<LogHandler>> handlers;
+ {
+ auto loggersByName = loggersByName_.wlock();
+ for (const auto& entry : *loggersByName) {
+ for (const auto& handler : entry.second->getHandlers()) {
+ handlers.emplace(handler);
+ }
+ }
+ }
+
+ // Call flush() on each handler
+ for (const auto& handler : handlers) {
+ handler->flush();
+ }
+}
+
LogLevel LoggerDB::xlogInit(
StringPiece categoryName,
std::atomic<LogLevel>* xlogCategoryLevel,
*/
void cleanupHandlers();
+ /**
+ * Call flush() on all LogHandler objects registered on any LogCategory in
+ * this LoggerDB.
+ */
+ void flushAllHandlers();
+
/**
* Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
* statement.
*/
#include <folly/experimental/logging/Logger.h>
#include <folly/experimental/logging/LoggerDB.h>
+#include <folly/experimental/logging/test/TestLogHandler.h>
#include <folly/portability/GTest.h>
using namespace folly;
LoggerDB db{LoggerDB::TESTING};
}
+TEST(LoggerDB, flushAllHandlers) {
+ LoggerDB db{LoggerDB::TESTING};
+ auto* cat1 = db.getCategory("foo");
+ auto* cat2 = db.getCategory("foo.bar.test");
+ auto* cat3 = db.getCategory("hello.world");
+ auto* cat4 = db.getCategory("other.category");
+
+ auto h1 = std::make_shared<TestLogHandler>();
+ auto h2 = std::make_shared<TestLogHandler>();
+ auto h3 = std::make_shared<TestLogHandler>();
+
+ cat1->addHandler(h1);
+
+ cat2->addHandler(h2);
+ cat2->addHandler(h3);
+
+ cat3->addHandler(h1);
+ cat3->addHandler(h2);
+ cat3->addHandler(h3);
+
+ cat4->addHandler(h1);
+
+ EXPECT_EQ(0, h1->getFlushCount());
+ EXPECT_EQ(0, h2->getFlushCount());
+ EXPECT_EQ(0, h3->getFlushCount());
+
+ // Calling flushAllHandlers() should only flush each handler once,
+ // even when they are attached to multiple categories.
+ db.flushAllHandlers();
+ EXPECT_EQ(1, h1->getFlushCount());
+ EXPECT_EQ(1, h2->getFlushCount());
+ EXPECT_EQ(1, h3->getFlushCount());
+
+ db.flushAllHandlers();
+ EXPECT_EQ(2, h1->getFlushCount());
+ EXPECT_EQ(2, h2->getFlushCount());
+ EXPECT_EQ(2, h3->getFlushCount());
+}
+
TEST(LoggerDB, processConfigString) {
LoggerDB db{LoggerDB::TESTING};
db.processConfigString("foo.bar=dbg5");
/**
* A LogHandler that simply keeps a vector of all LogMessages it receives.
+ *
+ * This class is not thread-safe. It is intended to be used in single-threaded
+ * tests.
*/
class TestLogHandler : public LogHandler {
public:
messages_.emplace_back(message, handlerCategory);
}
- void flush() override {}
+ void flush() override {
+ ++flushCount_;
+ }
+
+ uint64_t getFlushCount() const {
+ return flushCount_;
+ }
private:
std::vector<std::pair<LogMessage, const LogCategory*>> messages_;
+ uint64_t flushCount_{0};
};
}