Bug 1174785 - Part 1: Add LogModule, LogModuleManager, and LazyLogModule. r=froydnj

The logging interface is moved to xpcom/base, a LogModule wrapper for PR_Log is
added, a thread-safe LogModuleManager is added, and a LazyLogModule class used
to lazily load log modules in a thread-safe manner is added.
This commit is contained in:
Eric Rahm 2015-10-19 12:22:11 -07:00
parent a1e1611abe
commit 1e2da43ec5
7 changed files with 196 additions and 1 deletions

View File

@ -1254,6 +1254,8 @@ XRE_XPCShellMain(int argc, char** argv, char** envp)
NS_LogInit();
mozilla::LogModule::Init();
// A initializer to initialize histogram collection
// used by telemetry.
UniquePtr<base::StatisticsRecorder> telStats =

View File

@ -350,6 +350,8 @@ XRE_InitChildProcess(int aArgc,
// NB: This must be called before profiler_init
NS_LogInit();
mozilla::LogModule::Init();
char aLocal;
profiler_init(&aLocal);

100
xpcom/base/Logging.cpp Normal file
View File

@ -0,0 +1,100 @@
/* -*- 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 "mozilla/Logging.h"
#include <algorithm>
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticPtr.h"
#include "nsClassHashtable.h"
// NB: Initial amount determined by auditing the codebase for the total amount
// of unique module names and padding up to the next power of 2.
const uint32_t kInitialModuleCount = 256;
namespace mozilla {
/**
* Safely converts an integer into a valid LogLevel.
*/
LogLevel
Clamp(int32_t aLevel)
{
aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
return static_cast<LogLevel>(aLevel);
}
class LogModuleManager
{
public:
LogModuleManager()
: mModulesLock("logmodules")
, mModules(kInitialModuleCount)
{
}
~LogModuleManager()
{
// NB: mModules owns all of the log modules, they will get destroyed by
// its destructor.
}
LogModule* CreateOrGetModule(const char* aName)
{
OffTheBooksMutexAutoLock guard(mModulesLock);
LogModule* module = nullptr;
if (!mModules.Get(aName, &module)) {
// Create the PRLogModule, this will read any env vars that set the log
// level ahead of time. The module is held internally by NSPR, so it's
// okay to drop the pointer when leaving this scope.
PRLogModuleInfo* prModule = PR_NewLogModule(aName);
// NSPR does not impose a restriction on the values that log levels can
// be. LogModule uses the LogLevel enum class so we must clamp the value
// to a max of Verbose.
LogLevel logLevel = Clamp(prModule->level);
module = new LogModule(logLevel);
mModules.Put(aName, module);
}
return module;
}
private:
OffTheBooksMutex mModulesLock;
nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
};
StaticAutoPtr<LogModuleManager> sLogModuleManager;
LogModule*
LogModule::Get(const char* aName)
{
// This is just a pass through to the LogModuleManager so
// that the LogModuleManager implementation can be kept internal.
MOZ_ASSERT(sLogModuleManager != nullptr);
return sLogModuleManager->CreateOrGetModule(aName);
}
void
LogModule::Init()
{
// NB: This method is not threadsafe; it is expected to be called very early
// in startup prior to any other threads being run.
if (sLogModuleManager) {
// Already initialized.
return;
}
// NB: We intentionally do not register for ClearOnShutdown as that happens
// before all logging is complete. And, yes, that means we leak, but
// we're doing that intentionally.
sLogModuleManager = new LogModuleManager();
}
} // namespace mozilla

View File

@ -10,6 +10,8 @@
#include "prlog.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Likely.h"
// This file is a placeholder for a replacement to the NSPR logging framework
// that is defined in prlog.h. Currently it is just a pass through, but as
@ -43,6 +45,87 @@ enum class LogLevel {
Verbose,
};
class LogModule
{
public:
/**
* Retrieves the module with the given name. If it does not already exist
* it will be created.
*
* @param aName The name of the module.
* @return A log module for the given name. This may be shared.
*/
#if !defined(MOZILLA_XPCOMRT_API)
static LogModule* Get(const char* aName);
#else
// For simplicity, libxpcomrt doesn't supoort logging.
static LogModule* Get(const char* aName) { return nullptr; }
#endif
static void Init();
/**
* Indicates whether or not the given log level is enabled.
*/
bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
/**
* Retrieves the log module's current level.
*/
LogLevel Level() const { return mLevel; }
private:
friend class LogModuleManager;
explicit LogModule(LogLevel aLevel) : mLevel(aLevel) {}
LogModule(LogModule&) = delete;
LogModule& operator=(const LogModule&) = delete;
Atomic<LogLevel, Relaxed> mLevel;
};
/**
* Helper class that lazy loads the given log module. This is safe to use for
* declaring static references to log modules and can be used as a replacement
* for accessing a LogModule directly.
*
* Example usage:
* static LazyLogModule sLayoutLog("layout");
*
* void Foo() {
* MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
* }
*/
class LazyLogModule final
{
public:
explicit MOZ_CONSTEXPR LazyLogModule(const char* aLogName)
: mLogName(aLogName)
, mLog(nullptr)
{
}
operator LogModule*()
{
// NB: The use of an atomic makes the reading and assignment of mLog
// thread-safe. There is a small chance that mLog will be set more
// than once, but that's okay as it will be set to the same LogModule
// instance each time. Also note LogModule::Get is thread-safe.
LogModule* tmp = mLog;
if (MOZ_UNLIKELY(!tmp)) {
tmp = LogModule::Get(mLogName);
mLog = tmp;
}
return tmp;
}
private:
const char* const mLogName;
Atomic<LogModule*, ReleaseAcquire> mLog;
};
namespace detail {
inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
@ -50,6 +133,11 @@ inline bool log_test(const PRLogModuleInfo* module, LogLevel level) {
return module && module->level >= static_cast<int>(level);
}
inline bool log_test(const LogModule* module, LogLevel level) {
MOZ_ASSERT(level != LogLevel::Disabled);
return module && module->ShouldLog(level);
}
} // namespace detail
} // namespace mozilla

View File

@ -83,6 +83,7 @@ EXPORTS.mozilla += [
'HoldDropJSObjects.h',
'JSObjectHolder.h',
'LinuxUtils.h',
'Logging.h',
'nsMemoryInfoDumper.h',
'OwningNonNull.h',
'StaticMutex.h',
@ -107,6 +108,7 @@ UNIFIED_SOURCES += [
'ErrorNames.cpp',
'HoldDropJSObjects.cpp',
'JSObjectHolder.cpp',
'Logging.cpp',
'nsConsoleMessage.cpp',
'nsConsoleService.cpp',
'nsCycleCollector.cpp',

View File

@ -498,6 +498,8 @@ NS_InitXPCOM2(nsIServiceManager** aResult,
NS_LogInit();
mozilla::LogModule::Init();
JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation);
char aLocal;

View File

@ -75,7 +75,6 @@ EXPORTS.mozilla += [
'FileUtils.h',
'GenericFactory.h',
'IntentionalCrash.h',
'Logging.h',
'Monitor.h',
'Mutex.h',
'Observer.h',