blob: 2cf567c5525144c0b26180ef05453c7b92051d25 [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"
38#include "webrtc/base/stream.h"
39#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
76const int LogMessage::NO_LOGGING = LS_ERROR + 1;
77
78#if _DEBUG
79static const int LOG_DEFAULT = LS_INFO;
80#else // !_DEBUG
81static const int LOG_DEFAULT = LogMessage::NO_LOGGING;
82#endif // !_DEBUG
83
84// Global lock for log subsystem, only needed to serialize access to streams_.
85CriticalSection LogMessage::crit_;
86
87// By default, release builds don't log, debug builds at info level
88int LogMessage::min_sev_ = LOG_DEFAULT;
89int LogMessage::dbg_sev_ = LOG_DEFAULT;
90
91// Don't bother printing context for the ubiquitous INFO log messages
92int LogMessage::ctx_sev_ = LS_WARNING;
93
94// The list of logging streams currently configured.
95// Note: we explicitly do not clean this up, because of the uncertain ordering
96// of destructors at program exit. Let the person who sets the stream trigger
97// cleanup by setting to NULL, or let it leak (safe at program exit).
98LogMessage::StreamList LogMessage::streams_;
99
100// Boolean options default to false (0)
101bool LogMessage::thread_, LogMessage::timestamp_;
102
103// If we're in diagnostic mode, we'll be explicitly set that way; default=false.
104bool LogMessage::is_diagnostic_mode_ = false;
105
106LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev,
107 LogErrorContext err_ctx, int err, const char* module)
108 : severity_(sev),
109 warn_slow_logs_delay_(WARN_SLOW_LOGS_DELAY) {
110 if (timestamp_) {
111 uint32 time = TimeSince(LogStartTime());
112 // Also ensure WallClockStartTime is initialized, so that it matches
113 // LogStartTime.
114 WallClockStartTime();
115 print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
116 << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
117 << "] ";
118 }
119
120 if (thread_) {
121#if defined(WEBRTC_WIN)
122 DWORD id = GetCurrentThreadId();
123 print_stream_ << "[" << std::hex << id << std::dec << "] ";
124#endif // WEBRTC_WIN
125 }
126
127 if (severity_ >= ctx_sev_) {
128 print_stream_ << Describe(sev) << "(" << DescribeFile(file)
129 << ":" << line << "): ";
130 }
131
132 if (err_ctx != ERRCTX_NONE) {
133 std::ostringstream tmp;
134 tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
135 switch (err_ctx) {
136 case ERRCTX_ERRNO:
137 tmp << " " << strerror(err);
138 break;
139#if WEBRTC_WIN
140 case ERRCTX_HRESULT: {
141 char msgbuf[256];
142 DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
143 HMODULE hmod = GetModuleHandleA(module);
144 if (hmod)
145 flags |= FORMAT_MESSAGE_FROM_HMODULE;
146 if (DWORD len = FormatMessageA(
147 flags, hmod, err,
148 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
149 msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), NULL)) {
150 while ((len > 0) &&
151 isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
152 msgbuf[--len] = 0;
153 }
154 tmp << " " << msgbuf;
155 }
156 break;
157 }
158#endif // WEBRTC_WIN
159#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
160 case ERRCTX_OSSTATUS: {
161 tmp << " " << nonnull(GetMacOSStatusErrorString(err), "Unknown error");
162 if (const char* desc = GetMacOSStatusCommentString(err)) {
163 tmp << ": " << desc;
164 }
165 break;
166 }
167#endif // WEBRTC_MAC && !defined(WEBRTC_IOS)
168 default:
169 break;
170 }
171 extra_ = tmp.str();
172 }
173}
174
175LogMessage::~LogMessage() {
176 if (!extra_.empty())
177 print_stream_ << " : " << extra_;
178 print_stream_ << std::endl;
179
180 const std::string& str = print_stream_.str();
181 if (severity_ >= dbg_sev_) {
182 OutputToDebug(str, severity_);
183 }
184
185 uint32 before = Time();
186 // Must lock streams_ before accessing
187 CritScope cs(&crit_);
188 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
189 if (severity_ >= it->second) {
190 OutputToStream(it->first, str);
191 }
192 }
193 uint32 delay = TimeSince(before);
194 if (delay >= warn_slow_logs_delay_) {
195 LogMessage slow_log_warning =
196 rtc::LogMessage(__FILE__, __LINE__, LS_WARNING);
197 // If our warning is slow, we don't want to warn about it, because
198 // that would lead to inifinite recursion. So, give a really big
199 // number for the delay threshold.
200 slow_log_warning.warn_slow_logs_delay_ = UINT_MAX;
201 slow_log_warning.stream() << "Slow log: took " << delay << "ms to write "
202 << str.size() << " bytes.";
203 }
204}
205
206uint32 LogMessage::LogStartTime() {
207 static const uint32 g_start = Time();
208 return g_start;
209}
210
211uint32 LogMessage::WallClockStartTime() {
212 static const uint32 g_start_wallclock = time(NULL);
213 return g_start_wallclock;
214}
215
216void LogMessage::LogContext(int min_sev) {
217 ctx_sev_ = min_sev;
218}
219
220void LogMessage::LogThreads(bool on) {
221 thread_ = on;
222}
223
224void LogMessage::LogTimestamps(bool on) {
225 timestamp_ = on;
226}
227
228void LogMessage::LogToDebug(int min_sev) {
229 dbg_sev_ = min_sev;
230 UpdateMinLogSeverity();
231}
232
233void LogMessage::LogToStream(StreamInterface* stream, int min_sev) {
234 CritScope cs(&crit_);
235 // Discard and delete all previously installed streams
236 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
237 delete it->first;
238 }
239 streams_.clear();
240 // Install the new stream, if specified
241 if (stream) {
242 AddLogToStream(stream, min_sev);
243 }
244}
245
246int LogMessage::GetLogToStream(StreamInterface* stream) {
247 CritScope cs(&crit_);
248 int sev = NO_LOGGING;
249 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
250 if (!stream || stream == it->first) {
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000251 sev = std::min(sev, it->second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000252 }
253 }
254 return sev;
255}
256
257void LogMessage::AddLogToStream(StreamInterface* stream, int min_sev) {
258 CritScope cs(&crit_);
259 streams_.push_back(std::make_pair(stream, min_sev));
260 UpdateMinLogSeverity();
261}
262
263void LogMessage::RemoveLogToStream(StreamInterface* stream) {
264 CritScope cs(&crit_);
265 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
266 if (stream == it->first) {
267 streams_.erase(it);
268 break;
269 }
270 }
271 UpdateMinLogSeverity();
272}
273
274void LogMessage::ConfigureLogging(const char* params, const char* filename) {
275 int current_level = LS_VERBOSE;
276 int debug_level = GetLogToDebug();
277 int file_level = GetLogToStream();
278
279 std::vector<std::string> tokens;
280 tokenize(params, ' ', &tokens);
281
282 for (size_t i = 0; i < tokens.size(); ++i) {
283 if (tokens[i].empty())
284 continue;
285
286 // Logging features
287 if (tokens[i] == "tstamp") {
288 LogTimestamps();
289 } else if (tokens[i] == "thread") {
290 LogThreads();
291
292 // Logging levels
293 } else if (tokens[i] == "sensitive") {
294 current_level = LS_SENSITIVE;
295 } else if (tokens[i] == "verbose") {
296 current_level = LS_VERBOSE;
297 } else if (tokens[i] == "info") {
298 current_level = LS_INFO;
299 } else if (tokens[i] == "warning") {
300 current_level = LS_WARNING;
301 } else if (tokens[i] == "error") {
302 current_level = LS_ERROR;
303 } else if (tokens[i] == "none") {
304 current_level = NO_LOGGING;
305
306 // Logging targets
307 } else if (tokens[i] == "file") {
308 file_level = current_level;
309 } else if (tokens[i] == "debug") {
310 debug_level = current_level;
311 }
312 }
313
314#if defined(WEBRTC_WIN)
315 if ((NO_LOGGING != debug_level) && !::IsDebuggerPresent()) {
316 // First, attempt to attach to our parent's console... so if you invoke
317 // from the command line, we'll see the output there. Otherwise, create
318 // our own console window.
319 // Note: These methods fail if a console already exists, which is fine.
320 bool success = false;
321 typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD);
322 if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) {
323 // AttachConsole is defined on WinXP+.
324 if (PFN_AttachConsole attach_console = reinterpret_cast<PFN_AttachConsole>
325 (::GetProcAddress(kernel32, "AttachConsole"))) {
326 success = (FALSE != attach_console(ATTACH_PARENT_PROCESS));
327 }
328 ::FreeLibrary(kernel32);
329 }
330 if (!success) {
331 ::AllocConsole();
332 }
333 }
334#endif // WEBRTC_WIN
335
336 LogToDebug(debug_level);
337
338#if !defined(__native_client__) // No logging to file in NaCl.
339 scoped_ptr<FileStream> stream;
340 if (NO_LOGGING != file_level) {
341 stream.reset(new FileStream);
342 if (!stream->Open(filename, "wb", NULL) || !stream->DisableBuffering()) {
343 stream.reset();
344 }
345 }
346
347 LogToStream(stream.release(), file_level);
348#endif
349}
350
351int LogMessage::ParseLogSeverity(const std::string& value) {
352 int level = NO_LOGGING;
353 if (value == "LS_SENSITIVE") {
354 level = LS_SENSITIVE;
355 } else if (value == "LS_VERBOSE") {
356 level = LS_VERBOSE;
357 } else if (value == "LS_INFO") {
358 level = LS_INFO;
359 } else if (value == "LS_WARNING") {
360 level = LS_WARNING;
361 } else if (value == "LS_ERROR") {
362 level = LS_ERROR;
363 } else if (isdigit(value[0])) {
364 level = atoi(value.c_str()); // NOLINT
365 }
366 return level;
367}
368
369void LogMessage::UpdateMinLogSeverity() {
370 int min_sev = dbg_sev_;
371 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000372 min_sev = std::min(dbg_sev_, it->second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000373 }
374 min_sev_ = min_sev;
375}
376
377const char* LogMessage::Describe(LoggingSeverity sev) {
378 switch (sev) {
379 case LS_SENSITIVE: return "Sensitive";
380 case LS_VERBOSE: return "Verbose";
381 case LS_INFO: return "Info";
382 case LS_WARNING: return "Warning";
383 case LS_ERROR: return "Error";
384 default: return "<unknown>";
385 }
386}
387
388const char* LogMessage::DescribeFile(const char* file) {
389 const char* end1 = ::strrchr(file, '/');
390 const char* end2 = ::strrchr(file, '\\');
391 if (!end1 && !end2)
392 return file;
393 else
394 return (end1 > end2) ? end1 + 1 : end2 + 1;
395}
396
397void LogMessage::OutputToDebug(const std::string& str,
398 LoggingSeverity severity) {
399 bool log_to_stderr = true;
400#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && (!defined(DEBUG) || defined(NDEBUG))
401 // On the Mac, all stderr output goes to the Console log and causes clutter.
402 // So in opt builds, don't log to stderr unless the user specifically sets
403 // a preference to do so.
404 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
405 "logToStdErr",
406 kCFStringEncodingUTF8);
407 CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
408 if (key != NULL && domain != NULL) {
409 Boolean exists_and_is_valid;
410 Boolean should_log =
411 CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
412 // If the key doesn't exist or is invalid or is false, we will not log to
413 // stderr.
414 log_to_stderr = exists_and_is_valid && should_log;
415 }
416 if (key != NULL) {
417 CFRelease(key);
418 }
419#endif
420#if defined(WEBRTC_WIN)
421 // Always log to the debugger.
422 // Perhaps stderr should be controlled by a preference, as on Mac?
423 OutputDebugStringA(str.c_str());
424 if (log_to_stderr) {
425 // This handles dynamically allocated consoles, too.
426 if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
427 log_to_stderr = false;
428 DWORD written = 0;
429 ::WriteFile(error_handle, str.data(), static_cast<DWORD>(str.size()),
430 &written, 0);
431 }
432 }
433#endif // WEBRTC_WIN
434#if defined(WEBRTC_ANDROID)
435 // Android's logging facility uses severity to log messages but we
436 // need to map libjingle's severity levels to Android ones first.
437 // Also write to stderr which maybe available to executable started
438 // from the shell.
439 int prio;
440 switch (severity) {
441 case LS_SENSITIVE:
442 __android_log_write(ANDROID_LOG_INFO, kLibjingle, "SENSITIVE");
443 if (log_to_stderr) {
444 fprintf(stderr, "SENSITIVE");
445 fflush(stderr);
446 }
447 return;
448 case LS_VERBOSE:
449 prio = ANDROID_LOG_VERBOSE;
450 break;
451 case LS_INFO:
452 prio = ANDROID_LOG_INFO;
453 break;
454 case LS_WARNING:
455 prio = ANDROID_LOG_WARN;
456 break;
457 case LS_ERROR:
458 prio = ANDROID_LOG_ERROR;
459 break;
460 default:
461 prio = ANDROID_LOG_UNKNOWN;
462 }
463
464 int size = str.size();
465 int line = 0;
466 int idx = 0;
467 const int max_lines = size / kMaxLogLineSize + 1;
468 if (max_lines == 1) {
469 __android_log_print(prio, kLibjingle, "%.*s", size, str.c_str());
470 } else {
471 while (size > 0) {
472 const int len = std::min(size, kMaxLogLineSize);
473 // Use the size of the string in the format (str may have \0 in the
474 // middle).
475 __android_log_print(prio, kLibjingle, "[%d/%d] %.*s",
476 line + 1, max_lines,
477 len, str.c_str() + idx);
478 idx += len;
479 size -= len;
480 ++line;
481 }
482 }
483#endif // WEBRTC_ANDROID
484 if (log_to_stderr) {
485 fprintf(stderr, "%s", str.c_str());
486 fflush(stderr);
487 }
488}
489
490void LogMessage::OutputToStream(StreamInterface* stream,
491 const std::string& str) {
492 // If write isn't fully successful, what are we going to do, log it? :)
493 stream->WriteAll(str.data(), str.size(), NULL, NULL);
494}
495
496//////////////////////////////////////////////////////////////////////
497// Logging Helpers
498//////////////////////////////////////////////////////////////////////
499
500void LogMultiline(LoggingSeverity level, const char* label, bool input,
501 const void* data, size_t len, bool hex_mode,
502 LogMultilineState* state) {
503 if (!LOG_CHECK_LEVEL_V(level))
504 return;
505
506 const char * direction = (input ? " << " : " >> ");
507
508 // NULL data means to flush our count of unprintable characters.
509 if (!data) {
510 if (state && state->unprintable_count_[input]) {
511 LOG_V(level) << label << direction << "## "
512 << state->unprintable_count_[input]
513 << " consecutive unprintable ##";
514 state->unprintable_count_[input] = 0;
515 }
516 return;
517 }
518
519 // The ctype classification functions want unsigned chars.
520 const unsigned char* udata = static_cast<const unsigned char*>(data);
521
522 if (hex_mode) {
523 const size_t LINE_SIZE = 24;
524 char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
525 while (len > 0) {
526 memset(asc_line, ' ', sizeof(asc_line));
527 memset(hex_line, ' ', sizeof(hex_line));
andresp@webrtc.orgff689be2015-02-12 11:54:26 +0000528 size_t line_len = std::min(len, LINE_SIZE);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000529 for (size_t i = 0; i < line_len; ++i) {
530 unsigned char ch = udata[i];
531 asc_line[i] = isprint(ch) ? ch : '.';
532 hex_line[i*2 + i/4] = hex_encode(ch >> 4);
533 hex_line[i*2 + i/4 + 1] = hex_encode(ch & 0xf);
534 }
535 asc_line[sizeof(asc_line)-1] = 0;
536 hex_line[sizeof(hex_line)-1] = 0;
537 LOG_V(level) << label << direction
538 << asc_line << " " << hex_line << " ";
539 udata += line_len;
540 len -= line_len;
541 }
542 return;
543 }
544
545 size_t consecutive_unprintable = state ? state->unprintable_count_[input] : 0;
546
547 const unsigned char* end = udata + len;
548 while (udata < end) {
549 const unsigned char* line = udata;
550 const unsigned char* end_of_line = strchrn<unsigned char>(udata,
551 end - udata,
552 '\n');
553 if (!end_of_line) {
554 udata = end_of_line = end;
555 } else {
556 udata = end_of_line + 1;
557 }
558
559 bool is_printable = true;
560
561 // If we are in unprintable mode, we need to see a line of at least
562 // kMinPrintableLine characters before we'll switch back.
563 const ptrdiff_t kMinPrintableLine = 4;
564 if (consecutive_unprintable && ((end_of_line - line) < kMinPrintableLine)) {
565 is_printable = false;
566 } else {
567 // Determine if the line contains only whitespace and printable
568 // characters.
569 bool is_entirely_whitespace = true;
570 for (const unsigned char* pos = line; pos < end_of_line; ++pos) {
571 if (isspace(*pos))
572 continue;
573 is_entirely_whitespace = false;
574 if (!isprint(*pos)) {
575 is_printable = false;
576 break;
577 }
578 }
579 // Treat an empty line following unprintable data as unprintable.
580 if (consecutive_unprintable && is_entirely_whitespace) {
581 is_printable = false;
582 }
583 }
584 if (!is_printable) {
585 consecutive_unprintable += (udata - line);
586 continue;
587 }
588 // Print out the current line, but prefix with a count of prior unprintable
589 // characters.
590 if (consecutive_unprintable) {
591 LOG_V(level) << label << direction << "## " << consecutive_unprintable
592 << " consecutive unprintable ##";
593 consecutive_unprintable = 0;
594 }
595 // Strip off trailing whitespace.
596 while ((end_of_line > line) && isspace(*(end_of_line-1))) {
597 --end_of_line;
598 }
599 // Filter out any private data
600 std::string substr(reinterpret_cast<const char*>(line), end_of_line - line);
601 std::string::size_type pos_private = substr.find("Email");
602 if (pos_private == std::string::npos) {
603 pos_private = substr.find("Passwd");
604 }
605 if (pos_private == std::string::npos) {
606 LOG_V(level) << label << direction << substr;
607 } else {
608 LOG_V(level) << label << direction << "## omitted for privacy ##";
609 }
610 }
611
612 if (state) {
613 state->unprintable_count_[input] = consecutive_unprintable;
614 }
615}
616
617//////////////////////////////////////////////////////////////////////
618
619} // namespace rtc