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