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() { ...@@ -112,6 +112,10 @@ void LogCategory::clearHandlers() {
// LogHandler destructors. // LogHandler destructors.
} }
std::vector<std::shared_ptr<LogHandler>> LogCategory::getHandlers() const {
return *(handlers_.rlock());
}
void LogCategory::setLevel(LogLevel level, bool inherit) { void LogCategory::setLevel(LogLevel level, bool inherit) {
// We have to set the level through LoggerDB, since we require holding // We have to set the level through LoggerDB, since we require holding
// the LoggerDB lock to iterate through our children in case our effective // the LoggerDB lock to iterate through our children in case our effective
......
...@@ -150,6 +150,11 @@ class LogCategory { ...@@ -150,6 +150,11 @@ class LogCategory {
*/ */
void clearHandlers(); 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 */ /* Internal methods for use by other parts of the logging library code */
/** /**
......
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
*/ */
#include <folly/experimental/logging/LoggerDB.h> #include <folly/experimental/logging/LoggerDB.h>
#include <set>
#include <folly/Conv.h> #include <folly/Conv.h>
#include <folly/FileUtil.h> #include <folly/FileUtil.h>
#include <folly/String.h> #include <folly/String.h>
#include <folly/experimental/logging/LogCategory.h> #include <folly/experimental/logging/LogCategory.h>
#include <folly/experimental/logging/LogHandler.h>
#include <folly/experimental/logging/LogLevel.h> #include <folly/experimental/logging/LogLevel.h>
#include <folly/experimental/logging/Logger.h> #include <folly/experimental/logging/Logger.h>
#include <folly/experimental/logging/RateLimiter.h> #include <folly/experimental/logging/RateLimiter.h>
...@@ -40,7 +43,8 @@ class LoggerDBSingleton { ...@@ -40,7 +43,8 @@ class LoggerDBSingleton {
// //
// However, we do call db_->cleanupHandlers() to destroy any registered // However, we do call db_->cleanupHandlers() to destroy any registered
// LogHandler objects. The LogHandlers can be user-defined objects and may // 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(); db_->cleanupHandlers();
} }
...@@ -174,6 +178,26 @@ void LoggerDB::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( LogLevel LoggerDB::xlogInit(
StringPiece categoryName, StringPiece categoryName,
std::atomic<LogLevel>* xlogCategoryLevel, std::atomic<LogLevel>* xlogCategoryLevel,
......
...@@ -91,6 +91,12 @@ class LoggerDB { ...@@ -91,6 +91,12 @@ class LoggerDB {
*/ */
void cleanupHandlers(); 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() * Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
* statement. * statement.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
#include <folly/experimental/logging/Logger.h> #include <folly/experimental/logging/Logger.h>
#include <folly/experimental/logging/LoggerDB.h> #include <folly/experimental/logging/LoggerDB.h>
#include <folly/experimental/logging/test/TestLogHandler.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
using namespace folly; using namespace folly;
...@@ -34,6 +35,45 @@ TEST(LoggerDB, getCategory) { ...@@ -34,6 +35,45 @@ TEST(LoggerDB, getCategory) {
LoggerDB db{LoggerDB::TESTING}; 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) { TEST(LoggerDB, processConfigString) {
LoggerDB db{LoggerDB::TESTING}; LoggerDB db{LoggerDB::TESTING};
db.processConfigString("foo.bar=dbg5"); db.processConfigString("foo.bar=dbg5");
......
...@@ -25,6 +25,9 @@ namespace folly { ...@@ -25,6 +25,9 @@ namespace folly {
/** /**
* A LogHandler that simply keeps a vector of all LogMessages it receives. * 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 { class TestLogHandler : public LogHandler {
public: public:
...@@ -38,9 +41,16 @@ class TestLogHandler : public LogHandler { ...@@ -38,9 +41,16 @@ class TestLogHandler : public LogHandler {
messages_.emplace_back(message, handlerCategory); messages_.emplace_back(message, handlerCategory);
} }
void flush() override {} void flush() override {
++flushCount_;
}
uint64_t getFlushCount() const {
return flushCount_;
}
private: private:
std::vector<std::pair<LogMessage, const LogCategory*>> messages_; 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