summaryrefslogtreecommitdiffstats
path: root/xpcom/base/LogModulePrefWatcher.cpp
blob: bd06660533d40742efba486ddf0b9a13c4fc3a2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* -*- 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 "nsString.h"
#include "nsXULAppAPI.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";

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);
    }
    return;
  }

  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