1/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14==============================================================================*/
15
16#if defined(_WIN32)
17// prevent compile error because MSVC doesn't realize in debug build that
18// LOG(FATAL) finally invokes abort()
19#pragma warning(disable : 4716)
20#endif // _WIN32
21
22#ifndef TENSORFLOW_TSL_PLATFORM_DEFAULT_LOGGING_H_
23#define TENSORFLOW_TSL_PLATFORM_DEFAULT_LOGGING_H_
24
25// IWYU pragma: private, include "third_party/tensorflow/tsl/platform/logging.h"
26// IWYU pragma: friend third_party/tensorflow/tsl/platform/logging.h
27
28#include <atomic>
29#include <limits>
30#include <memory>
31#include <sstream>
32#include <string>
33#include <vector>
34
35#include "absl/base/log_severity.h"
36#include "absl/strings/string_view.h"
37#include "tensorflow/tsl/platform/macros.h"
38#include "tensorflow/tsl/platform/types.h"
39
40// TODO(mrry): Prevent this Windows.h #define from leaking out of our headers.
41#undef ERROR
42
43namespace tsl {
44const int INFO = 0; // base_logging::INFO;
45const int WARNING = 1; // base_logging::WARNING;
46const int ERROR = 2; // base_logging::ERROR;
47const int FATAL = 3; // base_logging::FATAL;
48const int NUM_SEVERITIES = 4; // base_logging::NUM_SEVERITIES;
49
50namespace internal {
51
52// Emit "message" as a log message to the log for the specified
53// "severity" as if it came from a LOG call at "fname:line"
54void LogString(const char* fname, int line, int severity,
55 const std::string& message);
56
57class LogMessage : public std::basic_ostringstream<char> {
58 public:
59 LogMessage(const char* fname, int line, int severity);
60 ~LogMessage() override;
61
62 // Change the location of the log message.
63 LogMessage& AtLocation(const char* fname, int line);
64
65 // Returns the maximum log level for VLOG statements.
66 // E.g., if MaxVLogLevel() is 2, then VLOG(2) statements will produce output,
67 // but VLOG(3) will not. Defaults to 0.
68 static int64_t MaxVLogLevel();
69
70 // Returns whether VLOG level lvl is activated for the file fname.
71 //
72 // E.g. if the environment variable TF_CPP_VMODULE contains foo=3 and fname is
73 // foo.cc and lvl is <= 3, this will return true. It will also return true if
74 // the level is lower or equal to TF_CPP_MAX_VLOG_LEVEL (default zero).
75 //
76 // It is expected that the result of this query will be cached in the VLOG-ing
77 // call site to avoid repeated lookups. This routine performs a hash-map
78 // access against the VLOG-ing specification provided by the env var.
79 static bool VmoduleActivated(const char* fname, int level);
80
81 protected:
82 void GenerateLogMessage();
83
84 private:
85 const char* fname_;
86 int line_;
87 int severity_;
88};
89
90// Uses the lower operator & precedence to voidify a LogMessage reference, so
91// that the ternary VLOG() implementation is balanced, type wise.
92struct Voidifier {
93 template <typename T>
94 void operator&(const T&) const {}
95};
96
97// LogMessageFatal ensures the process will exit in failure after
98// logging this message.
99class LogMessageFatal : public LogMessage {
100 public:
101 LogMessageFatal(const char* file, int line) TF_ATTRIBUTE_COLD;
102 TF_ATTRIBUTE_NORETURN ~LogMessageFatal() override;
103};
104
105// LogMessageNull supports the DVLOG macro by simply dropping any log messages.
106class LogMessageNull : public std::basic_ostringstream<char> {
107 public:
108 LogMessageNull() {}
109 ~LogMessageNull() override {}
110};
111
112#define _TF_LOG_INFO \
113 ::tsl::internal::LogMessage(__FILE__, __LINE__, ::tsl::INFO)
114#define _TF_LOG_WARNING \
115 ::tsl::internal::LogMessage(__FILE__, __LINE__, ::tsl::WARNING)
116#define _TF_LOG_ERROR \
117 ::tsl::internal::LogMessage(__FILE__, __LINE__, ::tsl::ERROR)
118#define _TF_LOG_FATAL ::tsl::internal::LogMessageFatal(__FILE__, __LINE__)
119
120#define _TF_LOG_QFATAL _TF_LOG_FATAL
121
122#define LOG(severity) _TF_LOG_##severity
123
124#ifdef IS_MOBILE_PLATFORM
125
126// Turn VLOG off when under mobile devices for considerations of binary size.
127#define VLOG_IS_ON(lvl) ((lvl) <= 0)
128
129#else
130
131// Otherwise, set TF_CPP_MAX_VLOG_LEVEL environment to update minimum log level
132// of VLOG, or TF_CPP_VMODULE to set the minimum log level for individual
133// translation units.
134#define VLOG_IS_ON(lvl) \
135 (([](int level, const char* fname) { \
136 static const bool vmodule_activated = \
137 ::tsl::internal::LogMessage::VmoduleActivated(fname, level); \
138 return vmodule_activated; \
139 })(lvl, __FILE__))
140
141#endif
142
143#define VLOG(level) \
144 TF_PREDICT_TRUE(!VLOG_IS_ON(level)) \
145 ? (void)0 \
146 : ::tsl::internal::Voidifier() & \
147 ::tsl::internal::LogMessage(__FILE__, __LINE__, tsl::INFO)
148
149// `DVLOG` behaves like `VLOG` in debug mode (i.e. `#ifndef NDEBUG`).
150// Otherwise, it compiles away and does nothing.
151#ifndef NDEBUG
152#define DVLOG VLOG
153#else
154#define DVLOG(verbose_level) \
155 while (false && (verbose_level) > 0) ::tsl::internal::LogMessageNull()
156#endif
157
158class LogEveryNState {
159 public:
160 bool ShouldLog(int n);
161 uint32_t counter() { return counter_.load(std::memory_order_relaxed); }
162
163 private:
164 std::atomic<uint32> counter_{0};
165};
166
167class LogFirstNState {
168 public:
169 bool ShouldLog(int n);
170 uint32 counter() { return counter_.load(std::memory_order_relaxed); }
171
172 private:
173 std::atomic<uint32> counter_{0};
174};
175
176class LogEveryPow2State {
177 public:
178 bool ShouldLog(int ignored);
179 uint32 counter() { return counter_.load(std::memory_order_relaxed); }
180
181 private:
182 std::atomic<uint32> counter_{0};
183};
184
185class LogEveryNSecState {
186 public:
187 bool ShouldLog(double seconds);
188 uint32 counter() { return counter_.load(std::memory_order_relaxed); }
189
190 private:
191 std::atomic<uint32> counter_{0};
192 // Cycle count according to CycleClock that we should next log at.
193 std::atomic<int64_t> next_log_time_cycles_{0};
194};
195
196// This macro has a lot going on!
197//
198// * A local static (`logging_internal_stateful_condition_state`) is
199// declared in a scope such that each `LOG_EVERY_N` (etc.) line has its own
200// state.
201// * `COUNTER`, the third variable, is used to support `<< COUNTER`. It is not
202// mangled, so shadowing can be a problem, albeit more of a
203// shoot-yourself-in-the-foot one. Don't name your variables `COUNTER`.
204// * A single for loop can declare state and also test
205// `condition && state.ShouldLog()`, but there's no way to constrain it to run
206// only once (or not at all) without declaring another variable. The outer
207// for-loop declares this variable (`do_log`).
208// * Using for loops instead of if statements means there's no risk of an
209// ambiguous dangling else statement.
210#define LOGGING_INTERNAL_STATEFUL_CONDITION(kind, condition, arg) \
211 for (bool logging_internal_stateful_condition_do_log(condition); \
212 logging_internal_stateful_condition_do_log; \
213 logging_internal_stateful_condition_do_log = false) \
214 for (static ::tsl::internal::Log##kind##State \
215 logging_internal_stateful_condition_state; \
216 logging_internal_stateful_condition_do_log && \
217 logging_internal_stateful_condition_state.ShouldLog(arg); \
218 logging_internal_stateful_condition_do_log = false) \
219 for (const uint32_t COUNTER ABSL_ATTRIBUTE_UNUSED = \
220 logging_internal_stateful_condition_state.counter(); \
221 logging_internal_stateful_condition_do_log; \
222 logging_internal_stateful_condition_do_log = false)
223
224// An instance of `LOG_EVERY_N` increments a hidden zero-initialized counter
225// every time execution passes through it and logs the specified message when
226// the counter's value is a multiple of `n`, doing nothing otherwise. Each
227// instance has its own counter. The counter's value can be logged by streaming
228// the symbol `COUNTER`. `LOG_EVERY_N` is thread-safe.
229// Example:
230//
231// for (const auto& user : all_users) {
232// LOG_EVERY_N(INFO, 1000) << "Processing user #" << COUNTER;
233// ProcessUser(user);
234// }
235#define LOG_EVERY_N(severity, n) \
236 LOGGING_INTERNAL_STATEFUL_CONDITION(EveryN, true, n) \
237 LOG(severity)
238// `LOG_FIRST_N` behaves like `LOG_EVERY_N` except that the specified message is
239// logged when the counter's value is less than `n`. `LOG_FIRST_N` is
240// thread-safe.
241#define LOG_FIRST_N(severity, n) \
242 LOGGING_INTERNAL_STATEFUL_CONDITION(FirstN, true, n) \
243 LOG(severity)
244// `LOG_EVERY_POW_2` behaves like `LOG_EVERY_N` except that the specified
245// message is logged when the counter's value is a power of 2.
246// `LOG_EVERY_POW_2` is thread-safe.
247#define LOG_EVERY_POW_2(severity) \
248 LOGGING_INTERNAL_STATEFUL_CONDITION(EveryPow2, true, 0) \
249 LOG(severity)
250// An instance of `LOG_EVERY_N_SEC` uses a hidden state variable to log the
251// specified message at most once every `n_seconds`. A hidden counter of
252// executions (whether a message is logged or not) is also maintained and can be
253// logged by streaming the symbol `COUNTER`. `LOG_EVERY_N_SEC` is thread-safe.
254// Example:
255//
256// LOG_EVERY_N_SEC(INFO, 2.5) << "Got " << COUNTER << " cookies so far";
257#define LOG_EVERY_N_SEC(severity, n_seconds) \
258 LOGGING_INTERNAL_STATEFUL_CONDITION(EveryNSec, true, n_seconds) \
259 LOG(severity)
260
261// CHECK dies with a fatal error if condition is not true. It is *not*
262// controlled by NDEBUG, so the check will be executed regardless of
263// compilation mode. Therefore, it is safe to do things like:
264// CHECK(fp->Write(x) == 4)
265#define CHECK(condition) \
266 if (TF_PREDICT_FALSE(!(condition))) \
267 LOG(FATAL) << "Check failed: " #condition " "
268
269// Function is overloaded for integral types to allow static const
270// integrals declared in classes and not defined to be used as arguments to
271// CHECK* macros. It's not encouraged though.
272template <typename T>
273inline const T& GetReferenceableValue(const T& t) {
274 return t;
275}
276inline char GetReferenceableValue(char t) { return t; }
277inline unsigned char GetReferenceableValue(unsigned char t) { return t; }
278inline signed char GetReferenceableValue(signed char t) { return t; }
279inline int16 GetReferenceableValue(int16_t t) { return t; }
280inline uint16 GetReferenceableValue(uint16 t) { return t; }
281inline int GetReferenceableValue(int t) { return t; }
282inline unsigned int GetReferenceableValue(unsigned int t) { return t; }
283inline int64_t GetReferenceableValue(int64_t t) { return t; }
284inline uint64 GetReferenceableValue(uint64 t) { return t; }
285
286// This formats a value for a failing CHECK_XX statement. Ordinarily,
287// it uses the definition for operator<<, with a few special cases below.
288template <typename T>
289inline void MakeCheckOpValueString(std::ostream* os, const T& v) {
290 (*os) << v;
291}
292
293// Overrides for char types provide readable values for unprintable
294// characters.
295template <>
296void MakeCheckOpValueString(std::ostream* os, const char& v);
297template <>
298void MakeCheckOpValueString(std::ostream* os, const signed char& v);
299template <>
300void MakeCheckOpValueString(std::ostream* os, const unsigned char& v);
301
302#if LANG_CXX11
303// We need an explicit specialization for std::nullptr_t.
304template <>
305void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& v);
306#endif
307
308// A container for a string pointer which can be evaluated to a bool -
309// true iff the pointer is non-NULL.
310struct CheckOpString {
311 explicit CheckOpString(string* str) : str_(str) {}
312 // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),
313 // so there's no point in cleaning up str_.
314 explicit operator bool() const { return TF_PREDICT_FALSE(str_ != nullptr); }
315 string* str_;
316};
317
318// Build the error message string. Specify no inlining for code size.
319template <typename T1, typename T2>
320string* MakeCheckOpString(const T1& v1, const T2& v2,
321 const char* exprtext) TF_ATTRIBUTE_NOINLINE;
322
323// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX
324// statement. See MakeCheckOpString for sample usage. Other
325// approaches were considered: use of a template method (e.g.,
326// base::BuildCheckOpString(exprtext, base::Print<T1>, &v1,
327// base::Print<T2>, &v2), however this approach has complications
328// related to volatile arguments and function-pointer arguments).
329class CheckOpMessageBuilder {
330 public:
331 // Inserts "exprtext" and " (" to the stream.
332 explicit CheckOpMessageBuilder(const char* exprtext);
333 // Deletes "stream_".
334 ~CheckOpMessageBuilder();
335 // For inserting the first variable.
336 std::ostream* ForVar1() { return stream_; }
337 // For inserting the second variable (adds an intermediate " vs. ").
338 std::ostream* ForVar2();
339 // Get the result (inserts the closing ")").
340 string* NewString();
341
342 private:
343 std::ostringstream* stream_;
344};
345
346template <typename T1, typename T2>
347string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) {
348 CheckOpMessageBuilder comb(exprtext);
349 MakeCheckOpValueString(comb.ForVar1(), v1);
350 MakeCheckOpValueString(comb.ForVar2(), v2);
351 return comb.NewString();
352}
353
354// Helper functions for CHECK_OP macro.
355// We use the full name Check_EQ, Check_NE, etc. in case the file including
356// base/logging.h provides its own #defines for the simpler names EQ, NE, etc.
357// This happens if, for example, those are used as token names in a
358// yacc grammar.
359// The (int, int) overload works around the issue that the compiler
360// will not instantiate the template version of the function on values of
361// unnamed enum type - see comment below.
362#define TF_DEFINE_CHECK_OP_IMPL(name, op) \
363 template <typename T1, typename T2> \
364 inline string* name##Impl(const T1& v1, const T2& v2, \
365 const char* exprtext) { \
366 if (TF_PREDICT_TRUE(v1 op v2)) \
367 return NULL; \
368 else \
369 return ::tsl::internal::MakeCheckOpString(v1, v2, exprtext); \
370 } \
371 inline string* name##Impl(int v1, int v2, const char* exprtext) { \
372 return name##Impl<int, int>(v1, v2, exprtext); \
373 }
374
375// The (size_t, int) and (int, size_t) specialization are to handle unsigned
376// comparison errors while still being thorough with the comparison.
377
378TF_DEFINE_CHECK_OP_IMPL(Check_EQ, ==)
379// Compilation error with CHECK_EQ(NULL, x)?
380// Use CHECK(x == NULL) instead.
381
382inline string* Check_EQImpl(int v1, size_t v2, const char* exprtext) {
383 if (TF_PREDICT_FALSE(v1 < 0))
384 ::tsl::internal::MakeCheckOpString(v1, v2, exprtext);
385
386 return Check_EQImpl(size_t(v1), v2, exprtext);
387}
388
389inline string* Check_EQImpl(size_t v1, int v2, const char* exprtext) {
390 return Check_EQImpl(v2, v1, exprtext);
391}
392
393TF_DEFINE_CHECK_OP_IMPL(Check_NE, !=)
394
395inline string* Check_NEImpl(int v1, size_t v2, const char* exprtext) {
396 if (v1 < 0) return NULL;
397
398 return Check_NEImpl(size_t(v1), v2, exprtext);
399}
400
401inline string* Check_NEImpl(size_t v1, int v2, const char* exprtext) {
402 return Check_NEImpl(v2, v1, exprtext);
403}
404
405TF_DEFINE_CHECK_OP_IMPL(Check_LE, <=)
406
407inline string* Check_LEImpl(int v1, size_t v2, const char* exprtext) {
408 if (v1 <= 0) return NULL;
409
410 return Check_LEImpl(size_t(v1), v2, exprtext);
411}
412
413inline string* Check_LEImpl(size_t v1, int v2, const char* exprtext) {
414 if (TF_PREDICT_FALSE(v2 < 0))
415 return ::tsl::internal::MakeCheckOpString(v1, v2, exprtext);
416 return Check_LEImpl(v1, size_t(v2), exprtext);
417}
418
419TF_DEFINE_CHECK_OP_IMPL(Check_LT, <)
420
421inline string* Check_LTImpl(int v1, size_t v2, const char* exprtext) {
422 if (v1 < 0) return NULL;
423
424 return Check_LTImpl(size_t(v1), v2, exprtext);
425}
426
427inline string* Check_LTImpl(size_t v1, int v2, const char* exprtext) {
428 if (v2 < 0) return ::tsl::internal::MakeCheckOpString(v1, v2, exprtext);
429 return Check_LTImpl(v1, size_t(v2), exprtext);
430}
431
432// Implement GE,GT in terms of LE,LT
433template <typename T1, typename T2>
434inline string* Check_GEImpl(const T1& v1, const T2& v2, const char* exprtext) {
435 return Check_LEImpl(v2, v1, exprtext);
436}
437
438template <typename T1, typename T2>
439inline string* Check_GTImpl(const T1& v1, const T2& v2, const char* exprtext) {
440 return Check_LTImpl(v2, v1, exprtext);
441}
442
443#undef TF_DEFINE_CHECK_OP_IMPL
444
445// In optimized mode, use CheckOpString to hint to compiler that
446// the while condition is unlikely.
447#define CHECK_OP_LOG(name, op, val1, val2) \
448 while (::tsl::internal::CheckOpString _result{::tsl::internal::name##Impl( \
449 ::tsl::internal::GetReferenceableValue(val1), \
450 ::tsl::internal::GetReferenceableValue(val2), #val1 " " #op " " #val2)}) \
451 ::tsl::internal::LogMessageFatal(__FILE__, __LINE__) << *(_result.str_)
452
453#define CHECK_OP(name, op, val1, val2) CHECK_OP_LOG(name, op, val1, val2)
454
455// CHECK_EQ/NE/...
456#define CHECK_EQ(val1, val2) CHECK_OP(Check_EQ, ==, val1, val2)
457#define CHECK_NE(val1, val2) CHECK_OP(Check_NE, !=, val1, val2)
458#define CHECK_LE(val1, val2) CHECK_OP(Check_LE, <=, val1, val2)
459#define CHECK_LT(val1, val2) CHECK_OP(Check_LT, <, val1, val2)
460#define CHECK_GE(val1, val2) CHECK_OP(Check_GE, >=, val1, val2)
461#define CHECK_GT(val1, val2) CHECK_OP(Check_GT, >, val1, val2)
462#define CHECK_NOTNULL(val) \
463 ::tsl::internal::CheckNotNull(__FILE__, __LINE__, \
464 "'" #val "' Must be non NULL", (val))
465
466#ifndef NDEBUG
467// DCHECK_EQ/NE/...
468#define DCHECK(condition) CHECK(condition)
469#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
470#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
471#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2)
472#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2)
473#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2)
474#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2)
475
476#else
477
478#define DCHECK(condition) \
479 while (false && (condition)) LOG(FATAL)
480
481// NDEBUG is defined, so DCHECK_EQ(x, y) and so on do nothing.
482// However, we still want the compiler to parse x and y, because
483// we don't want to lose potentially useful errors and warnings.
484// _DCHECK_NOP is a helper, and should not be used outside of this file.
485#define _TF_DCHECK_NOP(x, y) \
486 while (false && ((void)(x), (void)(y), 0)) LOG(FATAL)
487
488#define DCHECK_EQ(x, y) _TF_DCHECK_NOP(x, y)
489#define DCHECK_NE(x, y) _TF_DCHECK_NOP(x, y)
490#define DCHECK_LE(x, y) _TF_DCHECK_NOP(x, y)
491#define DCHECK_LT(x, y) _TF_DCHECK_NOP(x, y)
492#define DCHECK_GE(x, y) _TF_DCHECK_NOP(x, y)
493#define DCHECK_GT(x, y) _TF_DCHECK_NOP(x, y)
494
495#endif
496
497// These are for when you don't want a CHECK failure to print a verbose
498// stack trace. The implementation of CHECK* in this file already doesn't.
499#define QCHECK(condition) CHECK(condition)
500#define QCHECK_EQ(x, y) CHECK_EQ(x, y)
501#define QCHECK_NE(x, y) CHECK_NE(x, y)
502#define QCHECK_LE(x, y) CHECK_LE(x, y)
503#define QCHECK_LT(x, y) CHECK_LT(x, y)
504#define QCHECK_GE(x, y) CHECK_GE(x, y)
505#define QCHECK_GT(x, y) CHECK_GT(x, y)
506
507template <typename T>
508T&& CheckNotNull(const char* file, int line, const char* exprtext, T&& t) {
509 if (t == nullptr) {
510 LogMessageFatal(file, line) << string(exprtext);
511 }
512 return std::forward<T>(t);
513}
514
515int64_t MinLogLevelFromEnv();
516
517int64_t MaxVLogLevelFromEnv();
518
519} // namespace internal
520
521// LogSink support adapted from //base/logging.h
522//
523// `LogSink` is an interface which can be extended to intercept and process
524// all log messages. LogSink implementations must be thread-safe. A single
525// instance will be called from whichever thread is performing a logging
526// operation.
527class TFLogEntry {
528 static absl::LogSeverity AsAbslLogSeverity(int severity) {
529 return static_cast<absl::LogSeverity>(severity);
530 }
531
532 public:
533 explicit TFLogEntry(int severity, absl::string_view message)
534 : severity_(AsAbslLogSeverity(severity)), message_(message) {}
535
536 explicit TFLogEntry(int severity, absl::string_view fname, int line,
537 absl::string_view message)
538 : severity_(AsAbslLogSeverity(severity)),
539 fname_(fname),
540 line_(line),
541 message_(message) {}
542
543 absl::LogSeverity log_severity() const { return severity_; }
544 std::string FName() const { return fname_; }
545 int Line() const { return line_; }
546 std::string ToString() const { return message_; }
547 absl::string_view text_message() const { return message_; }
548
549 private:
550 const absl::LogSeverity severity_;
551 const std::string fname_;
552 int line_ = -1;
553 const std::string message_;
554};
555
556class TFLogSink {
557 public:
558 virtual ~TFLogSink() = default;
559
560 // `Send` is called synchronously during the log statement. The logging
561 // module guarantees not to call `Send` concurrently on the same log sink.
562 // Implementations should be careful not to call`LOG` or `CHECK` or take
563 // any locks that might be held by the `LOG` caller, to avoid deadlock.
564 //
565 // `e` is guaranteed to remain valid until the subsequent call to
566 // `WaitTillSent` completes, so implementations may store a pointer to or
567 // copy of `e` (e.g. in a thread local variable) for use in `WaitTillSent`.
568 virtual void Send(const TFLogEntry& entry) = 0;
569
570 // `WaitTillSent` blocks the calling thread (the thread that generated a log
571 // message) until the sink has finished processing the log message.
572 // `WaitTillSent` is called once per log message, following the call to
573 // `Send`. This may be useful when log messages are buffered or processed
574 // asynchronously by an expensive log sink.
575 // The default implementation returns immediately. Like `Send`,
576 // implementations should be careful not to call `LOG` or `CHECK or take any
577 // locks that might be held by the `LOG` caller, to avoid deadlock.
578 virtual void WaitTillSent() {}
579};
580
581// This is the default log sink. This log sink is used if there are no other
582// log sinks registered. To disable the default log sink, set the
583// "no_default_logger" Bazel config setting to true or define a
584// NO_DEFAULT_LOGGER preprocessor symbol. This log sink will always log to
585// stderr.
586class TFDefaultLogSink : public TFLogSink {
587 public:
588 void Send(const TFLogEntry& entry) override;
589};
590
591// Add or remove a `LogSink` as a consumer of logging data. Thread-safe.
592void TFAddLogSink(TFLogSink* sink);
593void TFRemoveLogSink(TFLogSink* sink);
594
595// Get all the log sinks. Thread-safe.
596std::vector<TFLogSink*> TFGetLogSinks();
597
598// Change verbose level of pre-defined files if envorionment
599// variable `env_var` is defined. This is currently a no op.
600void UpdateLogVerbosityIfDefined(const char* env_var);
601
602} // namespace tsl
603
604#endif // TENSORFLOW_TSL_PLATFORM_DEFAULT_LOGGING_H_
605