PageRenderTime 59ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/googleurl/base/logging.cc

https://bitbucket.org/codefirex/external_chromium
C++ | 380 lines | 318 code | 15 blank | 47 comment | 12 complexity | 7055b57b38a3f1c131a0b5a65248832a MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, MPL-2.0-no-copyleft-exception, GPL-2.0
  1. // Copyright 2007, Google Inc.
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are
  6. // met:
  7. //
  8. // * Redistributions of source code must retain the above copyright
  9. // notice, this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above
  11. // copyright notice, this list of conditions and the following disclaimer
  12. // in the documentation and/or other materials provided with the
  13. // distribution.
  14. // * Neither the name of Google Inc. nor the names of its
  15. // contributors may be used to endorse or promote products derived from
  16. // this software without specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. #include <ctime>
  30. #include <iomanip>
  31. #include <cstring>
  32. #include <windows.h>
  33. #include <tchar.h>
  34. #include <algorithm>
  35. #include "base/logging.h"
  36. namespace logging {
  37. const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
  38. "INFO", "WARNING", "ERROR", "FATAL" };
  39. int min_log_level = 0;
  40. LogLockingState lock_log_file = LOCK_LOG_FILE;
  41. LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
  42. const int kMaxFilteredLogLevel = LOG_WARNING;
  43. char* log_filter_prefix = NULL;
  44. // which log file to use? This is initialized by InitLogging or
  45. // will be lazily initialized to the default value when it is
  46. // first needed.
  47. TCHAR log_file_name[MAX_PATH] = { 0 };
  48. // this file is lazily opened and the handle may be NULL
  49. HANDLE log_file = NULL;
  50. // what should be prepended to each message?
  51. bool log_process_id = false;
  52. bool log_thread_id = false;
  53. bool log_timestamp = true;
  54. bool log_tickcount = false;
  55. // An assert handler override specified by the client to be called instead of
  56. // the debug message dialog.
  57. LogAssertHandlerFunction log_assert_handler = NULL;
  58. // The critical section is used if log file locking is false. It helps us
  59. // avoid problems with multiple threads writing to the log file at the same
  60. // time.
  61. bool initialized_critical_section = false;
  62. CRITICAL_SECTION log_critical_section;
  63. // When we don't use a critical section, we are using a global mutex. We
  64. // need to do this because LockFileEx is not thread safe
  65. HANDLE log_mutex = NULL;
  66. // Called by logging functions to ensure that debug_file is initialized
  67. // and can be used for writing. Returns false if the file could not be
  68. // initialized. debug_file will be NULL in this case.
  69. bool InitializeLogFileHandle() {
  70. if (log_file)
  71. return true;
  72. if (!log_file_name[0]) {
  73. // nobody has called InitLogging to specify a debug log file, so here we
  74. // initialize the log file name to the default
  75. GetModuleFileName(NULL, log_file_name, MAX_PATH);
  76. TCHAR* last_backslash = _tcsrchr(log_file_name, '\\');
  77. if (last_backslash)
  78. last_backslash[1] = 0; // name now ends with the backslash
  79. _tcscat_s(log_file_name, _T("debug.log"));
  80. }
  81. log_file = CreateFile(log_file_name, GENERIC_WRITE,
  82. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  83. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  84. if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
  85. // try the current directory
  86. log_file = CreateFile(_T(".\\debug.log"), GENERIC_WRITE,
  87. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  88. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  89. if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
  90. log_file = NULL;
  91. return false;
  92. }
  93. }
  94. SetFilePointer(log_file, 0, 0, FILE_END);
  95. return true;
  96. }
  97. void InitLogMutex() {
  98. if (!log_mutex) {
  99. // \ is not a legal character in mutex names so we replace \ with /
  100. std::wstring safe_name(log_file_name);
  101. std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
  102. std::wstring t(L"Global\\");
  103. t.append(safe_name);
  104. log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
  105. }
  106. }
  107. void InitLogging(const TCHAR* new_log_file, LoggingDestination logging_dest,
  108. LogLockingState lock_log, OldFileDeletionState delete_old) {
  109. if (log_file) {
  110. // calling InitLogging twice or after some log call has already opened the
  111. // default log file will re-initialize to the new options
  112. CloseHandle(log_file);
  113. log_file = NULL;
  114. }
  115. lock_log_file = lock_log;
  116. logging_destination = logging_dest;
  117. // ignore file options if logging is only to system
  118. if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
  119. return;
  120. _tcscpy_s(log_file_name, MAX_PATH, new_log_file);
  121. if (delete_old == DELETE_OLD_LOG_FILE)
  122. DeleteFile(log_file_name);
  123. if (lock_log_file == LOCK_LOG_FILE) {
  124. InitLogMutex();
  125. } else if (!initialized_critical_section) {
  126. // initialize the critical section
  127. InitializeCriticalSection(&log_critical_section);
  128. initialized_critical_section = true;
  129. }
  130. InitializeLogFileHandle();
  131. }
  132. void SetMinLogLevel(int level) {
  133. min_log_level = level;
  134. }
  135. void SetLogFilterPrefix(char* filter) {
  136. if (log_filter_prefix) {
  137. delete[] log_filter_prefix;
  138. log_filter_prefix = NULL;
  139. }
  140. if (filter) {
  141. size_t size = strlen(filter)+1;
  142. log_filter_prefix = new char[size];
  143. strcpy_s(log_filter_prefix, size, filter);
  144. }
  145. }
  146. void SetLogItems(bool enable_process_id, bool enable_thread_id,
  147. bool enable_timestamp, bool enable_tickcount) {
  148. log_process_id = enable_process_id;
  149. log_thread_id = enable_thread_id;
  150. log_timestamp = enable_timestamp;
  151. log_tickcount = enable_tickcount;
  152. }
  153. void SetLogAssertHandler(LogAssertHandlerFunction handler) {
  154. log_assert_handler = handler;
  155. }
  156. // Displays a message box to the user with the error message in it. For
  157. // Windows programs, it's possible that the message loop is messed up on
  158. // a fatal error, and creating a MessageBox will cause that message loop
  159. // to be run. Instead, we try to spawn another process that displays its
  160. // command line. We look for "Debug Message.exe" in the same directory as
  161. // the application. If it exists, we use it, otherwise, we use a regular
  162. // message box.
  163. void DisplayDebugMessage(const std::string& str) {
  164. if (str.empty())
  165. return;
  166. // look for the debug dialog program next to our application
  167. wchar_t prog_name[MAX_PATH];
  168. GetModuleFileNameW(NULL, prog_name, MAX_PATH);
  169. wchar_t* backslash = wcsrchr(prog_name, '\\');
  170. if (backslash)
  171. backslash[1] = 0;
  172. wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
  173. // stupid CreateProcess requires a non-const command line and may modify it.
  174. // We also want to use the wide string
  175. int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
  176. if (!charcount)
  177. return;
  178. scoped_array<wchar_t> cmdline(new wchar_t[charcount]);
  179. if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, cmdline.get(), charcount))
  180. return;
  181. STARTUPINFO startup_info;
  182. memset(&startup_info, 0, sizeof(startup_info));
  183. startup_info.cb = sizeof(startup_info);
  184. PROCESS_INFORMATION process_info;
  185. if (CreateProcessW(prog_name, cmdline.get(), NULL, NULL, false, 0, NULL,
  186. NULL, &startup_info, &process_info)) {
  187. WaitForSingleObject(process_info.hProcess, INFINITE);
  188. CloseHandle(process_info.hThread);
  189. CloseHandle(process_info.hProcess);
  190. } else {
  191. // debug process broken, let's just do a message box
  192. MessageBoxW(NULL, cmdline.get(), L"Fatal error", MB_OK | MB_ICONHAND);
  193. }
  194. }
  195. LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
  196. int ctr)
  197. : severity_(severity) {
  198. Init(file, line);
  199. }
  200. LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
  201. : severity_(LOG_FATAL) {
  202. Init(file, line);
  203. stream_ << "Check failed: " << (*result.str_);
  204. }
  205. LogMessage::LogMessage(const char* file, int line)
  206. : severity_(LOG_INFO) {
  207. Init(file, line);
  208. }
  209. LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
  210. : severity_(severity) {
  211. Init(file, line);
  212. }
  213. // writes the common header info to the stream
  214. void LogMessage::Init(const char* file, int line) {
  215. // log only the filename
  216. const char* last_slash = strrchr(file, '\\');
  217. if (last_slash)
  218. file = last_slash + 1;
  219. stream_ << '[';
  220. if (log_process_id)
  221. stream_ << GetCurrentProcessId() << ':';
  222. if (log_thread_id)
  223. stream_ << GetCurrentThreadId() << ':';
  224. if (log_timestamp) {
  225. time_t t = time(NULL);
  226. struct tm tm_time;
  227. localtime_s(&tm_time, &t);
  228. stream_ << std::setfill('0')
  229. << std::setw(2) << 1 + tm_time.tm_mon
  230. << std::setw(2) << tm_time.tm_mday
  231. << '/'
  232. << std::setw(2) << tm_time.tm_hour
  233. << std::setw(2) << tm_time.tm_min
  234. << std::setw(2) << tm_time.tm_sec
  235. << ':';
  236. }
  237. if (log_tickcount)
  238. stream_ << GetTickCount() << ':';
  239. stream_ << log_severity_names[severity_] << ":" << file << "(" << line << ")] ";
  240. message_start_ = stream_.pcount();
  241. }
  242. LogMessage::~LogMessage() {
  243. if (severity_ < min_log_level)
  244. return;
  245. std::string str_newline(stream_.str(), stream_.pcount());
  246. str_newline.append("\r\n");
  247. if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel &&
  248. str_newline.compare(message_start_, strlen(log_filter_prefix),
  249. log_filter_prefix) != 0) {
  250. goto cleanup;
  251. }
  252. if (logging_destination != LOG_ONLY_TO_FILE)
  253. OutputDebugStringA(str_newline.c_str());
  254. // write to log file
  255. if (logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
  256. InitializeLogFileHandle()) {
  257. // we can have multiple threads and/or processes, so try to prevent them from
  258. // clobbering each other's writes
  259. if (lock_log_file == LOCK_LOG_FILE) {
  260. // Ensure that the mutex is initialized in case the client app did not
  261. // call InitLogging. This is not thread safe. See below
  262. InitLogMutex();
  263. DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
  264. DCHECK(r != WAIT_ABANDONED);
  265. } else {
  266. // use the critical section
  267. if (!initialized_critical_section) {
  268. // The client app did not call InitLogging, and so the critical section
  269. // has not been created. We do this on demand, but if two threads try to
  270. // do this at the same time, there will be a race condition to create
  271. // the critical section. This is why InitLogging should be called from
  272. // the main thread at the beginning of execution.
  273. InitializeCriticalSection(&log_critical_section);
  274. initialized_critical_section = true;
  275. }
  276. EnterCriticalSection(&log_critical_section);
  277. }
  278. SetFilePointer(log_file, 0, 0, SEEK_END);
  279. DWORD num_written;
  280. WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(), &num_written, NULL);
  281. if (lock_log_file == LOCK_LOG_FILE) {
  282. ReleaseMutex(log_mutex);
  283. } else {
  284. LeaveCriticalSection(&log_critical_section);
  285. }
  286. }
  287. if (severity_ == LOG_FATAL) {
  288. // display a message or break into the debugger on a fatal error
  289. if (::IsDebuggerPresent()) {
  290. DebugBreak();
  291. } else {
  292. if (log_assert_handler) {
  293. log_assert_handler(std::string(stream_.str(), stream_.pcount()));
  294. } else {
  295. // don't use the string with the newline, get a fresh version to send to
  296. // the debug message process
  297. DisplayDebugMessage(std::string(stream_.str(), stream_.pcount()));
  298. TerminateProcess(GetCurrentProcess(), 1);
  299. }
  300. }
  301. }
  302. cleanup:
  303. // Calling stream_.str() freezes the stream buffer. A frozen buffer will
  304. // not be freed during strstreambuf destruction.
  305. stream_.freeze(false);
  306. }
  307. void CloseLogFile() {
  308. if (!log_file)
  309. return;
  310. CloseHandle(log_file);
  311. log_file = NULL;
  312. }
  313. } // namespace logging
  314. std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
  315. if (!wstr || !wstr[0])
  316. return out;
  317. // compute the length of the buffer we'll need
  318. int charcount = WideCharToMultiByte(CP_UTF8, 0, wstr, -1,
  319. NULL, 0, NULL, NULL);
  320. if (charcount == 0)
  321. return out;
  322. // convert
  323. scoped_array<char> buf(new char[charcount]);
  324. WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buf.get(), charcount, NULL, NULL);
  325. return out << buf.get();
  326. }