Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: base/logging.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/logging.h ('k') | base/logging/build.scons » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2003-2009 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15 //
16 // Tracing and logging system.
17 //
18 // The log output goes to "All Users/Application Data/Google/Update/Log/".
19 // The log configuration file is under
20 // "%Program Files%/C:\Program Files\Google\Common\Update".
21 // By default, the logging is on in debug modes and off in opt mode although in
22 // opt mode only the OPT_LOG statements write to the log.
23 //
24 // The log is open to all the users to write to. There is a known vulnerability
25 // where a DOS can be created by holding on to the logging mutex.
26 //
27 // In this module use of ASSERT & REPORT is banned. This is to prevent any
28 // possible recursion issues between logging (logging.h) and
29 // asserting/reporting (debug.h). Both are basement-level systems that need to
30 // work when almost nothing else works and interdependencies are best avoided.
31 // One unavoidable interdependency is that debugASSERT will send messages to
32 // the logger via Logger::OutputMessage - which then broadcasts it to each
33 // LogWriter's OutputMessage. So these methods should be as simple as
34 // possible. (Also, unlike asserting/reporting - this module will not avoid
35 // use of the heap, however the code executed from Logger::OutputMessage
36 // doesn't use the heap (for all the LogWriters in this file).)
37 //
38 // TODO(omaha): implement the minidump handling in terms of breakpad.
39 // Log initialization if full of lazy init. Consider doing
40 // eager init of log and its registered log writers when the
41 // log is created and initialized.
42 // Reimplement in terms of smart handles and locks.
43 // Reimplement without dependency on any other compilation unit
44 // that can call assert, verify, or the log itself.
45 // Redo the history logging feature
46
47 #include "omaha/base/logging.h"
48
49 #include <excpt.h> // Microsoft specific: structured exceptions.
50 #include <shlobj.h>
51 #include <shlwapi.h>
52 #include <string.h>
53 #include <atlpath.h>
54 #include <atlsecurity.h>
55 #include "base/basictypes.h"
56 #include "omaha/base/app_util.h"
57 #include "omaha/base/const_debug.h"
58 #include "omaha/base/constants.h"
59 #include "omaha/base/debug.h"
60 #include "omaha/base/etw_log_writer.h"
61 #include "omaha/base/file.h"
62 #include "omaha/base/path.h"
63 #include "omaha/base/string.h"
64 #include "omaha/base/time.h"
65 #include "omaha/base/utils.h"
66
67 namespace omaha {
68
69 // enforce ban on ASSERT/REPORT
70 #undef ASSERT
71 #undef REPORT
72
73 #ifdef LOGGING
74
75 #define kNumLockRetries (20)
76 #define kLockRetryDelayMs (50)
77
78 // Circular buffer to log history.
79 static wchar_t history_buffer[kMaxHistoryBufferSize];
80
81 // Index into the history buffer to begin writing at.
82 static int history_buffer_next_idx = 0;
83
84 // Indicates whether the history buffer has ever reached its full capacity.
85 // Once this boolean is true, if never becomes false.
86 static bool history_buffer_full = false;
87
88 //
89 // Table of category names to categories.
90 //
91 #define LC_ENTRY(lc_value) (L#lc_value), (lc_value)
92 struct {
93 wchar_t* category_name;
94 LogCategory category;
95 } static LogCategoryNames[] = {
96 LC_ENTRY(LC_UTIL),
97 LC_ENTRY(LC_SETUP),
98 LC_ENTRY(LC_SHELL),
99 LC_ENTRY(LC_CORE),
100 LC_ENTRY(LC_JS),
101 LC_ENTRY(LC_PLUGIN),
102 LC_ENTRY(LC_SERVICE),
103 LC_ENTRY(LC_OPT),
104 LC_ENTRY(LC_NET),
105 LC_ENTRY(LC_REPORT),
106 };
107
108 COMPILE_ASSERT(arraysize(LogCategoryNames) == LC_MAX_CAT - 1,
109 LogCategoryNames_missing_category);
110
111 static CString StripBackslashes(const CString& name) {
112 int n = String_FindChar(name, L'\\');
113 if (n == -1) {
114 return name;
115 } else {
116 CString result;
117 for (int i = 0; i < name.GetLength(); ++i) {
118 if (name[i] != L'\\') {
119 result += name[i];
120 }
121 }
122 return result;
123 }
124 }
125
126 static CString GetProcName() {
127 CString proc_name(app_util::GetAppNameWithoutExtension());
128 CString module_name(app_util::GetCurrentModuleNameWithoutExtension());
129 CString result(proc_name);
130 if (module_name.CompareNoCase(proc_name) != 0) {
131 result += L":";
132 result += module_name;
133 }
134 return result;
135 }
136
137 // Formats a line prefix with the current time min:sec:milisec if wanted,
138 // otherwise just the process:module.
139 static void FormatLinePrefix(bool show_time,
140 const wchar_t* proc_name,
141 CString& result) {
142 if (show_time) {
143 SYSTEMTIME system_time = {0};
144 GetLocalTime(&system_time);
145 result.Format(L"[%02d/%02d/%02d %02d:%02d:%02d.%03d]",
146 system_time.wMonth, system_time.wDay, system_time.wYear % 100,
147 system_time.wHour, system_time.wMinute, system_time.wSecond,
148 system_time.wMilliseconds);
149 }
150 result.AppendFormat(L"[%s][%u:%u]",
151 proc_name,
152 ::GetCurrentProcessId(),
153 ::GetCurrentThreadId());
154 }
155
156 static bool g_logging_valid = false;
157 static Logging g_logging;
158
159 // Singleton factory for the Logging object.
160 Logging* GetLogging() {
161 return g_logging_valid ? &g_logging : NULL;
162 }
163
164 // Force the logging system to be initialized during elaboration of static
165 // constructors, while the program is still single-threaded. This global
166 // static object will cause the static logger inside of GetLogging() to be
167 // constructed. However, it might not be the first call to GetLogging() -
168 // another static object in another object file might get constructed first,
169 // and in its constructor call a logging API. Just as long as it is done when
170 // the system is single threaded. (Reason: because the Logger object has a
171 // LLock object which has a Win32 critical section which needs to be
172 // initialized - only once!)
173
174
175 Logging::Logging()
176 : logging_initialized_(false),
177 logging_enabled_(true),
178 force_show_time_(false),
179 show_time_(true),
180 log_to_file_(true),
181 log_to_debug_out_(true),
182 append_to_file_(true),
183 logging_shutdown_(false),
184 num_writers_(0),
185 file_log_writer_(NULL),
186 debug_out_writer_(NULL),
187 etw_log_writer_(NULL),
188 is_initializing_(false),
189 log_file_name_(kDefaultLogFileName),
190 config_file_path_(GetConfigurationFilePath()) {
191 g_last_category_check_time = 0;
192 for (int i = 0; i < max_writers; ++i) {
193 writers_[i] = NULL;
194 }
195 proc_name_ = GetProcName();
196 g_logging_valid = true;
197
198 // Read initial settings from the config file.
199 ReadLoggingSettings();
200 }
201
202 // TODO(omaha): why aren't we using a mutexscope and what if an the code
203 // throws? Will the lock be unlocked?
204 Logging::~Logging() {
205 // Acquire the lock outside the try/except block so we'll always release it
206 lock_.Lock();
207
208 __try {
209 // prevent further access to the system
210 // necessary because the static destructors happen
211 // in a non-deterministic order
212 logging_shutdown_ = true;
213
214 // Delete all registered LogWriters
215 for (int i = 0; i < num_writers_; ++i) {
216 if (writers_[i]) {
217 delete writers_[i];
218 }
219 }
220
221 logging_initialized_ = false;
222 } __except(SehNoMinidump(GetExceptionCode(),
223 GetExceptionInformation(),
224 __FILE__,
225 __LINE__,
226 true)) {
227 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
228 logging_initialized_ = false;
229 }
230
231 g_logging_valid = false;
232 lock_.Unlock();
233 }
234
235 void Logging::UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat) {
236 if (cat_name == NULL) {
237 return;
238 }
239 if (cat >= LC_MAX_CAT) {
240 return;
241 }
242 int log_level = kDefaultLogLevel;
243 CString config_file = GetCurrentConfigurationFilePath();
244 if (!config_file.IsEmpty()) {
245 log_level = GetPrivateProfileInt(kConfigSectionLoggingLevel,
246 cat_name,
247 kDefaultLogLevel,
248 config_file);
249 }
250 category_list_[cat].enabled = (log_level != 0);
251 category_list_[cat].log_level = static_cast<LogLevel>(log_level);
252 }
253
254 void Logging::ReadLoggingSettings() {
255 CString config_file = GetCurrentConfigurationFilePath();
256 if (!config_file.IsEmpty()) {
257 logging_enabled_ = ::GetPrivateProfileInt(
258 kConfigSectionLoggingSettings,
259 kConfigAttrEnableLogging,
260 kDefaultLoggingEnabled,
261 config_file) == 0 ? false : true;
262
263 show_time_ = ::GetPrivateProfileInt(
264 kConfigSectionLoggingSettings,
265 kConfigAttrShowTime,
266 kDefaultShowTime,
267 config_file) == 0 ? false : true;
268
269 log_to_file_ = ::GetPrivateProfileInt(
270 kConfigSectionLoggingSettings,
271 kConfigAttrLogToFile,
272 kDefaultLogToFile,
273 config_file) == 0 ? false : true;
274
275 log_to_debug_out_ = ::GetPrivateProfileInt(
276 kConfigSectionLoggingSettings,
277 kConfigAttrLogToOutputDebug,
278 kDefaultLogToOutputDebug,
279 config_file) == 0 ? false : true;
280
281 append_to_file_ = ::GetPrivateProfileInt(
282 kConfigSectionLoggingSettings,
283 kConfigAttrAppendToFile,
284 kDefaultAppendToFile,
285 config_file) == 0 ? false : true;
286
287 ::GetPrivateProfileString(kConfigSectionLoggingSettings,
288 kConfigAttrLogFilePath,
289 kDefaultLogFileName,
290 CStrBuf(log_file_name_, MAX_PATH),
291 MAX_PATH,
292 config_file);
293 } else {
294 logging_enabled_ = kDefaultLoggingEnabled;
295 show_time_ = kDefaultShowTime;
296 log_to_file_ = kDefaultLogToFile;
297 log_to_debug_out_ = kDefaultLogToOutputDebug;
298 append_to_file_ = kDefaultAppendToFile;
299 log_file_name_ = kDefaultLogFileName;
300 }
301
302 if (force_show_time_) {
303 show_time_ = true;
304 }
305
306 // The "default" category is always enabled.
307 category_list_[LC_LOGGING].enabled = true;
308 category_list_[LC_LOGGING].log_level = LEVEL_ALL;
309
310 // Read each category from the ini file.
311 for (size_t i = 0; i < arraysize(LogCategoryNames); ++i) {
312 UpdateCatAndLevel(LogCategoryNames[i].category_name,
313 LogCategoryNames[i].category);
314 }
315
316 g_last_category_check_time = GetCurrent100NSTime();
317 }
318
319 CString Logging::GetDefaultLogDirectory() const {
320 CString path;
321 CStrBuf buf(path, MAX_PATH);
322 HRESULT hr = ::SHGetFolderPath(NULL,
323 CSIDL_COMMON_APPDATA,
324 NULL,
325 SHGFP_TYPE_CURRENT,
326 buf);
327 if (FAILED(hr)) {
328 return L"";
329 }
330 if (!::PathAppend(buf, OMAHA_REL_LOG_DIR)) {
331 return L"";
332 }
333 return path;
334 }
335
336 CString Logging::GetLogFilePath() const {
337 if (log_file_name_.IsEmpty()) {
338 return CString();
339 }
340
341 if (!ATLPath::IsRelative(log_file_name_)) {
342 return log_file_name_;
343 }
344
345 CString path = GetDefaultLogDirectory();
346 if (path.IsEmpty()) {
347 return CString();
348 }
349
350 if (!::PathAppend(CStrBuf(path, MAX_PATH), log_file_name_)) {
351 return CString();
352 }
353
354 return path;
355 }
356
357 void Logging::ConfigureETWLogWriter() {
358 // Always create the ETW log writer, as its log level is controlled
359 // at runtime through Event Tracing for Windows.
360 if (etw_log_writer_ == NULL) {
361 etw_log_writer_ = EtwLogWriter::Create();
362 if (debug_out_writer_ == NULL) {
363 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
364 L"Cannot create ETW log writer",
365 proc_name_));
366 }
367 }
368
369 if (etw_log_writer_ != NULL) {
370 InternalRegisterWriter(etw_log_writer_);
371 }
372 }
373
374 void Logging::ConfigureFileLogWriter() {
375 if (!log_to_file_) {
376 return;
377 }
378
379 // Create the logging file.
380 if (file_log_writer_ == NULL) {
381 CString path = GetLogFilePath();
382 if (path.IsEmpty()) {
383 return;
384 }
385
386 // Extract the final target directory which will not be what
387 // GetDefaultLogDirectory() returns if log_file_name_ is an absolute path.
388 CString log_file_dir = GetDirectoryFromPath(path);
389 if (!File::Exists(log_file_dir)) {
390 if (FAILED(CreateDir(log_file_dir, NULL))) {
391 return;
392 }
393 }
394 file_log_writer_ = FileLogWriter::Create(path, append_to_file_);
395 if (file_log_writer_ == NULL) {
396 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
397 L"Cannot create log writer to %s",
398 proc_name_, path));
399 }
400 }
401
402 if (file_log_writer_ != NULL) {
403 InternalRegisterWriter(file_log_writer_);
404 }
405 }
406
407 void Logging::ConfigureDebugOutLogWriter() {
408 if (!log_to_debug_out_) {
409 return;
410 }
411
412 if (debug_out_writer_ == NULL) {
413 debug_out_writer_ = OutputDebugStringLogWriter::Create();
414 if (debug_out_writer_ == NULL) {
415 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
416 L"Cannot create OutputDebugString log writer",
417 proc_name_));
418 }
419 }
420
421 if (debug_out_writer_ != NULL) {
422 InternalRegisterWriter(debug_out_writer_);
423 }
424 }
425
426 // Configures/unconfigures the log writers for the current settings.
427 bool Logging::ConfigureLogging() {
428 ConfigureETWLogWriter();
429 ConfigureFileLogWriter();
430 ConfigureDebugOutLogWriter();
431
432 return num_writers_ > 0;
433 }
434
435 void Logging::UnconfigureLogging() {
436 if (etw_log_writer_ != NULL) {
437 InternalUnregisterWriter(etw_log_writer_);
438 }
439 if (file_log_writer_ != NULL) {
440 InternalUnregisterWriter(file_log_writer_);
441 }
442 if (debug_out_writer_ != NULL) {
443 InternalUnregisterWriter(debug_out_writer_);
444 }
445 }
446
447 bool Logging::InternalInitialize() {
448 __try {
449 if (logging_shutdown_ == true) {
450 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
451 L"Calling the logging system after "
452 L"it has been shut down \n",
453 GetProcName()));
454 return false;
455 }
456
457 if (logging_initialized_ == true) {
458 return true;
459 }
460 // If something called by this method is attempting to do logging,
461 // just ignore it. The cost/benefit ratio is too high to do otherwise.
462 if (is_initializing_) {
463 return false;
464 }
465 is_initializing_ = true;
466
467 // Read the initial settings from the config file.
468 ReadLoggingSettings();
469
470 // Initialize logging system if enabled at start.
471 if (logging_enabled_) {
472 logging_initialized_ = ConfigureLogging();
473 }
474 } __except(SehNoMinidump(GetExceptionCode(),
475 GetExceptionInformation(),
476 __FILE__,
477 __LINE__,
478 true)) {
479 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
480 logging_initialized_ = false;
481 return false;
482 }
483
484 is_initializing_ = false;
485 return true;
486 }
487
488 bool Logging::InitializeLogging() {
489 // Double-checked locking idiom is broken, especially on multicore machines.
490 // TODO(omaha): understand how this works and fix it.
491 if (logging_shutdown_ == true) {
492 OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - Calling the logging "
493 L"system after it has been shut down \n",
494 GetProcName()));
495 return false;
496 }
497
498 if (logging_initialized_ == true) {
499 return true;
500 }
501
502 // Acquire the lock outside the try/except block so we'll always release it.
503 __mutexScope(lock_);
504 return InternalInitialize();
505 }
506
507 // Enables/disables the logging mechanism. Allows turning logging on/off
508 // in mid-run.
509 // TODO(omaha): same comment as for the destructor.
510 void Logging::EnableLogging() {
511 if (!InitializeLogging()) {
512 return;
513 }
514
515 // Acquire the lock outside the try/except block so we'll always release it.
516 lock_.Lock();
517
518 __try {
519 if (!logging_enabled_) {
520 ConfigureLogging();
521 logging_enabled_ = true;
522 }
523 } __except(SehNoMinidump(GetExceptionCode(),
524 GetExceptionInformation(),
525 __FILE__,
526 __LINE__,
527 true)) {
528 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
529 logging_enabled_ = false;
530 }
531
532 lock_.Unlock();
533 }
534
535 void Logging::DisableLogging() {
536 if (!InitializeLogging()) {
537 return;
538 }
539
540 // Acquire the lock outside the try/except block so we'll always release it.
541 lock_.Lock();
542
543 __try {
544 if (logging_enabled_) {
545 logging_enabled_ = false;
546 UnconfigureLogging();
547 }
548 } __except(SehNoMinidump(GetExceptionCode(),
549 GetExceptionInformation(),
550 __FILE__,
551 __LINE__,
552 true)) {
553 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
554 logging_enabled_ = false;
555 }
556
557 lock_.Unlock();
558 }
559
560 // Checks if logging is enabled - and updates logging settings from the
561 // configuration file every kLogSettingsCheckInterval seconds.
562 bool Logging::IsLoggingEnabled() {
563 if (!InitializeLogging()) {
564 return false;
565 }
566
567 // Dynamic update - including reading a new value of logging_enabled_.
568 bool prev_logging_enabled = logging_enabled_;
569 if (GetCurrent100NSTime() >
570 g_last_category_check_time + kLogSettingsCheckInterval) {
571 ReadLoggingSettings();
572 }
573
574 // If enabled state has changed either enable or disable logging.
575 if (prev_logging_enabled != logging_enabled_) {
576 if (logging_enabled_) {
577 EnableLogging();
578 } else {
579 DisableLogging();
580 }
581 }
582
583 return logging_enabled_;
584 }
585
586 bool Logging::IsLoggingAlreadyEnabled() const {
587 return logging_enabled_;
588 }
589
590 void Logging::ForceShowTimestamp(bool force_show_time) {
591 force_show_time_ = show_time_ = force_show_time;
592 }
593
594 // Get category level
595 LogLevel Logging::GetCatLevel(LogCategory category) const {
596 if (!IsLoggingAlreadyEnabled()) {
597 return kDefaultLogLevel;
598 }
599
600 if (category >= LC_MAX_CAT) {
601 return kDefaultLogLevel;
602 }
603
604 return category_list_[category].log_level;
605 }
606
607 // Check if logging is enabled for a given category and level
608 DWORD Logging::IsCatLevelEnabled(LogCategory category, LogLevel level) {
609 if (!IsLoggingEnabled()) {
610 return 0;
611 }
612
613 if (category >= LC_MAX_CAT) {
614 return 0;
615 }
616
617 // If the config value is to log: then log to all writers.
618 if (category_list_[category].enabled &&
619 level <= category_list_[category].log_level) {
620 return static_cast<DWORD>(all_writers_mask);
621 }
622
623 // Check each of the registered loggers to see if they want to override the
624 // negative config value.
625 DWORD mask = 0;
626 for (int i = num_writers_ - 1; i >= 0; --i) {
627 mask <<= 1;
628 if (writers_[i] && writers_[i]->IsCatLevelEnabled(category, level)) {
629 mask |= 1;
630 }
631 }
632
633 return mask;
634 }
635
636 // TODO(omaha): For now this is hard coded and there is no way to override
637 // writing other log categories into the history. Add a store_in_history
638 // boolean into the CategoryInfo struct that allows reading from the config
639 // file. This will enable other log categories to get buffered in the history.
640 bool Logging::IsCategoryEnabledForBuffering(LogCategory cat) {
641 return cat == LC_REPORT;
642 }
643
644 void Logging::LogMessage(LogCategory cat, LogLevel level,
645 const wchar_t* fmt, ...) {
646 va_list args;
647 va_start(args, fmt);
648 LogMessageVA(cat, level, fmt, args);
649 va_end(args);
650 }
651
652 void Logging::LogMessageVA(LogCategory cat, LogLevel level,
653 const wchar_t* fmt, va_list args) {
654 LogMessageMaskedVA(static_cast<DWORD>(all_writers_mask),
655 cat,
656 level,
657 fmt,
658 args);
659 }
660
661 void Logging::InternalLogMessageMaskedVA(DWORD writer_mask,
662 LogCategory cat,
663 LogLevel level,
664 CString* log_buffer,
665 CString* prefix,
666 const wchar_t* fmt,
667 va_list args) {
668 __try {
669 // Initial buffer size in characters.
670 // It will adjust dynamically if the message is bigger.
671 DWORD buffer_size = 512;
672
673 // Count of chars / bytes written.
674 int num_chars = 0;
675 bool result = false;
676
677 // Write the message in the buffer.
678 // Dynamically adjust the size to hold the entire message.
679
680 while ((num_chars = _vsnwprintf_s(
681 log_buffer->GetBufferSetLength(buffer_size),
682 buffer_size,
683 _TRUNCATE,
684 fmt,
685 args)) == -1) {
686 // Truncate if the message is too big.
687 if (buffer_size >= kMaxLogMessageSize) {
688 num_chars = buffer_size;
689 break;
690 }
691
692 // Get a buffer that is big enough.
693 buffer_size *= 2;
694 }
695
696 log_buffer->ReleaseBuffer(num_chars);
697
698 FormatLinePrefix(show_time_, proc_name_, *prefix);
699
700 // Log the message.
701 OutputInfo info(cat, level, *prefix, *log_buffer);
702 OutputMessage(writer_mask, &info);
703 } __except(SehSendMinidump(GetExceptionCode(),
704 GetExceptionInformation(),
705 kMinsTo100ns)) {
706 OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
707 OutputDebugString(fmt);
708 OutputDebugString(L"\n\r");
709 }
710 }
711
712 void Logging::LogMessageMaskedVA(DWORD writer_mask,
713 LogCategory cat,
714 LogLevel level,
715 const wchar_t* fmt,
716 va_list args) {
717 if (!fmt) {
718 return;
719 }
720
721 if (writer_mask == 0 && level > kMaxLevelToStoreInLogHistory) {
722 return;
723 }
724
725 CString log_buffer; // The buffer for formatted log messages.
726 CString prefix;
727
728 int i = 0;
729 while (++i <= kNumLockRetries) {
730 if (lock_.Lock(0)) {
731 InternalLogMessageMaskedVA(writer_mask, cat, level, &log_buffer,
732 &prefix, fmt, args);
733 lock_.Unlock();
734 break;
735 }
736
737 Sleep(kLockRetryDelayMs);
738 }
739
740 if (i > kNumLockRetries) {
741 OutputDebugStringA("LOG_SYSTEM: Couldn't acquire lock - ");
742 OutputDebugString(fmt);
743 OutputDebugString(L"\n\r");
744 }
745 }
746
747 void Logging::OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level,
748 const wchar_t* msg1, const wchar_t* msg2) {
749 OutputInfo info(cat, level, msg1, msg2);
750 OutputMessage(writer_mask, &info);
751 }
752
753 // Store log message in in-memory history buffer.
754 void Logging::StoreInHistory(const OutputInfo* output_info) {
755 AppendToHistory(output_info->msg1);
756 AppendToHistory(output_info->msg2);
757 AppendToHistory(L"\r\n");
758 }
759
760 // Append string to in-memory history buffer.
761 // history_buffer_next_idx points to the next index to write at,
762 // thus it should always be in (0 - kHistoryBufferEndIdx).
763 void Logging::AppendToHistory(const wchar_t* msg) {
764 int msg_len = wcslen(msg);
765 if (msg_len == 0) {
766 return;
767 }
768
769 if (msg_len >= kMaxHistoryBufferSize) {
770 // Write the first kMaxHistoryBufferSize chars.
771 memcpy(history_buffer, msg, kMaxHistoryBufferSize * sizeof(TCHAR));
772 history_buffer_next_idx = 0;
773 history_buffer_full = true;
774 return;
775 }
776
777 // Determine if the message fits into the portion of the buffer after
778 // history_buffer_next_idx.
779 if (msg_len + history_buffer_next_idx < kMaxHistoryBufferSize) {
780 memcpy(history_buffer + history_buffer_next_idx, msg,
781 msg_len * sizeof(TCHAR));
782 history_buffer_next_idx += msg_len;
783 return;
784 }
785
786 // Have to split the input message into the part that fits in
787 // history_buffer_next_idx to kMaxHistoryBufferSize and the remaining message.
788 int msg_first_part_len = kMaxHistoryBufferSize - history_buffer_next_idx;
789 int msg_second_part_len = msg_len - msg_first_part_len;
790 memcpy(history_buffer + history_buffer_next_idx,
791 msg,
792 msg_first_part_len * sizeof(TCHAR));
793
794 history_buffer_full = true;
795 history_buffer_next_idx = msg_second_part_len;
796 if (msg_second_part_len) {
797 memcpy(history_buffer,
798 msg + msg_first_part_len,
799 msg_second_part_len * sizeof(TCHAR));
800 }
801 }
802
803 // Retrieve in-memory history buffer.
804 CString Logging::GetHistory() {
805 CString history;
806
807 if (history_buffer_full) {
808 history.Append(history_buffer + history_buffer_next_idx,
809 kMaxHistoryBufferSize - history_buffer_next_idx);
810 }
811 history.Append(history_buffer, history_buffer_next_idx);
812
813 // Reset the history buffer to the original state.
814 history_buffer_next_idx = 0;
815 history_buffer_full = false;
816 memset(history_buffer, 0, kMaxHistoryBufferSize * sizeof(TCHAR));
817
818 return history;
819 }
820
821 void Logging::OutputMessage(DWORD writer_mask,
822 const OutputInfo* output_info) {
823 if (output_info->level <= kMaxLevelToStoreInLogHistory &&
824 IsCategoryEnabledForBuffering(output_info->category)) {
825 StoreInHistory(output_info);
826 }
827
828 for (int i = 0; i < num_writers_; ++i) {
829 if (writer_mask & 1) {
830 __try {
831 if (logging_enabled_ || writers_[i]->WantsToLogRegardless()) {
832 writers_[i]->OutputMessage(output_info);
833 }
834 }
835 __except(SehNoMinidump(GetExceptionCode(),
836 GetExceptionInformation(),
837 __FILE__,
838 __LINE__,
839 true)) {
840 // Just eat errors that happen from within the LogWriters. This is
841 // important so that if such an error happens when OutputMessage is
842 // called from debugASSERT we don't go recursively into more
843 // error handling ...
844 }
845 }
846 writer_mask >>= 1;
847 }
848 }
849
850 bool Logging::InternalRegisterWriter(LogWriter* log_writer) {
851 if (num_writers_ >= max_writers) {
852 return false;
853 }
854 writers_[num_writers_++] = log_writer;
855 return true;
856 }
857
858 bool Logging::RegisterWriter(LogWriter* log_writer) {
859 if (!InternalRegisterWriter(log_writer)) {
860 return false;
861 }
862 if (log_writer->WantsToLogRegardless()) {
863 EnableLogging();
864 }
865 return true;
866 }
867
868 bool Logging::InternalUnregisterWriter(LogWriter* log_writer) {
869 bool result = false;
870 for (int i = 0; i < num_writers_; ++i) {
871 if (writers_[i] == log_writer) {
872 // Replace this entry with last entry in array, then truncate.
873 writers_[i] = writers_[--num_writers_];
874 result = true;
875 break;
876 }
877 }
878 return result;
879 }
880
881 bool Logging::UnregisterWriter(LogWriter* log_writer) {
882 if (!InternalUnregisterWriter(log_writer)) {
883 return false;
884 }
885 if (num_writers_ == 0) {
886 DisableLogging();
887 }
888 return true;
889 }
890
891 // The primary configuration file under %PROGRAMFILES%\Google\Update is
892 // removed on uninstall. This is midly inconvenient during development
893 // therefore a fallback location for the configuration file is desired.
894 CString Logging::GetCurrentConfigurationFilePath() const {
895 if (!config_file_path_.IsEmpty() &&
896 File::Exists(config_file_path_)) {
897 return config_file_path_;
898 } else {
899 return L"";
900 }
901 }
902
903 CString Logging::GetConfigurationFilePath() const {
904 CString file_path;
905 CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive"));
906 if (!system_drive.IsEmpty()) {
907 file_path = system_drive;
908 file_path += L"\\";
909 }
910 return file_path + kLogConfigFileName;
911 }
912
913 LogWriter::LogWriter() {
914 }
915
916 LogWriter::~LogWriter() {
917 }
918
919 void LogWriter::Cleanup() {}
920
921 bool LogWriter::WantsToLogRegardless() const { return false; }
922
923 bool LogWriter::IsCatLevelEnabled(LogCategory, LogLevel) const {
924 return false;
925 }
926
927 void LogWriter::OutputMessage(const OutputInfo*) { }
928
929 bool LogWriter::Register() {
930 Logging* logger = GetLogging();
931 if (logger) {
932 return logger->RegisterWriter(this);
933 } else {
934 return false;
935 }
936 }
937
938 bool LogWriter::Unregister() {
939 Logging* logger = GetLogging();
940 if (logger) {
941 return logger->RegisterWriter(this);
942 } else {
943 return false;
944 }
945 }
946
947 // FileLogWriter
948
949 FileLogWriter* FileLogWriter::Create(const wchar_t* file_name, bool append) {
950 return new FileLogWriter(file_name, append);
951 }
952
953 FileLogWriter::FileLogWriter(const wchar_t* file_name, bool append)
954 : initialized_(false),
955 valid_(false),
956 file_name_(file_name),
957 log_file_mutex_(NULL),
958 log_file_(NULL),
959 append_(append),
960 max_file_size_(kDefaultMaxLogFileSize),
961 log_file_wide_(kDefaultLogFileWide) {
962 Logging* logger = GetLogging();
963 if (logger) {
964 CString config_file_path = logger->GetCurrentConfigurationFilePath();
965 if (!config_file_path.IsEmpty()) {
966 max_file_size_ = ::GetPrivateProfileInt(
967 kConfigSectionLoggingSettings,
968 kConfigAttrMaxLogFileSize,
969 kDefaultMaxLogFileSize,
970 config_file_path);
971 log_file_wide_ = ::GetPrivateProfileInt(
972 kConfigSectionLoggingSettings,
973 kConfigAttrLogFileWide,
974 kDefaultLogFileWide,
975 config_file_path) == 0 ? false : true;
976 } else {
977 max_file_size_ = kDefaultMaxLogFileSize;
978 log_file_wide_ = kDefaultLogFileWide;
979 }
980 proc_name_ = logger->proc_name();
981 }
982 }
983
984 FileLogWriter::~FileLogWriter() {
985 // TODO(omaha): Figure out a way to pass the proc_name - and possibly
986 // the show_time var - into here.
987 Logging* logger = GetLogging();
988 if (logger && logger->IsLoggingAlreadyEnabled()) {
989 // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage);
990 // OutputMessage(&info);
991 }
992 Cleanup();
993 }
994
995 void FileLogWriter::Initialize() {
996 if (initialized_) {
997 return;
998 }
999
1000 initialized_ = true;
1001
1002 bool already_created = CreateLoggingMutex();
1003 if (!log_file_mutex_) {
1004 return;
1005 }
1006
1007 if (already_created) {
1008 append_ = true;
1009 }
1010
1011 if (!GetMutex()) {
1012 ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
1013 L"Could not acquire logging mutex %s\n",
1014 proc_name_,
1015 log_file_mutex_name_));
1016 return;
1017 }
1018
1019 CreateLoggingFile();
1020 if (!log_file_) {
1021 ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
1022 L"Could not create logging file %s\n",
1023 proc_name_,
1024 file_name_));
1025 valid_ = false;
1026 }
1027
1028 valid_ = true;
1029 ReleaseMutex();
1030 }
1031
1032 void FileLogWriter::Cleanup() {
1033 if (log_file_) {
1034 ::CloseHandle(log_file_);
1035 }
1036 if (log_file_mutex_) {
1037 ::ReleaseMutex(log_file_mutex_);
1038 ::CloseHandle(log_file_mutex_);
1039 }
1040 }
1041
1042 bool FileLogWriter::CreateLoggingMutex() {
1043 log_file_mutex_name_ = StripBackslashes(kLoggingMutexName L"_" + file_name_);
1044 // TODO(omaha): I don't see where this class is used, but I guess the
1045 // caller is always in the same context. We should use the default security
1046 // here. If the caller can be in different contexts (System Service, Usermode
1047 // applications, etc), then we should revisit this code to give access only
1048 // to those who really need it. What if a malicious piece a code decide
1049 // to get the mutex and lock it? If it happens, everytime you try to log
1050 // something, the thread would hang for 500ms and then fail to log the
1051 // message.
1052 CSecurityDesc sd;
1053 GetEveryoneDaclSecurityDescriptor(&sd, GENERIC_ALL, GENERIC_ALL);
1054 CSecurityAttributes sa(sd);
1055 log_file_mutex_ = CreateMutexWithSyncAccess(log_file_mutex_name_, &sa);
1056 if (log_file_mutex_) {
1057 return ERROR_ALREADY_EXISTS == ::GetLastError();
1058 }
1059 return false;
1060 }
1061
1062 bool FileLogWriter::CreateLoggingFile() {
1063 uint32 file_size(0);
1064 File::GetFileSizeUnopen(file_name_, &file_size);
1065 if (file_size > max_file_size_) {
1066 ArchiveLoggingFile();
1067 }
1068 log_file_ = ::CreateFile(file_name_,
1069 GENERIC_WRITE,
1070 FILE_SHARE_WRITE | FILE_SHARE_READ,
1071 NULL,
1072 append_ ? OPEN_ALWAYS : CREATE_ALWAYS,
1073 FILE_ATTRIBUTE_NORMAL,
1074 NULL);
1075 if (log_file_ == INVALID_HANDLE_VALUE) {
1076 // The code in this file is written with the assumption that log_file_ is
1077 // NULL on creation errors. The easy fix is to set it to NULL here. The
1078 // long term fix should be implementing it in terms of a smart handle.
1079 log_file_ = NULL;
1080 return false;
1081 }
1082
1083 // Allow users to read, write, and delete the log file.
1084 ACCESS_MASK mask = GENERIC_READ | GENERIC_WRITE | DELETE;
1085 CDacl dacl;
1086 if (dacl.AddAllowedAce(ATL::Sids::Users(), mask)) {
1087 AtlSetDacl(file_name_, SE_FILE_OBJECT, dacl);
1088 }
1089
1090 // Insert a BOM in the newly created file.
1091 if (GetLastError() != ERROR_ALREADY_EXISTS && log_file_wide_) {
1092 DWORD num = 0;
1093 ::WriteFile(log_file_, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL);
1094 }
1095 return true;
1096 }
1097
1098 bool FileLogWriter::TruncateLoggingFile() {
1099 DWORD share_mode = FILE_SHARE_WRITE;
1100 HANDLE log_file = ::CreateFile(file_name_,
1101 GENERIC_WRITE,
1102 FILE_SHARE_WRITE | FILE_SHARE_READ,
1103 NULL,
1104 TRUNCATE_EXISTING,
1105 FILE_ATTRIBUTE_NORMAL,
1106 NULL);
1107 if (log_file_ == INVALID_HANDLE_VALUE) {
1108 return false;
1109 }
1110
1111 // Insert a BOM in the newly created file.
1112 if (log_file_wide_) {
1113 DWORD num = 0;
1114 ::WriteFile(log_file, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL);
1115 }
1116 ::CloseHandle(log_file);
1117 return true;
1118 }
1119
1120 bool FileLogWriter::ArchiveLoggingFile() {
1121 ::OutputDebugString(L"LOG_SYSTEM: trying to move log file to backup\n");
1122 CString backup_file_name = file_name_ + L".bak";
1123 HRESULT hr = File::Move(file_name_, backup_file_name, true);
1124 if (FAILED(hr)) {
1125 ::OutputDebugString(L"LOG_SYSTEM: failed to move log file to backup\n");
1126
1127 // Trying to move the log file when loggers have it open returns
1128 // ERROR_SHARING_VIOLATION. Each call to MoveFileAfterReboot inserts the
1129 // file into PendingFileRenames list. Moving files at reboot requires the
1130 // user to be either the LocalSystem account or in the Administrators
1131 // group.
1132 if (!IsArchivePending()) {
1133 File::MoveAfterReboot(file_name_, backup_file_name);
1134 }
1135 return false;
1136 }
1137 return true;
1138 }
1139
1140 bool FileLogWriter::IsArchivePending() {
1141 // We look at the PendingFileRenameOperations to see if our log file is
1142 // pending a rename. The list is a REG_MULTI_SZ, which is a sequence of
1143 // null-terminated strings, terminated by an empty string "\0".
1144 // The strings have the structure:
1145 // \??\file1\0!\??\file2\0\file3\0\0...\0\0. where file1 is to be renamed to
1146 // file2 and file3 is to be deleted.
1147 // It is valid for the PFR list to include an empty string in the middle
1148 // of the sequence.
1149 const wchar_t sub_key_name[] = L"SYSTEM\\CurrentControlSet\\Control\\"
1150 L"Session Manager";
1151 HKEY key = NULL;
1152 int res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1153 sub_key_name,
1154 0,
1155 KEY_READ,
1156 &key);
1157 if (res != ERROR_SUCCESS) {
1158 return false;
1159 }
1160 DWORD bytes = 0;
1161 DWORD type = REG_MULTI_SZ;
1162 res = ::RegQueryValueEx(key,
1163 L"PendingFileRenameOperations",
1164 0,
1165 &type,
1166 NULL,
1167 &bytes);
1168 if (!(res == ERROR_SUCCESS && type == REG_MULTI_SZ)) {
1169 return false;
1170 }
1171 scoped_array<byte> buf(new byte[bytes]);
1172 memset(buf.get(), 0, bytes);
1173 res = ::RegQueryValueEx(key,
1174 L"PendingFileRenameOperations",
1175 0,
1176 NULL,
1177 buf.get(),
1178 &bytes);
1179 if (res != ERROR_SUCCESS) {
1180 return false;
1181 }
1182 const wchar_t* multi_str = reinterpret_cast<const wchar_t*>(buf.get());
1183 size_t count = bytes / sizeof(*multi_str);
1184 const size_t kMaxRegistryValueLen = 1024 * 1024; // 1MB
1185 if (!(count >= 2 &&
1186 count < kMaxRegistryValueLen &&
1187 multi_str[count - 2] == L'\0' &&
1188 multi_str[count - 1] == L'\0')) {
1189 return false;
1190 }
1191 // The file names in the PFR list are prefixed by \??\.
1192 CString file_name = L"\\??\\" + file_name_;
1193 return FindFirstInMultiString(multi_str, count, file_name) != -1;
1194 }
1195
1196 int FileLogWriter::FindFirstInMultiString(const wchar_t* multi_str,
1197 size_t count,
1198 const wchar_t* str) {
1199 const wchar_t* p = multi_str;
1200 size_t i = 0;
1201 while (i < count) {
1202 p = multi_str + i;
1203 if (lstrcmp(p, str) == 0) {
1204 return i;
1205 } else {
1206 size_t len = lstrlen(p);
1207 i += len + 1;
1208 }
1209 }
1210 return -1;
1211 }
1212
1213 void FileLogWriter::OutputMessage(const OutputInfo* output_info) {
1214 if (!initialized_) {
1215 Initialize();
1216 }
1217
1218 if (!valid_) {
1219 return;
1220 }
1221
1222 // Acquire the mutex.
1223 if (!GetMutex()) {
1224 return;
1225 }
1226
1227 // Move to end of file.
1228 DWORD pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END);
1229 int64 stop_gap_file_size = kStopGapLogFileSizeFactor *
1230 static_cast<int64>(max_file_size_);
1231 if (pos >= stop_gap_file_size) {
1232 if (!TruncateLoggingFile()) {
1233 // Logging stops until the log can be archived over since we do not
1234 // want to overfill the disk.
1235 return;
1236 }
1237 }
1238 pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END);
1239
1240 // Write the date, followed by a CRLF
1241 DWORD written_size = 0;
1242 if (output_info->msg1) {
1243 if (log_file_wide_) {
1244 ::WriteFile(log_file_, output_info->msg1,
1245 lstrlen(output_info->msg1) * sizeof(wchar_t), &written_size,
1246 NULL);
1247 } else {
1248 CStringA msg(WideToAnsiDirect(output_info->msg1));
1249 ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size,
1250 NULL);
1251 }
1252 }
1253
1254 if (output_info->msg2) {
1255 if (log_file_wide_) {
1256 ::WriteFile(log_file_, output_info->msg2,
1257 lstrlen(output_info->msg2) * sizeof(wchar_t), &written_size,
1258 NULL);
1259 } else {
1260 CStringA msg(WideToAnsiDirect(output_info->msg2));
1261 ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size,
1262 NULL);
1263 }
1264 }
1265
1266 if (log_file_wide_) {
1267 ::WriteFile(log_file_, L"\r\n", 2 * sizeof(wchar_t), &written_size, NULL);
1268 } else {
1269 ::WriteFile(log_file_, "\r\n", 2, &written_size, NULL);
1270 }
1271
1272 ReleaseMutex();
1273 }
1274
1275 bool FileLogWriter::GetMutex() {
1276 if (!log_file_mutex_) {
1277 return false;
1278 }
1279
1280 DWORD res = ::WaitForSingleObject(log_file_mutex_, kMaxMutexWaitTimeMs);
1281 if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
1282 ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
1283 L"Could not acquire logging mutex %s\n",
1284 proc_name_, log_file_mutex_name_));
1285 valid_ = false;
1286 return false;
1287 }
1288
1289 return true;
1290 }
1291
1292 void FileLogWriter::ReleaseMutex() {
1293 if (log_file_mutex_) {
1294 ::ReleaseMutex(log_file_mutex_);
1295 }
1296 }
1297
1298 // OutputDebugStringLogWriter.
1299 OutputDebugStringLogWriter* OutputDebugStringLogWriter::Create() {
1300 return new OutputDebugStringLogWriter();
1301 }
1302
1303 OutputDebugStringLogWriter::OutputDebugStringLogWriter() {}
1304
1305 OutputDebugStringLogWriter::~OutputDebugStringLogWriter() {
1306 Logging* logger = GetLogging();
1307 if (logger && logger->IsLoggingAlreadyEnabled()) {
1308 // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage);
1309 // OutputMessage(&info);
1310 }
1311 Cleanup();
1312 }
1313
1314 void OutputDebugStringLogWriter::OutputMessage(const OutputInfo* output_info) {
1315 // Combine everything into one string so that messages coming from
1316 // multiple threads don't get interleaved.
1317 ::OutputDebugString(SPRINTF(L"%s%s\n", output_info->msg1, output_info->msg2));
1318 }
1319
1320 // OverrideConfigLogWriter.
1321 OverrideConfigLogWriter* OverrideConfigLogWriter::Create(LogCategory category,
1322 LogLevel level, LogWriter* log_writer, bool force_logging_enabled) {
1323 return new OverrideConfigLogWriter(category,
1324 level,
1325 log_writer,
1326 force_logging_enabled);
1327 }
1328
1329 OverrideConfigLogWriter::OverrideConfigLogWriter(LogCategory category,
1330 LogLevel level,
1331 LogWriter* log_writer,
1332 bool force_logging_enabled)
1333 : category_(category),
1334 level_(level),
1335 log_writer_(log_writer),
1336 force_logging_enabled_(force_logging_enabled) {}
1337
1338 void OverrideConfigLogWriter::Cleanup() {
1339 if (log_writer_) {
1340 delete log_writer_;
1341 }
1342 }
1343
1344 bool OverrideConfigLogWriter::WantsToLogRegardless() const {
1345 return force_logging_enabled_;
1346 }
1347
1348 bool OverrideConfigLogWriter::IsCatLevelEnabled(LogCategory category,
1349 LogLevel level) const {
1350 if (category != category_) {
1351 return false;
1352 }
1353 if (level > level_) {
1354 return false;
1355 }
1356 return true;
1357 }
1358
1359 void OverrideConfigLogWriter::OutputMessage(const OutputInfo* output_info) {
1360 if (log_writer_) {
1361 log_writer_->OutputMessage(output_info);
1362 }
1363 return;
1364 }
1365
1366 } // namespace omaha
1367
1368 #endif // LOGGING
1369
OLDNEW
« no previous file with comments | « base/logging.h ('k') | base/logging/build.scons » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698