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 constructors for use with XLOG() macros with no extra
114 * These are defined separately from the above constructor so that the work
115 * of initializing the XLOG LogCategory data is done in a separate function
116 * body defined in LogStreamProcessor.cpp. We intentionally want to avoid
117 * inlining this work at every XLOG() statement, to reduce the emitted code
121 XlogCategoryInfo<true>* categoryInfo,
123 folly::StringPiece categoryName,
124 bool isCategoryNameOverridden,
125 folly::StringPiece filename,
126 unsigned int lineNumber,
127 AppendType) noexcept;
129 XlogFileScopeInfo* fileScopeInfo,
131 folly::StringPiece filename,
132 unsigned int lineNumber,
133 AppendType) noexcept;
135 XlogFileScopeInfo* fileScopeInfo,
137 folly::StringPiece /* categoryName */,
138 bool /* isCategoryNameOverridden */,
139 folly::StringPiece filename,
140 unsigned int lineNumber,
142 : LogStreamProcessor(fileScopeInfo, level, filename, lineNumber, APPEND) {
146 * LogStreamProcessor constructor for use with a LOG() macro with arguments
147 * to be concatenated with folly::to<std::string>()
149 * Note that the filename argument is not copied. The caller should ensure
150 * that it points to storage that will remain valid for the lifetime of the
151 * LogStreamProcessor. (This is always the case for the __FILE__
152 * preprocessor macro.)
154 template <typename... Args>
156 const LogCategory* category,
158 folly::StringPiece filename,
159 unsigned int lineNumber,
161 Args&&... args) noexcept
162 : LogStreamProcessor(
168 createLogString(std::forward<Args>(args)...)) {}
171 * Versions of the above constructor for use in XLOG() statements.
173 template <typename... Args>
175 XlogCategoryInfo<true>* categoryInfo,
177 folly::StringPiece categoryName,
178 bool isCategoryNameOverridden,
179 folly::StringPiece filename,
180 unsigned int lineNumber,
182 Args&&... args) noexcept
183 : LogStreamProcessor(
187 isCategoryNameOverridden,
191 createLogString(std::forward<Args>(args)...)) {}
192 template <typename... Args>
194 XlogFileScopeInfo* fileScopeInfo,
196 folly::StringPiece /* categoryName */,
197 bool /* isCategoryNameOverridden */,
198 folly::StringPiece filename,
199 unsigned int lineNumber,
201 Args&&... args) noexcept
202 : LogStreamProcessor(
208 createLogString(std::forward<Args>(args)...)) {}
211 * LogStreamProcessor constructor for use with a LOG() macro with arguments
212 * to be concatenated with folly::to<std::string>()
214 * Note that the filename argument is not copied. The caller should ensure
215 * that it points to storage that will remain valid for the lifetime of the
216 * LogStreamProcessor. (This is always the case for the __FILE__
217 * preprocessor macro.)
219 template <typename... Args>
221 const LogCategory* category,
223 folly::StringPiece filename,
224 unsigned int lineNumber,
226 folly::StringPiece fmt,
227 Args&&... args) noexcept
228 : LogStreamProcessor(
234 formatLogString(fmt, std::forward<Args>(args)...)) {}
237 * Versions of the above constructor for use in XLOG() statements.
239 template <typename... Args>
241 XlogCategoryInfo<true>* categoryInfo,
243 folly::StringPiece categoryName,
244 bool isCategoryNameOverridden,
245 folly::StringPiece filename,
246 unsigned int lineNumber,
248 folly::StringPiece fmt,
249 Args&&... args) noexcept
250 : LogStreamProcessor(
254 isCategoryNameOverridden,
258 formatLogString(fmt, std::forward<Args>(args)...)) {}
260 template <typename... Args>
262 XlogFileScopeInfo* fileScopeInfo,
264 folly::StringPiece /* categoryName */,
265 bool /* isCategoryNameOverridden */,
266 folly::StringPiece filename,
267 unsigned int lineNumber,
269 folly::StringPiece fmt,
270 Args&&... args) noexcept
271 : LogStreamProcessor(
277 formatLogString(fmt, std::forward<Args>(args)...)) {}
279 ~LogStreamProcessor() noexcept;
282 * This version of operator&() is typically used when the user specifies
283 * log arguments using the << stream operator. The operator<<() generally
284 * returns a std::ostream&
286 void operator&(std::ostream& stream) noexcept;
289 * This version of operator&() is used when no extra arguments are specified
290 * with the << operator. In this case the & operator is applied directly to
291 * the temporary LogStream object.
293 void operator&(LogStream&& stream) noexcept;
295 std::ostream& stream() noexcept {
299 void logNow() noexcept;
302 enum InternalType { INTERNAL };
304 const LogCategory* category,
306 folly::StringPiece filename,
307 unsigned int lineNumber,
309 std::string&& msg) noexcept;
311 XlogCategoryInfo<true>* categoryInfo,
313 folly::StringPiece categoryName,
314 bool isCategoryNameOverridden,
315 folly::StringPiece filename,
316 unsigned int lineNumber,
318 std::string&& msg) noexcept;
320 XlogFileScopeInfo* fileScopeInfo,
322 folly::StringPiece filename,
323 unsigned int lineNumber,
325 std::string&& msg) noexcept;
327 std::string extractMessageString(LogStream& stream) noexcept;
330 * Construct a log message string using folly::to<std::string>()
332 * This function attempts to avoid throwing exceptions. If an error occurs
333 * during formatting, a message including the error details is returned
334 * instead. This is done to help ensure that log statements do not generate
335 * exceptions, but instead just log an error string when something goes wrong.
337 template <typename... Args>
338 FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
340 return folly::to<std::string>(std::forward<Args>(args)...);
341 } catch (const std::exception& ex) {
342 // This most likely means there was some error converting the arguments
343 // to strings. Handle the exception here, rather than letting it
344 // propagate up, since callers generally do not expect log statements to
347 // Just log an error message letting indicating that something went wrong
348 // formatting the log message.
349 return folly::to<std::string>(
350 "error constructing log message: ", ex.what());
355 * Construct a log message string using folly::sformat()
357 * This function attempts to avoid throwing exceptions. If an error occurs
358 * during formatting, a message including the error details is returned
359 * instead. This is done to help ensure that log statements do not generate
360 * exceptions, but instead just log an error string when something goes wrong.
362 template <typename... Args>
363 FOLLY_NOINLINE std::string formatLogString(
364 folly::StringPiece fmt,
365 const Args&... args) noexcept {
367 return folly::sformat(fmt, args...);
368 } catch (const std::exception& ex) {
369 // This most likely means that the caller had a bug in their format
370 // string/arguments. Handle the exception here, rather than letting it
371 // propagate up, since callers generally do not expect log statements to
374 // Log the format string and as much of the arguments as we can convert,
377 result.append("error formatting log message: ");
378 result.append(ex.what());
379 result.append("; format string: \"");
380 result.append(fmt.data(), fmt.size());
381 result.append("\", arguments: ");
382 fallbackFormat(&result, args...);
388 * Helper function generate a fallback version of the arguments in case
389 * folly::sformat() throws an exception.
391 * This attempts to convert each argument to a string using a similar
392 * mechanism to folly::to<std::string>(), if supported.
394 template <typename Arg1, typename... Args>
396 fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
397 detail::fallbackFormatOneArg(str, &arg1, 0);
399 fallbackFormat(str, remainder...);
402 template <typename Arg>
403 void fallbackFormat(std::string* str, const Arg& arg) {
404 detail::fallbackFormatOneArg(str, &arg, 0);
407 const LogCategory* const category_;
408 LogLevel const level_;
409 folly::StringPiece filename_;
410 unsigned int lineNumber_;
411 std::string message_;
416 * LogStreamVoidify() is a helper class used in the FB_LOG() and XLOG() macros.
418 * It's only purpose is to provide an & operator overload that returns void.
419 * This allows the log macros to expand roughly to:
421 * (logEnabled) ? (void)0
422 * : LogStreamVoidify{} & LogStreamProcessor{}.stream() << "msg";
424 * This enables the right hand (':') side of the ternary ? expression to have a
425 * void type, and allows various streaming operator expressions to be placed on
426 * the right hand side of the expression.
428 * Operator & is used since it has higher precedence than ?:, but lower
429 * precedence than <<.
431 * This class is templated on whether the log message is fatal so that the
432 * operator& can be declared [[noreturn]] for fatal log messages. This
433 * prevents the compiler from complaining about functions that do not return a
434 * value after a fatal log statement.
436 template <bool Fatal>
437 class LogStreamVoidify {
440 * In the default (non-fatal) case, the & operator implementation is a no-op.
442 * We perform the actual logging in the LogStreamProcessor destructor. It
443 * feels slightly hacky to perform logging in the LogStreamProcessor
444 * destructor instead of here, since the LogStreamProcessor destructor is not
445 * evaluated until the very end of the statement. In practice log
446 * statements really shouldn't be in the middle of larger statements with
447 * other side effects, so this ordering distinction shouldn't make much
450 * However, by keeping this function a no-op we reduce the amount of code
451 * generated for log statements. This function call can be completely
452 * eliminated by the compiler, leaving only the LogStreamProcessor destructor
453 * invocation, which cannot be eliminated.
455 void operator&(std::ostream&)noexcept {}
459 class LogStreamVoidify<true> {
462 * A specialized noreturn version of operator&() for fatal log statements.
464 [[noreturn]] void operator&(std::ostream&);