Commit 029911f0 authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot

logging: move object to string functions to their own header

Summary:
In LogStreamProcessor.h we previously had some `fallbackFormat()` functions
that attempted to convert arbitrary objects to strings in case an exception
was thrown when formatting them with `folly::format()`.

These functions basically attempt to perform best-effort conversion that
ideally should never fail (barring failure to append to the output string).
These functions attempt to use `toAppend()` for the object, similar to how
`folly::to<std::string>()` works.  However, if no `toAppend()` function is
available, or if it throws an exception, they fall back to returning a generic
string.

This diff moves these functions to a separate header file to make it easier to
re-use them in other parts of the code.  In particular I plan to update the
`XCHECK_EQ()` and related macros to use these functions to format its
arguments.

Reviewed By: chadaustin

Differential Revision: D14545304

fbshipit-source-id: 053a504340ba4513abc9bec186a31ffaa03a943a
parent 4924344d
...@@ -17,59 +17,16 @@ ...@@ -17,59 +17,16 @@
#include <folly/CPortability.h> #include <folly/CPortability.h>
#include <folly/Conv.h> #include <folly/Conv.h>
#include <folly/Demangle.h>
#include <folly/Format.h> #include <folly/Format.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/logging/LogCategory.h> #include <folly/logging/LogCategory.h>
#include <folly/logging/LogMessage.h> #include <folly/logging/LogMessage.h>
#include <folly/logging/LogStream.h> #include <folly/logging/LogStream.h>
#include <folly/logging/ObjectToString.h>
#include <cstdlib> #include <cstdlib>
namespace folly { namespace folly {
/*
* 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 {
#if 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('(');
#if 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>)");
}
} // namespace detail
template <bool IsInHeaderFile> template <bool IsInHeaderFile>
class XlogCategoryInfo; class XlogCategoryInfo;
class XlogFileScopeInfo; class XlogFileScopeInfo;
...@@ -406,31 +363,11 @@ class LogStreamProcessor { ...@@ -406,31 +363,11 @@ class LogStreamProcessor {
result.append("; format string: \""); result.append("; format string: \"");
result.append(fmt.data(), fmt.size()); result.append(fmt.data(), fmt.size());
result.append("\", arguments: "); result.append("\", arguments: ");
fallbackFormat(&result, args...); folly::logging::appendToString(result, args...);
return result; 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_; const LogCategory* const category_;
LogLevel const level_; LogLevel const level_;
folly::StringPiece filename_; folly::StringPiece filename_;
......
/*
* Copyright 2017-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/Conv.h>
#include <folly/Demangle.h>
#include <folly/Portability.h>
/*
* This file contains functions for converting arbitrary objects to strings for
* logging purposes.
*
* - folly::logging::objectToString(object) --> std::string
* - folly::logging::appendToString(result, object)
* - folly::logging::appendToString(result, object1, object2, ...)
*
* These functions behave somewhat similarly to `folly::to<std::string>(object)`
* but always produce a result, rather than failing to compile for objects that
* do not have a predefined mechanism for converting them to a string.
*
* If a `toAppend(std::string&, const Arg& object)` function has been defined
* for the given object type this will be used to format the object. (This is
* the same mechanism used by `folly::to<std::string>()`.) If a `toAppend()`
* function is not defined these functions fall back to emitting a string that
* includes the object type name, the object size, and a hex-dump representation
* of the object contents.
*
* When invoked with multiple arguments `appendToString()` puts ", " between
* each argument in the output. This also differs from
* `folly::to<std::string>()`, which puts no extra output between arguments.
*/
namespace folly {
namespace logging {
/*
* Helper functions for object to string conversion.
* 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 appendObjectToString(std::string& str, const Arg* arg, int) -> decltype(
toAppend(std::declval<Arg>(), std::declval<std::string*>()),
std::declval<void>()) {
str.push_back('(');
try {
#if FOLLY_HAS_RTTI
toAppend(folly::demangle(typeid(*arg)), &str);
str.append(": ");
#endif
toAppend(*arg, &str);
} catch (const std::exception& ex) {
str.append("<error_converting_to_string: ");
str.append(ex.what());
str.append(">");
}
str.push_back(')');
}
template <typename Arg>
inline void appendObjectToString(std::string& str, const Arg* arg, long) {
str.push_back('(');
#if 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>)");
}
} // namespace detail
/**
* Convert an arbitrary object to a string for logging purposes.
*/
template <typename Arg>
std::string objectToString(const Arg& arg) {
std::string result;
::folly::logging::detail::appendObjectToString(result, &arg, 0);
return result;
}
/**
* Append an arbitrary object to a string for logging purposes.
*/
template <typename Arg>
void appendToString(std::string& result, const Arg& arg) {
::folly::logging::detail::appendObjectToString(result, &arg, 0);
}
/**
* Append an arbitrary group of objects to a string for logging purposes.
*
* This function outputs a comma and space (", ") between each pair of objects.
*/
template <typename Arg1, typename... Args>
void appendToString(
std::string& result,
const Arg1& arg1,
const Args&... remainder) {
::folly::logging::detail::appendObjectToString(result, &arg1, 0);
result.append(", ");
::folly::logging::appendToString(result, remainder...);
}
} // namespace logging
} // namespace folly
...@@ -213,7 +213,8 @@ TEST_F(LoggerTest, formatFallbackError) { ...@@ -213,7 +213,8 @@ TEST_F(LoggerTest, formatFallbackError) {
R"(argument index out of range, max=2; )" R"(argument index out of range, max=2; )"
R"(format string: "param1: \{\}, param2: \{\}, \{\}", )" R"(format string: "param1: \{\}, param2: \{\}, \{\}", )"
R"(arguments: \((.*: )?1234\), )" R"(arguments: \((.*: )?1234\), )"
R"(\((.*ToStringFailure.*: )?<error_converting_to_string>\))")); R"(\((.*ToStringFailure.*: )?<error_converting_to_string: )"
R"(error converting ToStringFailure object to a string>\))"));
EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName())); EXPECT_EQ("LoggerTest.cpp", pathBasename(messages[0].first.getFileName()));
EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel()); EXPECT_EQ(LogLevel::WARN, messages[0].first.getLevel());
EXPECT_FALSE(messages[0].first.containsNewlines()); EXPECT_FALSE(messages[0].first.containsNewlines());
......
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