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 #define XLOG_IMPL(level, type, ...) \
116 (!XLOG_IS_ON_IMPL(level)) \
117 ? static_cast<void>(0) \
118 : ::folly::LogStreamProcessorT<::folly::isLogLevelFatal(level)>( \
119 XLOG_GET_CATEGORY_INTERNAL(), \
128 * Check if and XLOG() statement with the given log level would be enabled.
130 #define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
133 * Helper macro to implement of XLOG_IS_ON()
135 * This macro is used in the XLOG() implementation, and therefore must be as
136 * cheap as possible. It stores the category's LogLevel as a local static
137 * variable. The very first time this macro is evaluated it will look up the
138 * correct LogCategory and initialize the LogLevel. Subsequent calls then
139 * are only a single conditional log level check.
141 * The LogCategory object keeps track of this local LogLevel variable and
142 * automatically keeps it up-to-date when the category's effective level is
145 * Most of this code must live in the macro definition itself, and cannot be
146 * moved into a helper function: The getXlogCategoryName() call must be made as
147 * part of the macro expansion in order to work correctly. We also want to
148 * avoid calling it whenever possible. Therefore most of the logic must be
149 * done in the macro expansion.
151 * See XlogLevelInfo for the implementation details.
153 #define XLOG_IS_ON_IMPL(level) \
155 static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> _xlogLevel_; \
156 const auto _xlogLevelToCheck_ = (level); \
158 * Do an initial relaxed check. If this fails we know the category level \
159 * is initialized and the log admittance check failed. \
160 * Use LIKELY() to optimize for the case of disabled debug statements: \
161 * we disabled debug statements to be cheap. If the log message is \
162 * enabled then this check will still be minimal perf overhead compared to \
163 * the overall cost of logging it. \
166 _xlogLevelToCheck_ < \
167 _xlogLevel_.getLevel( \
168 &_xlogFileScopeInfo_, std::memory_order_relaxed))) { \
172 * Load the level value with memory_order_acquire, and check \
173 * to see if it is initialized or not. \
175 auto _xlogCurrentLevel_ = \
176 _xlogLevel_.getLevel(&_xlogFileScopeInfo_, std::memory_order_acquire); \
177 if (UNLIKELY(_xlogCurrentLevel_ == ::folly::LogLevel::UNINITIALIZED)) { \
178 _xlogCurrentLevel_ = _xlogLevel_.init( \
179 getXlogCategoryName(__FILE__), &_xlogFileScopeInfo_); \
181 return _xlogLevelToCheck_ >= _xlogCurrentLevel_; \
185 * Get the name of the log category that will be used by XLOG() statements
188 #define XLOG_GET_CATEGORY_NAME() getXlogCategoryName(__FILE__)
191 * Get a pointer to the LogCategory that will be used by XLOG() statements in
194 * This is just a small wrapper around a LoggerDB::getCategory() call.
195 * This must be implemented as a macro since it uses __FILE__, and that must
196 * expand to the correct filename based on where the macro is used.
198 #define XLOG_GET_CATEGORY() \
199 folly::LoggerDB::get()->getCategory(XLOG_GET_CATEGORY_NAME())
202 * Internal version of XLOG_GET_CATEGORY() that is used in the XLOG() macro.
204 * This macro is used in the XLOG() implementation, and therefore must be as
205 * cheap as possible. It stores the LogCategory* pointer as a local static
206 * variable. Only the first invocation has to look up the log category by
207 * name. Subsequent invocations re-use the already looked-up LogCategory
210 * This is only safe to call after XlogLevelInfo::init() has been called.
212 * Most of this code must live in the macro definition itself, and cannot be
213 * moved into a helper function: The getXlogCategoryName() call must be made as
214 * part of the macro expansion in order to work correctly. We also want to
215 * avoid calling it whenever possible. Therefore most of the logic must be
216 * done in the macro expansion.
218 * See XlogCategoryInfo for the implementation details.
220 #define XLOG_GET_CATEGORY_INTERNAL() \
222 static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> _xlogCategory_; \
223 if (!_xlogCategory_.isInitialized()) { \
224 return _xlogCategory_.init(getXlogCategoryName(__FILE__)); \
226 return _xlogCategory_.getCategory(&_xlogFileScopeInfo_); \
230 * XLOG_SET_CATEGORY_NAME() can be used to explicitly define the log category
231 * name used by all XLOG() and XLOGF() calls in this translation unit.
233 * This overrides the default behavior of picking a category name based on the
236 * This should be used at the top-level scope in a .cpp file, before any XLOG()
237 * or XLOGF() macros have been used in the file.
239 * XLOG_SET_CATEGORY_NAME() cannot be used inside header files.
241 #ifdef __INCLUDE_LEVEL__
242 #define XLOG_SET_CATEGORY_NAME(category) \
245 __INCLUDE_LEVEL__ == 0, \
246 "XLOG_SET_CATEGORY_NAME() should not be used in header files"); \
247 inline std::string getXlogCategoryName(const char*) { \
252 #define XLOG_SET_CATEGORY_NAME(category) \
254 inline std::string getXlogCategoryName(const char*) { \
261 * XLOG_IS_IN_HEADER_FILE evaluates to false if we can definitively tell if we
262 * are not in a header file. Otherwise, it evaluates to true.
264 #ifdef __INCLUDE_LEVEL__
265 #define XLOG_IS_IN_HEADER_FILE bool(__INCLUDE_LEVEL__ > 0)
267 // Without __INCLUDE_LEVEL__ we canot tell if we are in a header file or not,
268 // and must pessimstically assume we are always in a header file.
269 #define XLOG_IS_IN_HEADER_FILE true
274 class XlogFileScopeInfo {
276 #ifdef __INCLUDE_LEVEL__
277 std::atomic<::folly::LogLevel> level;
278 ::folly::LogCategory* category;
283 * A file-static XlogLevelInfo and XlogCategoryInfo object is declared for each
286 * We intentionally do not provide constructors for these structures, and rely
287 * on their members to be zero-initialized when the program starts. This
288 * ensures that everything will work as desired even if XLOG() statements are
289 * used during dynamic object initialization before main().
291 template <bool IsInHeaderFile>
292 struct XlogLevelInfo {
294 inline LogLevel getLevel(XlogFileScopeInfo*, std::memory_order order) {
295 return level_.load(order);
298 inline LogLevel init(folly::StringPiece categoryName, XlogFileScopeInfo*) {
299 return LoggerDB::get()->xlogInit(categoryName, &level_, nullptr);
303 // This member will always be zero-initialized on program start.
304 std::atomic<LogLevel> level_;
307 template <bool IsInHeaderFile>
308 struct XlogCategoryInfo {
310 bool isInitialized() const {
311 return isInitialized_.load(std::memory_order_acquire);
314 LogCategory* init(folly::StringPiece categoryName) {
315 return LoggerDB::get()->xlogInitCategory(
316 categoryName, &category_, &isInitialized_);
319 LogCategory* getCategory(XlogFileScopeInfo*) {
324 // These variables will always be zero-initialized on program start.
325 std::atomic<bool> isInitialized_;
326 LogCategory* category_;
329 #ifdef __INCLUDE_LEVEL__
331 * Specialization of XlogLevelInfo for XLOG() statements in the .cpp file being
332 * compiled. In this case we only define a single file-static LogLevel object
333 * for the entire file, rather than defining one for each XLOG() statement.
336 struct XlogLevelInfo<false> {
338 inline LogLevel getLevel(
339 XlogFileScopeInfo* fileScopeInfo,
340 std::memory_order order) {
341 return fileScopeInfo->level.load(order);
344 inline LogLevel init(
345 folly::StringPiece categoryName,
346 XlogFileScopeInfo* fileScopeInfo) {
347 return LoggerDB::get()->xlogInit(
348 categoryName, &fileScopeInfo->level, &fileScopeInfo->category);
353 * Specialization of XlogCategoryInfo for XLOG() statements in the .cpp file
354 * being compiled. In this case we only define a single file-static LogLevel
355 * object for the entire file, rather than defining one for each XLOG()
359 struct XlogCategoryInfo<false> {
361 constexpr bool isInitialized() const {
362 // XlogLevelInfo<false>::init() is always called before XlogCategoryInfo
363 // is used, and it will have already initialized fileScopeInfo.
364 // Therefore we never have to check if it is initialized yet here.
367 [[noreturn]] LogCategory* init(folly::StringPiece /* categoryName */) {
368 // This method is never used given that isInitialized() always returns true
371 LogCategory* getCategory(XlogFileScopeInfo* fileScopeInfo) {
372 return fileScopeInfo->category;
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.
394 * The default getXlogCategoryName() implementation.
395 * This will be used if XLOG_SET_CATEGORY_NAME() has not been used yet.
397 * This is a template purely so that XLOG_SET_CATEGORY_NAME() can define a more
398 * specific version if desired, allowing XLOG_SET_CATEGORY_NAME() to override
399 * this implementation once it has been used.
401 template <typename T>
402 inline std::string getXlogCategoryName(const T* filename) {
403 return ::folly::getXlogCategoryNameForFile(filename);
407 * File-scope LogLevel and LogCategory data for XLOG() statements,
408 * if __INCLUDE_LEVEL__ is supported.
410 * This allows us to only have one LogLevel and LogCategory pointer for the
411 * entire .cpp file, rather than needing a separate copy for each XLOG()
414 ::folly::XlogFileScopeInfo _xlogFileScopeInfo_;