| OLD | NEW |
| (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 // logging.h | |
| 17 // | |
| 18 // Tracing and logging system. | |
| 19 // Allows filtering of the log messages based on logging categories and levels. | |
| 20 | |
| 21 #ifndef OMAHA_BASE_LOGGING_H_ | |
| 22 #define OMAHA_BASE_LOGGING_H_ | |
| 23 | |
| 24 #include "omaha/base/constants.h" | |
| 25 #include "omaha/base/synchronized.h" | |
| 26 #include "omaha/base/time.h" | |
| 27 | |
| 28 #ifdef LOGGING | |
| 29 | |
| 30 // Logging levels. | |
| 31 enum LogLevel { | |
| 32 LEVEL_FATALERROR = -3, // crashing fatal error | |
| 33 LEVEL_ERROR = -2, // errors - recoverable but shouldn't happen | |
| 34 LE = -2, | |
| 35 LEVEL_WARNING = -1, // warnings | |
| 36 LW = -1, | |
| 37 L1 = 1, // for aprox. 10 logs per run | |
| 38 L2, // for aprox. 100 logs per run | |
| 39 L3, // for aprox. 1,000 logs per run | |
| 40 L4, // for aprox. 10,000 logs per run | |
| 41 L5, // for aprox. 100,000 logs per run | |
| 42 L6, // for > 1,000,000 logs per run | |
| 43 | |
| 44 // add above | |
| 45 LEVEL_ALL // all errors | |
| 46 }; | |
| 47 | |
| 48 #endif | |
| 49 | |
| 50 namespace omaha { | |
| 51 | |
| 52 #define kDefaultLoggingEnabled 1 | |
| 53 #define kLogConfigFileName MAIN_EXE_BASE_NAME _T(".ini") | |
| 54 #define kDefaultLogFileName MAIN_EXE_BASE_NAME _T(".log") | |
| 55 #define kDefaultLogFileWide 1 | |
| 56 #define kDefaultShowTime 1 | |
| 57 #define kDefaultAppendToFile 1 | |
| 58 | |
| 59 #ifdef _DEBUG | |
| 60 #define kDefaultMaxLogFileSize 0xFFFFFFFF // 4GB | |
| 61 #define kDefaultLogToFile 1 | |
| 62 #define kDefaultLogToOutputDebug 1 | |
| 63 #define kDefaultLogLevel L3 | |
| 64 #else | |
| 65 #define kDefaultMaxLogFileSize 10000000 // 10MB | |
| 66 #define kDefaultLogToFile 0 | |
| 67 #define kDefaultLogToOutputDebug 0 | |
| 68 #define kDefaultLogLevel L1 | |
| 69 #endif | |
| 70 | |
| 71 // Truncates the log file when the size of the log file is this many | |
| 72 // times over the MaxLogFileSize to prevent disk overfill. | |
| 73 #define kStopGapLogFileSizeFactor 10 | |
| 74 | |
| 75 // config file sections | |
| 76 #define kConfigSectionLoggingLevel L"LoggingLevel" | |
| 77 #define kConfigSectionLoggingSettings L"LoggingSettings" | |
| 78 | |
| 79 // config file attributes | |
| 80 #define kConfigAttrEnableLogging L"EnableLogging" | |
| 81 #define kConfigAttrShowTime L"ShowTime" | |
| 82 #define kConfigAttrLogToFile L"LogToFile" | |
| 83 #define kConfigAttrLogFilePath L"LogFilePath" | |
| 84 #define kConfigAttrLogFileWide L"LogFileWide" | |
| 85 #define kConfigAttrLogToOutputDebug L"LogToOutputDebug" | |
| 86 #define kConfigAttrAppendToFile L"AppendToFile" | |
| 87 #define kConfigAttrMaxLogFileSize L"MaxLogFileSize" | |
| 88 | |
| 89 #define kLoggingMutexName kLockPrefix L"logging_mutex" | |
| 90 #define kMaxMutexWaitTimeMs 500 | |
| 91 | |
| 92 // Does not allow messages bigger than 1 MB. | |
| 93 #define kMaxLogMessageSize (1024 * 1024) | |
| 94 | |
| 95 #define kLogSettingsCheckInterval (5 * kSecsTo100ns) | |
| 96 | |
| 97 #define kStartOfLogMessage \ | |
| 98 L"********************* NEW LOG *********************" | |
| 99 #define kEndOfLogMessage \ | |
| 100 L"********************* END LOG *********************" | |
| 101 | |
| 102 // TODO(omaha): Allow these defaults to be overriden in the config file. | |
| 103 #define kMaxLevelToStoreInLogHistory L2 | |
| 104 #define kMaxHistoryBufferSize 1024 | |
| 105 | |
| 106 #ifdef LOGGING | |
| 107 | |
| 108 #define LC_LOG(cat, level, msg) \ | |
| 109 do { \ | |
| 110 omaha::Logging* logger = omaha::GetLogging(); \ | |
| 111 if (logger) { \ | |
| 112 omaha::LoggingHelper(logger, cat, level, \ | |
| 113 logger->IsCatLevelEnabled(cat, level)) msg; \ | |
| 114 } \ | |
| 115 } while (0) | |
| 116 | |
| 117 #define LC_LOG_OPT(cat, level, msg) LC_LOG(cat, level, msg) | |
| 118 | |
| 119 #else | |
| 120 #define LC_LOG(cat, level, msg) ((void)0) | |
| 121 #endif | |
| 122 | |
| 123 #ifdef _DEBUG | |
| 124 #define LC_LOG_DEBUG(cat, level, msg) LC_LOG(cat, level, msg) | |
| 125 #else | |
| 126 #define LC_LOG_DEBUG(cat, level, msg) ((void)0) | |
| 127 #endif | |
| 128 | |
| 129 // Shortcuts for different logging categories - no need to specify the category. | |
| 130 #define CORE_LOG(x, y) LC_LOG_DEBUG(omaha::LC_CORE, x, y) | |
| 131 #define NET_LOG(x, y) LC_LOG_DEBUG(omaha::LC_NET, x, y) | |
| 132 #define PLUGIN_LOG(x, y) LC_LOG_DEBUG(omaha::LC_PLUGIN, x, y) | |
| 133 #define SERVICE_LOG(x, y) LC_LOG_DEBUG(omaha::LC_SERVICE, x, y) | |
| 134 #define SETUP_LOG(x, y) LC_LOG_DEBUG(omaha::LC_SETUP, x, y) | |
| 135 #define SHELL_LOG(x, y) LC_LOG_DEBUG(omaha::LC_SHELL, x, y) | |
| 136 #define UTIL_LOG(x, y) LC_LOG_DEBUG(omaha::LC_UTIL, x, y) | |
| 137 | |
| 138 #define OPT_LOG(x, y) LC_LOG_OPT(omaha::LC_OPT, x, y) | |
| 139 #define REPORT_LOG(x, y) LC_LOG_OPT(omaha::LC_REPORT, x, y) | |
| 140 | |
| 141 #ifdef LOGGING | |
| 142 | |
| 143 // Logging components. | |
| 144 // Maximum 32 categories unless mask is increased to 64 bits. | |
| 145 enum LogCategory { | |
| 146 LC_LOGGING = 0, | |
| 147 | |
| 148 // ADD BELOW - AND REMEMBER: | |
| 149 // - Add a line to the LogCategoryNames table in logging.cc!!! | |
| 150 // - Add to C:\GoogleUpdate.ini. | |
| 151 | |
| 152 LC_UTIL, | |
| 153 LC_SETUP, | |
| 154 LC_SHELL, | |
| 155 LC_CORE, | |
| 156 LC_JS, | |
| 157 LC_PLUGIN, | |
| 158 LC_SERVICE, | |
| 159 LC_OPT, | |
| 160 LC_NET, | |
| 161 LC_REPORT, | |
| 162 | |
| 163 // ADD ABOVE | |
| 164 | |
| 165 LC_MAX_CAT | |
| 166 }; | |
| 167 | |
| 168 #define kCatEnabledField L"Enabled" | |
| 169 #define kCatLevelField L"Level" | |
| 170 | |
| 171 struct CategoryInfo { | |
| 172 bool enabled; | |
| 173 LogLevel log_level; | |
| 174 }; | |
| 175 | |
| 176 // If you want to log anything else, add it to this structure. This structure | |
| 177 // basically says that each logged message is composed of two parts, and they | |
| 178 // are output one after the other. Intended to be used for a message "prefix" | |
| 179 // and the message itself (the prefix can contain the component name, the time, | |
| 180 // and any other logging system boilerplate, while the message is supplied by | |
| 181 // the component). (This basically saves having to copy a variable length - | |
| 182 // possibly very large - message just to tack it onto the end of the message | |
| 183 // prefix.) | |
| 184 struct OutputInfo { | |
| 185 LogCategory category; | |
| 186 LogLevel level; | |
| 187 const wchar_t* msg1; | |
| 188 const wchar_t* msg2; | |
| 189 | |
| 190 OutputInfo(LogCategory cat, LogLevel log_level, | |
| 191 const wchar_t* m1, const wchar_t* m2) | |
| 192 : category(cat), | |
| 193 level(log_level), | |
| 194 msg1(m1), | |
| 195 msg2(m2) {} | |
| 196 }; | |
| 197 | |
| 198 // The LogWriter - can decide whether to process message or not, then | |
| 199 // will process it. Actually, the message is processed if either a) the | |
| 200 // individual LogWriter wants to process it or b) it is marked as processable | |
| 201 // by settings in config ini. | |
| 202 // | |
| 203 // Included LogWriters: | |
| 204 // OutputDebugStringLogWriter - Logs to OutputDebugString() API | |
| 205 // FileLogWriter - Logs to a file | |
| 206 // OverrideConfigLogWriter - Overrides the level settings of a | |
| 207 // particular category, uses another writer to actually do the writing. | |
| 208 // Used, e.g., in installer to force SETUP_LOG messages to go to a file | |
| 209 // tr_setup_log.info even if the there is no trconfig.ini file. | |
| 210 // | |
| 211 // Not included LogWriters: | |
| 212 // StdLogWriter - Logs to stdout or stderr | |
| 213 // SubmitToGoogleLogWriter - When done logging submits the log file to | |
| 214 // Google's status-receiving server | |
| 215 class LogWriter { | |
| 216 protected: | |
| 217 LogWriter(); | |
| 218 virtual void Cleanup(); | |
| 219 public: | |
| 220 virtual ~LogWriter(); | |
| 221 | |
| 222 // Returns true if this Logging object wants to log even if the global | |
| 223 // "enable logging" flag is off. Useful for always creating a log, e.g., an | |
| 224 // install log, even without a GoogleUpdate.ini. | |
| 225 virtual bool WantsToLogRegardless() const; | |
| 226 | |
| 227 // Returns true if this Logging object wants to handle the message, | |
| 228 // regardless of other settings. | |
| 229 virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const; | |
| 230 | |
| 231 virtual void OutputMessage(const OutputInfo* output_info); | |
| 232 | |
| 233 // Registers and unregisters this LogWriter with the Logging system. When | |
| 234 // registered, the Logging class assumes ownership. | |
| 235 bool Register(); | |
| 236 bool Unregister(); | |
| 237 | |
| 238 private: | |
| 239 DISALLOW_EVIL_CONSTRUCTORS(LogWriter); | |
| 240 }; | |
| 241 | |
| 242 // A LogWriter that writes to a named file. | |
| 243 class FileLogWriter : public LogWriter { | |
| 244 protected: | |
| 245 FileLogWriter(const wchar_t* file_name, bool append); | |
| 246 ~FileLogWriter(); | |
| 247 virtual void Cleanup(); | |
| 248 | |
| 249 public: | |
| 250 static FileLogWriter* Create(const wchar_t* file_name, bool append); | |
| 251 virtual void OutputMessage(const OutputInfo* output_info); | |
| 252 | |
| 253 private: | |
| 254 void Initialize(); | |
| 255 bool CreateLoggingMutex(); | |
| 256 bool CreateLoggingFile(); | |
| 257 bool ArchiveLoggingFile(); | |
| 258 bool TruncateLoggingFile(); | |
| 259 bool GetMutex(); | |
| 260 void ReleaseMutex(); | |
| 261 | |
| 262 // Returns true if archiving of the log file is pending a computer restart. | |
| 263 bool IsArchivePending(); | |
| 264 | |
| 265 // Returns the first position of str inside of a MULTI_SZ of count characters | |
| 266 // including the terminating zeros. | |
| 267 static int FindFirstInMultiString(const wchar_t* multi_str, | |
| 268 size_t count, | |
| 269 const wchar_t* str); | |
| 270 | |
| 271 uint32 max_file_size_; | |
| 272 bool initialized_; | |
| 273 bool valid_; | |
| 274 bool append_; | |
| 275 bool log_file_wide_; | |
| 276 CString log_file_mutex_name_; | |
| 277 HANDLE log_file_mutex_; | |
| 278 CString file_name_; | |
| 279 HANDLE log_file_; | |
| 280 CString proc_name_; | |
| 281 | |
| 282 friend class FileLogWriterTest; | |
| 283 | |
| 284 DISALLOW_EVIL_CONSTRUCTORS(FileLogWriter); | |
| 285 }; | |
| 286 | |
| 287 // A LogWriter that uses OutputDebugString() to write messages. | |
| 288 class OutputDebugStringLogWriter : public LogWriter { | |
| 289 protected: | |
| 290 OutputDebugStringLogWriter(); | |
| 291 ~OutputDebugStringLogWriter(); | |
| 292 public: | |
| 293 static OutputDebugStringLogWriter* Create(); | |
| 294 virtual void OutputMessage(const OutputInfo* info); | |
| 295 private: | |
| 296 DISALLOW_EVIL_CONSTRUCTORS(OutputDebugStringLogWriter); | |
| 297 }; | |
| 298 | |
| 299 // A LogWriter that overrides the settings in trconfig.ini and sends messages | |
| 300 // to another LogWriter. Takes ownership of the other LogWriter. | |
| 301 class OverrideConfigLogWriter : public LogWriter { | |
| 302 protected: | |
| 303 OverrideConfigLogWriter(LogCategory category, LogLevel level, | |
| 304 LogWriter* log_writer, bool force_logging_enabled); | |
| 305 virtual void Cleanup(); | |
| 306 public: | |
| 307 static OverrideConfigLogWriter* Create(LogCategory category, LogLevel level, | |
| 308 LogWriter* log_writer, bool force_logging_enabled); | |
| 309 virtual bool WantsToLogRegardless() const; | |
| 310 virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const; | |
| 311 virtual void OutputMessage(const OutputInfo* output_info); | |
| 312 private: | |
| 313 LogCategory category_; | |
| 314 LogLevel level_; | |
| 315 LogWriter* log_writer_; | |
| 316 bool force_logging_enabled_; | |
| 317 DISALLOW_EVIL_CONSTRUCTORS(OverrideConfigLogWriter); | |
| 318 }; | |
| 319 | |
| 320 // This log writer outputs to Event Tracing for Windows. | |
| 321 class EtwLogWriter; | |
| 322 | |
| 323 // The Logging class - Singleton class | |
| 324 // Fine-grain logging based on categories and levels. | |
| 325 // Can log to a file, stdout or debugger. | |
| 326 class Logging { | |
| 327 public: | |
| 328 // constructor | |
| 329 Logging(); | |
| 330 | |
| 331 // destructor | |
| 332 ~Logging(); | |
| 333 | |
| 334 // Enables/disables the logging mechanism. Allows turning logging on/off | |
| 335 // in mid-run. Returns true for success (not for 'logging enabled'). | |
| 336 void EnableLogging(); | |
| 337 void DisableLogging(); | |
| 338 | |
| 339 // Checks if logging is enabled - and updates logging settings from the | |
| 340 // configuration file every kLogSettingsCheckInterval seconds | |
| 341 bool IsLoggingEnabled(); | |
| 342 | |
| 343 // Checks if logging is already enabled. It does not try to enable it. | |
| 344 bool IsLoggingAlreadyEnabled() const; | |
| 345 | |
| 346 // Overrides the config file settings for showing the time stamps. | |
| 347 void ForceShowTimestamp(bool force_show_time); | |
| 348 | |
| 349 // Checks if logging is enabled for a given category and level. | |
| 350 DWORD IsCatLevelEnabled(LogCategory category, LogLevel level); | |
| 351 LogLevel GetCatLevel(LogCategory category) const; | |
| 352 | |
| 353 // Logs a message. | |
| 354 void LogMessage(LogCategory cat, LogLevel level, const wchar_t* fmt, ...); | |
| 355 void LogMessageVA(LogCategory cat, LogLevel level, const wchar_t* fmt, | |
| 356 va_list args); | |
| 357 | |
| 358 // Retrieves the default location of the log directory. | |
| 359 CString GetDefaultLogDirectory() const; | |
| 360 | |
| 361 // Computes and returns the complete path of the log file. | |
| 362 CString GetLogFilePath() const; | |
| 363 | |
| 364 // Retrieves in-memory history buffer. | |
| 365 CString GetHistory(); | |
| 366 | |
| 367 // Returns the file path of the current GoogleUpdate.ini. | |
| 368 CString GetCurrentConfigurationFilePath() const; | |
| 369 | |
| 370 const CString& proc_name() const { return proc_name_; } | |
| 371 | |
| 372 bool IsCategoryEnabledForBuffering(LogCategory cat); | |
| 373 private: | |
| 374 bool InternalInitialize(); | |
| 375 void InternalLogMessageMaskedVA(DWORD writer_mask, | |
| 376 LogCategory cat, | |
| 377 LogLevel level, | |
| 378 CString* log_buffer, | |
| 379 CString* prefix, | |
| 380 const wchar_t* fmt, | |
| 381 va_list args); | |
| 382 | |
| 383 friend class LoggingHelper; | |
| 384 void LogMessageMaskedVA(DWORD writer_mask, LogCategory cat, LogLevel level, | |
| 385 const wchar_t* fmt, va_list args); | |
| 386 | |
| 387 // Stores log message in in-memory history buffer. | |
| 388 void StoreInHistory(const OutputInfo* output_info); | |
| 389 | |
| 390 // Appends string to in-memory history buffer. | |
| 391 void AppendToHistory(const wchar_t* msg); | |
| 392 | |
| 393 // Initializes the logging engine. Harmless to call multiple times. | |
| 394 bool InitializeLogging(); | |
| 395 | |
| 396 // Configures/unconfigures the log writers for the current settings. That | |
| 397 // is, given the current settings from GoogleUpdate.ini, either initializes | |
| 398 // and registers the file-out and debug-out logwriters, or unregisters them. | |
| 399 void ConfigureETWLogWriter(); | |
| 400 void ConfigureFileLogWriter(); | |
| 401 void ConfigureDebugOutLogWriter(); | |
| 402 bool ConfigureLogging(); | |
| 403 void UnconfigureLogging(); | |
| 404 | |
| 405 void UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat); | |
| 406 void ReadLoggingSettings(); | |
| 407 | |
| 408 // Returns the primary file path of the GoogleUpdate.ini. | |
| 409 CString GetConfigurationFilePath() const; | |
| 410 | |
| 411 // Returns the alternate file path of the GoogleUpdate.ini. | |
| 412 CString GetAltConfigurationFilePath() const; | |
| 413 | |
| 414 public: | |
| 415 | |
| 416 // Passes the messages along to other OutputMessage() | |
| 417 void OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level, | |
| 418 const wchar_t* msg1, const wchar_t* msg2); | |
| 419 | |
| 420 // Broadcasts the message to each LogWriter. | |
| 421 // It should be private but the function we want to be able to use this, | |
| 422 // debugASSERT is extern "C" and thus can't be declared a friend of | |
| 423 // Logging. | |
| 424 void OutputMessage(DWORD writer_mask, const OutputInfo* output_info); | |
| 425 | |
| 426 private: | |
| 427 | |
| 428 CategoryInfo category_list_[LC_MAX_CAT]; | |
| 429 | |
| 430 // Checks if logging is initialized. | |
| 431 bool logging_initialized_; | |
| 432 | |
| 433 // Is logging in the process of initializing? | |
| 434 bool is_initializing_; | |
| 435 | |
| 436 // The logging process name including the calling module. | |
| 437 CString proc_name_; | |
| 438 | |
| 439 // Serializes changing logging init/uninit/enable/disable status. | |
| 440 LLock lock_; | |
| 441 | |
| 442 // Bunch of settings from the config .ini file. | |
| 443 bool logging_enabled_; // Checks if logging is enabled. | |
| 444 bool force_show_time_; | |
| 445 bool show_time_; | |
| 446 bool log_to_file_; | |
| 447 CString log_file_name_; | |
| 448 bool log_to_debug_out_; | |
| 449 bool append_to_file_; | |
| 450 | |
| 451 // Signals the logging system is shutting down. | |
| 452 bool logging_shutdown_; | |
| 453 | |
| 454 // Checkpoint time for dynamic category updates. | |
| 455 time64 g_last_category_check_time; | |
| 456 | |
| 457 // The file path of the optional ini file which defines the logging | |
| 458 // configuration. | |
| 459 CString config_file_path_; | |
| 460 | |
| 461 private: | |
| 462 bool InternalRegisterWriter(LogWriter* log_writer); | |
| 463 bool InternalUnregisterWriter(LogWriter* log_writer); | |
| 464 | |
| 465 public: | |
| 466 bool RegisterWriter(LogWriter* log_writer); | |
| 467 bool UnregisterWriter(LogWriter* log_writer); | |
| 468 enum { all_writers_mask = -1 }; | |
| 469 | |
| 470 private: | |
| 471 enum { max_writers = 15 }; | |
| 472 int num_writers_; | |
| 473 LogWriter* writers_[max_writers]; | |
| 474 | |
| 475 LogWriter* file_log_writer_; | |
| 476 LogWriter* debug_out_writer_; | |
| 477 LogWriter* etw_log_writer_; | |
| 478 | |
| 479 friend class HistoryTest; | |
| 480 | |
| 481 DISALLOW_EVIL_CONSTRUCTORS(Logging); | |
| 482 }; | |
| 483 | |
| 484 // In order to make the logging macro LC_LOG work out we need to pass a | |
| 485 // parameter (the mask of loggers to write to) (*) to the actual logging | |
| 486 // method. However, the last parameter to the macro LC_LOG has its own | |
| 487 // parenthesis - it encloses multiple expressions (a format string and | |
| 488 // arguments). So this function object is used as an intermediary in order to | |
| 489 // hold the writer mask. | |
| 490 // | |
| 491 // (*) The mask needs to be transferred separately because we want to keep the | |
| 492 // LC_LOG structure of asking if the message is going to be logged before | |
| 493 // evaluating the arguments, and we can't store it in the singleton Logging | |
| 494 // object - wouldn't be thread-safe. | |
| 495 | |
| 496 class LoggingHelper { | |
| 497 public: | |
| 498 LoggingHelper(Logging* logger, LogCategory cat, | |
| 499 LogLevel level, DWORD writer_mask) | |
| 500 : logger_(logger), | |
| 501 category_(cat), | |
| 502 level_(level), | |
| 503 writer_mask_(writer_mask) {} | |
| 504 | |
| 505 void operator()(const wchar_t* fmt, ...) { | |
| 506 va_list args; | |
| 507 va_start(args, fmt); | |
| 508 logger_->LogMessageMaskedVA(writer_mask_, category_, level_, fmt, args); | |
| 509 va_end(args); | |
| 510 } | |
| 511 | |
| 512 private: | |
| 513 Logging* logger_; | |
| 514 DWORD writer_mask_; | |
| 515 LogLevel level_; | |
| 516 LogCategory category_; | |
| 517 | |
| 518 DISALLOW_EVIL_CONSTRUCTORS(LoggingHelper); | |
| 519 }; | |
| 520 | |
| 521 // Getter for the Logging singleton class. | |
| 522 Logging* GetLogging(); | |
| 523 | |
| 524 #endif // LOGGING | |
| 525 | |
| 526 } // namespace omaha | |
| 527 | |
| 528 #endif // OMAHA_BASE_LOGGING_H_ | |
| OLD | NEW |