forked from mirrors/gecko-dev
The previous patchset got backed out. Indeed it broke MOZ_LOG using the environment variable. The reason is that the preference watcher was called with an empty value after the log modules were set by the enviroment variable handler at init time. To fix this, now we read the environment variable as well when the pref is handled. If the pref is empty but the environment variable is set, the environment variable is used. This also fixes an issue where profiler stacks wouldn't be disabled. Differential Revision: https://phabricator.services.mozilla.com/D210785
193 lines
6.8 KiB
C++
193 lines
6.8 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "LogModulePrefWatcher.h"
|
|
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/Services.h"
|
|
#include "nsIObserverService.h"
|
|
#include "NSPRLogModulesParser.h"
|
|
#include "nsString.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "prenv.h"
|
|
#include "base/process_util.h"
|
|
|
|
static const char kLoggingPrefPrefix[] = "logging.";
|
|
static const char kLoggingConfigPrefPrefix[] = "logging.config";
|
|
static const int kLoggingConfigPrefixLen = sizeof(kLoggingConfigPrefPrefix) - 1;
|
|
static const char kLoggingPrefClearOnStartup[] =
|
|
"logging.config.clear_on_startup";
|
|
static const char kLoggingPrefLogFile[] = "logging.config.LOG_FILE";
|
|
static const char kLoggingPrefAddTimestamp[] = "logging.config.add_timestamp";
|
|
static const char kLoggingPrefSync[] = "logging.config.sync";
|
|
static const char kLoggingPrefStacks[] = "logging.config.profilerstacks";
|
|
static const char kLoggingPrefLogModules[] = "logging.config.modules";
|
|
|
|
namespace mozilla {
|
|
|
|
NS_IMPL_ISUPPORTS(LogModulePrefWatcher, nsIObserver)
|
|
|
|
/**
|
|
* Resets all the preferences in the logging. branch
|
|
* This is needed because we may crash while logging, and this would cause us
|
|
* to log after restarting as well.
|
|
*
|
|
* If logging after restart is desired, set the logging.config.clear_on_startup
|
|
* pref to false, or use the MOZ_LOG_FILE and MOZ_LOG_MODULES env vars.
|
|
*/
|
|
static void ResetExistingPrefs() {
|
|
nsTArray<nsCString> names;
|
|
nsresult rv =
|
|
Preferences::GetRootBranch()->GetChildList(kLoggingPrefPrefix, names);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
for (auto& name : names) {
|
|
// Clearing the pref will cause it to reload, thus resetting the log level
|
|
Preferences::ClearUser(name.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the log level from the given pref and updates the corresponding
|
|
* LogModule.
|
|
*/
|
|
static void LoadPrefValue(const char* aName) {
|
|
LogLevel logLevel = LogLevel::Disabled;
|
|
|
|
nsresult rv;
|
|
int32_t prefLevel = 0;
|
|
nsAutoCString prefValue;
|
|
|
|
if (strncmp(aName, kLoggingConfigPrefPrefix, kLoggingConfigPrefixLen) == 0) {
|
|
nsAutoCString prefName(aName);
|
|
|
|
if (prefName.EqualsLiteral(kLoggingPrefLogFile)) {
|
|
rv = Preferences::GetCString(aName, prefValue);
|
|
// The pref was reset. Clear the user file.
|
|
if (NS_FAILED(rv) || prefValue.IsEmpty()) {
|
|
LogModule::SetLogFile(nullptr);
|
|
return;
|
|
}
|
|
|
|
// If the pref value doesn't have a PID placeholder, append it to the end.
|
|
if (!strstr(prefValue.get(), MOZ_LOG_PID_TOKEN)) {
|
|
prefValue.AppendLiteral(MOZ_LOG_PID_TOKEN);
|
|
}
|
|
|
|
LogModule::SetLogFile(prefValue.BeginReading());
|
|
} else if (prefName.EqualsLiteral(kLoggingPrefAddTimestamp)) {
|
|
bool addTimestamp = Preferences::GetBool(aName, false);
|
|
LogModule::SetAddTimestamp(addTimestamp);
|
|
} else if (prefName.EqualsLiteral(kLoggingPrefSync)) {
|
|
bool sync = Preferences::GetBool(aName, false);
|
|
LogModule::SetIsSync(sync);
|
|
} else if (prefName.EqualsLiteral(kLoggingPrefStacks)) {
|
|
bool captureStacks = Preferences::GetBool(aName, false);
|
|
LogModule::SetCaptureStacks(captureStacks);
|
|
} else if (prefName.EqualsLiteral(kLoggingPrefLogModules)) {
|
|
// The content of the preference will be parsed as a MOZ_LOG string, then
|
|
// the corresponding log modules (if any) will be enabled, others will be
|
|
// disabled.
|
|
LogModule::DisableModules();
|
|
LogModule::SetCaptureStacks(false);
|
|
|
|
const char* modulesFromEnv = PR_GetEnv("MOZ_LOG");
|
|
const bool hasModulesEnv = modulesFromEnv && modulesFromEnv[0];
|
|
|
|
rv = Preferences::GetCString(aName, prefValue);
|
|
const bool hasModulesPref = NS_SUCCEEDED(rv) && !prefValue.IsEmpty();
|
|
|
|
if (hasModulesEnv || hasModulesPref) {
|
|
NSPRLogModulesParser(
|
|
hasModulesPref ? prefValue.BeginReading() : modulesFromEnv,
|
|
[](const char* aName, LogLevel aLevel, int32_t aValue) mutable {
|
|
// Only the special string "profilerstacks" is taken into account,
|
|
// because we're especially interested in usage with the Firefox
|
|
// Profiler.
|
|
if (strcmp(aName, "profilerstacks") == 0) {
|
|
LogModule::SetCaptureStacks(true);
|
|
} else {
|
|
LogModule::Get(aName)->SetLevel(aLevel);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Preferences::GetInt(aName, &prefLevel) == NS_OK) {
|
|
logLevel = ToLogLevel(prefLevel);
|
|
} else if (Preferences::GetCString(aName, prefValue) == NS_OK) {
|
|
if (prefValue.LowerCaseEqualsLiteral("error")) {
|
|
logLevel = LogLevel::Error;
|
|
} else if (prefValue.LowerCaseEqualsLiteral("warning")) {
|
|
logLevel = LogLevel::Warning;
|
|
} else if (prefValue.LowerCaseEqualsLiteral("info")) {
|
|
logLevel = LogLevel::Info;
|
|
} else if (prefValue.LowerCaseEqualsLiteral("debug")) {
|
|
logLevel = LogLevel::Debug;
|
|
} else if (prefValue.LowerCaseEqualsLiteral("verbose")) {
|
|
logLevel = LogLevel::Verbose;
|
|
}
|
|
}
|
|
|
|
const char* moduleName = aName + strlen(kLoggingPrefPrefix);
|
|
LogModule::Get(moduleName)->SetLevel(logLevel);
|
|
}
|
|
|
|
static void LoadExistingPrefs() {
|
|
nsIPrefBranch* root = Preferences::GetRootBranch();
|
|
if (!root) {
|
|
return;
|
|
}
|
|
|
|
nsTArray<nsCString> names;
|
|
nsresult rv = root->GetChildList(kLoggingPrefPrefix, names);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
for (auto& name : names) {
|
|
LoadPrefValue(name.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
LogModulePrefWatcher::LogModulePrefWatcher() = default;
|
|
|
|
void LogModulePrefWatcher::RegisterPrefWatcher() {
|
|
RefPtr<LogModulePrefWatcher> prefWatcher = new LogModulePrefWatcher();
|
|
Preferences::AddStrongObserver(prefWatcher, kLoggingPrefPrefix);
|
|
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (observerService && XRE_IsParentProcess()) {
|
|
observerService->AddObserver(prefWatcher,
|
|
"browser-delayed-startup-finished", false);
|
|
}
|
|
|
|
LoadExistingPrefs();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
LogModulePrefWatcher::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData) {
|
|
if (strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic) == 0) {
|
|
NS_LossyConvertUTF16toASCII prefName(aData);
|
|
LoadPrefValue(prefName.get());
|
|
} else if (strcmp("browser-delayed-startup-finished", aTopic) == 0) {
|
|
bool clear = Preferences::GetBool(kLoggingPrefClearOnStartup, true);
|
|
if (clear) {
|
|
ResetExistingPrefs();
|
|
}
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (observerService) {
|
|
observerService->RemoveObserver(this, "browser-delayed-startup-finished");
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace mozilla
|