Commit a5766338 authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot

logging: add LoggerDB::flushAllHandlers()

Summary:
Add a method to flush all LogHandler objects.

This will be necessary to implement FB_LOG(FATAL), as we will want to flush all
outstanding messages before crashing.

Reviewed By: wez

Differential Revision: D5189501

fbshipit-source-id: faf260b8e71e5dfed4a3b1c1aee32f072bd7b764
parent d65439dd
......@@ -112,6 +112,10 @@ void LogCategory::clearHandlers() {
// 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
......
......@@ -150,6 +150,11 @@ class LogCategory {
*/
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 */
/**
......
......@@ -15,10 +15,13 @@
*/
#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>
......@@ -40,7 +43,8 @@ class LoggerDBSingleton {
//
// 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();
}
......@@ -174,6 +178,26 @@ void LoggerDB::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,
......
......@@ -91,6 +91,12 @@ class LoggerDB {
*/
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.
......
......@@ -15,6 +15,7 @@
*/
#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;
......@@ -34,6 +35,45 @@ TEST(LoggerDB, getCategory) {
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");
......
......@@ -25,6 +25,9 @@ namespace folly {
/**
* 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:
......@@ -38,9 +41,16 @@ class TestLogHandler : public LogHandler {
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};
};
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment