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)) \
116 ? static_cast<void>(0) \
117 : ::folly::LogStreamProcessorT<::folly::isLogLevelFatal(level)>( \
118 XLOG_GET_CATEGORY(), \
127 * Check if and XLOG() statement with the given log level would be enabled.
129 #define XLOG_IS_ON(level) XLOG_IS_ON_IMPL(::folly::LogLevel::level)
132 * Helper macro to implement of XLOG_IS_ON()
134 * This macro is used in the XLOG() implementation, and therefore must be as
135 * cheap as possible. It stores the category's LogLevel as a local static
136 * variable. The very first time this macro is evaluated it will look up the
137 * correct LogCategory and initialize the LogLevel. Subsequent calls then
138 * are only a single conditional log level check.
140 * The LogCategory object keeps track of this local LogLevel variable and
141 * automatically keeps it up-to-date when the category's effective level is
144 * Most of this code must live in the macro definition itself, and cannot be
145 * moved into a helper function: The getXlogCategoryName() call must be made as
146 * part of the macro expansion in order to work correctly. We also want to
147 * avoid calling it whenever possible. Therefore most of the logic must be
148 * done in the macro expansion.
150 * See XlogLevelInfo for the implementation details.
152 #define XLOG_IS_ON_IMPL(level) \
154 static ::folly::XlogLevelInfo<XLOG_IS_IN_HEADER_FILE> _xlogLevel_; \
155 const auto _xlogLevelToCheck_ = (level); \
157 * Do an initial relaxed check. If this fails we know the category level \
158 * is initialized and the log admittance check failed. \
159 * Use LIKELY() to optimize for the case of disabled debug statements: \
160 * we disabled debug statements to be cheap. If the log message is \
161 * enabled then this check will still be minimal perf overhead compared to \
162 * the overall cost of logging it. \
165 _xlogLevelToCheck_ < \
166 _xlogLevel_.getLevel( \
167 &_xlogFileScopeInfo_, std::memory_order_relaxed))) { \
171 * Load the level value with memory_order_acquire, and check \
172 * to see if it is initialized or not. \
174 auto _xlogCurrentLevel_ = \
175 _xlogLevel_.getLevel(&_xlogFileScopeInfo_, std::memory_order_acquire); \
176 if (UNLIKELY(_xlogCurrentLevel_ == ::folly::LogLevel::UNINITIALIZED)) { \
177 _xlogCurrentLevel_ = _xlogLevel_.init( \
178 getXlogCategoryName(__FILE__), &_xlogFileScopeInfo_); \
180 return _xlogLevelToCheck_ >= _xlogCurrentLevel_; \
184 * Get a pointer to the LogCategory that will be used by XLOG() statements in
187 * This macro is used in the XLOG() implementation, and therefore must be as
188 * cheap as possible. It stores the LogCategory* pointer as a local static
189 * variable. Only the first invocation has to look up the log category by
190 * name. Subsequent invocations re-use the already looked-up LogCategory
193 * Most of this code must live in the macro definition itself, and cannot be
194 * moved into a helper function: The getXlogCategoryName() call must be made as
195 * part of the macro expansion in order to work correctly. We also want to
196 * avoid calling it whenever possible. Therefore most of the logic must be
197 * done in the macro expansion.
199 * See XlogCategoryInfo for the implementation details.
201 #define XLOG_GET_CATEGORY() \
203 static ::folly::XlogCategoryInfo<XLOG_IS_IN_HEADER_FILE> _xlogCategory_; \
204 if (!_xlogCategory_.isInitialized()) { \
205 return _xlogCategory_.init(getXlogCategoryName(__FILE__)); \
207 return _xlogCategory_.getCategory(&_xlogFileScopeInfo_); \
211 * XLOG_SET_CATEGORY() can be used to explicitly define the log category name
212 * used by all XLOG() and XLOGF() calls in this translation unit.
214 * This overrides the default behavior of picking a category name based on the
217 * This should be used at the top-level scope in a .cpp file, before any XLOG()
218 * or XLOGF() macros have been used in the file.
220 * XLOG_SET_CATEGORY() cannot be used inside header files.
222 #ifdef __INCLUDE_LEVEL__
223 #define XLOG_SET_CATEGORY(category) \
226 __INCLUDE_LEVEL__ == 0, \
227 "XLOG_SET_CATEGORY() should not be used in header files"); \
228 inline std::string getXlogCategoryName(const char*) { \
233 #define XLOG_SET_CATEGORY(category) \
235 inline std::string getXlogCategoryName(const char*) { \
242 * XLOG_IS_IN_HEADER_FILE evaluates to false if we can definitively tell if we
243 * are not in a header file. Otherwise, it evaluates to true.
245 #ifdef __INCLUDE_LEVEL__
246 #define XLOG_IS_IN_HEADER_FILE bool(__INCLUDE_LEVEL__ > 0)
248 // Without __INCLUDE_LEVEL__ we canot tell if we are in a header file or not,
249 // and must pessimstically assume we are always in a header file.
250 #define XLOG_IS_IN_HEADER_FILE true
255 class XlogFileScopeInfo {
257 #ifdef __INCLUDE_LEVEL__
258 std::atomic<::folly::LogLevel> level;
259 ::folly::LogCategory* category;
264 * A file-static XlogLevelInfo and XlogCategoryInfo object is declared for each
267 * We intentionally do not provide constructors for these structures, and rely
268 * on their members to be zero-initialized when the program starts. This
269 * ensures that everything will work as desired even if XLOG() statements are
270 * used during dynamic object initialization before main().
272 template <bool IsInHeaderFile>
273 struct XlogLevelInfo {
275 inline LogLevel getLevel(XlogFileScopeInfo*, std::memory_order order) {
276 return level_.load(order);
279 inline LogLevel init(folly::StringPiece categoryName, XlogFileScopeInfo*) {
280 return LoggerDB::get()->xlogInit(categoryName, &level_, nullptr);
284 // This member will always be zero-initialized on program start.
285 std::atomic<LogLevel> level_;
288 template <bool IsInHeaderFile>
289 struct XlogCategoryInfo {
291 bool isInitialized() {
292 return isInitialized_.load(std::memory_order_acquire);
295 LogCategory* init(folly::StringPiece categoryName) {
296 return LoggerDB::get()->xlogInitCategory(
297 categoryName, &category_, &isInitialized_);
300 LogCategory* getCategory(XlogFileScopeInfo*) {
305 // These variables will always be zero-initialized on program start.
306 std::atomic<bool> isInitialized_;
307 LogCategory* category_;
310 #ifdef __INCLUDE_LEVEL__
312 * Specialization of XlogLevelInfo for XLOG() statements in the .cpp file being
313 * compiled. In this case we only define a single file-static LogLevel object
314 * for the entire file, rather than defining one for each XLOG() statement.
317 struct XlogLevelInfo<false> {
319 inline LogLevel getLevel(
320 XlogFileScopeInfo* fileScopeInfo,
321 std::memory_order order) {
322 return fileScopeInfo->level.load(order);
325 inline LogLevel init(
326 folly::StringPiece categoryName,
327 XlogFileScopeInfo* fileScopeInfo) {
328 return LoggerDB::get()->xlogInit(
329 categoryName, &fileScopeInfo->level, &fileScopeInfo->category);
334 * Specialization of XlogCategoryInfo for XLOG() statements in the .cpp file
335 * being compiled. In this case we only define a single file-static LogLevel
336 * object for the entire file, rather than defining one for each XLOG()
340 struct XlogCategoryInfo<false> {
342 constexpr bool isInitialized() {
343 // XlogLevelInfo<false>::check() is always called before XlogCategoryInfo
344 // is used, and it will will have already initialized fileScopeInfo.
345 // Therefore we never have to check if it is initialized yet here.
348 LogCategory* init(folly::StringPiece) {
349 // This method is never used given that isInitialized() always returns true
353 LogCategory* getCategory(XlogFileScopeInfo* fileScopeInfo) {
354 return fileScopeInfo->category;
360 * Get the default XLOG() category name for the given filename.
362 * This function returns the category name that will be used by XLOG() if
363 * XLOG_SET_CATEGORY() has not been used.
365 std::string getXlogCategoryNameForFile(folly::StringPiece filename);
369 * We intentionally use an unnamed namespace inside a header file here.
371 * We want each .cpp file that uses xlog.h to get its own separate
372 * implementation of the following functions and variables.
376 * The default getXlogCategoryName() implementation.
377 * This will be used if XLOG_SET_CATEGORY() has not been used yet.
379 * This is a template purely so that XLOG_SET_CATEGORY() can define a more
380 * specific version if desired, allowing XLOG_SET_CATEGORY() to override this
381 * implementation once it has been used. The template paramete
383 template <typename T>
384 inline std::string getXlogCategoryName(const T* filename) {
385 return ::folly::getXlogCategoryNameForFile(filename);
389 * File-scope LogLevel and LogCategory data for XLOG() statements,
390 * if __INCLUDE_LEVEL__ is supported.
392 * This allows us to only have one LogLevel and LogCategory pointer for the
393 * entire .cpp file, rather than needing a separate copy for each XLOG()
396 ::folly::XlogFileScopeInfo _xlogFileScopeInfo_;