1/*
2 * Copyright 2017-present Facebook, Inc.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16#pragma once
17
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/logging/LogCategory.h>
24#include <folly/logging/LogMessage.h>
25#include <folly/logging/LogStream.h>
26#include <cstdlib>
27
28namespace folly {
29
30/*
31 * Helper functions for fallback-formatting of arguments if folly::format()
32 * throws an exception.
33 *
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()
36 * function to use.
37 */
38namespace detail {
39/* using override */
40using folly::toAppend;
41template <typename Arg>
42auto fallbackFormatOneArg(std::string* str, const Arg* arg, int) -> decltype(
43 toAppend(std::declval<Arg>(), std::declval<std::string*>()),
44 std::declval<void>()) {
45 str->push_back('(');
46 try {
47#ifdef FOLLY_HAS_RTTI
48 toAppend(folly::demangle(typeid(*arg)), str);
49 str->append(": ");
50#endif
51 toAppend(*arg, str);
52 } catch (const std::exception&) {
53 str->append("<error_converting_to_string>");
54 }
55 str->push_back(')');
56}
57
58template <typename Arg>
59inline void fallbackFormatOneArg(std::string* str, const Arg* arg, long) {
60 str->push_back('(');
61#ifdef FOLLY_HAS_RTTI
62 try {
63 toAppend(folly::demangle(typeid(*arg)), str);
64 str->append(": ");
65 } catch (const std::exception&) {
66 // Ignore the error
67 }
68#endif
69 str->append("<no_string_conversion>)");
70}
71} // namespace detail
72
73template <bool IsInHeaderFile>
74class XlogCategoryInfo;
75class XlogFileScopeInfo;
76
77/**
78 * LogStreamProcessor receives a LogStream and logs it.
79 *
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
82 * use.
83 *
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.
88 */
89class LogStreamProcessor {
90 public:
91 enum AppendType { APPEND };
92 enum FormatType { FORMAT };
93
94 /**
95 * LogStreamProcessor constructor for use with a LOG() macro with no extra
96 * arguments.
97 *
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.)
102 */
103 LogStreamProcessor(
104 const LogCategory* category,
105 LogLevel level,
106 folly::StringPiece filename,
107 unsigned int lineNumber,
108 folly::StringPiece functionName,
109 AppendType) noexcept;
110
111 /**
112 * LogStreamProcessor constructor for use with a LOG() macro with arguments
113 * to be concatenated with folly::to<std::string>()
114 *
115 * Note that the filename argument is not copied. The caller should ensure
116 * that it points to storage that will remain valid for the lifetime of the
117 * LogStreamProcessor. (This is always the case for the __FILE__
118 * preprocessor macro.)
119 */
120 template <typename... Args>
121 LogStreamProcessor(
122 const LogCategory* category,
123 LogLevel level,
124 folly::StringPiece filename,
125 unsigned int lineNumber,
126 folly::StringPiece functionName,
127 AppendType,
128 Args&&... args) noexcept
129 : LogStreamProcessor(
130 category,
131 level,
132 filename,
133 lineNumber,
134 functionName,
135 INTERNAL,
136 createLogString(std::forward<Args>(args)...)) {}
137
138 /**
139 * LogStreamProcessor constructor for use with a LOG() macro with arguments
140 * to be concatenated with folly::to<std::string>()
141 *
142 * Note that the filename argument is not copied. The caller should ensure
143 * that it points to storage that will remain valid for the lifetime of the
144 * LogStreamProcessor. (This is always the case for the __FILE__
145 * preprocessor macro.)
146 */
147 template <typename... Args>
148 LogStreamProcessor(
149 const LogCategory* category,
150 LogLevel level,
151 folly::StringPiece filename,
152 unsigned int lineNumber,
153 folly::StringPiece functionName,
154 FormatType,
155 folly::StringPiece fmt,
156 Args&&... args) noexcept
157 : LogStreamProcessor(
158 category,
159 level,
160 filename,
161 lineNumber,
162 functionName,
163 INTERNAL,
164 formatLogString(fmt, std::forward<Args>(args)...)) {}
165
166 /*
167 * Versions of the above constructors for use in XLOG() statements.
168 *
169 * These are defined separately from the above constructor so that the work
170 * of initializing the XLOG LogCategory data is done in a separate function
171 * body defined in LogStreamProcessor.cpp. We intentionally want to avoid
172 * inlining this work at every XLOG() statement, to reduce the emitted code
173 * size.
174 */
175 LogStreamProcessor(
176 XlogCategoryInfo<true>* categoryInfo,
177 LogLevel level,
178 folly::StringPiece categoryName,
179 bool isCategoryNameOverridden,
180 folly::StringPiece filename,
181 unsigned int lineNumber,
182 folly::StringPiece functionName,
183 AppendType) noexcept;
184 template <typename... Args>
185 LogStreamProcessor(
186 XlogCategoryInfo<true>* categoryInfo,
187 LogLevel level,
188 folly::StringPiece categoryName,
189 bool isCategoryNameOverridden,
190 folly::StringPiece filename,
191 unsigned int lineNumber,
192 folly::StringPiece functionName,
193 AppendType,
194 Args&&... args) noexcept
195 : LogStreamProcessor(
196 categoryInfo,
197 level,
198 categoryName,
199 isCategoryNameOverridden,
200 filename,
201 lineNumber,
202 functionName,
203 INTERNAL,
204 createLogString(std::forward<Args>(args)...)) {}
205 template <typename... Args>
206 LogStreamProcessor(
207 XlogCategoryInfo<true>* categoryInfo,
208 LogLevel level,
209 folly::StringPiece categoryName,
210 bool isCategoryNameOverridden,
211 folly::StringPiece filename,
212 unsigned int lineNumber,
213 folly::StringPiece functionName,
214 FormatType,
215 folly::StringPiece fmt,
216 Args&&... args) noexcept
217 : LogStreamProcessor(
218 categoryInfo,
219 level,
220 categoryName,
221 isCategoryNameOverridden,
222 filename,
223 lineNumber,
224 functionName,
225 INTERNAL,
226 formatLogString(fmt, std::forward<Args>(args)...)) {}
227
228#ifdef __INCLUDE_LEVEL__
229 /*
230 * Versions of the above constructors to use in XLOG() macros that appear in
231 * .cpp files. These are only used if the compiler supports the
232 * __INCLUDE_LEVEL__ macro, which we need to determine that the XLOG()
233 * statement is not in a header file.
234 *
235 * These behave identically to the XlogCategoryInfo<true> versions of the
236 * APIs, but slightly more optimized, and allow the XLOG() code to avoid
237 * storing category information at each XLOG() call site.
238 */
239 LogStreamProcessor(
240 XlogFileScopeInfo* fileScopeInfo,
241 LogLevel level,
242 folly::StringPiece filename,
243 unsigned int lineNumber,
244 folly::StringPiece functionName,
245 AppendType) noexcept;
246 LogStreamProcessor(
247 XlogFileScopeInfo* fileScopeInfo,
248 LogLevel level,
249 folly::StringPiece /* categoryName */,
250 bool /* isCategoryNameOverridden */,
251 folly::StringPiece filename,
252 unsigned int lineNumber,
253 folly::StringPiece functionName,
254 AppendType) noexcept
255 : LogStreamProcessor(
256 fileScopeInfo,
257 level,
258 filename,
259 lineNumber,
260 functionName,
261 APPEND) {}
262 template <typename... Args>
263 LogStreamProcessor(
264 XlogFileScopeInfo* fileScopeInfo,
265 LogLevel level,
266 folly::StringPiece /* categoryName */,
267 bool /* isCategoryNameOverridden */,
268 folly::StringPiece filename,
269 unsigned int lineNumber,
270 folly::StringPiece functionName,
271 AppendType,
272 Args&&... args) noexcept
273 : LogStreamProcessor(
274 fileScopeInfo,
275 level,
276 filename,
277 lineNumber,
278 functionName,
279 INTERNAL,
280 createLogString(std::forward<Args>(args)...)) {}
281 template <typename... Args>
282 LogStreamProcessor(
283 XlogFileScopeInfo* fileScopeInfo,
284 LogLevel level,
285 folly::StringPiece /* categoryName */,
286 bool /* isCategoryNameOverridden */,
287 folly::StringPiece filename,
288 unsigned int lineNumber,
289 folly::StringPiece functionName,
290 FormatType,
291 folly::StringPiece fmt,
292 Args&&... args) noexcept
293 : LogStreamProcessor(
294 fileScopeInfo,
295 level,
296 filename,
297 lineNumber,
298 functionName,
299 INTERNAL,
300 formatLogString(fmt, std::forward<Args>(args)...)) {}
301#endif
302
303 ~LogStreamProcessor() noexcept;
304
305 /**
306 * This version of operator&() is typically used when the user specifies
307 * log arguments using the << stream operator. The operator<<() generally
308 * returns a std::ostream&
309 */
310 void operator&(std::ostream& stream) noexcept;
311
312 /**
313 * This version of operator&() is used when no extra arguments are specified
314 * with the << operator. In this case the & operator is applied directly to
315 * the temporary LogStream object.
316 */
317 void operator&(LogStream&& stream) noexcept;
318
319 std::ostream& stream() noexcept {
320 return stream_;
321 }
322
323 void logNow() noexcept;
324
325 private:
326 enum InternalType { INTERNAL };
327 LogStreamProcessor(
328 const LogCategory* category,
329 LogLevel level,
330 folly::StringPiece filename,
331 unsigned int lineNumber,
332 folly::StringPiece functionName,
333 InternalType,
334 std::string&& msg) noexcept;
335 LogStreamProcessor(
336 XlogCategoryInfo<true>* categoryInfo,
337 LogLevel level,
338 folly::StringPiece categoryName,
339 bool isCategoryNameOverridden,
340 folly::StringPiece filename,
341 unsigned int lineNumber,
342 folly::StringPiece functionName,
343 InternalType,
344 std::string&& msg) noexcept;
345 LogStreamProcessor(
346 XlogFileScopeInfo* fileScopeInfo,
347 LogLevel level,
348 folly::StringPiece filename,
349 unsigned int lineNumber,
350 folly::StringPiece functionName,
351 InternalType,
352 std::string&& msg) noexcept;
353
354 std::string extractMessageString(LogStream& stream) noexcept;
355
356 /**
357 * Construct a log message string using folly::to<std::string>()
358 *
359 * This function attempts to avoid throwing exceptions. If an error occurs
360 * during formatting, a message including the error details is returned
361 * instead. This is done to help ensure that log statements do not generate
362 * exceptions, but instead just log an error string when something goes wrong.
363 */
364 template <typename... Args>
365 FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
366 try {
367 return folly::to<std::string>(std::forward<Args>(args)...);
368 } catch (const std::exception& ex) {
369 // This most likely means there was some error converting the arguments
370 // to strings. Handle the exception here, rather than letting it
371 // propagate up, since callers generally do not expect log statements to
372 // throw.
373 //
374 // Just log an error message letting indicating that something went wrong
375 // formatting the log message.
376 return folly::to<std::string>(
377 "error constructing log message: ", ex.what());
378 }
379 }
380
381 /**
382 * Construct a log message string using folly::sformat()
383 *
384 * This function attempts to avoid throwing exceptions. If an error occurs
385 * during formatting, a message including the error details is returned
386 * instead. This is done to help ensure that log statements do not generate
387 * exceptions, but instead just log an error string when something goes wrong.
388 */
389 template <typename... Args>
390 FOLLY_NOINLINE std::string formatLogString(
391 folly::StringPiece fmt,
392 const Args&... args) noexcept {
393 try {
394 return folly::sformat(fmt, args...);
395 } catch (const std::exception& ex) {
396 // This most likely means that the caller had a bug in their format
397 // string/arguments. Handle the exception here, rather than letting it
398 // propagate up, since callers generally do not expect log statements to
399 // throw.
400 //
401 // Log the format string and as much of the arguments as we can convert,
402 // to aid debugging.
403 std::string result;
404 result.append("error formatting log message: ");
405 result.append(ex.what());
406 result.append("; format string: \"");
407 result.append(fmt.data(), fmt.size());
408 result.append("\", arguments: ");
409 fallbackFormat(&result, args...);
410 return result;
411 }
412 }
413
414 /**
415 * Helper function generate a fallback version of the arguments in case
416 * folly::sformat() throws an exception.
417 *
418 * This attempts to convert each argument to a string using a similar
419 * mechanism to folly::to<std::string>(), if supported.
420 */
421 template <typename Arg1, typename... Args>
422 void
423 fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
424 detail::fallbackFormatOneArg(str, &arg1, 0);
425 str->append(", ");
426 fallbackFormat(str, remainder...);
427 }
428
429 template <typename Arg>
430 void fallbackFormat(std::string* str, const Arg& arg) {
431 detail::fallbackFormatOneArg(str, &arg, 0);
432 }
433
434 const LogCategory* const category_;
435 LogLevel const level_;
436 folly::StringPiece filename_;
437 unsigned int lineNumber_;
438 folly::StringPiece functionName_;
439 std::string message_;
440 LogStream stream_;
441};
442
443/**
444 * LogStreamVoidify() is a helper class used in the FB_LOG() and XLOG() macros.
445 *
446 * It's only purpose is to provide an & operator overload that returns void.
447 * This allows the log macros to expand roughly to:
448 *
449 * (logEnabled) ? (void)0
450 * : LogStreamVoidify{} & LogStreamProcessor{}.stream() << "msg";
451 *
452 * This enables the right hand (':') side of the ternary ? expression to have a
453 * void type, and allows various streaming operator expressions to be placed on
454 * the right hand side of the expression.
455 *
456 * Operator & is used since it has higher precedence than ?:, but lower
457 * precedence than <<.
458 *
459 * This class is templated on whether the log message is fatal so that the
460 * operator& can be declared [[noreturn]] for fatal log messages. This
461 * prevents the compiler from complaining about functions that do not return a
462 * value after a fatal log statement.
463 */
464template <bool Fatal>
465class LogStreamVoidify {
466 public:
467 /**
468 * In the default (non-fatal) case, the & operator implementation is a no-op.
469 *
470 * We perform the actual logging in the LogStreamProcessor destructor. It
471 * feels slightly hacky to perform logging in the LogStreamProcessor
472 * destructor instead of here, since the LogStreamProcessor destructor is not
473 * evaluated until the very end of the statement. In practice log
474 * statements really shouldn't be in the middle of larger statements with
475 * other side effects, so this ordering distinction shouldn't make much
476 * difference.
477 *
478 * However, by keeping this function a no-op we reduce the amount of code
479 * generated for log statements. This function call can be completely
480 * eliminated by the compiler, leaving only the LogStreamProcessor destructor
481 * invocation, which cannot be eliminated.
482 */
483 void operator&(std::ostream&)noexcept {}
484};
485
486template <>
487class LogStreamVoidify<true> {
488 public:
489 /**
490 * A specialized noreturn version of operator&() for fatal log statements.
491 */
492 [[noreturn]] void operator&(std::ostream&);
493};
494
495/**
496 * logDisabledHelper() is invoked in FB_LOG() and XLOG() statements if the log
497 * admittance check fails.
498 *
499 * This function exists solely to ensure that both sides of the log check are
500 * marked [[noreturn]] for fatal log messages. This allows the compiler to
501 * recognize that the full statement is noreturn, preventing warnings about
502 * missing return statements after fatal log messages.
503 *
504 * Unfortunately it does not appear possible to get the compiler to recognize
505 * that the disabled side of the log statement should never be reached for
506 * fatal messages. Even if we make the check something like
507 * `(isLogLevelFatal(level) || realCheck)`, where isLogLevelFatal() is
508 * constexpr, this is not sufficient for gcc or clang to recognize that the
509 * full expression is noreturn.
510 *
511 * Ideally this would just be a template function specialized on a boolean
512 * IsFatal parameter. Unfortunately this triggers a bug in clang, which does
513 * not like differing noreturn behavior for different template instantiations.
514 * Therefore we overload on integral_constant instead.
515 *
516 * clang-format also doesn't do a good job understanding this code and figuring
517 * out how to format it.
518 */
519// clang-format off
520inline void logDisabledHelper(std::false_type) noexcept {}
521[[noreturn]] void logDisabledHelper(std::true_type) noexcept;
522// clang-format on
523} // namespace folly
524