fune/xpcom/base/LogModulePrefWatcher.cpp
Julien Wajsberg bafb803cd9 Bug 1894703 - When the modules preference is removed or absent, use the MOZ_LOG env variable if present r=padenot
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
2024-05-23 08:14:17 +00:00

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