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/CPortability.h>
19 #include <folly/Conv.h>
20 #include <folly/Demangle.h>
21 #include <folly/Format.h>
22 #include <folly/Portability.h>
23 #include <folly/experimental/logging/LogCategory.h>
24 #include <folly/experimental/logging/LogMessage.h>
25 #include <folly/experimental/logging/LogStream.h>
31 * Helper functions for fallback-formatting of arguments if folly::format()
32 * throws an exception.
34 * These are in a detail namespace so that we can include a using directive in
35 * order to do proper argument-dependent lookup of the correct toAppend()
40 using folly::toAppend;
41 template <typename Arg>
42 auto fallbackFormatOneArg(std::string* str, const Arg* arg, int) -> decltype(
43 toAppend(std::declval<Arg>(), std::declval<std::string*>()),
44 std::declval<void>()) {
48 toAppend(folly::demangle(typeid(*arg)), str);
52 } catch (const std::exception&) {
53 str->append("<error_converting_to_string>");
58 template <typename Arg>
59 inline void fallbackFormatOneArg(std::string* str, const Arg* arg, long) {
63 toAppend(folly::demangle(typeid(*arg)), str);
65 } catch (const std::exception&) {
69 str->append("<no_string_conversion>)");
73 template <bool IsInHeaderFile>
74 class XlogCategoryInfo;
75 class XlogFileScopeInfo;
78 * LogStreamProcessor receives a LogStream and logs it.
80 * This class is primarily intended to be used through the FB_LOG*() macros.
81 * Its API is designed to support these macros, and is not designed for other
84 * The operator&() method is used to trigger the logging.
85 * This operator is used because it has lower precedence than <<, but higher
86 * precedence than the ? ternary operator, allowing it to bind with the correct
87 * precedence in the log macro implementations.
89 class LogStreamProcessor {
91 enum AppendType { APPEND };
92 enum FormatType { FORMAT };
95 * LogStreamProcessor constructor for use with a LOG() macro with no extra
98 * Note that the filename argument is not copied. The caller should ensure
99 * that it points to storage that will remain valid for the lifetime of the
100 * LogStreamProcessor. (This is always the case for the __FILE__
101 * preprocessor macro.)
104 const LogCategory* category,
106 folly::StringPiece filename,
107 unsigned int lineNumber,
108 AppendType) noexcept;
111 * LogStreamProcessor constructor for use with a LOG() macro with arguments
112 * to be concatenated with folly::to<std::string>()
114 * Note that the filename argument is not copied. The caller should ensure
115 * that it points to storage that will remain valid for the lifetime of the
116 * LogStreamProcessor. (This is always the case for the __FILE__
117 * preprocessor macro.)
119 template <typename... Args>
121 const LogCategory* category,
123 folly::StringPiece filename,
124 unsigned int lineNumber,
126 Args&&... args) noexcept
127 : LogStreamProcessor(
133 createLogString(std::forward<Args>(args)...)) {}
136 * LogStreamProcessor constructor for use with a LOG() macro with arguments
137 * to be concatenated with folly::to<std::string>()
139 * Note that the filename argument is not copied. The caller should ensure
140 * that it points to storage that will remain valid for the lifetime of the
141 * LogStreamProcessor. (This is always the case for the __FILE__
142 * preprocessor macro.)
144 template <typename... Args>
146 const LogCategory* category,
148 folly::StringPiece filename,
149 unsigned int lineNumber,
151 folly::StringPiece fmt,
152 Args&&... args) noexcept
153 : LogStreamProcessor(
159 formatLogString(fmt, std::forward<Args>(args)...)) {}
162 * Versions of the above constructors for use in XLOG() statements.
164 * These are defined separately from the above constructor so that the work
165 * of initializing the XLOG LogCategory data is done in a separate function
166 * body defined in LogStreamProcessor.cpp. We intentionally want to avoid
167 * inlining this work at every XLOG() statement, to reduce the emitted code
171 XlogCategoryInfo<true>* categoryInfo,
173 folly::StringPiece categoryName,
174 bool isCategoryNameOverridden,
175 folly::StringPiece filename,
176 unsigned int lineNumber,
177 AppendType) noexcept;
178 template <typename... Args>
180 XlogCategoryInfo<true>* categoryInfo,
182 folly::StringPiece categoryName,
183 bool isCategoryNameOverridden,
184 folly::StringPiece filename,
185 unsigned int lineNumber,
187 Args&&... args) noexcept
188 : LogStreamProcessor(
192 isCategoryNameOverridden,
196 createLogString(std::forward<Args>(args)...)) {}
197 template <typename... Args>
199 XlogCategoryInfo<true>* categoryInfo,
201 folly::StringPiece categoryName,
202 bool isCategoryNameOverridden,
203 folly::StringPiece filename,
204 unsigned int lineNumber,
206 folly::StringPiece fmt,
207 Args&&... args) noexcept
208 : LogStreamProcessor(
212 isCategoryNameOverridden,
216 formatLogString(fmt, std::forward<Args>(args)...)) {}
218 #ifdef __INCLUDE_LEVEL__
220 * Versions of the above constructors to use in XLOG() macros that appear in
221 * .cpp files. These are only used if the compiler supports the
222 * __INCLUDE_LEVEL__ macro, which we need to determine that the XLOG()
223 * statement is not in a header file.
225 * These behave identically to the XlogCategoryInfo<true> versions of the
226 * APIs, but slightly more optimized, and allow the XLOG() code to avoid
227 * storing category information at each XLOG() call site.
230 XlogFileScopeInfo* fileScopeInfo,
232 folly::StringPiece filename,
233 unsigned int lineNumber,
234 AppendType) noexcept;
236 XlogFileScopeInfo* fileScopeInfo,
238 folly::StringPiece /* categoryName */,
239 bool /* isCategoryNameOverridden */,
240 folly::StringPiece filename,
241 unsigned int lineNumber,
243 : LogStreamProcessor(fileScopeInfo, level, filename, lineNumber, APPEND) {
245 template <typename... Args>
247 XlogFileScopeInfo* fileScopeInfo,
249 folly::StringPiece /* categoryName */,
250 bool /* isCategoryNameOverridden */,
251 folly::StringPiece filename,
252 unsigned int lineNumber,
254 Args&&... args) noexcept
255 : LogStreamProcessor(
261 createLogString(std::forward<Args>(args)...)) {}
262 template <typename... Args>
264 XlogFileScopeInfo* fileScopeInfo,
266 folly::StringPiece /* categoryName */,
267 bool /* isCategoryNameOverridden */,
268 folly::StringPiece filename,
269 unsigned int lineNumber,
271 folly::StringPiece fmt,
272 Args&&... args) noexcept
273 : LogStreamProcessor(
279 formatLogString(fmt, std::forward<Args>(args)...)) {}
282 ~LogStreamProcessor() noexcept;
285 * This version of operator&() is typically used when the user specifies
286 * log arguments using the << stream operator. The operator<<() generally
287 * returns a std::ostream&
289 void operator&(std::ostream& stream) noexcept;
292 * This version of operator&() is used when no extra arguments are specified
293 * with the << operator. In this case the & operator is applied directly to
294 * the temporary LogStream object.
296 void operator&(LogStream&& stream) noexcept;
298 std::ostream& stream() noexcept {
302 void logNow() noexcept;
305 enum InternalType { INTERNAL };
307 const LogCategory* category,
309 folly::StringPiece filename,
310 unsigned int lineNumber,
312 std::string&& msg) noexcept;
314 XlogCategoryInfo<true>* categoryInfo,
316 folly::StringPiece categoryName,
317 bool isCategoryNameOverridden,
318 folly::StringPiece filename,
319 unsigned int lineNumber,
321 std::string&& msg) noexcept;
323 XlogFileScopeInfo* fileScopeInfo,
325 folly::StringPiece filename,
326 unsigned int lineNumber,
328 std::string&& msg) noexcept;
330 std::string extractMessageString(LogStream& stream) noexcept;
333 * Construct a log message string using folly::to<std::string>()
335 * This function attempts to avoid throwing exceptions. If an error occurs
336 * during formatting, a message including the error details is returned
337 * instead. This is done to help ensure that log statements do not generate
338 * exceptions, but instead just log an error string when something goes wrong.
340 template <typename... Args>
341 FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
343 return folly::to<std::string>(std::forward<Args>(args)...);
344 } catch (const std::exception& ex) {
345 // This most likely means there was some error converting the arguments
346 // to strings. Handle the exception here, rather than letting it
347 // propagate up, since callers generally do not expect log statements to
350 // Just log an error message letting indicating that something went wrong
351 // formatting the log message.
352 return folly::to<std::string>(
353 "error constructing log message: ", ex.what());
358 * Construct a log message string using folly::sformat()
360 * This function attempts to avoid throwing exceptions. If an error occurs
361 * during formatting, a message including the error details is returned
362 * instead. This is done to help ensure that log statements do not generate
363 * exceptions, but instead just log an error string when something goes wrong.
365 template <typename... Args>
366 FOLLY_NOINLINE std::string formatLogString(
367 folly::StringPiece fmt,
368 const Args&... args) noexcept {
370 return folly::sformat(fmt, args...);
371 } catch (const std::exception& ex) {
372 // This most likely means that the caller had a bug in their format
373 // string/arguments. Handle the exception here, rather than letting it
374 // propagate up, since callers generally do not expect log statements to
377 // Log the format string and as much of the arguments as we can convert,
380 result.append("error formatting log message: ");
381 result.append(ex.what());
382 result.append("; format string: \"");
383 result.append(fmt.data(), fmt.size());
384 result.append("\", arguments: ");
385 fallbackFormat(&result, args...);
391 * Helper function generate a fallback version of the arguments in case
392 * folly::sformat() throws an exception.
394 * This attempts to convert each argument to a string using a similar
395 * mechanism to folly::to<std::string>(), if supported.
397 template <typename Arg1, typename... Args>
399 fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
400 detail::fallbackFormatOneArg(str, &arg1, 0);
402 fallbackFormat(str, remainder...);
405 template <typename Arg>
406 void fallbackFormat(std::string* str, const Arg& arg) {
407 detail::fallbackFormatOneArg(str, &arg, 0);
410 const LogCategory* const category_;
411 LogLevel const level_;
412 folly::StringPiece filename_;
413 unsigned int lineNumber_;
414 std::string message_;
419 * LogStreamVoidify() is a helper class used in the FB_LOG() and XLOG() macros.
421 * It's only purpose is to provide an & operator overload that returns void.
422 * This allows the log macros to expand roughly to:
424 * (logEnabled) ? (void)0
425 * : LogStreamVoidify{} & LogStreamProcessor{}.stream() << "msg";
427 * This enables the right hand (':') side of the ternary ? expression to have a
428 * void type, and allows various streaming operator expressions to be placed on
429 * the right hand side of the expression.
431 * Operator & is used since it has higher precedence than ?:, but lower
432 * precedence than <<.
434 * This class is templated on whether the log message is fatal so that the
435 * operator& can be declared [[noreturn]] for fatal log messages. This
436 * prevents the compiler from complaining about functions that do not return a
437 * value after a fatal log statement.
439 template <bool Fatal>
440 class LogStreamVoidify {
443 * In the default (non-fatal) case, the & operator implementation is a no-op.
445 * We perform the actual logging in the LogStreamProcessor destructor. It
446 * feels slightly hacky to perform logging in the LogStreamProcessor
447 * destructor instead of here, since the LogStreamProcessor destructor is not
448 * evaluated until the very end of the statement. In practice log
449 * statements really shouldn't be in the middle of larger statements with
450 * other side effects, so this ordering distinction shouldn't make much
453 * However, by keeping this function a no-op we reduce the amount of code
454 * generated for log statements. This function call can be completely
455 * eliminated by the compiler, leaving only the LogStreamProcessor destructor
456 * invocation, which cannot be eliminated.
458 void operator&(std::ostream&)noexcept {}
462 class LogStreamVoidify<true> {
465 * A specialized noreturn version of operator&() for fatal log statements.
467 [[noreturn]] void operator&(std::ostream&);
471 * logDisabledHelper() is invoked in FB_LOG() and XLOG() statements if the log
472 * admittance check fails.
474 * This function exists solely to ensure that both sides of the log check are
475 * marked [[noreturn]] for fatal log messages. This allows the compiler to
476 * recognize that the full statement is noreturn, preventing warnings about
477 * missing return statements after fatal log messages.
479 * Unfortunately it does not appear possible to get the compiler to recognize
480 * that the disabled side of the log statement should never be reached for
481 * fatal messages. Even if we make the check something like
482 * `(isLogLevelFatal(level) || realCheck)`, where isLogLevelFatal() is
483 * constexpr, this is not sufficient for gcc or clang to recognize that the
484 * full expression is noreturn.
486 * Ideally this would just be a template function specialized on a boolean
487 * IsFatal parameter. Unfortunately this triggers a bug in clang, which does
488 * not like differing noreturn behavior for different template instantiations.
489 * Therefore we overload on integral_constant instead.
491 * clang-format also doesn't do a good job understanding this code and figuring
492 * out how to format it.
495 inline void logDisabledHelper(std::integral_constant<bool, false>) noexcept {}
496 [[noreturn]] void logDisabledHelper(
497 std::integral_constant<bool, true>) noexcept;