1#ifndef C10_UTIL_LOGGING_H_
2#define C10_UTIL_LOGGING_H_
3
4#include <climits>
5#include <exception>
6#include <functional>
7#include <limits>
8#include <sstream>
9
10#include <c10/macros/Macros.h>
11#include <c10/util/Exception.h>
12#include <c10/util/Flags.h>
13#include <c10/util/StringUtil.h>
14
15// CAFFE2_LOG_THRESHOLD is a compile time flag that would allow us to turn off
16// logging at compile time so no logging message below that level is produced
17// at all. The value should be between INT_MIN and CAFFE_FATAL.
18#ifndef CAFFE2_LOG_THRESHOLD
19// If we have not defined the compile time log threshold, we keep all the
20// log cases.
21#define CAFFE2_LOG_THRESHOLD INT_MIN
22#endif // CAFFE2_LOG_THRESHOLD
23
24// Below are different implementations for glog and non-glog cases.
25#ifdef C10_USE_GLOG
26#include <c10/util/logging_is_google_glog.h>
27#else // !C10_USE_GLOG
28#include <c10/util/logging_is_not_google_glog.h>
29#endif // C10_USE_GLOG
30
31C10_DECLARE_int(caffe2_log_level);
32C10_DECLARE_bool(caffe2_use_fatal_for_enforce);
33
34// Some versions of GLOG support less-spammy version of LOG_EVERY_MS. If it's
35// not available - just short-circuit to the always working one one.
36// We define the C10_ name to avoid confusing other files
37#ifdef LOG_EVERY_MS
38#define C10_LOG_EVERY_MS(severity, ms) LOG_EVERY_MS(severity, ms)
39#else
40#define C10_LOG_EVERY_MS(severity, ms) LOG(severity)
41#endif
42
43// Same for LOG_FIRST_N
44#ifdef LOG_FIRST_N
45#define C10_LOG_FIRST_N(severity, n) LOG_FIRST_N(severity, n)
46#else
47#define C10_LOG_FIRST_N(severity, n) LOG(severity)
48#endif
49
50// Same for LOG_EVERY_N
51#ifdef LOG_EVERY_N
52#define C10_LOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n)
53#else
54#define C10_LOG_EVERY_N(severity, n) LOG(severity)
55#endif
56
57namespace c10 {
58
59using std::string;
60
61// Functions that we use for initialization.
62C10_API bool InitCaffeLogging(int* argc, char** argv);
63C10_API void UpdateLoggingLevelsFromFlags();
64
65[[noreturn]] C10_API void ThrowEnforceNotMet(
66 const char* file,
67 const int line,
68 const char* condition,
69 const std::string& msg,
70 const void* caller = nullptr);
71
72[[noreturn]] C10_API void ThrowEnforceNotMet(
73 const char* file,
74 const int line,
75 const char* condition,
76 const char* msg,
77 const void* caller = nullptr);
78
79[[noreturn]] C10_API inline void ThrowEnforceNotMet(
80 const char* file,
81 const int line,
82 const char* condition,
83 detail::CompileTimeEmptyString /*msg*/,
84 const void* caller = nullptr) {
85 ThrowEnforceNotMet(file, line, condition, "", caller);
86}
87
88[[noreturn]] C10_API void ThrowEnforceFiniteNotMet(
89 const char* file,
90 const int line,
91 const char* condition,
92 const std::string& msg,
93 const void* caller = nullptr);
94
95[[noreturn]] C10_API void ThrowEnforceFiniteNotMet(
96 const char* file,
97 const int line,
98 const char* condition,
99 const char* msg,
100 const void* caller = nullptr);
101
102[[noreturn]] C10_API inline void ThrowEnforceFiniteNotMet(
103 const char* file,
104 const int line,
105 const char* condition,
106 detail::CompileTimeEmptyString /*msg*/,
107 const void* caller = nullptr) {
108 ThrowEnforceFiniteNotMet(file, line, condition, "", caller);
109}
110
111constexpr bool IsUsingGoogleLogging() {
112#ifdef C10_USE_GLOG
113 return true;
114#else
115 return false;
116#endif
117}
118
119/**
120 * A utility to allow one to show log info to stderr after the program starts.
121 *
122 * This is similar to calling GLOG's --logtostderr, or setting caffe2_log_level
123 * to smaller than INFO. You are recommended to only use this in a few sparse
124 * cases, such as when you want to write a tutorial or something. Normally, use
125 * the commandline flags to set the log level.
126 */
127C10_API void ShowLogInfoToStderr();
128
129C10_API void SetStackTraceFetcher(std::function<string(void)> fetcher);
130
131using EnforceNotMet = ::c10::Error;
132
133#define CAFFE_ENFORCE(condition, ...) \
134 do { \
135 if (C10_UNLIKELY(!(condition))) { \
136 ::c10::ThrowEnforceNotMet( \
137 __FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__)); \
138 } \
139 } while (false)
140
141#define CAFFE_ENFORCE_FINITE(condition, ...) \
142 do { \
143 if (C10_UNLIKELY(!(condition))) { \
144 ::c10::ThrowEnforceFiniteNotMet( \
145 __FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__)); \
146 } \
147 } while (false)
148
149#define CAFFE_ENFORCE_WITH_CALLER(condition, ...) \
150 do { \
151 if (C10_UNLIKELY(!(condition))) { \
152 ::c10::ThrowEnforceNotMet( \
153 __FILE__, __LINE__, #condition, ::c10::str(__VA_ARGS__), this); \
154 } \
155 } while (false)
156
157#define CAFFE_THROW(...) \
158 ::c10::ThrowEnforceNotMet(__FILE__, __LINE__, "", ::c10::str(__VA_ARGS__))
159
160/**
161 * Rich logging messages
162 *
163 * CAFFE_ENFORCE_THAT can be used with one of the "checker functions" that
164 * capture input argument values and add it to the exception message. E.g.
165 * `CAFFE_ENFORCE_THAT(Equals(foo(x), bar(y)), "Optional additional message")`
166 * would evaluate both foo and bar only once and if the results are not equal -
167 * include them in the exception message.
168 *
169 * Some of the basic checker functions like Equals or Greater are already
170 * defined below. Other header might define customized checkers by adding
171 * functions to caffe2::enforce_detail namespace. For example:
172 *
173 * namespace caffe2 { namespace enforce_detail {
174 * inline EnforceFailMessage IsVector(const vector<int64_t>& shape) {
175 * if (shape.size() == 1) { return EnforceOK(); }
176 * return c10::str("Shape ", shape, " is not a vector");
177 * }
178 * }}
179 *
180 * With further usages like `CAFFE_ENFORCE_THAT(IsVector(Input(0).dims()))`
181 *
182 * Convenient wrappers for binary operations like CAFFE_ENFORCE_EQ are provided
183 * too. Please use them instead of TORCH_CHECK_EQ and friends for failures in
184 * user-provided input.
185 */
186
187namespace enforce_detail {
188
189template <typename T1, typename T2>
190std::string enforceFailMsgImpl(const T1& x, const T2& y) {
191 return c10::str(x, " vs ", y);
192}
193
194template <typename T1, typename T2, typename... Args>
195std::string enforceFailMsgImpl(const T1& x, const T2& y, const Args&... args) {
196 return c10::str(x, " vs ", y, ". ", args...);
197}
198
199template <typename Pred, typename T1, typename T2, typename... Args>
200void enforceThatImpl(
201 Pred p,
202 const T1& lhs,
203 const T2& rhs,
204 const char* file,
205 int line,
206 const char* expr,
207 const void* caller,
208 const Args&... args) {
209 if (C10_UNLIKELY(!(p(lhs, rhs)))) {
210 ::c10::ThrowEnforceNotMet(
211 file,
212 line,
213 expr,
214 ::c10::enforce_detail::enforceFailMsgImpl(lhs, rhs, args...),
215 caller);
216 }
217}
218#define CAFFE_ENFORCE_THAT_IMPL(op, lhs, rhs, expr, ...) \
219 ::c10::enforce_detail::enforceThatImpl( \
220 op, lhs, rhs, __FILE__, __LINE__, expr, nullptr, ##__VA_ARGS__)
221
222#define CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER(op, lhs, rhs, expr, ...) \
223 ::c10::enforce_detail::enforceThatImpl( \
224 op, (lhs), (rhs), __FILE__, __LINE__, expr, this, ##__VA_ARGS__)
225
226} // namespace enforce_detail
227
228#define CAFFE_ENFORCE_THAT(cmp, op, lhs, rhs, ...) \
229 CAFFE_ENFORCE_THAT_IMPL(cmp, lhs, rhs, #lhs " " #op " " #rhs, ##__VA_ARGS__)
230
231#define CAFFE_ENFORCE_BINARY_OP(cmp, op, x, y, ...) \
232 CAFFE_ENFORCE_THAT_IMPL(cmp, x, y, #x " " #op " " #y, ##__VA_ARGS__)
233#define CAFFE_ENFORCE_EQ(x, y, ...) \
234 CAFFE_ENFORCE_BINARY_OP(std::equal_to<void>(), ==, x, y, ##__VA_ARGS__)
235#define CAFFE_ENFORCE_NE(x, y, ...) \
236 CAFFE_ENFORCE_BINARY_OP(std::not_equal_to<void>(), !=, x, y, ##__VA_ARGS__)
237#define CAFFE_ENFORCE_LE(x, y, ...) \
238 CAFFE_ENFORCE_BINARY_OP(std::less_equal<void>(), <=, x, y, ##__VA_ARGS__)
239#define CAFFE_ENFORCE_LT(x, y, ...) \
240 CAFFE_ENFORCE_BINARY_OP(std::less<void>(), <, x, y, ##__VA_ARGS__)
241#define CAFFE_ENFORCE_GE(x, y, ...) \
242 CAFFE_ENFORCE_BINARY_OP(std::greater_equal<void>(), >=, x, y, ##__VA_ARGS__)
243#define CAFFE_ENFORCE_GT(x, y, ...) \
244 CAFFE_ENFORCE_BINARY_OP(std::greater<void>(), >, x, y, ##__VA_ARGS__)
245
246#define CAFFE_ENFORCE_BINARY_OP_WITH_CALLER(cmp, op, x, y, ...) \
247 CAFFE_ENFORCE_THAT_IMPL_WITH_CALLER( \
248 cmp, x, y, #x " " #op " " #y, ##__VA_ARGS__)
249#define CAFFE_ENFORCE_EQ_WITH_CALLER(x, y, ...) \
250 CAFFE_ENFORCE_BINARY_OP_WITH_CALLER( \
251 std::equal_to<void>(), ==, x, y, ##__VA_ARGS__)
252#define CAFFE_ENFORCE_NE_WITH_CALLER(x, y, ...) \
253 CAFFE_ENFORCE_BINARY_OP_WITH_CALLER( \
254 std::not_equal_to<void>(), !=, x, y, ##__VA_ARGS__)
255#define CAFFE_ENFORCE_LE_WITH_CALLER(x, y, ...) \
256 CAFFE_ENFORCE_BINARY_OP_WITH_CALLER( \
257 std::less_equal<void>(), <=, x, y, ##__VA_ARGS__)
258#define CAFFE_ENFORCE_LT_WITH_CALLER(x, y, ...) \
259 CAFFE_ENFORCE_BINARY_OP_WITH_CALLER(std::less<void>(), <, x, y, ##__VA_ARGS__)
260#define CAFFE_ENFORCE_GE_WITH_CALLER(x, y, ...) \
261 CAFFE_ENFORCE_BINARY_OP_WITH_CALLER( \
262 std::greater_equal<void>(), >=, x, y, ##__VA_ARGS__)
263#define CAFFE_ENFORCE_GT_WITH_CALLER(x, y, ...) \
264 CAFFE_ENFORCE_BINARY_OP_WITH_CALLER( \
265 std::greater<void>(), >, x, y, ##__VA_ARGS__)
266
267/**
268 * Very lightweight logging for the first time API usage. It's beneficial for
269 * tracking of individual functionality usage in larger applications.
270 *
271 * In order to ensure light-weightedness of logging, we utilize static variable
272 * trick - LogAPIUsage will be invoked only once and further invocations will
273 * just do an atomic check.
274 *
275 * Example:
276 * // Logs caller info with an arbitrary text event, if there is a usage.
277 * C10_LOG_API_USAGE_ONCE("my_api");
278 */
279#define C10_LOG_API_USAGE_ONCE(...) \
280 C10_UNUSED static bool C10_ANONYMOUS_VARIABLE(logFlag) = \
281 ::c10::detail::LogAPIUsageFakeReturn(__VA_ARGS__);
282
283// API usage logging capabilities
284C10_API void SetAPIUsageLogger(std::function<void(const std::string&)> logger);
285C10_API void LogAPIUsage(const std::string& context);
286
287// PyTorch ddp usage logging capabilities
288// DDPLoggingData holds data that can be logged in applications
289// for analysis and debugging. Data structure is defined in
290// c10 directory so that it can be easily imported by both c10
291// and torch files.
292struct DDPLoggingData {
293 // logging fields that are string types.
294 std::map<std::string, std::string> strs_map;
295 // logging fields that are int64_t types.
296 std::map<std::string, int64_t> ints_map;
297};
298
299C10_API void SetPyTorchDDPUsageLogger(
300 std::function<void(const DDPLoggingData&)> logger);
301C10_API void LogPyTorchDDPUsage(const DDPLoggingData& ddpData);
302
303namespace detail {
304// Return value is needed to do the static variable initialization trick
305C10_API bool LogAPIUsageFakeReturn(const std::string& context);
306} // namespace detail
307
308// Initializes the c10 logger.
309C10_API void initLogging();
310
311} // namespace c10
312
313#endif // C10_UTIL_LOGGING_H_
314