logging: add support for streaming operators
[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/Conv.h>
19 #include <folly/Format.h>
20 #include <folly/experimental/logging/LogCategory.h>
21 #include <folly/experimental/logging/LogMessage.h>
22
23 namespace folly {
24
25 class LogStream;
26
27 /**
28  * LogStreamProcessor receives a LogStream and logs it.
29  *
30  * This class is primarily intended to be used through the FB_LOG*() macros.
31  * Its API is designed to support these macros, and is not designed for other
32  * use.
33  *
34  * The operator&() method is used to trigger the logging.
35  * This operator is used because it has lower precedence than <<, but higher
36  * precedence than the ? ternary operator, allowing it to bind with the correct
37  * precedence in the log macro implementations.
38  */
39 class LogStreamProcessor {
40  public:
41   enum AppendType { APPEND };
42   enum FormatType { FORMAT };
43
44   /**
45    * LogStreamProcessor constructor for use with a LOG() macro with no extra
46    * arguments.
47    *
48    * Note that the filename argument is not copied.  The caller should ensure
49    * that it points to storage that will remain valid for the lifetime of the
50    * LogStreamProcessor.  (This is always the case for the __FILE__
51    * preprocessor macro.)
52    */
53   LogStreamProcessor(
54       const LogCategory* category,
55       LogLevel level,
56       folly::StringPiece filename,
57       unsigned int lineNumber,
58       AppendType) noexcept
59       : category_{category},
60         level_{level},
61         filename_{filename},
62         lineNumber_{lineNumber} {}
63
64   /**
65    * LogStreamProcessor constructor for use with a LOG() macro with arguments
66    * to be concatenated with folly::to<std::string>()
67    *
68    * Note that the filename argument is not copied.  The caller should ensure
69    * that it points to storage that will remain valid for the lifetime of the
70    * LogStreamProcessor.  (This is always the case for the __FILE__
71    * preprocessor macro.)
72    */
73   template <typename... Args>
74   LogStreamProcessor(
75       const LogCategory* category,
76       LogLevel level,
77       const char* filename,
78       unsigned int lineNumber,
79       AppendType,
80       Args&&... args) noexcept
81       : category_{category},
82         level_{level},
83         filename_{filename},
84         lineNumber_{lineNumber},
85         message_{createLogString(std::forward<Args>(args)...)} {}
86
87   /**
88    * LogStreamProcessor constructor for use with a LOG() macro with arguments
89    * to be concatenated with folly::to<std::string>()
90    *
91    * Note that the filename argument is not copied.  The caller should ensure
92    * that it points to storage that will remain valid for the lifetime of the
93    * LogStreamProcessor.  (This is always the case for the __FILE__
94    * preprocessor macro.)
95    */
96   template <typename... Args>
97   LogStreamProcessor(
98       const LogCategory* category,
99       LogLevel level,
100       const char* filename,
101       unsigned int lineNumber,
102       FormatType,
103       folly::StringPiece fmt,
104       Args&&... args) noexcept
105       : category_{category},
106         level_{level},
107         filename_{filename},
108         lineNumber_{lineNumber},
109         message_{formatLogString(fmt, std::forward<Args>(args)...)} {}
110
111   /**
112    * This version of operator&() is typically used when the user specifies
113    * log arguments using the << stream operator.  The operator<<() generally
114    * returns a std::ostream&
115    */
116   void operator&(std::ostream& stream) noexcept;
117
118   /**
119    * This version of operator&() is used when no extra arguments are specified
120    * with the << operator.  In this case the & operator is applied directly to
121    * the temporary LogStream object.
122    */
123   void operator&(LogStream&& stream) noexcept;
124
125  private:
126   std::string extractMessageString(LogStream& stream) noexcept;
127
128   /**
129    * Construct a log message string using folly::to<std::string>()
130    *
131    * This function attempts to avoid throwing exceptions.  If an error occurs
132    * during formatting, a message including the error details is returned
133    * instead.  This is done to help ensure that log statements do not generate
134    * exceptions, but instead just log an error string when something goes wrong.
135    */
136   template <typename... Args>
137   std::string createLogString(Args&&... args) noexcept {
138     try {
139       return folly::to<std::string>(std::forward<Args>(args)...);
140     } catch (const std::exception& ex) {
141       // This most likely means there was some error converting the arguments
142       // to strings.  Handle the exception here, rather than letting it
143       // propagate up, since callers generally do not expect log statements to
144       // throw.
145       //
146       // Just log an error message letting indicating that something went wrong
147       // formatting the log message.
148       return folly::to<std::string>(
149           "error constructing log message: ", ex.what());
150     }
151   }
152
153   /**
154    * Construct a log message string using folly::sformat()
155    *
156    * This function attempts to avoid throwing exceptions.  If an error occurs
157    * during formatting, a message including the error details is returned
158    * instead.  This is done to help ensure that log statements do not generate
159    * exceptions, but instead just log an error string when something goes wrong.
160    */
161   template <typename... Args>
162   std::string formatLogString(folly::StringPiece fmt, Args&&... args) noexcept {
163     try {
164       return folly::sformat(fmt, std::forward<Args>(args)...);
165     } catch (const std::exception& ex) {
166       // This most likely means that the caller had a bug in their format
167       // string/arguments.  Handle the exception here, rather than letting it
168       // propagate up, since callers generally do not expect log statements to
169       // throw.
170       //
171       // Log the format string by itself, to help the developer at least
172       // identify the buggy format string in their code.
173       return folly::to<std::string>(
174           "error formatting log message: ",
175           ex.what(),
176           "; format string: \"",
177           fmt,
178           "\"");
179     }
180   }
181
182   const LogCategory* const category_;
183   LogLevel const level_;
184   folly::StringPiece filename_;
185   unsigned int lineNumber_;
186   std::string message_;
187 };
188 }