#pragma once
#include <folly/Conv.h>
+#include <folly/Demangle.h>
#include <folly/Format.h>
#include <folly/experimental/logging/LogCategory.h>
#include <folly/experimental/logging/LogMessage.h>
class LogStream;
+/*
+ * Helper functions for fallback-formatting of arguments if folly::format()
+ * throws an exception.
+ *
+ * These are in a detail namespace so that we can include a using directive in
+ * order to do proper argument-dependent lookup of the correct toAppend()
+ * function to use.
+ */
+namespace detail {
+/* using override */
+using folly::toAppend;
+template <typename Arg>
+auto fallbackFormatOneArg(std::string* str, const Arg* arg, int) -> decltype(
+ toAppend(std::declval<Arg>(), std::declval<std::string*>()),
+ std::declval<void>()) {
+ str->push_back('(');
+ try {
+#ifdef FOLLY_HAS_RTTI
+ toAppend(folly::demangle(typeid(*arg)), str);
+ str->append(": ");
+#endif
+ toAppend(*arg, str);
+ } catch (const std::exception&) {
+ str->append("<error_converting_to_string>");
+ }
+ str->push_back(')');
+}
+
+template <typename Arg>
+inline void fallbackFormatOneArg(std::string* str, const Arg* arg, long) {
+ str->push_back('(');
+#ifdef FOLLY_HAS_RTTI
+ try {
+ toAppend(folly::demangle(typeid(*arg)), str);
+ str->append(": ");
+ } catch (const std::exception&) {
+ // Ignore the error
+ }
+#endif
+ str->append("<no_string_conversion>)");
+}
+}
+
/**
* LogStreamProcessor receives a LogStream and logs it.
*
* exceptions, but instead just log an error string when something goes wrong.
*/
template <typename... Args>
- std::string formatLogString(folly::StringPiece fmt, Args&&... args) noexcept {
+ std::string formatLogString(
+ folly::StringPiece fmt,
+ const Args&... args) noexcept {
try {
- return folly::sformat(fmt, std::forward<Args>(args)...);
+ return folly::sformat(fmt, args...);
} catch (const std::exception& ex) {
// This most likely means that the caller had a bug in their format
// string/arguments. Handle the exception here, rather than letting it
// propagate up, since callers generally do not expect log statements to
// throw.
//
- // Log the format string by itself, to help the developer at least
- // identify the buggy format string in their code.
- return folly::to<std::string>(
- "error formatting log message: ",
- ex.what(),
- "; format string: \"",
- fmt,
- "\"");
+ // Log the format string and as much of the arguments as we can convert,
+ // to aid debugging.
+ std::string result;
+ result.append("error formatting log message: ");
+ result.append(ex.what());
+ result.append("; format string: \"");
+ result.append(fmt.data(), fmt.size());
+ result.append("\", arguments: ");
+ fallbackFormat(&result, args...);
+ return result;
}
}
+ /**
+ * Helper function generate a fallback version of the arguments in case
+ * folly::sformat() throws an exception.
+ *
+ * This attempts to convert each argument to a string using a similar
+ * mechanism to folly::to<std::string>(), if supported.
+ */
+ template <typename Arg1, typename... Args>
+ void
+ fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
+ detail::fallbackFormatOneArg(str, &arg1, 0);
+ str->append(", ");
+ fallbackFormat(str, remainder...);
+ }
+
+ template <typename Arg>
+ void fallbackFormat(std::string* str, const Arg& arg) {
+ detail::fallbackFormatOneArg(str, &arg, 0);
+ }
+
const LogCategory* const category_;
LogLevel const level_;
folly::StringPiece filename_;