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