2 * Copyright 2004-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <folly/Likely.h>
19 #include <folly/Range.h>
20 #include <folly/experimental/logging/LogStream.h>
21 #include <folly/experimental/logging/Logger.h>
22 #include <folly/experimental/logging/LoggerDB.h>
25 * This file contains the XLOG() and XLOGF() macros.
27 * These macros make it easy to use the logging library without having to
28 * manually pick log category names. All XLOG() and XLOGF() statements in a
29 * given file automatically use a LogCategory based on the current file name.
31 * For instance, in src/foo/bar.cpp, the default log category name will be
34 * If desired, the log category name used by XLOG() in a .cpp file may be
35 * overridden using XLOG_SET_CATEGORY() macro.
39 * Log a message to this file's default log category.
41 * By default the log category name is automatically picked based on the
42 * current filename. In src/foo/bar.cpp the log category name "src.foo.bar"
43 * will be used. In "lib/stuff/foo.h" the log category name will be
46 * Note that the filename is based on the __FILE__ macro defined by the
47 * compiler. This is typically dependent on the filename argument that you
48 * give to the compiler. For example, if you compile src/foo/bar.cpp by
49 * invoking the compiler inside src/foo and only give it "bar.cpp" as an
50 * argument, the category name will simply be "bar". In general XLOG() works
51 * best if you always invoke the compiler from the root directory of your
54 #define XLOG(level, ...) \
56 ::folly::LogLevel::level, \
57 ::folly::LogStreamProcessor::APPEND, \
61 * Log a message to this file's default log category, using a format string.
63 #define XLOGF(level, fmt, arg1, ...) \
65 ::folly::LogLevel::level, \
66 ::folly::LogStreamProcessor::FORMAT, \
72 * Helper macro used to implement XLOG() and XLOGF()
74 * Beware that the level argument is evalutated twice.
76 * This macro is somewhat tricky:
78 * - In order to support streaming argument support (with the << operator),
79 * the macro must expand to a single ternary ? expression. This is the only
80 * way we can avoid evaluating the log arguments if the log check fails,
81 * and still have the macro behave as expected when used as the body of an if
84 * - We need to store some static-scope local state in order to track the
85 * LogCategory to use. This is a bit tricky to do and still meet the
86 * requirements of being a single expression, but fortunately static
87 * variables inside a lambda work for this purpose.
89 * Inside header files, each XLOG() statement defines to static variables:
90 * - the LogLevel for this category
91 * - a pointer to the LogCategory
93 * If the __INCLUDE_LEVEL__ macro is available (both gcc and clang support
94 * this), then we we can detect when we are inside a .cpp file versus a
95 * header file. If we are inside a .cpp file, we can avoid declaring these
96 * variables once per XLOG() statement, and instead we only declare one copy
97 * of these variables for the entire file.
99 * - We want to make sure this macro is safe to use even from inside static
100 * initialization code that runs before main. We also want to make the log
101 * admittance check as cheap as possible, so that disabled debug logs have
102 * minimal overhead, and can be left in place even in performance senstive
105 * In order to do this, we rely on zero-initialization of variables with
106 * static storage duration. The LogLevel variable will always be
107 * 0-initialized before any code runs. Therefore the very first time an
108 * XLOG() statement is hit the initial log level check will always pass
109 * (since all level values are greater or equal to 0), and we then do a
110 * second check to see if the log level and category variables need to be
111 * initialized. On all subsequent calls, disabled log statements can be
112 * skipped with just a single check of the LogLevel.
114 #define XLOG_IMPL(level, type, ...) \
115 (!XLOG_IS_ON_IMPL(level)) ? static_cast<void>(0) \
116 : ::folly::LogStreamProcessor( \
117 XLOG_GET_CATEGORY(), \
126 * Check if and XLOG() statement with the given log level would be enabled.
128 #define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
131 * Helper macro to implement of XLOG_IS_ON()
133 * This macro is used in the XLOG() implementation, and therefore must be as
134 * cheap as possible. It stores the category's LogLevel as a local static
135 * variable. The very first time this macro is evaluated it will look up the
136 * correct LogCategory and initialize the LogLevel. Subsequent calls then
137 * are only a single conditional log level check.
139 * The LogCategory object keeps track of this local LogLevel variable and
140 * automatically keeps it up-to-date when the category's effective level is
143 * Most of this code must live in the macro definition itself, and cannot be
144 * moved into a helper function: The getXlogCategoryName() call must be made as
145 * part of the macro expansion in order to work correctly. We also want to
146 * avoid calling it whenever possible. Therefore most of the logic must be
147 * done in the macro expansion.
149 * See XlogLevelInfo for the implementation details.
151 #define XLOG_IS_ON_IMPL(level) \
153 static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> _xlogLevel_; \
154 const auto _xlogLevelToCheck_ = (level); \
156 * Do an initial relaxed check. If this fails we know the category level \
157 * is initialized and the log admittance check failed. \
158 * Use LIKELY() to optimize for the case of disabled debug statements: \
159 * we disabled debug statements to be cheap. If the log message is \
160 * enabled then this check will still be minimal perf overhead compared to \
161 * the overall cost of logging it. \
164 _xlogLevelToCheck_ < \
165 _xlogLevel_.getLevel( \
166 &_xlogFileScopeInfo_, std::memory_order_relaxed))) { \
170 * Load the level value with memory_order_acquire, and check \
171 * to see if it is initialized or not. \
173 auto _xlogCurrentLevel_ = \
174 _xlogLevel_.getLevel(&_xlogFileScopeInfo_, std::memory_order_acquire); \
175 if (UNLIKELY(_xlogCurrentLevel_ == ::folly::LogLevel::UNINITIALIZED)) { \
176 _xlogCurrentLevel_ = _xlogLevel_.init( \
177 getXlogCategoryName(__FILE__), &_xlogFileScopeInfo_); \
179 return _xlogLevelToCheck_ >= _xlogCurrentLevel_; \
183 * Get a pointer to the LogCategory that will be used by XLOG() statements in
186 * This macro is used in the XLOG() implementation, and therefore must be as
187 * cheap as possible. It stores the LogCategory* pointer as a local static
188 * variable. Only the first invocation has to look up the log category by
189 * name. Subsequent invocations re-use the already looked-up LogCategory
192 * Most of this code must live in the macro definition itself, and cannot be
193 * moved into a helper function: The getXlogCategoryName() call must be made as
194 * part of the macro expansion in order to work correctly. We also want to
195 * avoid calling it whenever possible. Therefore most of the logic must be
196 * done in the macro expansion.
198 * See XlogCategoryInfo for the implementation details.
200 #define XLOG_GET_CATEGORY() \
202 static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> _xlogCategory_; \
203 if (!_xlogCategory_.isInitialized()) { \
204 return _xlogCategory_.init(getXlogCategoryName(__FILE__)); \
206 return _xlogCategory_.getCategory(&_xlogFileScopeInfo_); \
210 * XLOG_SET_CATEGORY() can be used to explicitly define the log category name
211 * used by all XLOG() and XLOGF() calls in this translation unit.
213 * This overrides the default behavior of picking a category name based on the
216 * This should be used at the top-level scope in a .cpp file, before any XLOG()
217 * or XLOGF() macros have been used in the file.
219 * XLOG_SET_CATEGORY() cannot be used inside header files.
221 #ifdef __INCLUDE_LEVEL__
222 #define XLOG_SET_CATEGORY(category) \
225 __INCLUDE_LEVEL__ == 0, \
226 "XLOG_SET_CATEGORY() should not be used in header files"); \
227 inline std::string getXlogCategoryName(const char*) { \
232 #define XLOG_SET_CATEGORY(category) \
234 inline std::string getXlogCategoryName(const char*) { \
241 * XLOG_IS_IN_HEADER_FILE evaluates to false if we can definitively tell if we
242 * are not in a header file. Otherwise, it evaluates to true.
244 #ifdef __INCLUDE_LEVEL__
245 #define XLOG_IS_IN_HEADER_FILE bool(__INCLUDE_LEVEL__ > 0)
247 // Without __INCLUDE_LEVEL__ we canot tell if we are in a header file or not,
248 // and must pessimstically assume we are always in a header file.
249 #define XLOG_IS_IN_HEADER_FILE true
254 class XlogFileScopeInfo {
256 #ifdef __INCLUDE_LEVEL__
257 std::atomic<::folly::LogLevel> level;
258 ::folly::LogCategory* category;
263 * A file-static XlogLevelInfo and XlogCategoryInfo object is declared for each
266 * We intentionally do not provide constructors for these structures, and rely
267 * on their members to be zero-initialized when the program starts. This
268 * ensures that everything will work as desired even if XLOG() statements are
269 * used during dynamic object initialization before main().
271 template <bool IsInHeaderFile>
272 struct XlogLevelInfo {
274 inline LogLevel getLevel(XlogFileScopeInfo*, std::memory_order order) {
275 return level_.load(order);
278 inline LogLevel init(folly::StringPiece categoryName, XlogFileScopeInfo*) {
279 return LoggerDB::get()->xlogInit(categoryName, &level_, nullptr);
283 // This member will always be zero-initialized on program start.
284 std::atomic<LogLevel> level_;
287 template <bool IsInHeaderFile>
288 struct XlogCategoryInfo {
290 bool isInitialized() {
291 return isInitialized_.load(std::memory_order_acquire);
294 LogCategory* init(folly::StringPiece categoryName) {
295 return LoggerDB::get()->xlogInitCategory(
296 categoryName, &category_, &isInitialized_);
299 LogCategory* getCategory(XlogFileScopeInfo*) {
304 // These variables will always be zero-initialized on program start.
305 std::atomic<bool> isInitialized_;
306 LogCategory* category_;
309 #ifdef __INCLUDE_LEVEL__
311 * Specialization of XlogLevelInfo for XLOG() statements in the .cpp file being
312 * compiled. In this case we only define a single file-static LogLevel object
313 * for the entire file, rather than defining one for each XLOG() statement.
316 struct XlogLevelInfo<false> {
318 inline LogLevel getLevel(
319 XlogFileScopeInfo* fileScopeInfo,
320 std::memory_order order) {
321 return fileScopeInfo->level.load(order);
324 inline LogLevel init(
325 folly::StringPiece categoryName,
326 XlogFileScopeInfo* fileScopeInfo) {
327 return LoggerDB::get()->xlogInit(
328 categoryName, &fileScopeInfo->level, &fileScopeInfo->category);
333 * Specialization of XlogCategoryInfo for XLOG() statements in the .cpp file
334 * being compiled. In this case we only define a single file-static LogLevel
335 * object for the entire file, rather than defining one for each XLOG()
339 struct XlogCategoryInfo<false> {
341 constexpr bool isInitialized() {
342 // XlogLevelInfo<false>::check() is always called before XlogCategoryInfo
343 // is used, and it will will have already initialized fileScopeInfo.
344 // Therefore we never have to check if it is initialized yet here.
347 LogCategory* init(folly::StringPiece) {
348 // This method is never used given that isInitialized() always returns true
352 LogCategory* getCategory(XlogFileScopeInfo* fileScopeInfo) {
353 return fileScopeInfo->category;
359 * Get the default XLOG() category name for the given filename.
361 * This function returns the category name that will be used by XLOG() if
362 * XLOG_SET_CATEGORY() has not been used.
364 std::string getXlogCategoryNameForFile(folly::StringPiece filename);
368 * We intentionally use an unnamed namespace inside a header file here.
370 * We want each .cpp file that uses xlog.h to get its own separate
371 * implementation of the following functions and variables.
375 * The default getXlogCategoryName() implementation.
376 * This will be used if XLOG_SET_CATEGORY() has not been used yet.
378 * This is a template purely so that XLOG_SET_CATEGORY() can define a more
379 * specific version if desired, allowing XLOG_SET_CATEGORY() to override this
380 * implementation once it has been used. The template paramete
382 template <typename T>
383 inline std::string getXlogCategoryName(const T* filename) {
384 return ::folly::getXlogCategoryNameForFile(filename);
388 * File-scope LogLevel and LogCategory data for XLOG() statements,
389 * if __INCLUDE_LEVEL__ is supported.
391 * This allows us to only have one LogLevel and LogCategory pointer for the
392 * entire .cpp file, rather than needing a separate copy for each XLOG()
395 ::folly::XlogFileScopeInfo _xlogFileScopeInfo_;