experimental/logging/LogStream.h \
experimental/logging/LogStreamProcessor.h \
experimental/logging/LogWriter.h \
+ experimental/logging/printf.h \
experimental/logging/RateLimiter.h \
experimental/logging/StandardLogHandler.h \
experimental/logging/xlog.h \
*/
#include <folly/experimental/logging/LogStreamProcessor.h>
-#include <cassert>
-
#include <folly/experimental/logging/LogStream.h>
namespace folly {
+
+LogStreamProcessor::LogStreamProcessor(
+ const LogCategory* category,
+ LogLevel level,
+ folly::StringPiece filename,
+ unsigned int lineNumber,
+ AppendType) noexcept
+ : category_{category},
+ level_{level},
+ filename_{filename},
+ lineNumber_{lineNumber} {}
+
+LogStreamProcessor::LogStreamProcessor(
+ const LogCategory* category,
+ LogLevel level,
+ const char* filename,
+ unsigned int lineNumber,
+ InternalType,
+ std::string&& msg) noexcept
+ : category_{category},
+ level_{level},
+ filename_{filename},
+ lineNumber_{lineNumber},
+ message_{std::move(msg)} {}
+
void LogStreamProcessor::operator&(std::ostream& stream) noexcept {
// Note that admitMessage() is not noexcept and theoretically may throw.
// However, the only exception that should be possible is std::bad_alloc if
#include <folly/Conv.h>
#include <folly/Demangle.h>
#include <folly/Format.h>
+#include <folly/Portability.h>
#include <folly/experimental/logging/LogCategory.h>
#include <folly/experimental/logging/LogMessage.h>
#include <cstdlib>
LogLevel level,
folly::StringPiece filename,
unsigned int lineNumber,
- AppendType) noexcept
- : category_{category},
- level_{level},
- filename_{filename},
- lineNumber_{lineNumber} {}
+ AppendType) noexcept;
/**
* LogStreamProcessor constructor for use with a LOG() macro with arguments
unsigned int lineNumber,
AppendType,
Args&&... args) noexcept
- : category_{category},
- level_{level},
- filename_{filename},
- lineNumber_{lineNumber},
- message_{createLogString(std::forward<Args>(args)...)} {}
+ : LogStreamProcessor{category,
+ level,
+ filename,
+ lineNumber,
+ INTERNAL,
+ createLogString(std::forward<Args>(args)...)} {}
/**
* LogStreamProcessor constructor for use with a LOG() macro with arguments
FormatType,
folly::StringPiece fmt,
Args&&... args) noexcept
- : category_{category},
- level_{level},
- filename_{filename},
- lineNumber_{lineNumber},
- message_{formatLogString(fmt, std::forward<Args>(args)...)} {}
+ : LogStreamProcessor{category,
+ level,
+ filename,
+ lineNumber,
+ INTERNAL,
+ formatLogString(fmt, std::forward<Args>(args)...)} {}
/**
* This version of operator&() is typically used when the user specifies
void operator&(LogStream&& stream) noexcept;
private:
+ enum InternalType { INTERNAL };
+ LogStreamProcessor(
+ const LogCategory* category,
+ LogLevel level,
+ const char* filename,
+ unsigned int lineNumber,
+ InternalType,
+ std::string&& msg) noexcept;
+
std::string extractMessageString(LogStream& stream) noexcept;
/**
LogName.cpp \
LogStream.cpp \
LogStreamProcessor.cpp \
+ printf.cpp \
RateLimiter.cpp \
StandardLogHandler.cpp \
xlog.cpp
--- /dev/null
+/*
+ * 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/printf.h>
+
+#include <cstdarg>
+
+#include <folly/Conv.h>
+#include <folly/String.h>
+
+namespace folly {
+
+std::string loggingFormatPrintf(const char* format, ...) noexcept {
+ va_list ap;
+ va_start(ap, format);
+ SCOPE_EXIT {
+ va_end(ap);
+ };
+ try {
+ return stringVPrintf(format, ap);
+ } catch (const std::exception&) {
+ // We don't bother including the exception message here.
+ // The exceptions thrown by stringVPrintf() don't normally have much useful
+ // information regarding what precisely went wrong.
+ return folly::to<std::string>(
+ "error formatting printf-style log message: ", format);
+ }
+}
+}
--- /dev/null
+/*
+ * 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
+
+/*
+ * C-style printf-like macros for the logging library.
+ *
+ * These are defined in their own separate header file to discourage their use
+ * in new code. These macros make it somewhat easier to convert existing code
+ * using printf()-like statements to the logging library. However, new code
+ * should generally prefer to use one of the other macro forms instead
+ * (simple argument concatenation, folly::format based, or iostream-like).
+ *
+ * These use a "C" suffix to the macro name since these use C-style format
+ * syntax. (The "F" suffix is used for folly:format()-style.)
+ */
+
+#include <folly/experimental/logging/Logger.h>
+#include <folly/experimental/logging/xlog.h>
+
+namespace folly {
+std::string loggingFormatPrintf(
+ FOLLY_PRINTF_FORMAT const char* format,
+ ...) noexcept FOLLY_PRINTF_FORMAT_ATTR(1, 2);
+}
+
+/**
+ * Log a message to the specified logger using a printf-style format string.
+ */
+#define FB_LOGC(logger, level, fmt, ...) \
+ FB_LOG_IMPL( \
+ logger, \
+ ::folly::LogLevel::level, \
+ ::folly::LogStreamProcessor::APPEND, \
+ ::folly::loggingFormatPrintf(fmt, ##__VA_ARGS__))
+
+/**
+ * Log a message to the file's default log category using a printf-style format
+ * string.
+ */
+#define XLOGC(level, fmt, ...) \
+ XLOG_IMPL( \
+ ::folly::LogLevel::level, \
+ ::folly::LogStreamProcessor::APPEND, \
+ ::folly::loggingFormatPrintf(fmt, ##__VA_ARGS__))
--- /dev/null
+/*
+ * 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/printf.h>
+#include <folly/experimental/logging/test/TestLogHandler.h>
+#include <folly/portability/GTest.h>
+
+using namespace folly;
+using std::make_shared;
+
+TEST(PrintfTest, printfStyleMacros) {
+ LoggerDB db{LoggerDB::TESTING};
+ Logger logger{&db, "test"};
+ auto* category = logger.getCategory();
+
+ auto handler = make_shared<TestLogHandler>();
+ category->addHandler(handler);
+ category->setLevel(LogLevel::DEBUG, true);
+
+ Logger foo{&db, "test.foo.bar"};
+ Logger foobar{&db, "test.foo.bar"};
+ Logger footest{&db, "test.foo.test"};
+ Logger footest1234{&db, "test.foo.test.1234"};
+ Logger other{&db, "test.other"};
+ db.setLevel("test", LogLevel::ERROR);
+ db.setLevel("test.foo", LogLevel::DBG2);
+ db.setLevel("test.foo.test", LogLevel::DBG7);
+
+ auto& messages = handler->getMessages();
+
+ // test.other's effective level should be ERROR, so a warning
+ // message to it should be discarded
+ FB_LOGC(other, WARN, "this should be discarded: %d", 5);
+ ASSERT_EQ(0, messages.size());
+
+ // Disabled log messages should not evaluate their arguments
+ bool argumentEvaluated = false;
+ auto getValue = [&] {
+ argumentEvaluated = true;
+ return 5;
+ };
+ FB_LOGC(foobar, DBG3, "discarded message: %d", getValue());
+ EXPECT_FALSE(argumentEvaluated);
+
+ FB_LOGC(foobar, DBG1, "this message should pass: %d", getValue());
+ ASSERT_EQ(1, messages.size());
+ EXPECT_EQ("this message should pass: 5", messages[0].first.getMessage());
+ EXPECT_TRUE(argumentEvaluated);
+ messages.clear();
+
+ // The FB_LOGC() macro should work even if the format string does not contain
+ // any format sequences. Ideally people would just use FB_LOG() if they
+ // aren't actually formatting anything, but making FB_LOGC() work in this
+ // scenario still makes it easier for people to switch legacy printf-style
+ // code to FB_LOGC().
+ FB_LOGC(foobar, DBG1, "no actual format arguments");
+ ASSERT_EQ(1, messages.size());
+ EXPECT_EQ("no actual format arguments", messages[0].first.getMessage());
+ messages.clear();
+
+ // Similar checks with XLOGC()
+ auto* xlogCategory = XLOG_GET_CATEGORY();
+ xlogCategory->addHandler(handler);
+ xlogCategory->setLevel(LogLevel::DBG5, true);
+
+ argumentEvaluated = false;
+ XLOGC(DBG9, "failing log check: %d", getValue());
+ EXPECT_FALSE(argumentEvaluated);
+
+ XLOGC(DBG5, "passing log: %03d", getValue());
+ ASSERT_EQ(1, messages.size());
+ EXPECT_EQ("passing log: 005", messages[0].first.getMessage());
+ EXPECT_TRUE(argumentEvaluated);
+ messages.clear();
+
+ XLOGC(DBG1, "no xlog format arguments");
+ ASSERT_EQ(1, messages.size());
+ EXPECT_EQ("no xlog format arguments", messages[0].first.getMessage());
+ messages.clear();
+
+ // Errors attempting to format the message should not throw
+ FB_LOGC(footest1234, ERROR, "width overflow: %999999999999999999999d", 5);
+ ASSERT_EQ(1, messages.size());
+ EXPECT_EQ(
+ "error formatting printf-style log message: "
+ "width overflow: %999999999999999999999d",
+ messages[0].first.getMessage());
+ messages.clear();
+}