blob: ea7c0ff00f37c4e31c32c3c46f805aa831a6c2d1 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
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#if defined(WEBRTC_WIN)
12#define WIN32_LEAN_AND_MEAN
13#include <windows.h>
14#define snprintf _snprintf
15#undef ERROR // wingdi.h
16#endif
17
18#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
19#include <CoreServices/CoreServices.h>
20#elif defined(WEBRTC_ANDROID)
21#include <android/log.h>
22static const char kLibjingle[] = "libjingle";
23// Android has a 1024 limit on log inputs. We use 60 chars as an
24// approx for the header/tag portion.
25// See android/system/core/liblog/logd_write.c
26static const int kMaxLogLineSize = 1024 - 60;
27#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID
28
29#include <time.h>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000030#include <limits.h>
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000031
32#include <algorithm>
33#include <iomanip>
34#include <ostream>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000035#include <vector>
36
37#include "webrtc/base/logging.h"
Tommi0eefb4d2015-05-23 09:54:07 +020038#include "webrtc/base/scoped_ptr.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000039#include "webrtc/base/stringencode.h"
40#include "webrtc/base/stringutils.h"
41#include "webrtc/base/timeutils.h"
42
43namespace rtc {
44
45/////////////////////////////////////////////////////////////////////////////
46// Constant Labels
47/////////////////////////////////////////////////////////////////////////////
48
49const char * FindLabel(int value, const ConstantLabel entries[]) {
50 for (int i = 0; entries[i].label; ++i) {
51 if (value == entries[i].value) {
52 return entries[i].label;
53 }
54 }
55 return 0;
56}
57
58std::string ErrorName(int err, const ConstantLabel * err_table) {
59 if (err == 0)
60 return "No error";
61
62 if (err_table != 0) {
63 if (const char * value = FindLabel(err, err_table))
64 return value;
65 }
66
67 char buffer[16];
68 snprintf(buffer, sizeof(buffer), "0x%08x", err);
69 return buffer;
70}
71
72/////////////////////////////////////////////////////////////////////////////
73// LogMessage
74/////////////////////////////////////////////////////////////////////////////
75
Tommi0eefb4d2015-05-23 09:54:07 +020076// By default, release builds don't log, debug builds at info level
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000077#if _DEBUG
Tommi0eefb4d2015-05-23 09:54:07 +020078LoggingSeverity LogMessage::min_sev_ = LS_INFO;
79LoggingSeverity LogMessage::dbg_sev_ = LS_INFO;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000080#else // !_DEBUG
Tommi0eefb4d2015-05-23 09:54:07 +020081LoggingSeverity LogMessage::min_sev_ = LS_NONE;
82LoggingSeverity LogMessage::dbg_sev_ = LS_NONE;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000083#endif // !_DEBUG
84
85// Global lock for log subsystem, only needed to serialize access to streams_.
86CriticalSection LogMessage::crit_;
87
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000088// The list of logging streams currently configured.
89// Note: we explicitly do not clean this up, because of the uncertain ordering
90// of destructors at program exit. Let the person who sets the stream trigger
91// cleanup by setting to NULL, or let it leak (safe at program exit).
92LogMessage::StreamList LogMessage::streams_;
93
94// Boolean options default to false (0)
95bool LogMessage::thread_, LogMessage::timestamp_;
96
97// If we're in diagnostic mode, we'll be explicitly set that way; default=false.
98bool LogMessage::is_diagnostic_mode_ = false;
99
100LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev,
101 LogErrorContext err_ctx, int err, const char* module)
102 : severity_(sev),
103 warn_slow_logs_delay_(WARN_SLOW_LOGS_DELAY) {
104 if (timestamp_) {
105 uint32 time = TimeSince(LogStartTime());
106 // Also ensure WallClockStartTime is initialized, so that it matches
107 // LogStartTime.
108 WallClockStartTime();
109 print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
110 << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
111 << "] ";
112 }
113
114 if (thread_) {
115#if defined(WEBRTC_WIN)
116 DWORD id = GetCurrentThreadId();
117 print_stream_ << "[" << std::hex << id << std::dec << "] ";
Tommi0eefb4d2015-05-23 09:54:07 +0200118#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000119 }
120
121 if (err_ctx != ERRCTX_NONE) {
122 std::ostringstream tmp;
123 tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
124 switch (err_ctx) {
125 case ERRCTX_ERRNO:
126 tmp << " " << strerror(err);
127 break;
128#if WEBRTC_WIN
129 case ERRCTX_HRESULT: {
130 char msgbuf[256];
131 DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
132 HMODULE hmod = GetModuleHandleA(module);
133 if (hmod)
134 flags |= FORMAT_MESSAGE_FROM_HMODULE;
135 if (DWORD len = FormatMessageA(
136 flags, hmod, err,
137 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
138 msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) {
139 while ((len > 0) &&
140 isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
141 msgbuf[--len] = 0;
142 }
143 tmp << " " << msgbuf;
144 }
145 break;
146 }
Tommi0eefb4d2015-05-23 09:54:07 +0200147#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000148#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
149 case ERRCTX_OSSTATUS: {
150 tmp << " " << nonnull(GetMacOSStatusErrorString(err), "Unknown error");
151 if (const char* desc = GetMacOSStatusCommentString(err)) {
152 tmp << ": " << desc;
153 }
154 break;
155 }
156#endif // WEBRTC_MAC && !defined(WEBRTC_IOS)
157 default:
158 break;
159 }
160 extra_ = tmp.str();
161 }
162}
163
164LogMessage::~LogMessage() {
165 if (!extra_.empty())
166 print_stream_ << " : " << extra_;
167 print_stream_ << std::endl;
168
169 const std::string& str = print_stream_.str();
170 if (severity_ >= dbg_sev_) {
171 OutputToDebug(str, severity_);
172 }
173
174 uint32 before = Time();
175 // Must lock streams_ before accessing
176 CritScope cs(&crit_);
177 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
178 if (severity_ >= it->second) {
Tommi0eefb4d2015-05-23 09:54:07 +0200179 it->first->OnLogMessage(str);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000180 }
181 }
182 uint32 delay = TimeSince(before);
183 if (delay >= warn_slow_logs_delay_) {
184 LogMessage slow_log_warning =
185 rtc::LogMessage(__FILE__, __LINE__, LS_WARNING);
186 // If our warning is slow, we don't want to warn about it, because
187 // that would lead to inifinite recursion. So, give a really big
188 // number for the delay threshold.
189 slow_log_warning.warn_slow_logs_delay_ = UINT_MAX;
190 slow_log_warning.stream() << "Slow log: took " << delay << "ms to write "
191 << str.size() << " bytes.";
192 }
193}
194
195uint32 LogMessage::LogStartTime() {
196 static const uint32 g_start = Time();
197 return g_start;
198}
199
200uint32 LogMessage::WallClockStartTime() {
201 static const uint32 g_start_wallclock = time(NULL);
202 return g_start_wallclock;
203}
204
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000205void LogMessage::LogThreads(bool on) {
206 thread_ = on;
207}
208
209void LogMessage::LogTimestamps(bool on) {
210 timestamp_ = on;
211}
212
Tommi0eefb4d2015-05-23 09:54:07 +0200213void LogMessage::LogToDebug(LoggingSeverity min_sev) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000214 dbg_sev_ = min_sev;
215 UpdateMinLogSeverity();
216}
217
Tommi0eefb4d2015-05-23 09:54:07 +0200218void LogMessage::LogToStream(LogSink* stream, LoggingSeverity min_sev) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000219 CritScope cs(&crit_);
220 // Discard and delete all previously installed streams
221 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
222 delete it->first;
223 }
224 streams_.clear();
225 // Install the new stream, if specified
226 if (stream) {
227 AddLogToStream(stream, min_sev);
228 }
229}
230
Tommi0eefb4d2015-05-23 09:54:07 +0200231int LogMessage::GetLogToStream(LogSink* stream) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000232 CritScope cs(&crit_);
Tommi0eefb4d2015-05-23 09:54:07 +0200233 LoggingSeverity sev = LS_NONE;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000234 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
235 if (!stream || stream == it->first) {
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000236 sev = std::min(sev, it->second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000237 }
238 }
239 return sev;
240}
241
Tommi0eefb4d2015-05-23 09:54:07 +0200242void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000243 CritScope cs(&crit_);
244 streams_.push_back(std::make_pair(stream, min_sev));
245 UpdateMinLogSeverity();
246}
247
Tommi0eefb4d2015-05-23 09:54:07 +0200248void LogMessage::RemoveLogToStream(LogSink* stream) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000249 CritScope cs(&crit_);
250 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
251 if (stream == it->first) {
252 streams_.erase(it);
253 break;
254 }
255 }
256 UpdateMinLogSeverity();
257}
258
Tommi0eefb4d2015-05-23 09:54:07 +0200259void LogMessage::ConfigureLogging(const char* params) {
260 LoggingSeverity current_level = LS_VERBOSE;
261 LoggingSeverity debug_level = GetLogToDebug();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000262
263 std::vector<std::string> tokens;
264 tokenize(params, ' ', &tokens);
265
Tommi0eefb4d2015-05-23 09:54:07 +0200266 for (const std::string& token : tokens) {
267 if (token.empty())
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000268 continue;
269
270 // Logging features
Tommi0eefb4d2015-05-23 09:54:07 +0200271 if (token == "tstamp") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000272 LogTimestamps();
Tommi0eefb4d2015-05-23 09:54:07 +0200273 } else if (token == "thread") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000274 LogThreads();
275
276 // Logging levels
Tommi0eefb4d2015-05-23 09:54:07 +0200277 } else if (token == "sensitive") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000278 current_level = LS_SENSITIVE;
Tommi0eefb4d2015-05-23 09:54:07 +0200279 } else if (token == "verbose") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000280 current_level = LS_VERBOSE;
Tommi0eefb4d2015-05-23 09:54:07 +0200281 } else if (token == "info") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000282 current_level = LS_INFO;
Tommi0eefb4d2015-05-23 09:54:07 +0200283 } else if (token == "warning") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000284 current_level = LS_WARNING;
Tommi0eefb4d2015-05-23 09:54:07 +0200285 } else if (token == "error") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000286 current_level = LS_ERROR;
Tommi0eefb4d2015-05-23 09:54:07 +0200287 } else if (token == "none") {
288 current_level = LS_NONE;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000289
290 // Logging targets
Tommi0eefb4d2015-05-23 09:54:07 +0200291 } else if (token == "debug") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000292 debug_level = current_level;
293 }
294 }
295
296#if defined(WEBRTC_WIN)
Tommi0eefb4d2015-05-23 09:54:07 +0200297 if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000298 // First, attempt to attach to our parent's console... so if you invoke
299 // from the command line, we'll see the output there. Otherwise, create
300 // our own console window.
301 // Note: These methods fail if a console already exists, which is fine.
302 bool success = false;
303 typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD);
304 if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) {
305 // AttachConsole is defined on WinXP+.
306 if (PFN_AttachConsole attach_console = reinterpret_cast<PFN_AttachConsole>
307 (::GetProcAddress(kernel32, "AttachConsole"))) {
308 success = (FALSE != attach_console(ATTACH_PARENT_PROCESS));
309 }
310 ::FreeLibrary(kernel32);
311 }
312 if (!success) {
313 ::AllocConsole();
314 }
315 }
Tommi0eefb4d2015-05-23 09:54:07 +0200316#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000317
318 LogToDebug(debug_level);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000319}
320
321void LogMessage::UpdateMinLogSeverity() {
Tommi0eefb4d2015-05-23 09:54:07 +0200322 LoggingSeverity min_sev = dbg_sev_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000323 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000324 min_sev = std::min(dbg_sev_, it->second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000325 }
326 min_sev_ = min_sev;
327}
328
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000329void LogMessage::OutputToDebug(const std::string& str,
330 LoggingSeverity severity) {
331 bool log_to_stderr = true;
332#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && (!defined(DEBUG) || defined(NDEBUG))
333 // On the Mac, all stderr output goes to the Console log and causes clutter.
334 // So in opt builds, don't log to stderr unless the user specifically sets
335 // a preference to do so.
336 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
337 "logToStdErr",
338 kCFStringEncodingUTF8);
339 CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
340 if (key != NULL && domain != NULL) {
341 Boolean exists_and_is_valid;
342 Boolean should_log =
343 CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
344 // If the key doesn't exist or is invalid or is false, we will not log to
345 // stderr.
346 log_to_stderr = exists_and_is_valid && should_log;
347 }
348 if (key != NULL) {
349 CFRelease(key);
350 }
351#endif
352#if defined(WEBRTC_WIN)
353 // Always log to the debugger.
354 // Perhaps stderr should be controlled by a preference, as on Mac?
355 OutputDebugStringA(str.c_str());
356 if (log_to_stderr) {
357 // This handles dynamically allocated consoles, too.
358 if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
359 log_to_stderr = false;
360 DWORD written = 0;
361 ::WriteFile(error_handle, str.data(), static_cast<DWORD>(str.size()),
362 &written, 0);
363 }
364 }
Tommi0eefb4d2015-05-23 09:54:07 +0200365#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000366#if defined(WEBRTC_ANDROID)
367 // Android's logging facility uses severity to log messages but we
368 // need to map libjingle's severity levels to Android ones first.
369 // Also write to stderr which maybe available to executable started
370 // from the shell.
371 int prio;
372 switch (severity) {
373 case LS_SENSITIVE:
374 __android_log_write(ANDROID_LOG_INFO, kLibjingle, "SENSITIVE");
375 if (log_to_stderr) {
376 fprintf(stderr, "SENSITIVE");
377 fflush(stderr);
378 }
379 return;
380 case LS_VERBOSE:
381 prio = ANDROID_LOG_VERBOSE;
382 break;
383 case LS_INFO:
384 prio = ANDROID_LOG_INFO;
385 break;
386 case LS_WARNING:
387 prio = ANDROID_LOG_WARN;
388 break;
389 case LS_ERROR:
390 prio = ANDROID_LOG_ERROR;
391 break;
392 default:
393 prio = ANDROID_LOG_UNKNOWN;
394 }
395
396 int size = str.size();
397 int line = 0;
398 int idx = 0;
399 const int max_lines = size / kMaxLogLineSize + 1;
400 if (max_lines == 1) {
401 __android_log_print(prio, kLibjingle, "%.*s", size, str.c_str());
402 } else {
403 while (size > 0) {
404 const int len = std::min(size, kMaxLogLineSize);
405 // Use the size of the string in the format (str may have \0 in the
406 // middle).
407 __android_log_print(prio, kLibjingle, "[%d/%d] %.*s",
408 line + 1, max_lines,
409 len, str.c_str() + idx);
410 idx += len;
411 size -= len;
412 ++line;
413 }
414 }
415#endif // WEBRTC_ANDROID
416 if (log_to_stderr) {
417 fprintf(stderr, "%s", str.c_str());
418 fflush(stderr);
419 }
420}
421
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000422//////////////////////////////////////////////////////////////////////
423// Logging Helpers
424//////////////////////////////////////////////////////////////////////
425
426void LogMultiline(LoggingSeverity level, const char* label, bool input,
427 const void* data, size_t len, bool hex_mode,
428 LogMultilineState* state) {
429 if (!LOG_CHECK_LEVEL_V(level))
430 return;
431
432 const char * direction = (input ? " << " : " >> ");
433
434 // NULL data means to flush our count of unprintable characters.
435 if (!data) {
436 if (state && state->unprintable_count_[input]) {
437 LOG_V(level) << label << direction << "## "
438 << state->unprintable_count_[input]
439 << " consecutive unprintable ##";
440 state->unprintable_count_[input] = 0;
441 }
442 return;
443 }
444
445 // The ctype classification functions want unsigned chars.
446 const unsigned char* udata = static_cast<const unsigned char*>(data);
447
448 if (hex_mode) {
449 const size_t LINE_SIZE = 24;
450 char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
451 while (len > 0) {
452 memset(asc_line, ' ', sizeof(asc_line));
453 memset(hex_line, ' ', sizeof(hex_line));
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000454 size_t line_len = std::min(len, LINE_SIZE);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000455 for (size_t i = 0; i < line_len; ++i) {
456 unsigned char ch = udata[i];
457 asc_line[i] = isprint(ch) ? ch : '.';
458 hex_line[i*2 + i/4] = hex_encode(ch >> 4);
459 hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf);
460 }
461 asc_line[sizeof(asc_line)-1] = 0;
462 hex_line[sizeof(hex_line)-1] = 0;
463 LOG_V(level) << label << direction
464 << asc_line << " " << hex_line << " ";
465 udata += line_len;
466 len -= line_len;
467 }
468 return;
469 }
470
471 size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0;
472
473 const unsigned char* end = udata + len;
474 while (udata < end) {
475 const unsigned char* line = udata;
476 const unsigned char* end_of_line = strchrn<unsigned char>(udata,
477 end - udata,
478 '\n');
479 if (!end_of_line) {
480 udata = end_of_line = end;
481 } else {
482 udata = end_of_line + 1;
483 }
484
485 bool is_printable = true;
486
487 // If we are in unprintable mode, we need to see a line of at least
488 // kMinPrintableLine characters before we'll switch back.
489 const ptrdiff_t kMinPrintableLine = 4;
490 if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) {
491 is_printable = false;
492 } else {
493 // Determine if the line contains only whitespace and printable
494 // characters.
495 bool is_entirely_whitespace = true;
496 for (const unsigned char* pos = line; pos < end_of_line; ++pos) {
497 if (isspace(*pos))
498 continue;
499 is_entirely_whitespace = false;
500 if (!isprint(*pos)) {
501 is_printable = false;
502 break;
503 }
504 }
505 // Treat an empty line following unprintable data as unprintable.
506 if (consecutive_unprintable && is_entirely_whitespace) {
507 is_printable = false;
508 }
509 }
510 if (!is_printable) {
511 consecutive_unprintable += (udata - line);
512 continue;
513 }
514 // Print out the current line, but prefix with a count of prior unprintable
515 // characters.
516 if (consecutive_unprintable) {
517 LOG_V(level) << label << direction << "## " << consecutive_unprintable
518 << " consecutive unprintable ##";
519 consecutive_unprintable = 0;
520 }
521 // Strip off trailing whitespace.
522 while ((end_of_line > line) && isspace(*(end_of_line-1))) {
523 --end_of_line;
524 }
525 // Filter out any private data
526 std::string substr(reinterpret_cast<const char*>(line), end_of_line - line);
527 std::string::size_type pos_private = substr.find("Email");
528 if (pos_private == std::string::npos) {
529 pos_private = substr.find("Passwd");
530 }
531 if (pos_private == std::string::npos) {
532 LOG_V(level) << label << direction << substr;
533 } else {
534 LOG_V(level) << label << direction << "## omitted for privacy ##";
535 }
536 }
537
538 if (state) {
539 state->unprintable_count_[input] = consecutive_unprintable;
540 }
541}
542
543//////////////////////////////////////////////////////////////////////
544
545} // namespace rtc