Consistently have the namespace closing comment
[folly.git] / folly / experimental / logging / LogStreamProcessor.h
1 /*
2  * Copyright 2004-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/experimental/logging/LogCategory.h>
24 #include <folly/experimental/logging/LogMessage.h>
25 #include <folly/experimental/logging/LogStream.h>
26 #include <cstdlib>
27
28 namespace 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  */
38 namespace detail {
39 /* using override */
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>()) {
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
58 template <typename Arg>
59 inline 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
73 template <bool IsInHeaderFile>
74 class XlogCategoryInfo;
75 class 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  */
89 class 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       AppendType) noexcept;
109
110   /**
111    * LogStreamProcessor constructor for use with a LOG() macro with arguments
112    * to be concatenated with folly::to<std::string>()
113    *
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.)
118    */
119   template <typename... Args>
120   LogStreamProcessor(
121       const LogCategory* category,
122       LogLevel level,
123       folly::StringPiece filename,
124       unsigned int lineNumber,
125       AppendType,
126       Args&&... args) noexcept
127       : LogStreamProcessor(
128             category,
129             level,
130             filename,
131             lineNumber,
132             INTERNAL,
133             createLogString(std::forward<Args>(args)...)) {}
134
135   /**
136    * LogStreamProcessor constructor for use with a LOG() macro with arguments
137    * to be concatenated with folly::to<std::string>()
138    *
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.)
143    */
144   template <typename... Args>
145   LogStreamProcessor(
146       const LogCategory* category,
147       LogLevel level,
148       folly::StringPiece filename,
149       unsigned int lineNumber,
150       FormatType,
151       folly::StringPiece fmt,
152       Args&&... args) noexcept
153       : LogStreamProcessor(
154             category,
155             level,
156             filename,
157             lineNumber,
158             INTERNAL,
159             formatLogString(fmt, std::forward<Args>(args)...)) {}
160
161   /*
162    * Versions of the above constructors for use in XLOG() statements.
163    *
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
168    * size.
169    */
170   LogStreamProcessor(
171       XlogCategoryInfo<true>* categoryInfo,
172       LogLevel level,
173       folly::StringPiece categoryName,
174       bool isCategoryNameOverridden,
175       folly::StringPiece filename,
176       unsigned int lineNumber,
177       AppendType) noexcept;
178   template <typename... Args>
179   LogStreamProcessor(
180       XlogCategoryInfo<true>* categoryInfo,
181       LogLevel level,
182       folly::StringPiece categoryName,
183       bool isCategoryNameOverridden,
184       folly::StringPiece filename,
185       unsigned int lineNumber,
186       AppendType,
187       Args&&... args) noexcept
188       : LogStreamProcessor(
189             categoryInfo,
190             level,
191             categoryName,
192             isCategoryNameOverridden,
193             filename,
194             lineNumber,
195             INTERNAL,
196             createLogString(std::forward<Args>(args)...)) {}
197   template <typename... Args>
198   LogStreamProcessor(
199       XlogCategoryInfo<true>* categoryInfo,
200       LogLevel level,
201       folly::StringPiece categoryName,
202       bool isCategoryNameOverridden,
203       folly::StringPiece filename,
204       unsigned int lineNumber,
205       FormatType,
206       folly::StringPiece fmt,
207       Args&&... args) noexcept
208       : LogStreamProcessor(
209             categoryInfo,
210             level,
211             categoryName,
212             isCategoryNameOverridden,
213             filename,
214             lineNumber,
215             INTERNAL,
216             formatLogString(fmt, std::forward<Args>(args)...)) {}
217
218 #ifdef __INCLUDE_LEVEL__
219   /*
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.
224    *
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.
228    */
229   LogStreamProcessor(
230       XlogFileScopeInfo* fileScopeInfo,
231       LogLevel level,
232       folly::StringPiece filename,
233       unsigned int lineNumber,
234       AppendType) noexcept;
235   LogStreamProcessor(
236       XlogFileScopeInfo* fileScopeInfo,
237       LogLevel level,
238       folly::StringPiece /* categoryName */,
239       bool /* isCategoryNameOverridden */,
240       folly::StringPiece filename,
241       unsigned int lineNumber,
242       AppendType) noexcept
243       : LogStreamProcessor(fileScopeInfo, level, filename, lineNumber, APPEND) {
244   }
245   template <typename... Args>
246   LogStreamProcessor(
247       XlogFileScopeInfo* fileScopeInfo,
248       LogLevel level,
249       folly::StringPiece /* categoryName */,
250       bool /* isCategoryNameOverridden */,
251       folly::StringPiece filename,
252       unsigned int lineNumber,
253       AppendType,
254       Args&&... args) noexcept
255       : LogStreamProcessor(
256             fileScopeInfo,
257             level,
258             filename,
259             lineNumber,
260             INTERNAL,
261             createLogString(std::forward<Args>(args)...)) {}
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       FormatType,
271       folly::StringPiece fmt,
272       Args&&... args) noexcept
273       : LogStreamProcessor(
274             fileScopeInfo,
275             level,
276             filename,
277             lineNumber,
278             INTERNAL,
279             formatLogString(fmt, std::forward<Args>(args)...)) {}
280 #endif
281
282   ~LogStreamProcessor() noexcept;
283
284   /**
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&
288    */
289   void operator&(std::ostream& stream) noexcept;
290
291   /**
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.
295    */
296   void operator&(LogStream&& stream) noexcept;
297
298   std::ostream& stream() noexcept {
299     return stream_;
300   }
301
302   void logNow() noexcept;
303
304  private:
305   enum InternalType { INTERNAL };
306   LogStreamProcessor(
307       const LogCategory* category,
308       LogLevel level,
309       folly::StringPiece filename,
310       unsigned int lineNumber,
311       InternalType,
312       std::string&& msg) noexcept;
313   LogStreamProcessor(
314       XlogCategoryInfo<true>* categoryInfo,
315       LogLevel level,
316       folly::StringPiece categoryName,
317       bool isCategoryNameOverridden,
318       folly::StringPiece filename,
319       unsigned int lineNumber,
320       InternalType,
321       std::string&& msg) noexcept;
322   LogStreamProcessor(
323       XlogFileScopeInfo* fileScopeInfo,
324       LogLevel level,
325       folly::StringPiece filename,
326       unsigned int lineNumber,
327       InternalType,
328       std::string&& msg) noexcept;
329
330   std::string extractMessageString(LogStream& stream) noexcept;
331
332   /**
333    * Construct a log message string using folly::to<std::string>()
334    *
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.
339    */
340   template <typename... Args>
341   FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
342     try {
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
348       // throw.
349       //
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());
354     }
355   }
356
357   /**
358    * Construct a log message string using folly::sformat()
359    *
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.
364    */
365   template <typename... Args>
366   FOLLY_NOINLINE std::string formatLogString(
367       folly::StringPiece fmt,
368       const Args&... args) noexcept {
369     try {
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
375       // throw.
376       //
377       // Log the format string and as much of the arguments as we can convert,
378       // to aid debugging.
379       std::string result;
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...);
386       return result;
387     }
388   }
389
390   /**
391    * Helper function generate a fallback version of the arguments in case
392    * folly::sformat() throws an exception.
393    *
394    * This attempts to convert each argument to a string using a similar
395    * mechanism to folly::to<std::string>(), if supported.
396    */
397   template <typename Arg1, typename... Args>
398   void
399   fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
400     detail::fallbackFormatOneArg(str, &arg1, 0);
401     str->append(", ");
402     fallbackFormat(str, remainder...);
403   }
404
405   template <typename Arg>
406   void fallbackFormat(std::string* str, const Arg& arg) {
407     detail::fallbackFormatOneArg(str, &arg, 0);
408   }
409
410   const LogCategory* const category_;
411   LogLevel const level_;
412   folly::StringPiece filename_;
413   unsigned int lineNumber_;
414   std::string message_;
415   LogStream stream_;
416 };
417
418 /**
419  * LogStreamVoidify() is a helper class used in the FB_LOG() and XLOG() macros.
420  *
421  * It's only purpose is to provide an & operator overload that returns void.
422  * This allows the log macros to expand roughly to:
423  *
424  *   (logEnabled) ? (void)0
425  *                : LogStreamVoidify{} & LogStreamProcessor{}.stream() << "msg";
426  *
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.
430  *
431  * Operator & is used since it has higher precedence than ?:, but lower
432  * precedence than <<.
433  *
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.
438  */
439 template <bool Fatal>
440 class LogStreamVoidify {
441  public:
442   /**
443    * In the default (non-fatal) case, the & operator implementation is a no-op.
444    *
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
451    * difference.
452    *
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.
457    */
458   void operator&(std::ostream&)noexcept {}
459 };
460
461 template <>
462 class LogStreamVoidify<true> {
463  public:
464   /**
465    * A specialized noreturn version of operator&() for fatal log statements.
466    */
467   [[noreturn]] void operator&(std::ostream&);
468 };
469
470 /**
471  * logDisabledHelper() is invoked in FB_LOG() and XLOG() statements if the log
472  * admittance check fails.
473  *
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.
478  *
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.
485  *
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.
490  *
491  * clang-format also doesn't do a good job understanding this code and figuring
492  * out how to format it.
493  */
494 // clang-format off
495 inline void logDisabledHelper(std::integral_constant<bool, false>) noexcept {}
496 [[noreturn]] void logDisabledHelper(
497         std::integral_constant<bool, true>) noexcept;
498 // clang-format on
499 } // namespace folly