xref: /aosp_15_r20/external/webrtc/rtc_base/logging.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "rtc_base/logging.h"
12 
13 #include <string.h>
14 
15 #if RTC_LOG_ENABLED()
16 
17 #if defined(WEBRTC_WIN)
18 #include <windows.h>
19 #if _MSC_VER < 1900
20 #define snprintf _snprintf
21 #endif
22 #undef ERROR  // wingdi.h
23 #endif
24 
25 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
26 #include <CoreServices/CoreServices.h>
27 #elif defined(WEBRTC_ANDROID)
28 #include <android/log.h>
29 
30 // Android has a 1024 limit on log inputs. We use 60 chars as an
31 // approx for the header/tag portion.
32 // See android/system/logging/liblog/logd_write.c
33 static const int kMaxLogLineSize = 1024 - 60;
34 #endif  // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID
35 
36 #include <inttypes.h>
37 #include <stdio.h>
38 #include <time.h>
39 
40 #include <algorithm>
41 #include <cstdarg>
42 #include <vector>
43 
44 #include "absl/base/attributes.h"
45 #include "absl/strings/string_view.h"
46 #include "api/units/timestamp.h"
47 #include "rtc_base/checks.h"
48 #include "rtc_base/platform_thread_types.h"
49 #include "rtc_base/string_encode.h"
50 #include "rtc_base/string_utils.h"
51 #include "rtc_base/strings/string_builder.h"
52 #include "rtc_base/synchronization/mutex.h"
53 #include "rtc_base/thread_annotations.h"
54 #include "rtc_base/time_utils.h"
55 
56 namespace rtc {
57 namespace {
58 
59 // By default, release builds don't log, debug builds at info level
60 #if !defined(NDEBUG)
61 constexpr LoggingSeverity kDefaultLoggingSeverity = LS_INFO;
62 #else
63 constexpr LoggingSeverity kDefaultLoggingSeverity = LS_NONE;
64 #endif
65 
66 // Note: `g_min_sev` and `g_dbg_sev` can be changed while running.
67 LoggingSeverity g_min_sev = kDefaultLoggingSeverity;
68 LoggingSeverity g_dbg_sev = kDefaultLoggingSeverity;
69 
70 // Return the filename portion of the string (that following the last slash).
FilenameFromPath(const char * file)71 const char* FilenameFromPath(const char* file) {
72   const char* end1 = ::strrchr(file, '/');
73   const char* end2 = ::strrchr(file, '\\');
74   if (!end1 && !end2)
75     return file;
76   else
77     return (end1 > end2) ? end1 + 1 : end2 + 1;
78 }
79 
80 // Global lock for log subsystem, only needed to serialize access to streams_.
GetLoggingLock()81 webrtc::Mutex& GetLoggingLock() {
82   static webrtc::Mutex& mutex = *new webrtc::Mutex();
83   return mutex;
84 }
85 
86 }  // namespace
87 
DefaultLogLine() const88 std::string LogLineRef::DefaultLogLine() const {
89   rtc::StringBuilder log_output;
90   if (timestamp_ != webrtc::Timestamp::MinusInfinity()) {
91     // TODO(kwiberg): Switch to absl::StrFormat, if binary size is ok.
92     char timestamp[50];  // Maximum string length of an int64_t is 20.
93     int len =
94         snprintf(timestamp, sizeof(timestamp), "[%03" PRId64 ":%03" PRId64 "]",
95                  timestamp_.ms() / 1000, timestamp_.ms() % 1000);
96     RTC_DCHECK_LT(len, sizeof(timestamp));
97     log_output << timestamp;
98   }
99   if (thread_id_.has_value()) {
100     log_output << "[" << *thread_id_ << "] ";
101   }
102   if (!filename_.empty()) {
103 #if defined(WEBRTC_ANDROID)
104     log_output << "(line " << line_ << "): ";
105 #else
106     log_output << "(" << filename_ << ":" << line_ << "): ";
107 #endif
108   }
109   log_output << message_;
110   return log_output.Release();
111 }
112 
113 /////////////////////////////////////////////////////////////////////////////
114 // LogMessage
115 /////////////////////////////////////////////////////////////////////////////
116 
117 bool LogMessage::log_to_stderr_ = true;
118 
119 // The list of logging streams currently configured.
120 // Note: we explicitly do not clean this up, because of the uncertain ordering
121 // of destructors at program exit.  Let the person who sets the stream trigger
122 // cleanup by setting to null, or let it leak (safe at program exit).
123 ABSL_CONST_INIT LogSink* LogMessage::streams_ RTC_GUARDED_BY(GetLoggingLock()) =
124     nullptr;
125 ABSL_CONST_INIT std::atomic<bool> LogMessage::streams_empty_ = {true};
126 
127 // Boolean options default to false.
128 ABSL_CONST_INIT bool LogMessage::log_thread_ = false;
129 ABSL_CONST_INIT bool LogMessage::log_timestamp_ = false;
130 
LogMessage(const char * file,int line,LoggingSeverity sev)131 LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev)
132     : LogMessage(file, line, sev, ERRCTX_NONE, 0) {}
133 
LogMessage(const char * file,int line,LoggingSeverity sev,LogErrorContext err_ctx,int err)134 LogMessage::LogMessage(const char* file,
135                        int line,
136                        LoggingSeverity sev,
137                        LogErrorContext err_ctx,
138                        int err) {
139   log_line_.set_severity(sev);
140   if (log_timestamp_) {
141     int64_t log_start_time = LogStartTime();
142     // Use SystemTimeMillis so that even if tests use fake clocks, the timestamp
143     // in log messages represents the real system time.
144     int64_t time = TimeDiff(SystemTimeMillis(), log_start_time);
145     // Also ensure WallClockStartTime is initialized, so that it matches
146     // LogStartTime.
147     WallClockStartTime();
148     log_line_.set_timestamp(webrtc::Timestamp::Millis(time));
149   }
150 
151   if (log_thread_) {
152     log_line_.set_thread_id(CurrentThreadId());
153   }
154 
155   if (file != nullptr) {
156     log_line_.set_filename(FilenameFromPath(file));
157     log_line_.set_line(line);
158 #if defined(WEBRTC_ANDROID)
159     log_line_.set_tag(log_line_.filename());
160 #endif
161   }
162 
163   if (err_ctx != ERRCTX_NONE) {
164     char tmp_buf[1024];
165     SimpleStringBuilder tmp(tmp_buf);
166     tmp.AppendFormat("[0x%08X]", err);
167     switch (err_ctx) {
168       case ERRCTX_ERRNO:
169         tmp << " " << strerror(err);
170         break;
171 #ifdef WEBRTC_WIN
172       case ERRCTX_HRESULT: {
173         char msgbuf[256];
174         DWORD flags =
175             FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
176         if (DWORD len = FormatMessageA(
177                 flags, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
178                 msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) {
179           while ((len > 0) &&
180                  isspace(static_cast<unsigned char>(msgbuf[len - 1]))) {
181             msgbuf[--len] = 0;
182           }
183           tmp << " " << msgbuf;
184         }
185         break;
186       }
187 #endif  // WEBRTC_WIN
188       default:
189         break;
190     }
191     extra_ = tmp.str();
192   }
193 }
194 
195 #if defined(WEBRTC_ANDROID)
LogMessage(const char * file,int line,LoggingSeverity sev,const char * tag)196 LogMessage::LogMessage(const char* file,
197                        int line,
198                        LoggingSeverity sev,
199                        const char* tag)
200     : LogMessage(file, line, sev, ERRCTX_NONE, /*err=*/0) {
201   log_line_.set_tag(tag);
202   print_stream_ << tag << ": ";
203 }
204 #endif
205 
~LogMessage()206 LogMessage::~LogMessage() {
207   FinishPrintStream();
208 
209   log_line_.set_message(print_stream_.Release());
210 
211   if (log_line_.severity() >= g_dbg_sev) {
212     OutputToDebug(log_line_);
213   }
214 
215   webrtc::MutexLock lock(&GetLoggingLock());
216   for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) {
217     if (log_line_.severity() >= entry->min_severity_) {
218       entry->OnLogMessage(log_line_);
219     }
220   }
221 }
222 
AddTag(const char * tag)223 void LogMessage::AddTag(const char* tag) {
224 #ifdef WEBRTC_ANDROID
225   log_line_.set_tag(tag);
226 #endif
227 }
228 
stream()229 rtc::StringBuilder& LogMessage::stream() {
230   return print_stream_;
231 }
232 
GetMinLogSeverity()233 int LogMessage::GetMinLogSeverity() {
234   return g_min_sev;
235 }
236 
GetLogToDebug()237 LoggingSeverity LogMessage::GetLogToDebug() {
238   return g_dbg_sev;
239 }
LogStartTime()240 int64_t LogMessage::LogStartTime() {
241   static const int64_t g_start = SystemTimeMillis();
242   return g_start;
243 }
244 
WallClockStartTime()245 uint32_t LogMessage::WallClockStartTime() {
246   static const uint32_t g_start_wallclock = time(nullptr);
247   return g_start_wallclock;
248 }
249 
LogThreads(bool on)250 void LogMessage::LogThreads(bool on) {
251   log_thread_ = on;
252 }
253 
LogTimestamps(bool on)254 void LogMessage::LogTimestamps(bool on) {
255   log_timestamp_ = on;
256 }
257 
LogToDebug(LoggingSeverity min_sev)258 void LogMessage::LogToDebug(LoggingSeverity min_sev) {
259   g_dbg_sev = min_sev;
260   webrtc::MutexLock lock(&GetLoggingLock());
261   UpdateMinLogSeverity();
262 }
263 
SetLogToStderr(bool log_to_stderr)264 void LogMessage::SetLogToStderr(bool log_to_stderr) {
265   log_to_stderr_ = log_to_stderr;
266 }
267 
GetLogToStream(LogSink * stream)268 int LogMessage::GetLogToStream(LogSink* stream) {
269   webrtc::MutexLock lock(&GetLoggingLock());
270   LoggingSeverity sev = LS_NONE;
271   for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) {
272     if (stream == nullptr || stream == entry) {
273       sev = std::min(sev, entry->min_severity_);
274     }
275   }
276   return sev;
277 }
278 
AddLogToStream(LogSink * stream,LoggingSeverity min_sev)279 void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {
280   webrtc::MutexLock lock(&GetLoggingLock());
281   stream->min_severity_ = min_sev;
282   stream->next_ = streams_;
283   streams_ = stream;
284   streams_empty_.store(false, std::memory_order_relaxed);
285   UpdateMinLogSeverity();
286 }
287 
RemoveLogToStream(LogSink * stream)288 void LogMessage::RemoveLogToStream(LogSink* stream) {
289   webrtc::MutexLock lock(&GetLoggingLock());
290   for (LogSink** entry = &streams_; *entry != nullptr;
291        entry = &(*entry)->next_) {
292     if (*entry == stream) {
293       *entry = (*entry)->next_;
294       break;
295     }
296   }
297   streams_empty_.store(streams_ == nullptr, std::memory_order_relaxed);
298   UpdateMinLogSeverity();
299 }
300 
ConfigureLogging(absl::string_view params)301 void LogMessage::ConfigureLogging(absl::string_view params) {
302   LoggingSeverity current_level = LS_VERBOSE;
303   LoggingSeverity debug_level = GetLogToDebug();
304 
305   std::vector<std::string> tokens;
306   tokenize(params, ' ', &tokens);
307 
308   for (const std::string& token : tokens) {
309     if (token.empty())
310       continue;
311 
312     // Logging features
313     if (token == "tstamp") {
314       LogTimestamps();
315     } else if (token == "thread") {
316       LogThreads();
317 
318       // Logging levels
319     } else if (token == "verbose") {
320       current_level = LS_VERBOSE;
321     } else if (token == "info") {
322       current_level = LS_INFO;
323     } else if (token == "warning") {
324       current_level = LS_WARNING;
325     } else if (token == "error") {
326       current_level = LS_ERROR;
327     } else if (token == "none") {
328       current_level = LS_NONE;
329 
330       // Logging targets
331     } else if (token == "debug") {
332       debug_level = current_level;
333     }
334   }
335 
336 #if defined(WEBRTC_WIN) && !defined(WINUWP)
337   if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) {
338     // First, attempt to attach to our parent's console... so if you invoke
339     // from the command line, we'll see the output there.  Otherwise, create
340     // our own console window.
341     // Note: These methods fail if a console already exists, which is fine.
342     if (!AttachConsole(ATTACH_PARENT_PROCESS))
343       ::AllocConsole();
344   }
345 #endif  // defined(WEBRTC_WIN) && !defined(WINUWP)
346 
347   LogToDebug(debug_level);
348 }
349 
UpdateMinLogSeverity()350 void LogMessage::UpdateMinLogSeverity()
351     RTC_EXCLUSIVE_LOCKS_REQUIRED(GetLoggingLock()) {
352   LoggingSeverity min_sev = g_dbg_sev;
353   for (LogSink* entry = streams_; entry != nullptr; entry = entry->next_) {
354     min_sev = std::min(min_sev, entry->min_severity_);
355   }
356   g_min_sev = min_sev;
357 }
358 
OutputToDebug(const LogLineRef & log_line)359 void LogMessage::OutputToDebug(const LogLineRef& log_line) {
360   std::string msg_str = log_line.DefaultLogLine();
361   bool log_to_stderr = log_to_stderr_;
362 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG)
363   // On the Mac, all stderr output goes to the Console log and causes clutter.
364   // So in opt builds, don't log to stderr unless the user specifically sets
365   // a preference to do so.
366   CFStringRef key = CFStringCreateWithCString(
367       kCFAllocatorDefault, "logToStdErr", kCFStringEncodingUTF8);
368   CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
369   if (key != nullptr && domain != nullptr) {
370     Boolean exists_and_is_valid;
371     Boolean should_log =
372         CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
373     // If the key doesn't exist or is invalid or is false, we will not log to
374     // stderr.
375     log_to_stderr = exists_and_is_valid && should_log;
376   }
377   if (key != nullptr) {
378     CFRelease(key);
379   }
380 #endif  // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG)
381 
382 #if defined(WEBRTC_WIN)
383   // Always log to the debugger.
384   // Perhaps stderr should be controlled by a preference, as on Mac?
385   OutputDebugStringA(msg_str.c_str());
386   if (log_to_stderr) {
387     // This handles dynamically allocated consoles, too.
388     if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
389       log_to_stderr = false;
390       DWORD written = 0;
391       ::WriteFile(error_handle, msg_str.c_str(),
392                   static_cast<DWORD>(msg_str.size()), &written, 0);
393     }
394   }
395 #endif  // WEBRTC_WIN
396 
397 #if defined(WEBRTC_ANDROID)
398   // Android's logging facility uses severity to log messages but we
399   // need to map libjingle's severity levels to Android ones first.
400   // Also write to stderr which maybe available to executable started
401   // from the shell.
402   int prio;
403   switch (log_line.severity()) {
404     case LS_VERBOSE:
405       prio = ANDROID_LOG_VERBOSE;
406       break;
407     case LS_INFO:
408       prio = ANDROID_LOG_INFO;
409       break;
410     case LS_WARNING:
411       prio = ANDROID_LOG_WARN;
412       break;
413     case LS_ERROR:
414       prio = ANDROID_LOG_ERROR;
415       break;
416     default:
417       prio = ANDROID_LOG_UNKNOWN;
418   }
419 
420   int size = msg_str.size();
421   int current_line = 0;
422   int idx = 0;
423   const int max_lines = size / kMaxLogLineSize + 1;
424   if (max_lines == 1) {
425     __android_log_print(prio, log_line.tag().data(), "%.*s", size,
426                         msg_str.c_str());
427   } else {
428     while (size > 0) {
429       const int len = std::min(size, kMaxLogLineSize);
430       // Use the size of the string in the format (msg may have \0 in the
431       // middle).
432       __android_log_print(prio, log_line.tag().data(), "[%d/%d] %.*s",
433                           current_line + 1, max_lines, len,
434                           msg_str.c_str() + idx);
435       idx += len;
436       size -= len;
437       ++current_line;
438     }
439   }
440 #endif  // WEBRTC_ANDROID
441   if (log_to_stderr) {
442     fprintf(stderr, "%s", msg_str.c_str());
443     fflush(stderr);
444   }
445 }
446 
447 // static
IsNoop(LoggingSeverity severity)448 bool LogMessage::IsNoop(LoggingSeverity severity) {
449   if (severity >= g_dbg_sev || severity >= g_min_sev)
450     return false;
451   return streams_empty_.load(std::memory_order_relaxed);
452 }
453 
FinishPrintStream()454 void LogMessage::FinishPrintStream() {
455   if (!extra_.empty())
456     print_stream_ << " : " << extra_;
457   print_stream_ << "\n";
458 }
459 
460 namespace webrtc_logging_impl {
461 
Log(const LogArgType * fmt,...)462 void Log(const LogArgType* fmt, ...) {
463   va_list args;
464   va_start(args, fmt);
465 
466   LogMetadataErr meta;
467   const char* tag = nullptr;
468   switch (*fmt) {
469     case LogArgType::kLogMetadata: {
470       meta = {va_arg(args, LogMetadata), ERRCTX_NONE, 0};
471       break;
472     }
473     case LogArgType::kLogMetadataErr: {
474       meta = va_arg(args, LogMetadataErr);
475       break;
476     }
477 #ifdef WEBRTC_ANDROID
478     case LogArgType::kLogMetadataTag: {
479       const LogMetadataTag tag_meta = va_arg(args, LogMetadataTag);
480       meta = {{nullptr, 0, tag_meta.severity}, ERRCTX_NONE, 0};
481       tag = tag_meta.tag;
482       break;
483     }
484 #endif
485     default: {
486       RTC_DCHECK_NOTREACHED();
487       va_end(args);
488       return;
489     }
490   }
491 
492   LogMessage log_message(meta.meta.File(), meta.meta.Line(),
493                          meta.meta.Severity(), meta.err_ctx, meta.err);
494   if (tag) {
495     log_message.AddTag(tag);
496   }
497 
498   for (++fmt; *fmt != LogArgType::kEnd; ++fmt) {
499     switch (*fmt) {
500       case LogArgType::kInt:
501         log_message.stream() << va_arg(args, int);
502         break;
503       case LogArgType::kLong:
504         log_message.stream() << va_arg(args, long);
505         break;
506       case LogArgType::kLongLong:
507         log_message.stream() << va_arg(args, long long);
508         break;
509       case LogArgType::kUInt:
510         log_message.stream() << va_arg(args, unsigned);
511         break;
512       case LogArgType::kULong:
513         log_message.stream() << va_arg(args, unsigned long);
514         break;
515       case LogArgType::kULongLong:
516         log_message.stream() << va_arg(args, unsigned long long);
517         break;
518       case LogArgType::kDouble:
519         log_message.stream() << va_arg(args, double);
520         break;
521       case LogArgType::kLongDouble:
522         log_message.stream() << va_arg(args, long double);
523         break;
524       case LogArgType::kCharP: {
525         const char* s = va_arg(args, const char*);
526         log_message.stream() << (s ? s : "(null)");
527         break;
528       }
529       case LogArgType::kStdString:
530         log_message.stream() << *va_arg(args, const std::string*);
531         break;
532       case LogArgType::kStringView:
533         log_message.stream() << *va_arg(args, const absl::string_view*);
534         break;
535       case LogArgType::kVoidP:
536         log_message.stream() << rtc::ToHex(
537             reinterpret_cast<uintptr_t>(va_arg(args, const void*)));
538         break;
539       default:
540         RTC_DCHECK_NOTREACHED();
541         va_end(args);
542         return;
543     }
544   }
545 
546   va_end(args);
547 }
548 
549 }  // namespace webrtc_logging_impl
550 }  // namespace rtc
551 #endif
552 
553 namespace rtc {
554 // Default implementation, override is recomended.
OnLogMessage(const LogLineRef & log_line)555 void LogSink::OnLogMessage(const LogLineRef& log_line) {
556 #if defined(WEBRTC_ANDROID)
557   OnLogMessage(log_line.DefaultLogLine(), log_line.severity(),
558                log_line.tag().data());
559 #else
560   OnLogMessage(log_line.DefaultLogLine(), log_line.severity());
561 #endif
562 }
563 
564 // Inefficient default implementation, override is recommended.
OnLogMessage(const std::string & msg,LoggingSeverity severity,const char * tag)565 void LogSink::OnLogMessage(const std::string& msg,
566                            LoggingSeverity severity,
567                            const char* tag) {
568   OnLogMessage(tag + (": " + msg), severity);
569 }
570 
OnLogMessage(const std::string & msg,LoggingSeverity)571 void LogSink::OnLogMessage(const std::string& msg,
572                            LoggingSeverity /* severity */) {
573   OnLogMessage(msg);
574 }
575 
576 // Inefficient default implementation, override is recommended.
OnLogMessage(absl::string_view msg,LoggingSeverity severity,const char * tag)577 void LogSink::OnLogMessage(absl::string_view msg,
578                            LoggingSeverity severity,
579                            const char* tag) {
580   OnLogMessage(tag + (": " + std::string(msg)), severity);
581 }
582 
OnLogMessage(absl::string_view msg,LoggingSeverity)583 void LogSink::OnLogMessage(absl::string_view msg,
584                            LoggingSeverity /* severity */) {
585   OnLogMessage(msg);
586 }
587 
OnLogMessage(absl::string_view msg)588 void LogSink::OnLogMessage(absl::string_view msg) {
589   OnLogMessage(std::string(msg));
590 }
591 }  // namespace rtc
592