4f743d1d051689a366e6a88f4df0bb68dd348f95
[folly.git] / folly / experimental / logging / Logger.h
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 #pragma once
17
18 #include <folly/Conv.h>
19 #include <folly/Format.h>
20 #include <folly/experimental/logging/LogCategory.h>
21 #include <folly/experimental/logging/LogLevel.h>
22
23 /**
24  * Log a message to the specified logger.
25  *
26  * This macro avoids evaluating the log arguments unless the log level check
27  * succeeds.
28  */
29 #define FB_LOG(logger, level, msg, ...)                             \
30   do {                                                              \
31     const auto fbLogLevelTmp = ::folly::LogLevel::level;            \
32     const auto& fbLoggerTmp = (logger);                             \
33     if (fbLoggerTmp.logCheck(fbLogLevelTmp)) {                      \
34       fbLoggerTmp.log(                                              \
35           fbLogLevelTmp, __FILE__, __LINE__, (msg), ##__VA_ARGS__); \
36     }                                                               \
37   } while (0)
38
39 /**
40  * Log a message to the specified logger, using a folly::format() string.
41  *
42  * The arguments will be processed using folly::format().  The format syntax
43  * is similar to Python format strings.
44  *
45  * This macro avoids evaluating the log arguments unless the log level check
46  * succeeds.
47  */
48 #define FB_LOGF(logger, level, fmt, ...)                            \
49   do {                                                              \
50     const auto fbLogLevelTmp = ::folly::LogLevel::level;            \
51     const auto& fbLoggerTmp = (logger);                             \
52     if (fbLoggerTmp.logCheck(fbLogLevelTmp)) {                      \
53       fbLoggerTmp.logf(                                             \
54           fbLogLevelTmp, __FILE__, __LINE__, (fmt), ##__VA_ARGS__); \
55     }                                                               \
56   } while (0)
57
58 namespace folly {
59
60 class LoggerDB;
61 class LogMessage;
62
63 /**
64  * Logger is the class you will normally use to log messages.
65  *
66  * The Logger is really just a small wrapper class that contains a pointer
67  * to the appropriate LogCategory object.  It exists to allow for easy static
68  * initialization of log categories, as well as to provide fast checking of the
69  * current effective log level.
70  */
71 class Logger {
72  public:
73   /**
74    * Construct a Logger for the given category name.
75    *
76    * A LogCategory object for this category will be created if one does not
77    * already exist.
78    */
79   explicit Logger(folly::StringPiece name);
80
81   /**
82    * Construct a Logger pointing to an existing LogCategory object.
83    */
84   explicit Logger(LogCategory* cat);
85
86   /**
87    * Construct a Logger for a specific LoggerDB object, rather than the main
88    * singleton.
89    *
90    * This is primarily intended for use in unit tests.
91    */
92   Logger(LoggerDB* db, folly::StringPiece name);
93
94   /**
95    * Get the effective level for this logger.
96    *
97    * This is the minimum log level of this logger, or any of its parents.
98    * Log messages below this level will be ignored, while messages at or
99    * above this level need to be processed by this logger or one of its
100    * parents.
101    */
102   LogLevel getEffectiveLevel() const {
103     return category_->getEffectiveLevel();
104   }
105
106   /**
107    * Check whether this Logger or any of its parent Loggers would do anything
108    * with a log message at the given level.
109    */
110   bool logCheck(LogLevel level) const {
111     // We load the effective level using std::memory_order_relaxed.
112     //
113     // We want to make log checks as lightweight as possible.  It's fine if we
114     // don't immediately respond to changes made to the log level from other
115     // threads.  We can wait until some other operation triggers a memory
116     // barrier before we honor the new log level setting.  No other memory
117     // accesses depend on the log level value.  Callers should not rely on all
118     // other threads to immediately stop logging as soon as they decrease the
119     // log level for a given category.
120     return category_->getEffectiveLevelRelaxed() <= level;
121   }
122
123   /**
124    * Unconditionally log a message.
125    *
126    * The caller is responsible for calling logCheck() before log() to ensure
127    * that this log message should be admitted.  This is typically done with one
128    * of the logging macros.
129    */
130   void log(
131       LogLevel level,
132       folly::StringPiece filename,
133       unsigned int lineNumber,
134       std::string&& msg) const;
135   void log(
136       LogLevel level,
137       folly::StringPiece filename,
138       unsigned int lineNumber,
139       folly::StringPiece msg) const;
140
141   /**
142    * Unconditionally log a message.
143    *
144    * This concatenates the arguments into a string using
145    * folly::to<std::string>()
146    */
147   template <typename... Args>
148   void log(
149       LogLevel level,
150       folly::StringPiece filename,
151       unsigned int lineNumber,
152       Args&&... args) const {
153     std::string msg;
154     try {
155       msg = folly::to<std::string>(std::forward<Args>(args)...);
156     } catch (const std::exception& ex) {
157       // This most likely means there was some error converting the arguments
158       // to strings.  Handle the exception here, rather than letting it
159       // propagate up, since callers generally do not expect log statements to
160       // throw.
161       //
162       // Just log an error message letting indicating that something went wrong
163       // formatting the log message.
164       msg =
165           folly::to<std::string>("error constructing log message: ", ex.what());
166     }
167     log(level, filename, lineNumber, std::move(msg));
168   }
169
170   /**
171    * Unconditionally log a message using a format string.
172    *
173    * This uses folly::format() to format the message.
174    */
175   template <typename... Args>
176   void logf(
177       LogLevel level,
178       folly::StringPiece filename,
179       unsigned int lineNumber,
180       folly::StringPiece fmt,
181       Args&&... args) const {
182     std::string msg;
183     try {
184       msg = folly::sformat(fmt, std::forward<Args>(args)...);
185     } catch (const std::exception& ex) {
186       // This most likely means that the caller had a bug in their format
187       // string/arguments.  Handle the exception here, rather than letting it
188       // propagate up, since callers generally do not expect log statements to
189       // throw.
190       //
191       // Log the format string by itself, to help the developer at least
192       // identify the buggy format string in their code.
193       msg = folly::to<std::string>(
194           "error formatting log message: ",
195           ex.what(),
196           "; format string: ",
197           fmt);
198     }
199     log(level, filename, lineNumber, std::move(msg));
200   }
201
202   /**
203    * Get the LogCategory that this Logger refers to.
204    */
205   LogCategory* getCategory() const {
206     return category_;
207   }
208
209  private:
210   LogCategory* const category_{nullptr};
211 };
212 }