2 * Copyright 2004-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <folly/Conv.h>
19 #include <folly/Demangle.h>
20 #include <folly/Format.h>
21 #include <folly/experimental/logging/LogCategory.h>
22 #include <folly/experimental/logging/LogMessage.h>
30 * Helper functions for fallback-formatting of arguments if folly::format()
31 * throws an exception.
33 * These are in a detail namespace so that we can include a using directive in
34 * order to do proper argument-dependent lookup of the correct toAppend()
39 using folly::toAppend;
40 template <typename Arg>
41 auto fallbackFormatOneArg(std::string* str, const Arg* arg, int) -> decltype(
42 toAppend(std::declval<Arg>(), std::declval<std::string*>()),
43 std::declval<void>()) {
47 toAppend(folly::demangle(typeid(*arg)), str);
51 } catch (const std::exception&) {
52 str->append("<error_converting_to_string>");
57 template <typename Arg>
58 inline void fallbackFormatOneArg(std::string* str, const Arg* arg, long) {
62 toAppend(folly::demangle(typeid(*arg)), str);
64 } catch (const std::exception&) {
68 str->append("<no_string_conversion>)");
73 * LogStreamProcessor receives a LogStream and logs it.
75 * This class is primarily intended to be used through the FB_LOG*() macros.
76 * Its API is designed to support these macros, and is not designed for other
79 * The operator&() method is used to trigger the logging.
80 * This operator is used because it has lower precedence than <<, but higher
81 * precedence than the ? ternary operator, allowing it to bind with the correct
82 * precedence in the log macro implementations.
84 class LogStreamProcessor {
86 enum AppendType { APPEND };
87 enum FormatType { FORMAT };
90 * LogStreamProcessor constructor for use with a LOG() macro with no extra
93 * Note that the filename argument is not copied. The caller should ensure
94 * that it points to storage that will remain valid for the lifetime of the
95 * LogStreamProcessor. (This is always the case for the __FILE__
96 * preprocessor macro.)
99 const LogCategory* category,
101 folly::StringPiece filename,
102 unsigned int lineNumber,
104 : category_{category},
107 lineNumber_{lineNumber} {}
110 * LogStreamProcessor constructor for use with a LOG() macro with arguments
111 * to be concatenated with folly::to<std::string>()
113 * Note that the filename argument is not copied. The caller should ensure
114 * that it points to storage that will remain valid for the lifetime of the
115 * LogStreamProcessor. (This is always the case for the __FILE__
116 * preprocessor macro.)
118 template <typename... Args>
120 const LogCategory* category,
122 const char* filename,
123 unsigned int lineNumber,
125 Args&&... args) noexcept
126 : category_{category},
129 lineNumber_{lineNumber},
130 message_{createLogString(std::forward<Args>(args)...)} {}
133 * LogStreamProcessor constructor for use with a LOG() macro with arguments
134 * to be concatenated with folly::to<std::string>()
136 * Note that the filename argument is not copied. The caller should ensure
137 * that it points to storage that will remain valid for the lifetime of the
138 * LogStreamProcessor. (This is always the case for the __FILE__
139 * preprocessor macro.)
141 template <typename... Args>
143 const LogCategory* category,
145 const char* filename,
146 unsigned int lineNumber,
148 folly::StringPiece fmt,
149 Args&&... args) noexcept
150 : category_{category},
153 lineNumber_{lineNumber},
154 message_{formatLogString(fmt, std::forward<Args>(args)...)} {}
157 * This version of operator&() is typically used when the user specifies
158 * log arguments using the << stream operator. The operator<<() generally
159 * returns a std::ostream&
161 void operator&(std::ostream& stream) noexcept;
164 * This version of operator&() is used when no extra arguments are specified
165 * with the << operator. In this case the & operator is applied directly to
166 * the temporary LogStream object.
168 void operator&(LogStream&& stream) noexcept;
171 std::string extractMessageString(LogStream& stream) noexcept;
174 * Construct a log message string using folly::to<std::string>()
176 * This function attempts to avoid throwing exceptions. If an error occurs
177 * during formatting, a message including the error details is returned
178 * instead. This is done to help ensure that log statements do not generate
179 * exceptions, but instead just log an error string when something goes wrong.
181 template <typename... Args>
182 std::string createLogString(Args&&... args) noexcept {
184 return folly::to<std::string>(std::forward<Args>(args)...);
185 } catch (const std::exception& ex) {
186 // This most likely means there was some error converting the arguments
187 // to strings. Handle the exception here, rather than letting it
188 // propagate up, since callers generally do not expect log statements to
191 // Just log an error message letting indicating that something went wrong
192 // formatting the log message.
193 return folly::to<std::string>(
194 "error constructing log message: ", ex.what());
199 * Construct a log message string using folly::sformat()
201 * This function attempts to avoid throwing exceptions. If an error occurs
202 * during formatting, a message including the error details is returned
203 * instead. This is done to help ensure that log statements do not generate
204 * exceptions, but instead just log an error string when something goes wrong.
206 template <typename... Args>
207 std::string formatLogString(
208 folly::StringPiece fmt,
209 const Args&... args) noexcept {
211 return folly::sformat(fmt, args...);
212 } catch (const std::exception& ex) {
213 // This most likely means that the caller had a bug in their format
214 // string/arguments. Handle the exception here, rather than letting it
215 // propagate up, since callers generally do not expect log statements to
218 // Log the format string and as much of the arguments as we can convert,
221 result.append("error formatting log message: ");
222 result.append(ex.what());
223 result.append("; format string: \"");
224 result.append(fmt.data(), fmt.size());
225 result.append("\", arguments: ");
226 fallbackFormat(&result, args...);
232 * Helper function generate a fallback version of the arguments in case
233 * folly::sformat() throws an exception.
235 * This attempts to convert each argument to a string using a similar
236 * mechanism to folly::to<std::string>(), if supported.
238 template <typename Arg1, typename... Args>
240 fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
241 detail::fallbackFormatOneArg(str, &arg1, 0);
243 fallbackFormat(str, remainder...);
246 template <typename Arg>
247 void fallbackFormat(std::string* str, const Arg& arg) {
248 detail::fallbackFormatOneArg(str, &arg, 0);
251 const LogCategory* const category_;
252 LogLevel const level_;
253 folly::StringPiece filename_;
254 unsigned int lineNumber_;
255 std::string message_;
259 * This template subclass of LogStreamProcessor exists primarily so that
260 * we can specify the [[noreturn]] attribute correctly on operator&()
261 * This lets the compiler know that code after LOG(FATAL) is unreachable.
263 template <bool Fatal>
264 class LogStreamProcessorT : public LogStreamProcessor {
266 using LogStreamProcessor::LogStreamProcessor;
268 void operator&(std::ostream& stream) noexcept {
269 LogStreamProcessor::operator&(stream);
271 void operator&(LogStream&& stream) noexcept {
272 LogStreamProcessor::operator&(std::move(stream));
277 class LogStreamProcessorT<true> : public LogStreamProcessor {
279 using LogStreamProcessor::LogStreamProcessor;
281 [[noreturn]] void operator&(std::ostream& stream) noexcept {
282 LogStreamProcessor::operator&(stream);
283 // We'll never actually reach here: the LogCategory code is responsible for
284 // crashing on FATAL messages. However, add an abort() call so the
285 // compiler knows we really cannot return here.
288 [[noreturn]] void operator&(LogStream&& stream) noexcept {
289 LogStreamProcessor::operator&(std::move(stream));
290 // We'll never actually reach here: the LogCategory code is responsible for
291 // crashing on FATAL messages. However, add an abort() call so the
292 // compiler knows we really cannot return here.