2415ed4acf674bdb3cbb461640c1a1a2f3d50239
[folly.git] / folly / experimental / logging / LogStreamProcessor.cpp
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 #include <folly/experimental/logging/LogStreamProcessor.h>
17
18 #include <folly/experimental/logging/LogStream.h>
19 #include <folly/experimental/logging/xlog.h>
20
21 namespace folly {
22
23 LogStreamProcessor::LogStreamProcessor(
24     const LogCategory* category,
25     LogLevel level,
26     folly::StringPiece filename,
27     unsigned int lineNumber,
28     AppendType) noexcept
29     : LogStreamProcessor(
30           category,
31           level,
32           filename,
33           lineNumber,
34           INTERNAL,
35           std::string()) {}
36
37 LogStreamProcessor::LogStreamProcessor(
38     XlogCategoryInfo<true>* categoryInfo,
39     LogLevel level,
40     folly::StringPiece categoryName,
41     bool isCategoryNameOverridden,
42     folly::StringPiece filename,
43     unsigned int lineNumber,
44     AppendType) noexcept
45     : LogStreamProcessor(
46           categoryInfo,
47           level,
48           categoryName,
49           isCategoryNameOverridden,
50           filename,
51           lineNumber,
52           INTERNAL,
53           std::string()) {}
54
55 LogStreamProcessor::LogStreamProcessor(
56     XlogFileScopeInfo* fileScopeInfo,
57     LogLevel level,
58     folly::StringPiece filename,
59     unsigned int lineNumber,
60     AppendType) noexcept
61     : LogStreamProcessor(
62           fileScopeInfo,
63           level,
64           filename,
65           lineNumber,
66           INTERNAL,
67           std::string()) {}
68
69 LogStreamProcessor::LogStreamProcessor(
70     const LogCategory* category,
71     LogLevel level,
72     folly::StringPiece filename,
73     unsigned int lineNumber,
74     InternalType,
75     std::string&& msg) noexcept
76     : category_{category},
77       level_{level},
78       filename_{filename},
79       lineNumber_{lineNumber},
80       message_{std::move(msg)},
81       stream_{this} {}
82
83 namespace {
84 LogCategory* getXlogCategory(
85     XlogCategoryInfo<true>* categoryInfo,
86     folly::StringPiece categoryName,
87     bool isCategoryNameOverridden) {
88   if (!categoryInfo->isInitialized()) {
89     return categoryInfo->init(categoryName, isCategoryNameOverridden);
90   }
91   return categoryInfo->getCategory(&xlog_detail::xlogFileScopeInfo);
92 }
93
94 LogCategory* getXlogCategory(XlogFileScopeInfo* fileScopeInfo) {
95   // By the time a LogStreamProcessor is created, the XlogFileScopeInfo object
96   // should have already been initialized to perform the log level check.
97   // Therefore we never need to check if it is initialized here.
98   return fileScopeInfo->category;
99 }
100 }
101
102 /**
103  * Construct a LogStreamProcessor from an XlogCategoryInfo.
104  *
105  * We intentionally define this in LogStreamProcessor.cpp instead of
106  * LogStreamProcessor.h to avoid having it inlined at every XLOG() call site,
107  * to reduce the emitted code size.
108  */
109 LogStreamProcessor::LogStreamProcessor(
110     XlogCategoryInfo<true>* categoryInfo,
111     LogLevel level,
112     folly::StringPiece categoryName,
113     bool isCategoryNameOverridden,
114     folly::StringPiece filename,
115     unsigned int lineNumber,
116     InternalType,
117     std::string&& msg) noexcept
118     : category_{getXlogCategory(
119           categoryInfo,
120           categoryName,
121           isCategoryNameOverridden)},
122       level_{level},
123       filename_{filename},
124       lineNumber_{lineNumber},
125       message_{std::move(msg)},
126       stream_{this} {}
127
128 /**
129  * Construct a LogStreamProcessor from an XlogFileScopeInfo.
130  *
131  * We intentionally define this in LogStreamProcessor.cpp instead of
132  * LogStreamProcessor.h to avoid having it inlined at every XLOG() call site,
133  * to reduce the emitted code size.
134  */
135 LogStreamProcessor::LogStreamProcessor(
136     XlogFileScopeInfo* fileScopeInfo,
137     LogLevel level,
138     folly::StringPiece filename,
139     unsigned int lineNumber,
140     InternalType,
141     std::string&& msg) noexcept
142     : category_{getXlogCategory(fileScopeInfo)},
143       level_{level},
144       filename_{filename},
145       lineNumber_{lineNumber},
146       message_{std::move(msg)},
147       stream_{this} {}
148
149 /*
150  * We intentionally define the LogStreamProcessor destructor in
151  * LogStreamProcessor.cpp instead of LogStreamProcessor.h to avoid having it
152  * emitted inline at every log statement site.  This helps reduce the emitted
153  * code size for each log statement.
154  */
155 LogStreamProcessor::~LogStreamProcessor() noexcept {
156   // The LogStreamProcessor destructor is responsible for logging the message.
157   // Doing this in the destructor avoids an separate function call to log the
158   // message being emitted inline at every log statement site.
159   logNow();
160 }
161
162 void LogStreamProcessor::logNow() noexcept {
163   // Note that admitMessage() is not noexcept and theoretically may throw.
164   // However, the only exception that should be possible is std::bad_alloc if
165   // we fail to allocate memory.  We intentionally let our noexcept specifier
166   // crash in that case, since the program likely won't be able to continue
167   // anyway.
168   //
169   // Any other error here is unexpected and we also want to fail hard
170   // in that situation too.
171   category_->admitMessage(LogMessage{category_,
172                                      level_,
173                                      filename_,
174                                      lineNumber_,
175                                      extractMessageString(stream_)});
176 }
177
178 std::string LogStreamProcessor::extractMessageString(
179     LogStream& stream) noexcept {
180   if (stream.empty()) {
181     return std::move(message_);
182   }
183
184   if (message_.empty()) {
185     return stream.extractString();
186   }
187   message_.append(stream.extractString());
188   return std::move(message_);
189 }
190
191 void LogStreamVoidify<true>::operator&(std::ostream& stream) {
192   // Non-fatal log messages wait until the LogStreamProcessor destructor to log
193   // the message.  However for fatal messages we log immediately in the &
194   // operator, since it is marked noreturn.
195   //
196   // This does result in slightly larger emitted code for fatal log messages
197   // (since the operator & call cannot be completely omitted).  However, fatal
198   // log messages should typically be much more rare than non-fatal messages,
199   // so the small amount of extra overhead shouldn't be a big deal.
200   auto& logStream = static_cast<LogStream&>(stream);
201   logStream.getProcessor()->logNow();
202   abort();
203 }
204
205 void logDisabledHelper(std::integral_constant<bool, true>) noexcept {
206   // This function can only be reached if we had a disabled fatal log message.
207   // This should never happen: LogCategory::setLevelLocked() does not allow
208   // setting the threshold for a category lower than FATAL (in production
209   // builds) or DFATAL (in debug builds).
210   abort();
211 }
212 }