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