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>
26 * This file contains the XLOG() and XLOGF() macros.
28 * These macros make it easy to use the logging library without having to
29 * manually pick log category names. All XLOG() and XLOGF() statements in a
30 * given file automatically use a LogCategory based on the current file name.
32 * For instance, in src/foo/bar.cpp, the default log category name will be
35 * If desired, the log category name used by XLOG() in a .cpp file may be
36 * overridden using XLOG_SET_CATEGORY_NAME() macro.
40 * Log a message to this file's default log category.
42 * By default the log category name is automatically picked based on the
43 * current filename. In src/foo/bar.cpp the log category name "src.foo.bar"
44 * will be used. In "lib/stuff/foo.h" the log category name will be
47 * Note that the filename is based on the __FILE__ macro defined by the
48 * compiler. This is typically dependent on the filename argument that you
49 * give to the compiler. For example, if you compile src/foo/bar.cpp by
50 * invoking the compiler inside src/foo and only give it "bar.cpp" as an
51 * argument, the category name will simply be "bar". In general XLOG() works
52 * best if you always invoke the compiler from the root directory of your
55 #define XLOG(level, ...) \
57 ::folly::LogLevel::level, \
58 ::folly::LogStreamProcessor::APPEND, \
62 * Log a message to this file's default log category, using a format string.
64 #define XLOGF(level, fmt, arg1, ...) \
66 ::folly::LogLevel::level, \
67 ::folly::LogStreamProcessor::FORMAT, \
73 * Helper macro used to implement XLOG() and XLOGF()
75 * Beware that the level argument is evalutated twice.
77 * This macro is somewhat tricky:
79 * - In order to support streaming argument support (with the << operator),
80 * the macro must expand to a single ternary ? expression. This is the only
81 * way we can avoid evaluating the log arguments if the log check fails,
82 * and still have the macro behave as expected when used as the body of an if
85 * - We need to store some static-scope local state in order to track the
86 * LogCategory to use. This is a bit tricky to do and still meet the
87 * requirements of being a single expression, but fortunately static
88 * variables inside a lambda work for this purpose.
90 * Inside header files, each XLOG() statement defines to static variables:
91 * - the LogLevel for this category
92 * - a pointer to the LogCategory
94 * If the __INCLUDE_LEVEL__ macro is available (both gcc and clang support
95 * this), then we we can detect when we are inside a .cpp file versus a
96 * header file. If we are inside a .cpp file, we can avoid declaring these
97 * variables once per XLOG() statement, and instead we only declare one copy
98 * of these variables for the entire file.
100 * - We want to make sure this macro is safe to use even from inside static
101 * initialization code that runs before main. We also want to make the log
102 * admittance check as cheap as possible, so that disabled debug logs have
103 * minimal overhead, and can be left in place even in performance senstive
106 * In order to do this, we rely on zero-initialization of variables with
107 * static storage duration. The LogLevel variable will always be
108 * 0-initialized before any code runs. Therefore the very first time an
109 * XLOG() statement is hit the initial log level check will always pass
110 * (since all level values are greater or equal to 0), and we then do a
111 * second check to see if the log level and category variables need to be
112 * initialized. On all subsequent calls, disabled log statements can be
113 * skipped with just a single check of the LogLevel.
115 /* clang-format off */
116 #define XLOG_IMPL(level, type, ...) \
117 (!XLOG_IS_ON_IMPL(level)) \
118 ? ::folly::logDisabledHelper( \
119 std::integral_constant<bool, ::folly::isLogLevelFatal(level)>{}) \
120 : ::folly::LogStreamVoidify< ::folly::isLogLevelFatal(level)>{} & \
121 ::folly::LogStreamProcessor( \
123 static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> \
125 return _xlogCategory_.getInfo( \
126 &xlog_detail::xlogFileScopeInfo); \
129 xlog_detail::getXlogCategoryName(__FILE__, 0), \
130 xlog_detail::isXlogCategoryOverridden(0), \
136 /* clang-format on */
139 * Check if and XLOG() statement with the given log level would be enabled.
141 * The level parameter must be an unqualified LogLevel enum value.
143 #define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
146 * Helper macro to implement of XLOG_IS_ON()
148 * This macro is used in the XLOG() implementation, and therefore must be as
149 * cheap as possible. It stores the category's LogLevel as a local static
150 * variable. The very first time this macro is evaluated it will look up the
151 * correct LogCategory and initialize the LogLevel. Subsequent calls then
152 * are only a single conditional log level check.
154 * The LogCategory object keeps track of this local LogLevel variable and
155 * automatically keeps it up-to-date when the category's effective level is
158 * See XlogLevelInfo for the implementation details.
160 #define XLOG_IS_ON_IMPL(level) \
162 static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> _xlogLevel_; \
163 return _xlogLevel_.check( \
165 xlog_detail::getXlogCategoryName(__FILE__, 0), \
166 xlog_detail::isXlogCategoryOverridden(0), \
167 &xlog_detail::xlogFileScopeInfo); \
171 * Get the name of the log category that will be used by XLOG() statements
174 #define XLOG_GET_CATEGORY_NAME() \
175 (xlog_detail::isXlogCategoryOverridden(0) \
176 ? xlog_detail::getXlogCategoryName(__FILE__, 0) \
177 : ::folly::getXlogCategoryNameForFile(__FILE__))
180 * Get a pointer to the LogCategory that will be used by XLOG() statements in
183 * This is just a small wrapper around a LoggerDB::getCategory() call.
184 * This must be implemented as a macro since it uses __FILE__, and that must
185 * expand to the correct filename based on where the macro is used.
187 #define XLOG_GET_CATEGORY() \
188 folly::LoggerDB::get()->getCategory(XLOG_GET_CATEGORY_NAME())
191 * XLOG_SET_CATEGORY_NAME() can be used to explicitly define the log category
192 * name used by all XLOG() and XLOGF() calls in this translation unit.
194 * This overrides the default behavior of picking a category name based on the
197 * This should be used at the top-level scope in a .cpp file, before any XLOG()
198 * or XLOGF() macros have been used in the file.
200 * XLOG_SET_CATEGORY_NAME() cannot be used inside header files.
202 #ifdef __INCLUDE_LEVEL__
203 #define XLOG_SET_CATEGORY_CHECK \
205 __INCLUDE_LEVEL__ == 0, \
206 "XLOG_SET_CATEGORY_NAME() should not be used in header files");
208 #define XLOG_SET_CATEGORY_CHECK
211 #define XLOG_SET_CATEGORY_NAME(category) \
213 namespace xlog_detail { \
214 XLOG_SET_CATEGORY_CHECK \
215 constexpr inline folly::StringPiece getXlogCategoryName( \
216 folly::StringPiece, \
220 constexpr inline bool isXlogCategoryOverridden(int) { \
227 * XLOG_IS_IN_HEADER_FILE evaluates to false if we can definitively tell if we
228 * are not in a header file. Otherwise, it evaluates to true.
230 #ifdef __INCLUDE_LEVEL__
231 #define XLOG_IS_IN_HEADER_FILE bool(__INCLUDE_LEVEL__ > 0)
233 // Without __INCLUDE_LEVEL__ we canot tell if we are in a header file or not,
234 // and must pessimstically assume we are always in a header file.
235 #define XLOG_IS_IN_HEADER_FILE true
240 class XlogFileScopeInfo {
242 #ifdef __INCLUDE_LEVEL__
243 std::atomic<::folly::LogLevel> level;
244 ::folly::LogCategory* category;
249 * A file-static XlogLevelInfo and XlogCategoryInfo object is declared for each
252 * We intentionally do not provide constructors for these structures, and rely
253 * on their members to be zero-initialized when the program starts. This
254 * ensures that everything will work as desired even if XLOG() statements are
255 * used during dynamic object initialization before main().
257 template <bool IsInHeaderFile>
258 class XlogLevelInfo {
261 LogLevel levelToCheck,
262 folly::StringPiece categoryName,
264 XlogFileScopeInfo*) {
265 // Do an initial relaxed check. If this fails we know the category level
266 // is initialized and the log admittance check failed.
267 // Use LIKELY() to optimize for the case of disabled debug statements:
268 // we disabled debug statements to be cheap. If the log message is
269 // enabled then this check will still be minimal perf overhead compared to
270 // the overall cost of logging it.
271 if (LIKELY(levelToCheck < level_.load(std::memory_order_relaxed))) {
275 // If we are still here, then either:
276 // - The log level check actually passed, or
277 // - level_ has not been initialized yet, and we have to initialize it and
278 // then re-perform the check.
280 // Do this work in a separate helper method. It is intentionally defined
281 // in the xlog.cpp file to avoid inlining, to reduce the amount of code
282 // emitted for each XLOG() statement.
283 auto currentLevel = loadLevelFull(categoryName, isOverridden);
284 return levelToCheck >= currentLevel;
288 LogLevel loadLevelFull(folly::StringPiece categoryName, bool isOverridden);
290 // XlogLevelInfo objects are always defined with static storage.
291 // This member will always be zero-initialized on program start.
292 std::atomic<LogLevel> level_;
295 template <bool IsInHeaderFile>
296 class XlogCategoryInfo {
298 bool isInitialized() const {
299 return isInitialized_.load(std::memory_order_acquire);
302 LogCategory* init(folly::StringPiece categoryName, bool isOverridden);
304 LogCategory* getCategory(XlogFileScopeInfo*) {
309 * Get a pointer to pass into the LogStreamProcessor constructor,
310 * so that it is able to look up the LogCategory information.
312 XlogCategoryInfo<IsInHeaderFile>* getInfo(XlogFileScopeInfo*) {
317 // These variables will always be zero-initialized on program start.
318 std::atomic<bool> isInitialized_;
319 LogCategory* category_;
322 #ifdef __INCLUDE_LEVEL__
324 * Specialization of XlogLevelInfo for XLOG() statements in the .cpp file being
325 * compiled. In this case we only define a single file-static LogLevel object
326 * for the entire file, rather than defining one for each XLOG() statement.
329 class XlogLevelInfo<false> {
332 LogLevel levelToCheck,
333 folly::StringPiece categoryName,
335 XlogFileScopeInfo* fileScopeInfo) {
336 // As above in the non-specialized XlogFileScopeInfo code, do a simple
337 // relaxed check first.
340 fileScopeInfo->level.load(::std::memory_order_relaxed))) {
344 // If we are still here we the file-scope log level either needs to be
345 // initalized, or the log level check legitimately passed.
347 loadLevelFull(categoryName, isOverridden, fileScopeInfo);
348 return levelToCheck >= currentLevel;
352 static LogLevel loadLevelFull(
353 folly::StringPiece categoryName,
355 XlogFileScopeInfo* fileScopeInfo);
359 * Specialization of XlogCategoryInfo for XLOG() statements in the .cpp file
360 * being compiled. In this case we only define a single file-static LogLevel
361 * object for the entire file, rather than defining one for each XLOG()
365 class XlogCategoryInfo<false> {
368 * Get a pointer to pass into the LogStreamProcessor constructor,
369 * so that it is able to look up the LogCategory information.
371 XlogFileScopeInfo* getInfo(XlogFileScopeInfo* fileScopeInfo) {
372 return fileScopeInfo;
378 * Get the default XLOG() category name for the given filename.
380 * This function returns the category name that will be used by XLOG() if
381 * XLOG_SET_CATEGORY_NAME() has not been used.
383 std::string getXlogCategoryNameForFile(folly::StringPiece filename);
387 * We intentionally use an unnamed namespace inside a header file here.
389 * We want each .cpp file that uses xlog.h to get its own separate
390 * implementation of the following functions and variables.
393 namespace xlog_detail {
395 * The default getXlogCategoryName() function.
397 * By default this simply returns the filename argument passed in.
398 * The default isXlogCategoryOverridden() function returns false, indicating
399 * that the return value from getXlogCategoryName() needs to be converted
400 * using getXlogCategoryNameForFile().
402 * These are two separate steps because getXlogCategoryName() itself needs to
403 * remain constexpr--it is always evaluated in XLOG() statements, but we only
404 * want to call getXlogCategoryNameForFile() the very first time through, when
405 * we have to initialize the LogCategory object.
407 * This is a template function purely so that XLOG_SET_CATEGORY_NAME() can
408 * define a more specific version of this function that will take precedence
411 template <typename T>
412 constexpr inline folly::StringPiece getXlogCategoryName(
413 folly::StringPiece filename,
419 * The default isXlogCategoryOverridden() function.
421 * This returns false indicating that the category name has not been
422 * overridden, so getXlogCategoryName() returns a raw filename that needs
423 * to be translated with getXlogCategoryNameForFile().
425 * This is a template function purely so that XLOG_SET_CATEGORY_NAME() can
426 * define a more specific version of this function that will take precedence
429 template <typename T>
430 constexpr inline bool isXlogCategoryOverridden(T) {
435 * File-scope LogLevel and LogCategory data for XLOG() statements,
436 * if __INCLUDE_LEVEL__ is supported.
438 * This allows us to only have one LogLevel and LogCategory pointer for the
439 * entire .cpp file, rather than needing a separate copy for each XLOG()
442 ::folly::XlogFileScopeInfo xlogFileScopeInfo;
443 } // namespace xlog_detail