OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/logging.h" | 5 #include "base/logging.h" |
6 | 6 |
7 #if defined(OS_WIN) | 7 #if defined(OS_WIN) |
8 #include <io.h> | 8 #include <io.h> |
9 #include <windows.h> | 9 #include <windows.h> |
10 typedef HANDLE FileHandle; | 10 typedef HANDLE FileHandle; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 #include "base/utf_string_conversions.h" | 53 #include "base/utf_string_conversions.h" |
54 | 54 |
55 namespace logging { | 55 namespace logging { |
56 | 56 |
57 bool g_enable_dcheck = false; | 57 bool g_enable_dcheck = false; |
58 | 58 |
59 const char* const log_severity_names[LOG_NUM_SEVERITIES] = { | 59 const char* const log_severity_names[LOG_NUM_SEVERITIES] = { |
60 "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; | 60 "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; |
61 | 61 |
62 int min_log_level = 0; | 62 int min_log_level = 0; |
63 LogLockingState lock_log_file = LOCK_LOG_FILE; | |
64 | 63 |
65 // The default set here for logging_destination will only be used if | 64 // The default set here for logging_destination will only be used if |
66 // InitLogging is not called. On Windows, use a file next to the exe; | 65 // InitLogging is not called. On Windows, use a file next to the exe; |
67 // on POSIX platforms, where it may not even be possible to locate the | 66 // on POSIX platforms, where it may not even be possible to locate the |
68 // executable on disk, use stderr. | 67 // executable on disk, use stderr. |
69 #if defined(OS_WIN) | 68 #if defined(OS_WIN) |
70 LoggingDestination logging_destination = LOG_ONLY_TO_FILE; | 69 LoggingDestination logging_destination = LOG_ONLY_TO_FILE; |
71 #elif defined(OS_POSIX) | 70 #elif defined(OS_POSIX) |
72 LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG; | 71 LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG; |
73 #endif | 72 #endif |
(...skipping 28 matching lines...) Expand all Loading... |
102 | 101 |
103 // An assert handler override specified by the client to be called instead of | 102 // An assert handler override specified by the client to be called instead of |
104 // the debug message dialog and process termination. | 103 // the debug message dialog and process termination. |
105 LogAssertHandlerFunction log_assert_handler = NULL; | 104 LogAssertHandlerFunction log_assert_handler = NULL; |
106 // An report handler override specified by the client to be called instead of | 105 // An report handler override specified by the client to be called instead of |
107 // the debug message dialog. | 106 // the debug message dialog. |
108 LogReportHandlerFunction log_report_handler = NULL; | 107 LogReportHandlerFunction log_report_handler = NULL; |
109 // A log message handler that gets notified of every log message we process. | 108 // A log message handler that gets notified of every log message we process. |
110 LogMessageHandlerFunction log_message_handler = NULL; | 109 LogMessageHandlerFunction log_message_handler = NULL; |
111 | 110 |
112 // The lock is used if log file locking is false. It helps us avoid problems | |
113 // with multiple threads writing to the log file at the same time. Use | |
114 // LockImpl directly instead of using Lock, because Lock makes logging calls. | |
115 static LockImpl* log_lock = NULL; | |
116 | |
117 // When we don't use a lock, we are using a global mutex. We need to do this | |
118 // because LockFileEx is not thread safe. | |
119 #if defined(OS_WIN) | |
120 MutexHandle log_mutex = NULL; | |
121 #elif defined(OS_POSIX) | |
122 pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; | |
123 #endif | |
124 | |
125 // Helper functions to wrap platform differences. | 111 // Helper functions to wrap platform differences. |
126 | 112 |
127 int32 CurrentProcessId() { | 113 int32 CurrentProcessId() { |
128 #if defined(OS_WIN) | 114 #if defined(OS_WIN) |
129 return GetCurrentProcessId(); | 115 return GetCurrentProcessId(); |
130 #elif defined(OS_POSIX) | 116 #elif defined(OS_POSIX) |
131 return getpid(); | 117 return getpid(); |
132 #endif | 118 #endif |
133 } | 119 } |
134 | 120 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 } | 157 } |
172 | 158 |
173 void DeleteFilePath(const PathString& log_name) { | 159 void DeleteFilePath(const PathString& log_name) { |
174 #if defined(OS_WIN) | 160 #if defined(OS_WIN) |
175 DeleteFile(log_name.c_str()); | 161 DeleteFile(log_name.c_str()); |
176 #else | 162 #else |
177 unlink(log_name.c_str()); | 163 unlink(log_name.c_str()); |
178 #endif | 164 #endif |
179 } | 165 } |
180 | 166 |
| 167 void GetDefaultLogFile(PathString default_log_file) { |
| 168 #if defined(OS_WIN) |
| 169 // On Windows we use the same path as the exe. |
| 170 wchar_t module_name[MAX_PATH]; |
| 171 GetModuleFileName(NULL, module_name, MAX_PATH); |
| 172 default_log_file = module_name; |
| 173 std::wstring::size_type last_backslash = |
| 174 default_log_file.rfind('\\', default_log_file.size()); |
| 175 if (last_backslash != std::wstring::npos) |
| 176 default_log_file.erase(last_backslash + 1); |
| 177 default_log_file += L"debug.log"; |
| 178 #elif defined(OS_POSIX) |
| 179 // On other platforms we just use the current directory. |
| 180 default_log_file = "debug.log"; |
| 181 #endif |
| 182 } |
| 183 |
| 184 // This class acts as a wrapper for locking the logging files. |
| 185 // LoggingLock::Init() should be called from the main thread before any logging |
| 186 // is done. Then whenever logging, be sure to have a local LoggingLock |
| 187 // instance on the stack. This will ensure that the lock is unlocked upon |
| 188 // exiting the frame. |
| 189 // LoggingLocks can not be nested. |
| 190 class LoggingLock { |
| 191 public: |
| 192 LoggingLock() { |
| 193 LockLogging(); |
| 194 } |
| 195 |
| 196 ~LoggingLock() { |
| 197 UnlockLogging(); |
| 198 } |
| 199 |
| 200 static void Init(LogLockingState lock_log, const PathChar* new_log_file) { |
| 201 if (initialized) |
| 202 return; |
| 203 lock_log_file = lock_log; |
| 204 if (lock_log_file == LOCK_LOG_FILE) { |
| 205 #if defined(OS_WIN) |
| 206 if (!log_mutex) { |
| 207 std::wstring safe_name; |
| 208 if (new_log_file) |
| 209 safe_name = new_log_file; |
| 210 else |
| 211 GetDefaultLogFile(safe_name); |
| 212 // \ is not a legal character in mutex names so we replace \ with / |
| 213 std::replace(safe_name.begin(), safe_name.end(), '\\', '/'); |
| 214 std::wstring t(L"Global\\"); |
| 215 t.append(safe_name); |
| 216 log_mutex = ::CreateMutex(NULL, FALSE, t.c_str()); |
| 217 } |
| 218 #endif |
| 219 } else { |
| 220 log_lock = new LockImpl(); |
| 221 } |
| 222 initialized = true; |
| 223 } |
| 224 |
| 225 private: |
| 226 static void LockLogging() { |
| 227 if (lock_log_file == LOCK_LOG_FILE) { |
| 228 #if defined(OS_WIN) |
| 229 ::WaitForSingleObject(log_mutex, INFINITE); |
| 230 // WaitForSingleObject could have returned WAIT_ABANDONED. We don't |
| 231 // abort the process here. UI tests might be crashy sometimes, |
| 232 // and aborting the test binary only makes the problem worse. |
| 233 // We also don't use LOG macros because that might lead to an infinite |
| 234 // loop. For more info see http://crbug.com/18028. |
| 235 #elif defined(OS_POSIX) |
| 236 pthread_mutex_lock(&log_mutex); |
| 237 #endif |
| 238 } else { |
| 239 // use the lock |
| 240 log_lock->Lock(); |
| 241 } |
| 242 } |
| 243 |
| 244 static void UnlockLogging() { |
| 245 if (lock_log_file == LOCK_LOG_FILE) { |
| 246 #if defined(OS_WIN) |
| 247 ReleaseMutex(log_mutex); |
| 248 #elif defined(OS_POSIX) |
| 249 pthread_mutex_unlock(&log_mutex); |
| 250 #endif |
| 251 } else { |
| 252 log_lock->Unlock(); |
| 253 } |
| 254 } |
| 255 |
| 256 // The lock is used if log file locking is false. It helps us avoid problems |
| 257 // with multiple threads writing to the log file at the same time. Use |
| 258 // LockImpl directly instead of using Lock, because Lock makes logging calls. |
| 259 static LockImpl* log_lock; |
| 260 |
| 261 // When we don't use a lock, we are using a global mutex. We need to do this |
| 262 // because LockFileEx is not thread safe. |
| 263 #if defined(OS_WIN) |
| 264 static MutexHandle log_mutex; |
| 265 #elif defined(OS_POSIX) |
| 266 static pthread_mutex_t log_mutex; |
| 267 #endif |
| 268 |
| 269 static bool initialized; |
| 270 static LogLockingState lock_log_file; |
| 271 }; |
| 272 |
| 273 // static |
| 274 bool LoggingLock::initialized = false; |
| 275 // static |
| 276 LockImpl* LoggingLock::log_lock = NULL; |
| 277 // static |
| 278 LogLockingState LoggingLock::lock_log_file = LOCK_LOG_FILE; |
| 279 |
| 280 #if defined(OS_WIN) |
| 281 // static |
| 282 MutexHandle LoggingLock::log_mutex = NULL; |
| 283 #elif defined(OS_POSIX) |
| 284 pthread_mutex_t LoggingLock::log_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 285 #endif |
| 286 |
181 // Called by logging functions to ensure that debug_file is initialized | 287 // Called by logging functions to ensure that debug_file is initialized |
182 // and can be used for writing. Returns false if the file could not be | 288 // and can be used for writing. Returns false if the file could not be |
183 // initialized. debug_file will be NULL in this case. | 289 // initialized. debug_file will be NULL in this case. |
184 bool InitializeLogFileHandle() { | 290 bool InitializeLogFileHandle() { |
185 if (log_file) | 291 if (log_file) |
186 return true; | 292 return true; |
187 | 293 |
188 if (!log_file_name) { | 294 if (!log_file_name) { |
189 // Nobody has called InitLogging to specify a debug log file, so here we | 295 // Nobody has called InitLogging to specify a debug log file, so here we |
190 // initialize the log file name to a default. | 296 // initialize the log file name to a default. |
191 #if defined(OS_WIN) | 297 log_file_name = new PathString(); |
192 // On Windows we use the same path as the exe. | 298 GetDefaultLogFile(*log_file_name); |
193 wchar_t module_name[MAX_PATH]; | |
194 GetModuleFileName(NULL, module_name, MAX_PATH); | |
195 log_file_name = new std::wstring(module_name); | |
196 std::wstring::size_type last_backslash = | |
197 log_file_name->rfind('\\', log_file_name->size()); | |
198 if (last_backslash != std::wstring::npos) | |
199 log_file_name->erase(last_backslash + 1); | |
200 *log_file_name += L"debug.log"; | |
201 #elif defined(OS_POSIX) | |
202 // On other platforms we just use the current directory. | |
203 log_file_name = new std::string("debug.log"); | |
204 #endif | |
205 } | 299 } |
206 | 300 |
207 if (logging_destination == LOG_ONLY_TO_FILE || | 301 if (logging_destination == LOG_ONLY_TO_FILE || |
208 logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { | 302 logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { |
209 #if defined(OS_WIN) | 303 #if defined(OS_WIN) |
210 log_file = CreateFile(log_file_name->c_str(), GENERIC_WRITE, | 304 log_file = CreateFile(log_file_name->c_str(), GENERIC_WRITE, |
211 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 305 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
212 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | 306 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
213 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { | 307 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { |
214 // try the current directory | 308 // try the current directory |
215 log_file = CreateFile(L".\\debug.log", GENERIC_WRITE, | 309 log_file = CreateFile(L".\\debug.log", GENERIC_WRITE, |
216 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | 310 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
217 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | 311 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
218 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { | 312 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { |
219 log_file = NULL; | 313 log_file = NULL; |
220 return false; | 314 return false; |
221 } | 315 } |
222 } | 316 } |
223 SetFilePointer(log_file, 0, 0, FILE_END); | 317 SetFilePointer(log_file, 0, 0, FILE_END); |
224 #elif defined(OS_POSIX) | 318 #elif defined(OS_POSIX) |
225 log_file = fopen(log_file_name->c_str(), "a"); | 319 log_file = fopen(log_file_name->c_str(), "a"); |
226 if (log_file == NULL) | 320 if (log_file == NULL) |
227 return false; | 321 return false; |
228 #endif | 322 #endif |
229 } | 323 } |
230 | 324 |
231 return true; | 325 return true; |
232 } | 326 } |
233 | 327 |
234 void InitLogMutex() { | |
235 #if defined(OS_WIN) | |
236 if (!log_mutex) { | |
237 // \ is not a legal character in mutex names so we replace \ with / | |
238 std::wstring safe_name(*log_file_name); | |
239 std::replace(safe_name.begin(), safe_name.end(), '\\', '/'); | |
240 std::wstring t(L"Global\\"); | |
241 t.append(safe_name); | |
242 log_mutex = ::CreateMutex(NULL, FALSE, t.c_str()); | |
243 } | |
244 #elif defined(OS_POSIX) | |
245 // statically initialized | |
246 #endif | |
247 } | |
248 | |
249 void BaseInitLoggingImpl(const PathChar* new_log_file, | 328 void BaseInitLoggingImpl(const PathChar* new_log_file, |
250 LoggingDestination logging_dest, | 329 LoggingDestination logging_dest, |
251 LogLockingState lock_log, | 330 LogLockingState lock_log, |
252 OldFileDeletionState delete_old) { | 331 OldFileDeletionState delete_old) { |
253 g_enable_dcheck = | 332 g_enable_dcheck = |
254 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableDCHECK); | 333 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableDCHECK); |
| 334 LoggingLock::Init(lock_log, new_log_file); |
| 335 |
| 336 LoggingLock logging_lock; |
255 | 337 |
256 if (log_file) { | 338 if (log_file) { |
257 // calling InitLogging twice or after some log call has already opened the | 339 // calling InitLogging twice or after some log call has already opened the |
258 // default log file will re-initialize to the new options | 340 // default log file will re-initialize to the new options |
259 CloseFile(log_file); | 341 CloseFile(log_file); |
260 log_file = NULL; | 342 log_file = NULL; |
261 } | 343 } |
262 | 344 |
263 lock_log_file = lock_log; | |
264 logging_destination = logging_dest; | 345 logging_destination = logging_dest; |
265 | 346 |
266 // ignore file options if logging is disabled or only to system | 347 // ignore file options if logging is disabled or only to system |
267 if (logging_destination == LOG_NONE || | 348 if (logging_destination == LOG_NONE || |
268 logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG) | 349 logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG) |
269 return; | 350 return; |
270 | 351 |
271 if (!log_file_name) | 352 if (!log_file_name) |
272 log_file_name = new PathString(); | 353 log_file_name = new PathString(); |
273 *log_file_name = new_log_file; | 354 *log_file_name = new_log_file; |
274 if (delete_old == DELETE_OLD_LOG_FILE) | 355 if (delete_old == DELETE_OLD_LOG_FILE) |
275 DeleteFilePath(*log_file_name); | 356 DeleteFilePath(*log_file_name); |
276 | 357 |
277 if (lock_log_file == LOCK_LOG_FILE) { | 358 InitializeLogFileHandle(); |
278 InitLogMutex(); | |
279 } else if (!log_lock) { | |
280 log_lock = new LockImpl(); | |
281 } | |
282 | 359 |
283 InitializeLogFileHandle(); | |
284 } | 360 } |
285 | 361 |
286 void SetMinLogLevel(int level) { | 362 void SetMinLogLevel(int level) { |
287 min_log_level = level; | 363 min_log_level = level; |
288 } | 364 } |
289 | 365 |
290 int GetMinLogLevel() { | 366 int GetMinLogLevel() { |
291 return min_log_level; | 367 return min_log_level; |
292 } | 368 } |
293 | 369 |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
510 fflush(stderr); | 586 fflush(stderr); |
511 } | 587 } |
512 } else if (severity_ >= kAlwaysPrintErrorLevel) { | 588 } else if (severity_ >= kAlwaysPrintErrorLevel) { |
513 // When we're only outputting to a log file, above a certain log level, we | 589 // When we're only outputting to a log file, above a certain log level, we |
514 // should still output to stderr so that we can better detect and diagnose | 590 // should still output to stderr so that we can better detect and diagnose |
515 // problems with unit tests, especially on the buildbots. | 591 // problems with unit tests, especially on the buildbots. |
516 fprintf(stderr, "%s", str_newline.c_str()); | 592 fprintf(stderr, "%s", str_newline.c_str()); |
517 fflush(stderr); | 593 fflush(stderr); |
518 } | 594 } |
519 | 595 |
| 596 // We can have multiple threads and/or processes, so try to prevent them |
| 597 // from clobbering each other's writes. |
| 598 // If the client app did not call InitLogging, and the lock has not |
| 599 // been created do it now. We do this on demand, but if two threads try |
| 600 // to do this at the same time, there will be a race condition to create |
| 601 // the lock. This is why InitLogging should be called from the main |
| 602 // thread at the beginning of execution. |
| 603 LoggingLock::Init(LOCK_LOG_FILE, NULL); |
520 // write to log file | 604 // write to log file |
521 if (logging_destination != LOG_NONE && | 605 if (logging_destination != LOG_NONE && |
522 logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG && | 606 logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG) { |
523 InitializeLogFileHandle()) { | 607 LoggingLock logging_lock; |
524 // We can have multiple threads and/or processes, so try to prevent them | 608 if (InitializeLogFileHandle()) { |
525 // from clobbering each other's writes. | |
526 if (lock_log_file == LOCK_LOG_FILE) { | |
527 // Ensure that the mutex is initialized in case the client app did not | |
528 // call InitLogging. This is not thread safe. See below. | |
529 InitLogMutex(); | |
530 | |
531 #if defined(OS_WIN) | 609 #if defined(OS_WIN) |
532 ::WaitForSingleObject(log_mutex, INFINITE); | 610 SetFilePointer(log_file, 0, 0, SEEK_END); |
533 // WaitForSingleObject could have returned WAIT_ABANDONED. We don't | 611 DWORD num_written; |
534 // abort the process here. UI tests might be crashy sometimes, | 612 WriteFile(log_file, |
535 // and aborting the test binary only makes the problem worse. | 613 static_cast<const void*>(str_newline.c_str()), |
536 // We also don't use LOG macros because that might lead to an infinite | 614 static_cast<DWORD>(str_newline.length()), |
537 // loop. For more info see http://crbug.com/18028. | 615 &num_written, |
538 #elif defined(OS_POSIX) | 616 NULL); |
539 pthread_mutex_lock(&log_mutex); | 617 #else |
| 618 fprintf(log_file, "%s", str_newline.c_str()); |
| 619 fflush(log_file); |
540 #endif | 620 #endif |
541 } else { | |
542 // use the lock | |
543 if (!log_lock) { | |
544 // The client app did not call InitLogging, and so the lock has not | |
545 // been created. We do this on demand, but if two threads try to do | |
546 // this at the same time, there will be a race condition to create | |
547 // the lock. This is why InitLogging should be called from the main | |
548 // thread at the beginning of execution. | |
549 log_lock = new LockImpl(); | |
550 } | |
551 log_lock->Lock(); | |
552 } | |
553 | |
554 #if defined(OS_WIN) | |
555 SetFilePointer(log_file, 0, 0, SEEK_END); | |
556 DWORD num_written; | |
557 WriteFile(log_file, | |
558 static_cast<const void*>(str_newline.c_str()), | |
559 static_cast<DWORD>(str_newline.length()), | |
560 &num_written, | |
561 NULL); | |
562 #else | |
563 fprintf(log_file, "%s", str_newline.c_str()); | |
564 fflush(log_file); | |
565 #endif | |
566 | |
567 if (lock_log_file == LOCK_LOG_FILE) { | |
568 #if defined(OS_WIN) | |
569 ReleaseMutex(log_mutex); | |
570 #elif defined(OS_POSIX) | |
571 pthread_mutex_unlock(&log_mutex); | |
572 #endif | |
573 } else { | |
574 log_lock->Unlock(); | |
575 } | 621 } |
576 } | 622 } |
577 | 623 |
578 if (severity_ == LOG_FATAL) { | 624 if (severity_ == LOG_FATAL) { |
579 // display a message or break into the debugger on a fatal error | 625 // display a message or break into the debugger on a fatal error |
580 if (DebugUtil::BeingDebugged()) { | 626 if (DebugUtil::BeingDebugged()) { |
581 DebugUtil::BreakDebugger(); | 627 DebugUtil::BreakDebugger(); |
582 } else { | 628 } else { |
583 if (log_assert_handler) { | 629 if (log_assert_handler) { |
584 // make a copy of the string for the handler out of paranoia | 630 // make a copy of the string for the handler out of paranoia |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
688 : err_(err), | 734 : err_(err), |
689 log_message_(file, line, severity) { | 735 log_message_(file, line, severity) { |
690 } | 736 } |
691 | 737 |
692 ErrnoLogMessage::~ErrnoLogMessage() { | 738 ErrnoLogMessage::~ErrnoLogMessage() { |
693 stream() << ": " << safe_strerror(err_); | 739 stream() << ": " << safe_strerror(err_); |
694 } | 740 } |
695 #endif // OS_WIN | 741 #endif // OS_WIN |
696 | 742 |
697 void CloseLogFile() { | 743 void CloseLogFile() { |
| 744 LoggingLock logging_lock; |
| 745 |
698 if (!log_file) | 746 if (!log_file) |
699 return; | 747 return; |
700 | 748 |
701 CloseFile(log_file); | 749 CloseFile(log_file); |
702 log_file = NULL; | 750 log_file = NULL; |
703 } | 751 } |
704 | 752 |
705 void RawLog(int level, const char* message) { | 753 void RawLog(int level, const char* message) { |
706 if (level >= min_log_level) { | 754 if (level >= min_log_level) { |
707 size_t bytes_written = 0; | 755 size_t bytes_written = 0; |
(...skipping 23 matching lines...) Expand all Loading... |
731 | 779 |
732 if (level == LOG_FATAL) | 780 if (level == LOG_FATAL) |
733 DebugUtil::BreakDebugger(); | 781 DebugUtil::BreakDebugger(); |
734 } | 782 } |
735 | 783 |
736 } // namespace logging | 784 } // namespace logging |
737 | 785 |
738 std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) { | 786 std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) { |
739 return out << WideToUTF8(std::wstring(wstr)); | 787 return out << WideToUTF8(std::wstring(wstr)); |
740 } | 788 } |
OLD | NEW |