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

logging: add XLOG() and XLOGF() logging macros

Summary:
This adds new `XLOG()` and `XLOGF()` macros to the logging library.  These are
similar to `FB_LOG()` and `FB_LOGF()`, but do not require a Logger argument.
Instead, the log category is picked automatically based on the current file
name.  The basic algorithm for picking the category name is to replace
directory separators with '.', and to strip off the filename extension.

For instance, all `XLOG()` statements in `src/foo/bar.cpp` will log to the
category `src.foo.bar`.  This also works correctly in header files: `XLOG()`
statements in `src/foo/mylib.h` will log to `src.foo.mylib`

This should generally result in a good log category hierarchy without the user
having to spend additional time picking category names--we simply re-use the
decisions that they already made for their directory layout.

In general I expect the `XLOG()` macros to be convenient enough that users will
use `XLOG()` in almost all cases rather than using `FB_LOG()`.

The log category name used by `XLOG()` statements can be overridden using
`XLOG_SET_CATEGORY()`, but this only works in .cpp files, not in header files.

Reviewed By: wez

Differential Revision: D4920257

fbshipit-source-id: 7ffafd9a4c87e6fb5eb35d86e0eb86ef1ed5be95
parent 1d83c51f
...@@ -317,6 +317,8 @@ if (BUILD_TESTS) ...@@ -317,6 +317,8 @@ if (BUILD_TESTS)
TEST logging-test TEST logging-test
HEADERS HEADERS
TestLogHandler.h TestLogHandler.h
XlogHeader1.h
XlogHeader2.h
SOURCES SOURCES
LogCategoryTest.cpp LogCategoryTest.cpp
LoggerDBTest.cpp LoggerDBTest.cpp
...@@ -325,6 +327,9 @@ if (BUILD_TESTS) ...@@ -325,6 +327,9 @@ if (BUILD_TESTS)
LogMessageTest.cpp LogMessageTest.cpp
LogNameTest.cpp LogNameTest.cpp
LogStreamTest.cpp LogStreamTest.cpp
XlogFile1.cpp
XlogFile2.cpp
XlogTest.cpp
DIRECTORY fibers/test/ DIRECTORY fibers/test/
TEST fibers_test SOURCES FibersTest.cpp TEST fibers_test SOURCES FibersTest.cpp
......
...@@ -130,6 +130,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -130,6 +130,7 @@ nobase_follyinclude_HEADERS = \
experimental/logging/LogStreamProcessor.h \ experimental/logging/LogStreamProcessor.h \
experimental/logging/Logger.h \ experimental/logging/Logger.h \
experimental/logging/LoggerDB.h \ experimental/logging/LoggerDB.h \
experimental/logging/xlog.h \
experimental/NestedCommandLineApp.h \ experimental/NestedCommandLineApp.h \
experimental/observer/detail/Core.h \ experimental/observer/detail/Core.h \
experimental/observer/detail/GraphCycleDetector.h \ experimental/observer/detail/GraphCycleDetector.h \
......
...@@ -121,11 +121,16 @@ void LogCategory::setLevel(LogLevel level, bool inherit) { ...@@ -121,11 +121,16 @@ void LogCategory::setLevel(LogLevel level, bool inherit) {
} }
void LogCategory::setLevelLocked(LogLevel level, bool inherit) { void LogCategory::setLevelLocked(LogLevel level, bool inherit) {
// Truncate to LogLevel::MAX_LEVEL to make sure it does not conflict // Clamp the value to MIN_LEVEL and MAX_LEVEL.
// with our flag bits. //
// This makes sure that UNINITIALIZED is always less than any valid level
// value, and that level values cannot conflict with our flag bits.
if (level > LogLevel::MAX_LEVEL) { if (level > LogLevel::MAX_LEVEL) {
level = LogLevel::MAX_LEVEL; level = LogLevel::MAX_LEVEL;
} else if (level < LogLevel::MIN_LEVEL) {
level = LogLevel::MIN_LEVEL;
} }
// Make sure the inherit flag is always off for the root logger. // Make sure the inherit flag is always off for the root logger.
if (!parent_) { if (!parent_) {
inherit = false; inherit = false;
...@@ -161,6 +166,11 @@ void LogCategory::updateEffectiveLevel(LogLevel newEffectiveLevel) { ...@@ -161,6 +166,11 @@ void LogCategory::updateEffectiveLevel(LogLevel newEffectiveLevel) {
return; return;
} }
// Update all of the values in xlogLevels_
for (auto* levelPtr : xlogLevels_) {
levelPtr->store(newEffectiveLevel, std::memory_order_release);
}
// Update all children loggers // Update all children loggers
LogCategory* child = firstChild_; LogCategory* child = firstChild_;
while (child != nullptr) { while (child != nullptr) {
...@@ -180,4 +190,8 @@ void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) { ...@@ -180,4 +190,8 @@ void LogCategory::parentLevelUpdated(LogLevel parentEffectiveLevel) {
auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel); auto newEffectiveLevel = std::min(myLevel, parentEffectiveLevel);
updateEffectiveLevel(newEffectiveLevel); updateEffectiveLevel(newEffectiveLevel);
} }
void LogCategory::registerXlogLevel(std::atomic<LogLevel>* levelPtr) {
xlogLevels_.push_back(levelPtr);
}
} }
...@@ -129,19 +129,6 @@ class LogCategory { ...@@ -129,19 +129,6 @@ class LogCategory {
*/ */
void setLevel(LogLevel level, bool inherit = true); void setLevel(LogLevel level, bool inherit = true);
/**
* Process a log message.
*
* This method generally should be invoked through the Logger APIs,
* rather than calling this directly.
*
* This method assumes that log level admittance checks have already been
* performed. This method unconditionally passes the message to the
* LogHandlers attached to this LogCategory, without any additional log level
* checks (apart from the ones done in the LogHandlers).
*/
void processMessage(const LogMessage& message) const;
/** /**
* Get the LoggerDB that this LogCategory belongs to. * Get the LoggerDB that this LogCategory belongs to.
* *
...@@ -163,6 +150,21 @@ class LogCategory { ...@@ -163,6 +150,21 @@ class LogCategory {
*/ */
void clearHandlers(); void clearHandlers();
/* Internal methods for use by other parts of the logging library code */
/**
* Process a log message.
*
* This method generally should be invoked only through the logging macros,
* rather than calling this directly.
*
* This method assumes that log level admittance checks have already been
* performed. This method unconditionally passes the message to the
* LogHandlers attached to this LogCategory, without any additional log level
* checks (apart from the ones done in the LogHandlers).
*/
void processMessage(const LogMessage& message) const;
/** /**
* Note: setLevelLocked() may only be called while holding the main * Note: setLevelLocked() may only be called while holding the main
* LoggerDB lock. * LoggerDB lock.
...@@ -171,6 +173,18 @@ class LogCategory { ...@@ -171,6 +173,18 @@ class LogCategory {
*/ */
void setLevelLocked(LogLevel level, bool inherit); void setLevelLocked(LogLevel level, bool inherit);
/**
* Register a std::atomic<LogLevel> value used by XLOG*() macros to check the
* effective level for this category.
*
* The LogCategory will keep this value updated whenever its effective log
* level changes.
*
* This function should only be invoked by LoggerDB, and the LoggerDB lock
* must be held when calling it.
*/
void registerXlogLevel(std::atomic<LogLevel>* levelPtr);
private: private:
enum : uint32_t { FLAG_INHERIT = 0x80000000 }; enum : uint32_t { FLAG_INHERIT = 0x80000000 };
...@@ -228,5 +242,14 @@ class LogCategory { ...@@ -228,5 +242,14 @@ class LogCategory {
*/ */
LogCategory* firstChild_{nullptr}; LogCategory* firstChild_{nullptr};
LogCategory* nextSibling_{nullptr}; LogCategory* nextSibling_{nullptr};
/**
* A list of LogLevel values used by XLOG*() statements for this LogCategory.
* The XLOG*() statements will check these values. We ensure they are kept
* up-to-date each time the effective log level changes for this category.
*
* This list may only be accessed while holding the main LoggerDB lock.
*/
std::vector<std::atomic<LogLevel>*> xlogLevels_;
}; };
} }
...@@ -45,7 +45,9 @@ LogLevel stringToLogLevel(StringPiece name) { ...@@ -45,7 +45,9 @@ LogLevel stringToLogLevel(StringPiece name) {
lowerName.subtract(1); lowerName.subtract(1);
} }
if (lowerName == "none") { if (lowerName == "uninitialized") {
return LogLevel::UNINITIALIZED;
} else if (lowerName == "none") {
return LogLevel::NONE; return LogLevel::NONE;
} else if (lowerName == "debug") { } else if (lowerName == "debug") {
return LogLevel::DEBUG; return LogLevel::DEBUG;
...@@ -80,7 +82,9 @@ LogLevel stringToLogLevel(StringPiece name) { ...@@ -80,7 +82,9 @@ LogLevel stringToLogLevel(StringPiece name) {
} }
string logLevelToString(LogLevel level) { string logLevelToString(LogLevel level) {
if (level == LogLevel::NONE) { if (level == LogLevel::UNINITIALIZED) {
return "LogLevel::UNINITIALIZED";
} else if (level == LogLevel::NONE) {
return "LogLevel::NONE"; return "LogLevel::NONE";
} else if (level == LogLevel::DEBUG) { } else if (level == LogLevel::DEBUG) {
return "LogLevel::DEBUG"; return "LogLevel::DEBUG";
......
...@@ -34,7 +34,9 @@ namespace folly { ...@@ -34,7 +34,9 @@ namespace folly {
* DBG1 is one level higher of verbosity, etc. * DBG1 is one level higher of verbosity, etc.
*/ */
enum class LogLevel : uint32_t { enum class LogLevel : uint32_t {
NONE = 0, UNINITIALIZED = 0,
NONE = 1,
MIN_LEVEL = 1,
DEBUG = 900, DEBUG = 900,
DBG0 = 1000, DBG0 = 1000,
......
...@@ -170,4 +170,49 @@ void LoggerDB::cleanupHandlers() { ...@@ -170,4 +170,49 @@ void LoggerDB::cleanupHandlers() {
category->clearHandlers(); category->clearHandlers();
} }
} }
LogLevel LoggerDB::xlogInit(
StringPiece categoryName,
std::atomic<LogLevel>* xlogCategoryLevel,
LogCategory** xlogCategory) {
// Hold the lock for the duration of the operation
// xlogInit() may be called from multiple threads simultaneously.
// Only one needs to perform the initialization.
auto loggersByName = loggersByName_.wlock();
if (xlogCategory != nullptr && *xlogCategory != nullptr) {
// The xlogCategory was already initialized before we acquired the lock
return (*xlogCategory)->getEffectiveLevel();
}
auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
if (xlogCategory) {
// Set *xlogCategory before we update xlogCategoryLevel below.
// This is important, since the XLOG() macros check xlogCategoryLevel to
// tell if *xlogCategory has been initialized yet.
*xlogCategory = category;
}
auto level = category->getEffectiveLevel();
xlogCategoryLevel->store(level, std::memory_order_release);
category->registerXlogLevel(xlogCategoryLevel);
return level;
}
LogCategory* LoggerDB::xlogInitCategory(
StringPiece categoryName,
LogCategory** xlogCategory,
std::atomic<bool>* isInitialized) {
// Hold the lock for the duration of the operation
// xlogInitCategory() may be called from multiple threads simultaneously.
// Only one needs to perform the initialization.
auto loggersByName = loggersByName_.wlock();
if (isInitialized->load(std::memory_order_acquire)) {
// The xlogCategory was already initialized before we acquired the lock
return *xlogCategory;
}
auto* category = getOrCreateCategoryLocked(*loggersByName, categoryName);
*xlogCategory = category;
isInitialized->store(true, std::memory_order_release);
return category;
}
} }
...@@ -90,6 +90,21 @@ class LoggerDB { ...@@ -90,6 +90,21 @@ class LoggerDB {
*/ */
void cleanupHandlers(); void cleanupHandlers();
/**
* Initialize the LogCategory* and std::atomic<LogLevel> used by an XLOG()
* statement.
*
* Returns the current effective LogLevel of the category.
*/
LogLevel xlogInit(
folly::StringPiece categoryName,
std::atomic<LogLevel>* xlogCategoryLevel,
LogCategory** xlogCategory);
LogCategory* xlogInitCategory(
folly::StringPiece categoryName,
LogCategory** xlogCategory,
std::atomic<bool>* isInitialized);
enum TestConstructorArg { TESTING }; enum TestConstructorArg { TESTING };
/** /**
......
...@@ -11,7 +11,8 @@ libfollylogging_la_SOURCES = \ ...@@ -11,7 +11,8 @@ libfollylogging_la_SOURCES = \
LogMessage.cpp \ LogMessage.cpp \
LogName.cpp \ LogName.cpp \
LogStream.cpp \ LogStream.cpp \
LogStreamProcessor.cpp LogStreamProcessor.cpp \
xlog.cpp
libfollylogging_la_LIBADD = $(top_builddir)/libfolly.la libfollylogging_la_LIBADD = $(top_builddir)/libfolly.la
libfollylogging_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LT_VERSION) libfollylogging_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LT_VERSION)
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
using namespace folly; using namespace folly;
TEST(LogLevel, fromString) { TEST(LogLevel, fromString) {
EXPECT_EQ(LogLevel::UNINITIALIZED, stringToLogLevel("uninitialized"));
EXPECT_EQ(LogLevel::UNINITIALIZED, stringToLogLevel("UnInitialized"));
EXPECT_EQ(
LogLevel::UNINITIALIZED, stringToLogLevel("LogLevel::UNINITIALIZED"));
EXPECT_EQ(LogLevel::NONE, stringToLogLevel("none")); EXPECT_EQ(LogLevel::NONE, stringToLogLevel("none"));
EXPECT_EQ(LogLevel::NONE, stringToLogLevel("NONE")); EXPECT_EQ(LogLevel::NONE, stringToLogLevel("NONE"));
EXPECT_EQ(LogLevel::NONE, stringToLogLevel("NoNe")); EXPECT_EQ(LogLevel::NONE, stringToLogLevel("NoNe"));
...@@ -68,6 +73,8 @@ TEST(LogLevel, fromString) { ...@@ -68,6 +73,8 @@ TEST(LogLevel, fromString) {
} }
TEST(LogLevel, toString) { TEST(LogLevel, toString) {
EXPECT_EQ(
"LogLevel::UNINITIALIZED", logLevelToString(LogLevel::UNINITIALIZED));
EXPECT_EQ("LogLevel::NONE", logLevelToString(LogLevel::NONE)); EXPECT_EQ("LogLevel::NONE", logLevelToString(LogLevel::NONE));
EXPECT_EQ("LogLevel::INFO", logLevelToString(LogLevel::INFO)); EXPECT_EQ("LogLevel::INFO", logLevelToString(LogLevel::INFO));
EXPECT_EQ("LogLevel::WARN", logLevelToString(LogLevel::WARN)); EXPECT_EQ("LogLevel::WARN", logLevelToString(LogLevel::WARN));
...@@ -98,6 +105,7 @@ TEST(LogLevel, toStringAndBack) { ...@@ -98,6 +105,7 @@ TEST(LogLevel, toStringAndBack) {
}; };
// Check all of the named levels // Check all of the named levels
checkLevel(LogLevel::UNINITIALIZED);
checkLevel(LogLevel::NONE); checkLevel(LogLevel::NONE);
checkLevel(LogLevel::DEBUG); checkLevel(LogLevel::DEBUG);
checkLevel(LogLevel::DBG0); checkLevel(LogLevel::DBG0);
......
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/logging/test/XlogHeader1.h>
#include <folly/experimental/logging/xlog.h>
namespace logging_test {
void testXlogFile1Dbg1(folly::StringPiece msg) {
XLOG(DBG1, "file1: ", msg);
}
}
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/logging/test/XlogHeader1.h>
#include <folly/experimental/logging/xlog.h>
namespace logging_test {
void testXlogFile2Dbg1(folly::StringPiece msg) {
XLOG(DBG1, "file2: ", msg);
}
}
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/Range.h>
#include <folly/experimental/logging/xlog.h>
namespace logging_test {
// A sample functions that uses XLOGF() macros in a header file.
inline void testXlogHdrLoop(size_t numIters, folly::StringPiece arg) {
XLOGF(DBG1, "starting: {}", arg);
for (size_t n = 0; n < numIters; ++n) {
XLOGF(DBG5, "test: {}", arg);
}
XLOGF(DBG1, "finished: {}", arg);
}
// Prototypes for functions defined in XlogFile1.cpp and XlogFile2.cpp
void testXlogFile1Dbg1(folly::StringPiece msg);
void testXlogFile2Dbg1(folly::StringPiece msg);
}
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/Range.h>
#include <folly/experimental/logging/xlog.h>
namespace logging_test {
inline void testXlogHdrFunction(folly::StringPiece str, int value) {
XLOG(DBG3, "test: ", str, "=", value);
}
}
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/logging/LogCategory.h>
#include <folly/experimental/logging/LogHandler.h>
#include <folly/experimental/logging/LogMessage.h>
#include <folly/experimental/logging/LoggerDB.h>
#include <folly/experimental/logging/test/TestLogHandler.h>
#include <folly/experimental/logging/test/XlogHeader1.h>
#include <folly/experimental/logging/test/XlogHeader2.h>
#include <folly/experimental/logging/xlog.h>
#include <folly/portability/GTest.h>
using namespace folly;
using std::make_shared;
XLOG_SET_CATEGORY("xlog_test.main_file");
// Note that the XLOG* macros always use the main LoggerDB singleton.
// There is no way to get them to use a test LoggerDB during unit tests.
//
// Therefore any configuration we do here affects the main log category
// settings for the entire program. Fortunately all of the other unit tests do
// use testing LoggerDB objects.
TEST(Xlog, xlog) {
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(INFO, "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(DBG1, "testing: ", 1, 2, 3);
ASSERT_EQ(1, messages.size());
EXPECT_EQ("testing: 123", messages[0].first.getMessage());
EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
<< "unexpected file name: " << messages[0].first.getFileName();
EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
EXPECT_EQ("xlog_test", messages[0].second->getName());
messages.clear();
XLOGF(WARN, "number: {:>3d}; string: {}", 12, "foo");
ASSERT_EQ(1, messages.size());
EXPECT_EQ("number: 12; string: foo", messages[0].first.getMessage());
EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
<< "unexpected file name: " << messages[0].first.getFileName();
EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
EXPECT_EQ("xlog_test", messages[0].second->getName());
messages.clear();
XLOG(DBG2, "this log check should not pass");
EXPECT_EQ(0, messages.size());
messages.clear();
// Test stream arguments to XLOG()
XLOG(INFO) << "stream test: " << 1 << ", two, " << 3;
ASSERT_EQ(1, messages.size());
EXPECT_EQ("stream test: 1, two, 3", messages[0].first.getMessage());
EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogTest.cpp"))
<< "unexpected file name: " << messages[0].first.getFileName();
EXPECT_EQ(LogLevel::INFO, messages[0].first.getLevel());
EXPECT_EQ("xlog_test.main_file", messages[0].first.getCategory()->getName());
EXPECT_EQ("xlog_test", messages[0].second->getName());
messages.clear();
}
TEST(Xlog, perFileCategoryHandling) {
using namespace logging_test;
auto handler = make_shared<TestLogHandler>();
LoggerDB::get()
->getCategory("folly.experimental.logging.test")
->addHandler(handler);
LoggerDB::get()->setLevel("folly.experimental.logging.test", LogLevel::DBG9);
auto& messages = handler->getMessages();
// Use the simple helper function in XlogHeader2
testXlogHdrFunction("factor", 99);
ASSERT_EQ(1, messages.size());
EXPECT_EQ("test: factor=99", messages[0].first.getMessage());
EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogHeader2.h"))
<< "unexpected file name: " << messages[0].first.getFileName();
EXPECT_EQ(LogLevel::DBG3, messages[0].first.getLevel());
EXPECT_EQ(
"folly.experimental.logging.test.XlogHeader2",
messages[0].first.getCategory()->getName());
EXPECT_EQ("folly.experimental.logging.test", messages[0].second->getName());
messages.clear();
// Test the loop function from XlogHeader1
testXlogHdrLoop(3, "hello world");
ASSERT_EQ(5, messages.size());
EXPECT_EQ("starting: hello world", messages[0].first.getMessage());
EXPECT_TRUE(messages[0].first.getFileName().endsWith("XlogHeader1.h"))
<< "unexpected file name: " << messages[0].first.getFileName();
EXPECT_EQ(LogLevel::DBG1, messages[0].first.getLevel());
EXPECT_EQ(
"folly.experimental.logging.test.XlogHeader1",
messages[0].first.getCategory()->getName());
EXPECT_EQ("folly.experimental.logging.test", messages[0].second->getName());
EXPECT_EQ("test: hello world", messages[1].first.getMessage());
EXPECT_EQ("test: hello world", messages[2].first.getMessage());
EXPECT_EQ("test: hello world", messages[3].first.getMessage());
EXPECT_EQ("finished: hello world", messages[4].first.getMessage());
EXPECT_EQ(LogLevel::DBG5, messages[1].first.getLevel());
EXPECT_EQ(LogLevel::DBG5, messages[2].first.getLevel());
EXPECT_EQ(LogLevel::DBG5, messages[3].first.getLevel());
EXPECT_EQ(LogLevel::DBG1, messages[4].first.getLevel());
messages.clear();
// Reduce the log level so that the messages inside the loop
// should not be logged.
LoggerDB::get()->setLevel("folly.experimental.logging.test", LogLevel::DBG2);
testXlogHdrLoop(300, "hello world");
ASSERT_EQ(2, messages.size());
EXPECT_EQ("starting: hello world", messages[0].first.getMessage());
EXPECT_EQ("finished: hello world", messages[1].first.getMessage());
messages.clear();
// Call the helpers function in XlogFile1.cpp and XlogFile2.cpp and makes
// sure their categories are reported correctly.
testXlogFile1Dbg1("foobar 1234");
ASSERT_EQ(1, messages.size());
EXPECT_EQ("file1: foobar 1234", messages[0].first.getMessage());
EXPECT_EQ(
"folly.experimental.logging.test.XlogFile1",
messages[0].first.getCategory()->getName());
messages.clear();
testXlogFile2Dbg1("hello world");
ASSERT_EQ(1, messages.size());
EXPECT_EQ("file2: hello world", messages[0].first.getMessage());
EXPECT_EQ(
"folly.experimental.logging.test.XlogFile2",
messages[0].first.getCategory()->getName());
messages.clear();
// Adjust the log level and make sure the changes take effect for the .cpp
// file categories
LoggerDB::get()->setLevel("folly.experimental.logging.test", LogLevel::INFO);
testXlogFile1Dbg1("log check should fail now");
testXlogFile2Dbg1("this should fail too");
EXPECT_EQ(0, messages.size());
messages.clear();
LoggerDB::get()->setLevel(
"folly.experimental.logging.test.XlogFile1", LogLevel::DBG1);
testXlogFile1Dbg1("this log check should pass now");
testXlogFile2Dbg1("but this one should still fail");
ASSERT_EQ(1, messages.size());
EXPECT_EQ(
"file1: this log check should pass now", messages[0].first.getMessage());
EXPECT_EQ(
"folly.experimental.logging.test.XlogFile1",
messages[0].first.getCategory()->getName());
messages.clear();
}
/*
* Copyright 2004-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/experimental/logging/xlog.h>
#include <folly/Synchronized.h>
using folly::StringPiece;
namespace folly {
namespace {
/**
* buck copies header files from their original location in the source tree
* and places them under buck-out/ with a path like
* buck-out/<rule-name-components>/<original-path>
*
* We want to strip off the buck-out/<rule-name-components> portion,
* so that the filename we use is just the original path in the source tree.
*
* The <rule-name-component> section should always end in a path component that
* includes a '#': it's format is <rule-name>#<parameters>, where <parameters>
* is a comma separated list that never includes '/'.
*
* Search for the first path component with a '#', and strip off everything up
* to this component.
*/
StringPiece stripBuckOutPrefix(StringPiece filename) {
size_t idx = 0;
while (true) {
auto end = filename.find('/', idx);
if (end == StringPiece::npos) {
// We were unable to find where the buck-out prefix should end.
return filename;
}
auto component = filename.subpiece(idx, end - idx);
if (component.find('#') != StringPiece::npos) {
return filename.subpiece(end + 1);
}
idx = end + 1;
}
}
} // unnamed namespace
std::string getXlogCategoryNameForFile(StringPiece filename) {
// Buck mangles the directory layout for header files. Rather than including
// them from their original location, it moves them into deep directories
// inside buck-out, and includes them from there.
//
// If this path looks like a buck header directory, try to strip off the
// buck-specific portion.
if (filename.startsWith("buck-out/")) {
filename = stripBuckOutPrefix(filename);
}
std::string categoryName = filename.str();
// Translate slashes to dots, to turn the directory layout into
// a category hierarchy.
size_t lastDot = std::string::npos;
for (size_t n = 0; n < categoryName.size(); ++n) {
if (categoryName[n] == '/') {
categoryName[n] = '.';
lastDot = std::string::npos;
} else if (categoryName[n] == '.') {
lastDot = n;
}
}
// Strip off the filename extension, if one was present.
if (lastDot != std::string::npos) {
categoryName.resize(lastDot);
}
return categoryName;
}
}
This diff is collapsed.
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