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

logging: treat `/` and `\\` as category name separators

Summary:
Update the LogName code to also treat `/` and `\\` as category separators, just
like `.`.

This makes it easy to use directory and file names directly as log category
names.  The XLOG() code already turns file names in to log category names.
Previously `getXlogCategoryNameForFile()` had to do its own translation of
directory separators into `.`.  Users manually had to perform this translation
on their own when setting the configuration for specific categories.

This change makes it possible for users to specify the filename as input
directly when updating the logging configuration and it automatically updates
the correct category.

This also allows us to avoid allocating a new string in
`getXlogCategoryNameForFile()` since we no longer have to translate directory
separator characters there.

Reviewed By: chadaustin

Differential Revision: D7348520

fbshipit-source-id: 0e996f4351798b4636786a14ebb2d8820a2cc153
parent 93474ecf
...@@ -15,85 +15,109 @@ ...@@ -15,85 +15,109 @@
*/ */
#include <folly/experimental/logging/LogName.h> #include <folly/experimental/logging/LogName.h>
namespace {
constexpr bool isSeparator(char c) {
return c == '.' || c == '/' || c == '\\';
}
} // namespace
namespace folly { namespace folly {
std::string LogName::canonicalize(StringPiece input) { std::string LogName::canonicalize(StringPiece input) {
std::string cname; std::string cname;
cname.reserve(input.size()); cname.reserve(input.size());
// Ignore trailing '.'s // Ignore trailing category separator characters
size_t end = input.size(); size_t end = input.size();
while (end > 0 && input[end - 1] == '.') { while (end > 0 && isSeparator(input[end - 1])) {
--end; --end;
} }
bool ignoreDot = true; bool ignoreSeparator = true;
for (size_t idx = 0; idx < end; ++idx) { for (size_t idx = 0; idx < end; ++idx) {
if (ignoreDot && input[idx] == '.') { if (isSeparator(input[idx])) {
if (ignoreSeparator) {
continue; continue;
} }
cname.push_back('.');
ignoreSeparator = true;
} else {
cname.push_back(input[idx]); cname.push_back(input[idx]);
ignoreDot = (input[idx] == '.'); ignoreSeparator = false;
}
} }
return cname; return cname;
} }
size_t LogName::hash(StringPiece name) { size_t LogName::hash(StringPiece name) {
// Code based on StringPiece::hash(), but which ignores leading and // Code based on StringPiece::hash(), but which ignores leading and trailing
// trailing '.' characters, as well as multiple consecutive '.' characters, // category separator characters, as well as multiple consecutive separator
// so equivalent names result in the same hash. // characters, so equivalent names result in the same hash.
uint32_t hash = 5381; uint32_t hash = 5381;
size_t end = name.size(); size_t end = name.size();
while (end > 0 && name[end - 1] == '.') { while (end > 0 && isSeparator(name[end - 1])) {
--end; --end;
} }
bool ignoreDot = true; bool ignoreSeparator = true;
for (size_t idx = 0; idx < end; ++idx) { for (size_t idx = 0; idx < end; ++idx) {
if (ignoreDot && name[idx] == '.') { uint8_t value;
if (isSeparator(name[idx])) {
if (ignoreSeparator) {
continue; continue;
} }
hash = ((hash << 5) + hash) + name[idx]; value = '.';
// If this character was a '.', ignore subsequent consecutive '.'s ignoreSeparator = true;
ignoreDot = (name[idx] == '.'); } else {
value = static_cast<uint8_t>(name[idx]);
ignoreSeparator = false;
}
hash = ((hash << 5) + hash) + value;
} }
return hash; return hash;
} }
int LogName::cmp(StringPiece a, StringPiece b) { int LogName::cmp(StringPiece a, StringPiece b) {
// Ignore trailing '.'s // Ignore trailing separators
auto stripTrailingDots = [](StringPiece& s) { auto stripTrailingSeparators = [](StringPiece& s) {
while (!s.empty() && s.back() == '.') { while (!s.empty() && isSeparator(s.back())) {
s.uncheckedSubtract(1); s.uncheckedSubtract(1);
} }
}; };
stripTrailingDots(a); stripTrailingSeparators(a);
stripTrailingDots(b); stripTrailingSeparators(b);
// Advance ptr until it no longer points to a '.' // Advance ptr until it no longer points to a category separator.
// This is used to skip over consecutive sequences of '.' characters. // This is used to skip over consecutive sequences of separator characters.
auto skipOverDots = [](StringPiece& s) { auto skipOverSeparators = [](StringPiece& s) {
while (!s.empty() && s.front() == '.') { while (!s.empty() && isSeparator(s.front())) {
s.uncheckedAdvance(1); s.uncheckedAdvance(1);
} }
}; };
bool ignoreDot = true; bool ignoreSeparator = true;
while (true) { while (true) {
if (ignoreDot) { if (ignoreSeparator) {
skipOverDots(a); skipOverSeparators(a);
skipOverDots(b); skipOverSeparators(b);
} }
if (a.empty()) { if (a.empty()) {
return b.empty() ? 0 : -1; return b.empty() ? 0 : -1;
} else if (b.empty()) { } else if (b.empty()) {
return 1; return 1;
} }
if (isSeparator(a.front())) {
if (!isSeparator(b.front())) {
return '.' - b.front();
}
ignoreSeparator = true;
} else {
if (a.front() != b.front()) { if (a.front() != b.front()) {
return a.front() - b.front(); return a.front() - b.front();
} }
ignoreDot = (a.front() == '.'); ignoreSeparator = false;
}
a.uncheckedAdvance(1); a.uncheckedAdvance(1);
b.uncheckedAdvance(1); b.uncheckedAdvance(1);
} }
...@@ -106,19 +130,19 @@ StringPiece LogName::getParent(StringPiece name) { ...@@ -106,19 +130,19 @@ StringPiece LogName::getParent(StringPiece name) {
ssize_t idx = name.size(); ssize_t idx = name.size();
// Skip over any trailing '.' characters // Skip over any trailing separator characters
while (idx > 0 && name[idx - 1] == '.') { while (idx > 0 && isSeparator(name[idx - 1])) {
--idx; --idx;
} }
// Now walk backwards to the next '.' character // Now walk backwards to the next separator character
while (idx > 0 && name[idx - 1] != '.') { while (idx > 0 && !isSeparator(name[idx - 1])) {
--idx; --idx;
} }
// And again skip over any '.' characters, in case there are multiple // And again skip over any separator characters, in case there are multiple
// repeated characters. // repeated characters.
while (idx > 0 && name[idx - 1] == '.') { while (idx > 0 && isSeparator(name[idx - 1])) {
--idx; --idx;
} }
......
...@@ -23,16 +23,17 @@ namespace folly { ...@@ -23,16 +23,17 @@ namespace folly {
* The LogName class contains utility functions for processing log category * The LogName class contains utility functions for processing log category
* names. It primarily handles canonicalization of names. * names. It primarily handles canonicalization of names.
* *
* For instance, "foo.bar", "foo..bar", and ".foo.bar..." all refer to the same * For instance, "foo.bar", "foo/bar", "foo..bar", and ".foo.bar..." all refer
* log category. * to the same log category.
*/ */
class LogName { class LogName {
public: public:
/** /**
* Return a canonicalized version of the log name. * Return a canonicalized version of the log name.
* *
* Leading and trailing '.' characters are removed, and all sequences of * '/' and '\\' characters are converted to '.', then leading and trailing
* consecutive '.' characters are replaced with a single '.' * '.' characters are removed, and all sequences of consecutive '.'
* characters are replaced with a single '.'
*/ */
static std::string canonicalize(folly::StringPiece name); static std::string canonicalize(folly::StringPiece name);
......
...@@ -21,17 +21,29 @@ using namespace folly; ...@@ -21,17 +21,29 @@ using namespace folly;
TEST(LogName, canonicalize) { TEST(LogName, canonicalize) {
EXPECT_EQ("", LogName::canonicalize(".")); EXPECT_EQ("", LogName::canonicalize("."));
EXPECT_EQ("", LogName::canonicalize("...")); EXPECT_EQ("", LogName::canonicalize("..."));
EXPECT_EQ("", LogName::canonicalize("/"));
EXPECT_EQ("", LogName::canonicalize("\\"));
EXPECT_EQ("", LogName::canonicalize(".//..\\\\./"));
EXPECT_EQ("foo.bar", LogName::canonicalize(".foo..bar.")); EXPECT_EQ("foo.bar", LogName::canonicalize(".foo..bar."));
EXPECT_EQ("a.b.c", LogName::canonicalize("a.b.c")); EXPECT_EQ("a.b.c", LogName::canonicalize("a.b.c"));
EXPECT_EQ("a.b.c", LogName::canonicalize("a/b/c"));
EXPECT_EQ("a.b.c", LogName::canonicalize("a/b/c/"));
EXPECT_EQ("a.b.c", LogName::canonicalize("a..b.c...")); EXPECT_EQ("a.b.c", LogName::canonicalize("a..b.c..."));
EXPECT_EQ("a.b.c", LogName::canonicalize("....a.b.c")); EXPECT_EQ("a.b.c", LogName::canonicalize("....a.b.c"));
EXPECT_EQ("a.b.c", LogName::canonicalize("a.b.c....")); EXPECT_EQ("a.b.c", LogName::canonicalize("a.b.c...."));
EXPECT_EQ("a.b.c", LogName::canonicalize("////a.b.c"));
EXPECT_EQ("a.b.c", LogName::canonicalize("a.b.c////"));
EXPECT_EQ("a.b.c", LogName::canonicalize("/a.b.//.c/"));
} }
TEST(LogName, getParent) { TEST(LogName, getParent) {
EXPECT_EQ("", LogName::getParent("foo")); EXPECT_EQ("", LogName::getParent("foo"));
EXPECT_EQ("", LogName::getParent(".foo")); EXPECT_EQ("", LogName::getParent(".foo"));
EXPECT_EQ("foo", LogName::getParent("foo.bar")); EXPECT_EQ("foo", LogName::getParent("foo.bar"));
EXPECT_EQ("foo", LogName::getParent("foo/bar"));
EXPECT_EQ("foo", LogName::getParent("foo\\bar"));
EXPECT_EQ("foo", LogName::getParent("foo\\bar/"));
EXPECT_EQ("foo", LogName::getParent("foo\\bar\\"));
EXPECT_EQ("foo..bar", LogName::getParent("foo..bar..test")); EXPECT_EQ("foo..bar", LogName::getParent("foo..bar..test"));
EXPECT_EQ("..foo..bar", LogName::getParent("..foo..bar..test..")); EXPECT_EQ("..foo..bar", LogName::getParent("..foo..bar..test.."));
} }
...@@ -40,7 +52,11 @@ TEST(LogName, hash) { ...@@ -40,7 +52,11 @@ TEST(LogName, hash) {
EXPECT_EQ(LogName::hash("foo"), LogName::hash("foo.")); EXPECT_EQ(LogName::hash("foo"), LogName::hash("foo."));
EXPECT_EQ(LogName::hash(".foo..bar"), LogName::hash("foo.bar...")); EXPECT_EQ(LogName::hash(".foo..bar"), LogName::hash("foo.bar..."));
EXPECT_EQ(LogName::hash("a.b.c..d."), LogName::hash("..a.b.c.d.")); EXPECT_EQ(LogName::hash("a.b.c..d."), LogName::hash("..a.b.c.d."));
EXPECT_EQ(LogName::hash("a.b.c.d"), LogName::hash("/a/b/c/d/"));
EXPECT_EQ(LogName::hash("a.b.c.d"), LogName::hash("a\\b\\c/d/"));
EXPECT_EQ(LogName::hash(""), LogName::hash(".")); EXPECT_EQ(LogName::hash(""), LogName::hash("."));
EXPECT_EQ(LogName::hash(""), LogName::hash("//"));
EXPECT_EQ(LogName::hash(""), LogName::hash("\\"));
EXPECT_EQ(LogName::hash(""), LogName::hash("....")); EXPECT_EQ(LogName::hash(""), LogName::hash("...."));
// Hashes for different category names should generally be different. // Hashes for different category names should generally be different.
...@@ -52,9 +68,11 @@ TEST(LogName, hash) { ...@@ -52,9 +68,11 @@ TEST(LogName, hash) {
TEST(LogName, cmp) { TEST(LogName, cmp) {
EXPECT_EQ(0, LogName::cmp("foo", "foo.")); EXPECT_EQ(0, LogName::cmp("foo", "foo."));
EXPECT_EQ(0, LogName::cmp("foo", "foo/"));
EXPECT_EQ(0, LogName::cmp(".foo..bar", "foo.bar...")); EXPECT_EQ(0, LogName::cmp(".foo..bar", "foo.bar..."));
EXPECT_EQ(0, LogName::cmp(".foo.bar", "foo...bar...")); EXPECT_EQ(0, LogName::cmp(".foo.bar", "foo...bar..."));
EXPECT_EQ(0, LogName::cmp("a.b.c..d.", "..a.b.c.d.")); EXPECT_EQ(0, LogName::cmp("a.b.c..d.", "..a.b.c.d."));
EXPECT_EQ(0, LogName::cmp("a.b.c..d.", "\\/a.b/c/d."));
EXPECT_EQ(0, LogName::cmp("", ".")); EXPECT_EQ(0, LogName::cmp("", "."));
EXPECT_EQ(0, LogName::cmp("", "....")); EXPECT_EQ(0, LogName::cmp("", "...."));
......
...@@ -191,20 +191,27 @@ TEST(Xlog, getXlogCategoryName) { ...@@ -191,20 +191,27 @@ TEST(Xlog, getXlogCategoryName) {
EXPECT_EQ("foo.cpp", getXlogCategoryNameForFile("foo.cpp")); EXPECT_EQ("foo.cpp", getXlogCategoryNameForFile("foo.cpp"));
EXPECT_EQ("foo.h", getXlogCategoryNameForFile("foo.h")); EXPECT_EQ("foo.h", getXlogCategoryNameForFile("foo.h"));
// Directory separators should be translated to "." // Directory separators should be translated to "." during LogName
EXPECT_EQ("src.test.foo.cpp", getXlogCategoryNameForFile("src/test/foo.cpp")); // canonicalization
EXPECT_EQ("src.test.foo.h", getXlogCategoryNameForFile("src/test/foo.h")); EXPECT_EQ("src/test/foo.cpp", getXlogCategoryNameForFile("src/test/foo.cpp"));
EXPECT_EQ(
"src.test.foo.cpp",
LogName::canonicalize(getXlogCategoryNameForFile("src/test/foo.cpp")));
EXPECT_EQ("src/test/foo.h", getXlogCategoryNameForFile("src/test/foo.h"));
EXPECT_EQ(
"src.test.foo.h",
LogName::canonicalize(getXlogCategoryNameForFile("src/test/foo.h")));
// Buck's directory prefixes for generated source files // Buck's directory prefixes for generated source files
// should be stripped out // should be stripped out
EXPECT_EQ( EXPECT_EQ(
"myproject.generated_header.h", "myproject.generated_header.h",
getXlogCategoryNameForFile( LogName::canonicalize(getXlogCategoryNameForFile(
"buck-out/gen/myproject#headers/myproject/generated_header.h")); "buck-out/gen/myproject#headers/myproject/generated_header.h")));
EXPECT_EQ( EXPECT_EQ(
"foo.bar.test.h", "foo.bar.test.h",
getXlogCategoryNameForFile( LogName::canonicalize(getXlogCategoryNameForFile(
"buck-out/gen/foo/bar#header-map,headers/foo/bar/test.h")); "buck-out/gen/foo/bar#header-map,headers/foo/bar/test.h")));
} }
TEST(Xlog, xlogStripFilename) { TEST(Xlog, xlogStripFilename) {
......
...@@ -54,7 +54,7 @@ StringPiece stripBuckOutPrefix(StringPiece filename) { ...@@ -54,7 +54,7 @@ StringPiece stripBuckOutPrefix(StringPiece filename) {
} }
} // namespace } // namespace
std::string getXlogCategoryNameForFile(StringPiece filename) { StringPiece getXlogCategoryNameForFile(StringPiece filename) {
// Buck mangles the directory layout for header files. Rather than including // Buck mangles the directory layout for header files. Rather than including
// them from their original location, it moves them into deep directories // them from their original location, it moves them into deep directories
// inside buck-out, and includes them from there. // inside buck-out, and includes them from there.
...@@ -65,17 +65,7 @@ std::string getXlogCategoryNameForFile(StringPiece filename) { ...@@ -65,17 +65,7 @@ std::string getXlogCategoryNameForFile(StringPiece filename) {
filename = stripBuckOutPrefix(filename); filename = stripBuckOutPrefix(filename);
} }
std::string categoryName = filename.str(); return filename;
// Translate slashes to dots, to turn the directory layout into
// a category hierarchy.
for (size_t n = 0; n < categoryName.size(); ++n) {
if (xlogIsDirSeparator(categoryName[n])) {
categoryName[n] = '.';
}
}
return categoryName;
} }
template <bool IsInHeaderFile> template <bool IsInHeaderFile>
......
...@@ -399,7 +399,7 @@ class XlogCategoryInfo<false> { ...@@ -399,7 +399,7 @@ class XlogCategoryInfo<false> {
* This function returns the category name that will be used by XLOG() if * This function returns the category name that will be used by XLOG() if
* XLOG_SET_CATEGORY_NAME() has not been used. * XLOG_SET_CATEGORY_NAME() has not been used.
*/ */
std::string getXlogCategoryNameForFile(folly::StringPiece filename); folly::StringPiece getXlogCategoryNameForFile(folly::StringPiece filename);
constexpr bool xlogIsDirSeparator(char c) { constexpr bool xlogIsDirSeparator(char c) {
return c == '/' || (kIsWindows && c == '\\'); return c == '/' || (kIsWindows && c == '\\');
......
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