xref: /aosp_15_r20/external/tensorflow/tensorflow/tsl/platform/default/logging.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/tsl/platform/default/logging.h"
17 
18 // TODO(b/142492876): Avoid depending on absl internal.
19 #ifdef TF_ANDROID_ENABLE_LOG_EVERY_N_SECONDS
20 #include "absl/base/internal/cycleclock.h"
21 #endif
22 #if !defined(PLATFORM_POSIX_ANDROID)
23 #include "absl/base/internal/sysinfo.h"
24 #endif
25 #include "tensorflow/core/platform/env_time.h"
26 #include "tensorflow/core/platform/macros.h"
27 #ifdef TF_ANDROID_ENABLE_LOGSINK
28 #include "tensorflow/core/platform/mutex.h"
29 #endif
30 
31 #if defined(PLATFORM_POSIX_ANDROID)
32 #include <android/log.h>
33 #include <iostream>
34 #include <sstream>
35 #endif
36 
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 
41 #include <algorithm>
42 #include <queue>
43 #include <unordered_map>
44 
45 #ifdef __ANDROID__
46 #include <unistd.h>
47 #endif
48 
49 namespace tensorflow {
50 
51 namespace internal {
52 namespace {
53 
54 #ifdef TF_ANDROID_ENABLE_LOGSINK
55 // This is an internal singleton class that manages the log sinks. It allows
56 // adding and removing the log sinks, as well as handling sending log messages
57 // to all the registered log sinks.
58 class TFLogSinks {
59  public:
60   // Gets the TFLogSinks instance. This is the entry point for using this class.
61   static TFLogSinks& Instance();
62 
63   // Adds a log sink. The sink argument must not be a nullptr. TFLogSinks
64   // takes ownership of the pointer, the user must not free the pointer.
65   // The pointer will remain valid until the application terminates or
66   // until TFLogSinks::Remove is called for the same pointer value.
67   void Add(TFLogSink* sink);
68 
69   // Removes a log sink. This will also erase the sink object. The pointer
70   // to the sink becomes invalid after this call.
71   void Remove(TFLogSink* sink);
72 
73   // Gets the currently registered log sinks.
74   std::vector<TFLogSink*> GetSinks() const;
75 
76   // Sends a log message to all registered log sinks.
77   //
78   // If there are no log sinks are registered:
79   //
80   // NO_DEFAULT_LOGGER is defined:
81   // Up to 128 messages will be queued until a log sink is added.
82   // The queue will then be logged to the first added log sink.
83   //
84   // NO_DEFAULT_LOGGER is not defined:
85   // The messages will be logged using the default logger. The default logger
86   // will log to stdout on all platforms except for Android. On Androit the
87   // default Android logger will be used.
88   void Send(const TFLogEntry& entry);
89 
90  private:
91   TFLogSinks();
92   void SendToSink(TFLogSink& sink, const TFLogEntry& entry);
93 
94   std::queue<TFLogEntry> log_entry_queue_;
95   static const size_t kMaxLogEntryQueueSize = 128;
96 
97   mutable tensorflow::mutex mutex_;
98   std::vector<TFLogSink*> sinks_;
99 };
100 
TFLogSinks()101 TFLogSinks::TFLogSinks() {
102 #ifndef NO_DEFAULT_LOGGER
103   static TFDefaultLogSink* default_sink = new TFDefaultLogSink();
104   sinks_.emplace_back(default_sink);
105 #endif
106 }
107 
Instance()108 TFLogSinks& TFLogSinks::Instance() {
109   static TFLogSinks* instance = new TFLogSinks();
110   return *instance;
111 }
112 
Add(TFLogSink * sink)113 void TFLogSinks::Add(TFLogSink* sink) {
114   assert(sink != nullptr && "The sink must not be a nullptr");
115 
116   tensorflow::mutex_lock lock(mutex_);
117   sinks_.emplace_back(sink);
118 
119   // If this is the only sink log all the queued up messages to this sink
120   if (sinks_.size() == 1) {
121     while (!log_entry_queue_.empty()) {
122       for (const auto& sink : sinks_) {
123         SendToSink(*sink, log_entry_queue_.front());
124       }
125       log_entry_queue_.pop();
126     }
127   }
128 }
129 
Remove(TFLogSink * sink)130 void TFLogSinks::Remove(TFLogSink* sink) {
131   assert(sink != nullptr && "The sink must not be a nullptr");
132 
133   tensorflow::mutex_lock lock(mutex_);
134   auto it = std::find(sinks_.begin(), sinks_.end(), sink);
135   if (it != sinks_.end()) sinks_.erase(it);
136 }
137 
GetSinks() const138 std::vector<TFLogSink*> TFLogSinks::GetSinks() const {
139   tensorflow::mutex_lock lock(mutex_);
140   return sinks_;
141 }
142 
Send(const TFLogEntry & entry)143 void TFLogSinks::Send(const TFLogEntry& entry) {
144   tensorflow::mutex_lock lock(mutex_);
145 
146   // If we don't have any sinks registered, queue them up
147   if (sinks_.empty()) {
148     // If we've exceeded the maximum queue size, drop the oldest entries
149     while (log_entry_queue_.size() >= kMaxLogEntryQueueSize) {
150       log_entry_queue_.pop();
151     }
152     log_entry_queue_.push(entry);
153     return;
154   }
155 
156   // If we have items in the queue, push them out first
157   while (!log_entry_queue_.empty()) {
158     for (const auto& sink : sinks_) {
159       SendToSink(*sink, log_entry_queue_.front());
160     }
161     log_entry_queue_.pop();
162   }
163 
164   // ... and now we can log the current log entry
165   for (const auto& sink : sinks_) {
166     SendToSink(*sink, entry);
167   }
168 }
169 
SendToSink(TFLogSink & sink,const TFLogEntry & entry)170 void TFLogSinks::SendToSink(TFLogSink& sink, const TFLogEntry& entry) {
171   sink.Send(entry);
172   sink.WaitTillSent();
173 }
174 #endif  // TF_ANDROID_ENABLE_LOGSINK
175 
176 // A class for managing the text file to which VLOG output is written.
177 // If the environment variable TF_CPP_VLOG_FILENAME is set, all VLOG
178 // calls are redirected from stderr to a file with corresponding name.
179 class VlogFileMgr {
180  public:
181   // Determines if the env variable is set and if necessary
182   // opens the file for write access.
183   VlogFileMgr();
184   // Closes the file.
185   ~VlogFileMgr();
186   // Returns either a pointer to the file or stderr.
187   FILE* FilePtr() const;
188 
189  private:
190   FILE* vlog_file_ptr;
191   char* vlog_file_name;
192 };
193 
VlogFileMgr()194 VlogFileMgr::VlogFileMgr() {
195   vlog_file_name = getenv("TF_CPP_VLOG_FILENAME");
196   vlog_file_ptr =
197       vlog_file_name == nullptr ? nullptr : fopen(vlog_file_name, "w");
198 
199   if (vlog_file_ptr == nullptr) {
200     vlog_file_ptr = stderr;
201   }
202 }
203 
~VlogFileMgr()204 VlogFileMgr::~VlogFileMgr() {
205   if (vlog_file_ptr != stderr) {
206     fclose(vlog_file_ptr);
207   }
208 }
209 
FilePtr() const210 FILE* VlogFileMgr::FilePtr() const { return vlog_file_ptr; }
211 
ParseInteger(const char * str,size_t size)212 int ParseInteger(const char* str, size_t size) {
213   // Ideally we would use env_var / safe_strto64, but it is
214   // hard to use here without pulling in a lot of dependencies,
215   // so we use std:istringstream instead
216   string integer_str(str, size);
217   std::istringstream ss(integer_str);
218   int level = 0;
219   ss >> level;
220   return level;
221 }
222 
223 // Parse log level (int64) from environment variable (char*)
LogLevelStrToInt(const char * tf_env_var_val)224 int64_t LogLevelStrToInt(const char* tf_env_var_val) {
225   if (tf_env_var_val == nullptr) {
226     return 0;
227   }
228   return ParseInteger(tf_env_var_val, strlen(tf_env_var_val));
229 }
230 
231 // Using StringPiece breaks Windows build.
232 struct StringData {
233   struct Hasher {
operator ()tensorflow::internal::__anon81f52f100111::StringData::Hasher234     size_t operator()(const StringData& sdata) const {
235       // For dependency reasons, we cannot use hash.h here. Use DBJHash instead.
236       size_t hash = 5381;
237       const char* data = sdata.data;
238       for (const char* top = data + sdata.size; data < top; ++data) {
239         hash = ((hash << 5) + hash) + (*data);
240       }
241       return hash;
242     }
243   };
244 
245   StringData() = default;
StringDatatensorflow::internal::__anon81f52f100111::StringData246   StringData(const char* data, size_t size) : data(data), size(size) {}
247 
operator ==tensorflow::internal::__anon81f52f100111::StringData248   bool operator==(const StringData& rhs) const {
249     return size == rhs.size && memcmp(data, rhs.data, size) == 0;
250   }
251 
252   const char* data = nullptr;
253   size_t size = 0;
254 };
255 
256 using VmoduleMap = std::unordered_map<StringData, int, StringData::Hasher>;
257 
258 // Returns a mapping from module name to VLOG level, derived from the
259 // TF_CPP_VMODULE environment variable; ownership is transferred to the caller.
VmodulesMapFromEnv()260 VmoduleMap* VmodulesMapFromEnv() {
261   // The value of the env var is supposed to be of the form:
262   //    "foo=1,bar=2,baz=3"
263   const char* env = getenv("TF_CPP_VMODULE");
264   if (env == nullptr) {
265     // If there is no TF_CPP_VMODULE configuration (most common case), return
266     // nullptr so that the ShouldVlogModule() API can fast bail out of it.
267     return nullptr;
268   }
269   // The memory returned by getenv() can be invalidated by following getenv() or
270   // setenv() calls. And since we keep references to it in the VmoduleMap in
271   // form of StringData objects, make a copy of it.
272   const char* env_data = strdup(env);
273   VmoduleMap* result = new VmoduleMap();
274   while (true) {
275     const char* eq = strchr(env_data, '=');
276     if (eq == nullptr) {
277       break;
278     }
279     const char* after_eq = eq + 1;
280 
281     // Comma either points at the next comma delimiter, or at a null terminator.
282     // We check that the integer we parse ends at this delimiter.
283     const char* comma = strchr(after_eq, ',');
284     const char* new_env_data;
285     if (comma == nullptr) {
286       comma = strchr(after_eq, '\0');
287       new_env_data = comma;
288     } else {
289       new_env_data = comma + 1;
290     }
291     (*result)[StringData(env_data, eq - env_data)] =
292         ParseInteger(after_eq, comma - after_eq);
293     env_data = new_env_data;
294   }
295   return result;
296 }
297 
EmitThreadIdFromEnv()298 bool EmitThreadIdFromEnv() {
299   const char* tf_env_var_val = getenv("TF_CPP_LOG_THREAD_ID");
300   return tf_env_var_val == nullptr
301              ? false
302              : ParseInteger(tf_env_var_val, strlen(tf_env_var_val)) != 0;
303 }
304 
305 }  // namespace
306 
MinLogLevelFromEnv()307 int64_t MinLogLevelFromEnv() {
308   // We don't want to print logs during fuzzing as that would slow fuzzing down
309   // by almost 2x. So, if we are in fuzzing mode (not just running a test), we
310   // return a value so that nothing is actually printed. Since LOG uses >=
311   // (see ~LogMessage in this file) to see if log messages need to be printed,
312   // the value we're interested on to disable printing is the maximum severity.
313   // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
314 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
315   return tensorflow::NUM_SEVERITIES;
316 #else
317   const char* tf_env_var_val = getenv("TF_CPP_MIN_LOG_LEVEL");
318   return LogLevelStrToInt(tf_env_var_val);
319 #endif
320 }
321 
MaxVLogLevelFromEnv()322 int64_t MaxVLogLevelFromEnv() {
323   // We don't want to print logs during fuzzing as that would slow fuzzing down
324   // by almost 2x. So, if we are in fuzzing mode (not just running a test), we
325   // return a value so that nothing is actually printed. Since VLOG uses <=
326   // (see VLOG_IS_ON in logging.h) to see if log messages need to be printed,
327   // the value we're interested on to disable printing is 0.
328   // See also http://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode
329 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
330   return 0;
331 #else
332   const char* tf_env_var_val = getenv("TF_CPP_MAX_VLOG_LEVEL");
333   return LogLevelStrToInt(tf_env_var_val);
334 #endif
335 }
336 
LogMessage(const char * fname,int line,int severity)337 LogMessage::LogMessage(const char* fname, int line, int severity)
338     : fname_(fname), line_(line), severity_(severity) {}
339 
AtLocation(const char * fname,int line)340 LogMessage& LogMessage::AtLocation(const char* fname, int line) {
341   fname_ = fname;
342   line_ = line;
343   return *this;
344 }
345 
~LogMessage()346 LogMessage::~LogMessage() {
347   // Read the min log level once during the first call to logging.
348   static int64_t min_log_level = MinLogLevelFromEnv();
349   if (severity_ >= min_log_level) {
350     GenerateLogMessage();
351   }
352 }
353 
GenerateLogMessage()354 void LogMessage::GenerateLogMessage() {
355 #ifdef TF_ANDROID_ENABLE_LOGSINK
356   TFLogSinks::Instance().Send(TFLogEntry(severity_, fname_, line_, str()));
357 #else
358   static bool log_thread_id = EmitThreadIdFromEnv();
359   uint64 now_micros = EnvTime::NowMicros();
360   time_t now_seconds = static_cast<time_t>(now_micros / 1000000);
361   int32 micros_remainder = static_cast<int32>(now_micros % 1000000);
362   const size_t time_buffer_size = 30;
363   char time_buffer[time_buffer_size];
364   strftime(time_buffer, time_buffer_size, "%Y-%m-%d %H:%M:%S",
365            localtime(&now_seconds));
366   const size_t tid_buffer_size = 10;
367   char tid_buffer[tid_buffer_size] = "";
368 #ifdef __ANDROID__
369   if (log_thread_id) {
370     snprintf(tid_buffer, sizeof(tid_buffer), " %7u", gettid());
371   }
372 #endif
373   // TODO(jeff,sanjay): Replace this with something that logs through the env.
374   fprintf(stderr, "%s.%06d: %c%s %s:%d] %s\n", time_buffer, micros_remainder,
375           "IWEF"[severity_], tid_buffer, fname_, line_, str().c_str());
376 #endif  // TF_ANDROID_ENABLE_LOGSINK
377 }
378 
MaxVLogLevel()379 int64_t LogMessage::MaxVLogLevel() {
380   static int64_t max_vlog_level = MaxVLogLevelFromEnv();
381   return max_vlog_level;
382 }
383 
VmoduleActivated(const char * fname,int level)384 bool LogMessage::VmoduleActivated(const char* fname, int level) {
385   if (level <= MaxVLogLevel()) {
386     return true;
387   }
388   static VmoduleMap* vmodules = VmodulesMapFromEnv();
389   if (TF_PREDICT_TRUE(vmodules == nullptr)) {
390     return false;
391   }
392   const char* last_slash = strrchr(fname, '/');
393   const char* module_start = last_slash == nullptr ? fname : last_slash + 1;
394   const char* dot_after = strchr(module_start, '.');
395   const char* module_limit =
396       dot_after == nullptr ? strchr(fname, '\0') : dot_after;
397   StringData module(module_start, module_limit - module_start);
398   auto it = vmodules->find(module);
399   return it != vmodules->end() && it->second >= level;
400 }
401 
LogMessageFatal(const char * file,int line)402 LogMessageFatal::LogMessageFatal(const char* file, int line)
403     : LogMessage(file, line, FATAL) {}
~LogMessageFatal()404 LogMessageFatal::~LogMessageFatal() {
405   // abort() ensures we don't return (we promised we would not via
406   // ATTRIBUTE_NORETURN).
407   GenerateLogMessage();
408   abort();
409 }
410 
LogString(const char * fname,int line,int severity,const string & message)411 void LogString(const char* fname, int line, int severity,
412                const string& message) {
413   LogMessage(fname, line, severity) << message;
414 }
415 
416 template <>
MakeCheckOpValueString(std::ostream * os,const char & v)417 void MakeCheckOpValueString(std::ostream* os, const char& v) {
418   if (v >= 32 && v <= 126) {
419     (*os) << "'" << v << "'";
420   } else {
421     (*os) << "char value " << static_cast<int16>(v);
422   }
423 }
424 
425 template <>
MakeCheckOpValueString(std::ostream * os,const signed char & v)426 void MakeCheckOpValueString(std::ostream* os, const signed char& v) {
427   if (v >= 32 && v <= 126) {
428     (*os) << "'" << v << "'";
429   } else {
430     (*os) << "signed char value " << static_cast<int16>(v);
431   }
432 }
433 
434 template <>
MakeCheckOpValueString(std::ostream * os,const unsigned char & v)435 void MakeCheckOpValueString(std::ostream* os, const unsigned char& v) {
436   if (v >= 32 && v <= 126) {
437     (*os) << "'" << v << "'";
438   } else {
439     (*os) << "unsigned char value " << static_cast<uint16>(v);
440   }
441 }
442 
443 #if LANG_CXX11
444 template <>
MakeCheckOpValueString(std::ostream * os,const std::nullptr_t & v)445 void MakeCheckOpValueString(std::ostream* os, const std::nullptr_t& v) {
446   (*os) << "nullptr";
447 }
448 #endif
449 
CheckOpMessageBuilder(const char * exprtext)450 CheckOpMessageBuilder::CheckOpMessageBuilder(const char* exprtext)
451     : stream_(new std::ostringstream) {
452   *stream_ << "Check failed: " << exprtext << " (";
453 }
454 
~CheckOpMessageBuilder()455 CheckOpMessageBuilder::~CheckOpMessageBuilder() { delete stream_; }
456 
ForVar2()457 std::ostream* CheckOpMessageBuilder::ForVar2() {
458   *stream_ << " vs. ";
459   return stream_;
460 }
461 
NewString()462 string* CheckOpMessageBuilder::NewString() {
463   *stream_ << ")";
464   return new string(stream_->str());
465 }
466 
467 namespace {
468 // The following code behaves like AtomicStatsCounter::LossyAdd() for
469 // speed since it is fine to lose occasional updates.
470 // Returns old value of *counter.
LossyIncrement(std::atomic<uint32> * counter)471 uint32 LossyIncrement(std::atomic<uint32>* counter) {
472   const uint32 value = counter->load(std::memory_order_relaxed);
473   counter->store(value + 1, std::memory_order_relaxed);
474   return value;
475 }
476 }  // namespace
477 
ShouldLog(int n)478 bool LogEveryNState::ShouldLog(int n) {
479   return n != 0 && (LossyIncrement(&counter_) % n) == 0;
480 }
481 
ShouldLog(int n)482 bool LogFirstNState::ShouldLog(int n) {
483   const int counter_value =
484       static_cast<int>(counter_.load(std::memory_order_relaxed));
485   if (counter_value < n) {
486     counter_.store(counter_value + 1, std::memory_order_relaxed);
487     return true;
488   }
489   return false;
490 }
491 
ShouldLog(int ignored)492 bool LogEveryPow2State::ShouldLog(int ignored) {
493   const uint32 new_value = LossyIncrement(&counter_) + 1;
494   return (new_value & (new_value - 1)) == 0;
495 }
496 
497 #ifdef TF_ANDROID_ENABLE_LOG_EVERY_N_SECONDS
ShouldLog(double seconds)498 bool LogEveryNSecState::ShouldLog(double seconds) {
499   LossyIncrement(&counter_);
500   const int64_t now_cycles = absl::base_internal::CycleClock::Now();
501   int64_t next_cycles = next_log_time_cycles_.load(std::memory_order_relaxed);
502   do {
503     if (now_cycles <= next_cycles) return false;
504   } while (!next_log_time_cycles_.compare_exchange_weak(
505       next_cycles,
506       now_cycles + seconds * absl::base_internal::CycleClock::Frequency(),
507       std::memory_order_relaxed, std::memory_order_relaxed));
508   return true;
509 }
510 #endif  // TF_ANDROID_ENABLE_LOG_EVERY_N_SECONDS
511 
512 }  // namespace internal
513 
514 #ifdef TF_ANDROID_ENABLE_LOGSINK
TFAddLogSink(TFLogSink * sink)515 void TFAddLogSink(TFLogSink* sink) {
516   internal::TFLogSinks::Instance().Add(sink);
517 }
518 
TFRemoveLogSink(TFLogSink * sink)519 void TFRemoveLogSink(TFLogSink* sink) {
520   internal::TFLogSinks::Instance().Remove(sink);
521 }
522 
TFGetLogSinks()523 std::vector<TFLogSink*> TFGetLogSinks() {
524   return internal::TFLogSinks::Instance().GetSinks();
525 }
526 
Send(const TFLogEntry & entry)527 void TFDefaultLogSink::Send(const TFLogEntry& entry) {
528 #ifdef PLATFORM_POSIX_ANDROID
529   int android_log_level;
530   switch (entry.log_severity()) {
531     case absl::LogSeverity::kInfo:
532       android_log_level = ANDROID_LOG_INFO;
533       break;
534     case absl::LogSeverity::kWarning:
535       android_log_level = ANDROID_LOG_WARN;
536       break;
537     case absl::LogSeverity::kError:
538       android_log_level = ANDROID_LOG_ERROR;
539       break;
540     case absl::LogSeverity::kFatal:
541       android_log_level = ANDROID_LOG_FATAL;
542       break;
543     default:
544       if (entry.log_severity() < absl::LogSeverity::kInfo) {
545         android_log_level = ANDROID_LOG_VERBOSE;
546       } else {
547         android_log_level = ANDROID_LOG_ERROR;
548       }
549       break;
550   }
551 
552   std::stringstream ss;
553   const auto& fname = entry.FName();
554   auto pos = fname.find("/");
555   ss << (pos != std::string::npos ? fname.substr(pos + 1) : fname) << ":"
556      << entry.Line() << " " << entry.ToString();
557   __android_log_write(android_log_level, "native", ss.str().c_str());
558 
559   // Also log to stderr (for standalone Android apps).
560   // Don't use 'std::cerr' since it crashes on Android.
561   fprintf(stderr, "native : %s\n", ss.str().c_str());
562 
563   // Android logging at level FATAL does not terminate execution, so abort()
564   // is still required to stop the program.
565   if (entry.log_severity() == absl::LogSeverity::kFatal) {
566     abort();
567   }
568 #else   // PLATFORM_POSIX_ANDROID
569   static const internal::VlogFileMgr vlog_file;
570   static bool log_thread_id = internal::EmitThreadIdFromEnv();
571   uint64 now_micros = EnvTime::NowMicros();
572   time_t now_seconds = static_cast<time_t>(now_micros / 1000000);
573   int32_t micros_remainder = static_cast<int32>(now_micros % 1000000);
574   const size_t time_buffer_size = 30;
575   char time_buffer[time_buffer_size];
576   strftime(time_buffer, time_buffer_size, "%Y-%m-%d %H:%M:%S",
577            localtime(&now_seconds));
578   const size_t tid_buffer_size = 10;
579   char tid_buffer[tid_buffer_size] = "";
580   if (log_thread_id) {
581     snprintf(tid_buffer, sizeof(tid_buffer), " %7u",
582              absl::base_internal::GetTID());
583   }
584 
585   char sev;
586   switch (entry.log_severity()) {
587     case absl::LogSeverity::kInfo:
588       sev = 'I';
589       break;
590 
591     case absl::LogSeverity::kWarning:
592       sev = 'W';
593       break;
594 
595     case absl::LogSeverity::kError:
596       sev = 'E';
597       break;
598 
599     case absl::LogSeverity::kFatal:
600       sev = 'F';
601       break;
602 
603     default:
604       assert(false && "Unknown logging severity");
605       sev = '?';
606       break;
607   }
608 
609   fprintf(vlog_file.FilePtr(), "%s.%06d: %c%s %s:%d] %s\n", time_buffer,
610           micros_remainder, sev, tid_buffer, entry.FName().c_str(),
611           entry.Line(), entry.ToString().c_str());
612 #endif  // PLATFORM_POSIX_ANDROID
613 }
614 #endif  // TF_ANDROID_ENABLE_LOGSINK
615 
UpdateLogVerbosityIfDefined(const char * env_var)616 void UpdateLogVerbosityIfDefined(const char* env_var) {}
617 
618 }  // namespace tensorflow
619