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