logging: improve the AsyncFileWriter flush test()
[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 }
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 constructors for use with XLOG() macros with no extra
112    * arguments.
113    *
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
118    * size.
119    */
120   LogStreamProcessor(
121       XlogCategoryInfo<true>* categoryInfo,
122       LogLevel level,
123       folly::StringPiece categoryName,
124       bool isCategoryNameOverridden,
125       folly::StringPiece filename,
126       unsigned int lineNumber,
127       AppendType) noexcept;
128   LogStreamProcessor(
129       XlogFileScopeInfo* fileScopeInfo,
130       LogLevel level,
131       folly::StringPiece filename,
132       unsigned int lineNumber,
133       AppendType) noexcept;
134   LogStreamProcessor(
135       XlogFileScopeInfo* fileScopeInfo,
136       LogLevel level,
137       folly::StringPiece /* categoryName */,
138       bool /* isCategoryNameOverridden */,
139       folly::StringPiece filename,
140       unsigned int lineNumber,
141       AppendType) noexcept
142       : LogStreamProcessor(fileScopeInfo, level, filename, lineNumber, APPEND) {
143   }
144
145   /**
146    * LogStreamProcessor constructor for use with a LOG() macro with arguments
147    * to be concatenated with folly::to<std::string>()
148    *
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.)
153    */
154   template <typename... Args>
155   LogStreamProcessor(
156       const LogCategory* category,
157       LogLevel level,
158       folly::StringPiece filename,
159       unsigned int lineNumber,
160       AppendType,
161       Args&&... args) noexcept
162       : LogStreamProcessor(
163             category,
164             level,
165             filename,
166             lineNumber,
167             INTERNAL,
168             createLogString(std::forward<Args>(args)...)) {}
169
170   /**
171    * Versions of the above constructor for use in XLOG() statements.
172    */
173   template <typename... Args>
174   LogStreamProcessor(
175       XlogCategoryInfo<true>* categoryInfo,
176       LogLevel level,
177       folly::StringPiece categoryName,
178       bool isCategoryNameOverridden,
179       folly::StringPiece filename,
180       unsigned int lineNumber,
181       AppendType,
182       Args&&... args) noexcept
183       : LogStreamProcessor(
184             categoryInfo,
185             level,
186             categoryName,
187             isCategoryNameOverridden,
188             filename,
189             lineNumber,
190             INTERNAL,
191             createLogString(std::forward<Args>(args)...)) {}
192   template <typename... Args>
193   LogStreamProcessor(
194       XlogFileScopeInfo* fileScopeInfo,
195       LogLevel level,
196       folly::StringPiece /* categoryName */,
197       bool /* isCategoryNameOverridden */,
198       folly::StringPiece filename,
199       unsigned int lineNumber,
200       AppendType,
201       Args&&... args) noexcept
202       : LogStreamProcessor(
203             fileScopeInfo,
204             level,
205             filename,
206             lineNumber,
207             INTERNAL,
208             createLogString(std::forward<Args>(args)...)) {}
209
210   /**
211    * LogStreamProcessor constructor for use with a LOG() macro with arguments
212    * to be concatenated with folly::to<std::string>()
213    *
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.)
218    */
219   template <typename... Args>
220   LogStreamProcessor(
221       const LogCategory* category,
222       LogLevel level,
223       folly::StringPiece filename,
224       unsigned int lineNumber,
225       FormatType,
226       folly::StringPiece fmt,
227       Args&&... args) noexcept
228       : LogStreamProcessor(
229             category,
230             level,
231             filename,
232             lineNumber,
233             INTERNAL,
234             formatLogString(fmt, std::forward<Args>(args)...)) {}
235
236   /**
237    * Versions of the above constructor for use in XLOG() statements.
238    */
239   template <typename... Args>
240   LogStreamProcessor(
241       XlogCategoryInfo<true>* categoryInfo,
242       LogLevel level,
243       folly::StringPiece categoryName,
244       bool isCategoryNameOverridden,
245       folly::StringPiece filename,
246       unsigned int lineNumber,
247       FormatType,
248       folly::StringPiece fmt,
249       Args&&... args) noexcept
250       : LogStreamProcessor(
251             categoryInfo,
252             level,
253             categoryName,
254             isCategoryNameOverridden,
255             filename,
256             lineNumber,
257             INTERNAL,
258             formatLogString(fmt, std::forward<Args>(args)...)) {}
259
260   template <typename... Args>
261   LogStreamProcessor(
262       XlogFileScopeInfo* fileScopeInfo,
263       LogLevel level,
264       folly::StringPiece /* categoryName */,
265       bool /* isCategoryNameOverridden */,
266       folly::StringPiece filename,
267       unsigned int lineNumber,
268       FormatType,
269       folly::StringPiece fmt,
270       Args&&... args) noexcept
271       : LogStreamProcessor(
272             fileScopeInfo,
273             level,
274             filename,
275             lineNumber,
276             INTERNAL,
277             formatLogString(fmt, std::forward<Args>(args)...)) {}
278
279   ~LogStreamProcessor() noexcept;
280
281   /**
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&
285    */
286   void operator&(std::ostream& stream) noexcept;
287
288   /**
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.
292    */
293   void operator&(LogStream&& stream) noexcept;
294
295   std::ostream& stream() noexcept {
296     return stream_;
297   }
298
299   void logNow() noexcept;
300
301  private:
302   enum InternalType { INTERNAL };
303   LogStreamProcessor(
304       const LogCategory* category,
305       LogLevel level,
306       folly::StringPiece filename,
307       unsigned int lineNumber,
308       InternalType,
309       std::string&& msg) noexcept;
310   LogStreamProcessor(
311       XlogCategoryInfo<true>* categoryInfo,
312       LogLevel level,
313       folly::StringPiece categoryName,
314       bool isCategoryNameOverridden,
315       folly::StringPiece filename,
316       unsigned int lineNumber,
317       InternalType,
318       std::string&& msg) noexcept;
319   LogStreamProcessor(
320       XlogFileScopeInfo* fileScopeInfo,
321       LogLevel level,
322       folly::StringPiece filename,
323       unsigned int lineNumber,
324       InternalType,
325       std::string&& msg) noexcept;
326
327   std::string extractMessageString(LogStream& stream) noexcept;
328
329   /**
330    * Construct a log message string using folly::to<std::string>()
331    *
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.
336    */
337   template <typename... Args>
338   FOLLY_NOINLINE std::string createLogString(Args&&... args) noexcept {
339     try {
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
345       // throw.
346       //
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());
351     }
352   }
353
354   /**
355    * Construct a log message string using folly::sformat()
356    *
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.
361    */
362   template <typename... Args>
363   FOLLY_NOINLINE std::string formatLogString(
364       folly::StringPiece fmt,
365       const Args&... args) noexcept {
366     try {
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
372       // throw.
373       //
374       // Log the format string and as much of the arguments as we can convert,
375       // to aid debugging.
376       std::string result;
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...);
383       return result;
384     }
385   }
386
387   /**
388    * Helper function generate a fallback version of the arguments in case
389    * folly::sformat() throws an exception.
390    *
391    * This attempts to convert each argument to a string using a similar
392    * mechanism to folly::to<std::string>(), if supported.
393    */
394   template <typename Arg1, typename... Args>
395   void
396   fallbackFormat(std::string* str, const Arg1& arg1, const Args&... remainder) {
397     detail::fallbackFormatOneArg(str, &arg1, 0);
398     str->append(", ");
399     fallbackFormat(str, remainder...);
400   }
401
402   template <typename Arg>
403   void fallbackFormat(std::string* str, const Arg& arg) {
404     detail::fallbackFormatOneArg(str, &arg, 0);
405   }
406
407   const LogCategory* const category_;
408   LogLevel const level_;
409   folly::StringPiece filename_;
410   unsigned int lineNumber_;
411   std::string message_;
412   LogStream stream_;
413 };
414
415 /**
416  * LogStreamVoidify() is a helper class used in the FB_LOG() and XLOG() macros.
417  *
418  * It's only purpose is to provide an & operator overload that returns void.
419  * This allows the log macros to expand roughly to:
420  *
421  *   (logEnabled) ? (void)0
422  *                : LogStreamVoidify{} & LogStreamProcessor{}.stream() << "msg";
423  *
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.
427  *
428  * Operator & is used since it has higher precedence than ?:, but lower
429  * precedence than <<.
430  *
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.
435  */
436 template <bool Fatal>
437 class LogStreamVoidify {
438  public:
439   /**
440    * In the default (non-fatal) case, the & operator implementation is a no-op.
441    *
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
448    * difference.
449    *
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.
454    */
455   void operator&(std::ostream&)noexcept {}
456 };
457
458 template <>
459 class LogStreamVoidify<true> {
460  public:
461   /**
462    * A specialized noreturn version of operator&() for fatal log statements.
463    */
464   [[noreturn]] void operator&(std::ostream&);
465 };
466 }