Skip to content
Permalink
Browse files
Add Logger observer and helper class
https://bugs.webkit.org/show_bug.cgi?id=176106

Reviewed by Andy Estes.

Source/WebCore/PAL:

* pal/Logger.h:
(PAL::LogArgument::toString): Add new variants.
(PAL::Logger::Observer::~Observer):
(PAL::Logger::logAlways const): Constify.
(PAL::Logger::error const): Ditto.
(PAL::Logger::warning const): Ditto.
(PAL::Logger::notice const): Ditto.
(PAL::Logger::info const): Ditto.
(PAL::Logger::debug const): Ditto.
(PAL::Logger::willLog const): Ditto.
(PAL::Logger::MethodAndPointer::MethodAndPointer): Add class name, make object pointer const.
(PAL::Logger::addObserver): New.
(PAL::Logger::removeObserver): New.
(PAL::Logger::Logger):
(PAL::Logger::log):
(PAL::Logger::observers):
(PAL::LogHelper::willLog const):
(PAL::LogArgument<Logger::MethodAndPointer>::toString):

Tools:

* TestWebKitAPI/Tests/WebCore/Logging.cpp:
(TestWebKitAPI::LoggingTest::LoggingTest):
(TestWebKitAPI::LoggingTest::Logger::create):
(TestWebKitAPI::TEST_F):
(TestWebKitAPI::LogObserver::log):
(TestWebKitAPI::LogObserver::channel const):


Canonical link: https://commits.webkit.org/192795@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221388 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
eric-carlson committed Aug 30, 2017
1 parent 9c3cc8c commit 8241cd7b0b2a99a4a0db053a7d37fcb195795074
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 19 deletions.
@@ -1,3 +1,29 @@
2017-08-30 Eric Carlson <eric.carlson@apple.com>

Add Logger observer and helper class
https://bugs.webkit.org/show_bug.cgi?id=176106

Reviewed by Andy Estes.

* pal/Logger.h:
(PAL::LogArgument::toString): Add new variants.
(PAL::Logger::Observer::~Observer):
(PAL::Logger::logAlways const): Constify.
(PAL::Logger::error const): Ditto.
(PAL::Logger::warning const): Ditto.
(PAL::Logger::notice const): Ditto.
(PAL::Logger::info const): Ditto.
(PAL::Logger::debug const): Ditto.
(PAL::Logger::willLog const): Ditto.
(PAL::Logger::MethodAndPointer::MethodAndPointer): Add class name, make object pointer const.
(PAL::Logger::addObserver): New.
(PAL::Logger::removeObserver): New.
(PAL::Logger::Logger):
(PAL::Logger::log):
(PAL::Logger::observers):
(PAL::LogHelper::willLog const):
(PAL::LogArgument<Logger::MethodAndPointer>::toString):

2017-08-30 Andy Estes <aestes@apple.com>

[Mac] Upstream Accessibility-related WebKitSystemInterface functions
@@ -37,24 +37,33 @@ namespace PAL {

template<typename T>
struct LogArgument {
template<typename U = T> static typename std::enable_if<std::is_same<U, bool>::value, String>::type toString(bool argument) { return argument ? ASCIILiteral("true") : ASCIILiteral("false"); }
template<typename U = T> static typename std::enable_if<std::is_same<U, int>::value, String>::type toString(int argument) { return String::number(argument); }
template<typename U = T> static typename std::enable_if<std::is_same<U, float>::value, String>::type toString(float argument) { return String::number(argument); }
template<typename U = T> static typename std::enable_if<std::is_same<U, double>::value, String>::type toString(double argument) { return String::number(argument); }
template<typename U = T> static typename std::enable_if<std::is_same<U, String>::value, String>::type toString(String argument) { return argument; }
template<typename U = T> static typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, AtomicString>::value, String>::type toString(AtomicString argument) { return argument.string(); }
template<typename U = T> static typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, String>::value, String>::type toString(String argument) { return argument; }
template<typename U = T> static typename std::enable_if<std::is_same<U, const char*>::value, String>::type toString(const char* argument) { return String(argument); }
template<size_t length> static String toString(const char (&argument)[length]) { return String(argument); }
};

class Logger : public RefCounted<Logger> {
WTF_MAKE_NONCOPYABLE(Logger);
public:

class Observer {
public:
virtual ~Observer() { }
virtual void didLogMessage(const WTFLogChannel&, const String&) = 0;
};

static Ref<Logger> create(const void* owner)
{
return adoptRef(*new Logger(owner));
}

template<typename... Arguments>
inline void logAlways(WTFLogChannel& channel, const Arguments&... arguments)
inline void logAlways(WTFLogChannel& channel, const Arguments&... arguments) const
{
#if RELEASE_LOG_DISABLED
// "Standard" WebCore logging goes to stderr, which is captured in layout test output and can generally be a problem
@@ -69,7 +78,7 @@ class Logger : public RefCounted<Logger> {
}

template<typename... Arguments>
inline void error(WTFLogChannel& channel, const Arguments&... arguments)
inline void error(WTFLogChannel& channel, const Arguments&... arguments) const
{
if (!willLog(channel, WTFLogLevelError))
return;
@@ -78,7 +87,7 @@ class Logger : public RefCounted<Logger> {
}

template<typename... Arguments>
inline void warning(WTFLogChannel& channel, const Arguments&... arguments)
inline void warning(WTFLogChannel& channel, const Arguments&... arguments) const
{
if (!willLog(channel, WTFLogLevelWarning))
return;
@@ -87,7 +96,7 @@ class Logger : public RefCounted<Logger> {
}

template<typename... Arguments>
inline void notice(WTFLogChannel& channel, const Arguments&... arguments)
inline void notice(WTFLogChannel& channel, const Arguments&... arguments) const
{
if (!willLog(channel, WTFLogLevelNotice))
return;
@@ -96,7 +105,7 @@ class Logger : public RefCounted<Logger> {
}

template<typename... Arguments>
inline void info(WTFLogChannel& channel, const Arguments&... arguments)
inline void info(WTFLogChannel& channel, const Arguments&... arguments) const
{
if (!willLog(channel, WTFLogLevelInfo))
return;
@@ -105,15 +114,15 @@ class Logger : public RefCounted<Logger> {
}

template<typename... Arguments>
inline void debug(WTFLogChannel& channel, const Arguments&... arguments)
inline void debug(WTFLogChannel& channel, const Arguments&... arguments) const
{
if (!willLog(channel, WTFLogLevelDebug))
return;

log(channel, arguments...);
}

inline bool willLog(WTFLogChannel& channel, WTFLogLevel level) const
inline bool willLog(const WTFLogChannel& channel, WTFLogLevel level) const
{
if (level != WTFLogLevelAlways && level > channel.level)
return false;
@@ -133,40 +142,95 @@ class Logger : public RefCounted<Logger> {
}

struct MethodAndPointer {
MethodAndPointer(const char* methodName, void* objectPtr)
: methodName(methodName)
, objectPtr(reinterpret_cast<uintptr_t>(objectPtr))
MethodAndPointer(const char* methodName, const void* objectPtr)
: methodName { methodName }
, objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
{
}

const char* methodName;
uintptr_t objectPtr;
MethodAndPointer(const char* className, const char* methodName, const void* objectPtr)
: className { className }
, methodName { methodName }
, objectPtr { reinterpret_cast<uintptr_t>(objectPtr) }
{
}

const char* className { nullptr };
const char* methodName { nullptr };
const uintptr_t objectPtr { 0 };
};

static inline void addObserver(Observer& observer)
{
observers().append(observer);
}
static inline void removeObserver(Observer& observer)
{
observers().removeFirstMatching([&observer](auto anObserver) {
return &anObserver.get() == &observer;
});
}

private:
Logger(const void* owner) { m_owner = owner; }
Logger(const void* owner)
: m_owner { owner }
{
}

template<typename... Argument>
static inline void log(WTFLogChannel& channel, const Argument&... arguments)
{
String string = makeString(LogArgument<Argument>::toString(arguments)...);
String logMessage = makeString(LogArgument<Argument>::toString(arguments)...);

#if RELEASE_LOG_DISABLED
WTFLog(&channel, "%s", string.utf8().data());
WTFLog(&channel, "%s", logMessage.utf8().data());
#else
os_log(channel.osLogChannel, "%{public}s", string.utf8().data());
os_log(channel.osLogChannel, "%{public}s", logMessage.utf8().data());
#endif

for (Observer& observer : observers())
observer.didLogMessage(channel, logMessage);
}

static Vector<std::reference_wrapper<Observer>>& observers()
{
static NeverDestroyed<Vector<std::reference_wrapper<Observer>>> observers;
return observers;
}

const void* m_owner;
bool m_enabled { true };
};

class LogHelper {
public:
virtual ~LogHelper() = default;

virtual const Logger& logger() const = 0;
virtual const char* className() const = 0;
virtual WTFLogChannel& logChannel() const = 0;

inline bool willLog(WTFLogLevel level) const { return logger().willLog(logChannel(), level); }

#define ALWAYS_LOG(...) logger().logAlways(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
#define ERROR_LOG(...) logger().error(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
#define WARNING_LOG(...) logger().warning(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
#define NOTICE_LOG(...) logger().notice(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
#define INFO_LOG(...) logger().info(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)
#define DEBUG_LOG(...) logger().debug(logChannel(), Logger::MethodAndPointer(className(), __func__, this), ##__VA_ARGS__)

};

template <>
struct LogArgument<Logger::MethodAndPointer> {
static String toString(const Logger::MethodAndPointer& value)
{
StringBuilder builder;

if (value.className) {
builder.append(value.className);
builder.appendLiteral("::");
}
builder.append(value.methodName);
builder.appendLiteral("(0x");
appendUnsigned64AsHex(value.objectPtr, builder);
@@ -176,4 +240,3 @@ struct LogArgument<Logger::MethodAndPointer> {
};

} // namespace PAL

@@ -1,3 +1,17 @@
2017-08-30 Eric Carlson <eric.carlson@apple.com>

Add Logger observer and helper class
https://bugs.webkit.org/show_bug.cgi?id=176106

Reviewed by Andy Estes.

* TestWebKitAPI/Tests/WebCore/Logging.cpp:
(TestWebKitAPI::LoggingTest::LoggingTest):
(TestWebKitAPI::LoggingTest::Logger::create):
(TestWebKitAPI::TEST_F):
(TestWebKitAPI::LogObserver::log):
(TestWebKitAPI::LogObserver::channel const):

2017-08-29 Filip Pizlo <fpizlo@apple.com>

Initial reference implementation scaffolding for ArrayLang
@@ -55,8 +55,13 @@ static const size_t logChannelCount = sizeof(testLogChannels) / sizeof(testLogCh

namespace TestWebKitAPI {

class LoggingTest : public testing::Test {
class LoggingTest : public testing::Test, public LogHelper {
public:
LoggingTest()
: m_logger { Logger::create(this) }
{
}

void SetUp() final
{
WTF::initializeMainThread();
@@ -94,7 +99,13 @@ class LoggingTest : public testing::Test {
return result.toString();
}

const Logger& logger() const final { return m_logger.get(); }
const char* className() const final { return "LoggingTest"; }
WTFLogChannel& logChannel() const final { return TestChannel1; }

private:

Ref<Logger> m_logger;
int m_descriptors[2];
FILE* m_stderr;
};
@@ -267,6 +278,7 @@ TEST_F(LoggingTest, Logger)
EXPECT_TRUE(logger->enabled());

WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelError);
EXPECT_TRUE(logger->willLog(TestChannel1, WTFLogLevelError));
logger->error(TestChannel1, "You're using coconuts!");
EXPECT_TRUE(output().contains("You're using coconuts!", false));
logger->warning(TestChannel1, "You're using coconuts!");
@@ -302,10 +314,107 @@ TEST_F(LoggingTest, Logger)
EXPECT_TRUE(output().contains("I shall taunt you a second time!", false));

logger->setEnabled(this, false);
EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevelError));
EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevelWarning));
EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevelNotice));
EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevelInfo));
EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevelDebug));
EXPECT_FALSE(logger->enabled());
logger->logAlways(TestChannel1, "You've got two empty halves of coconuts");
EXPECT_EQ(0u, output().length());

logger->setEnabled(this, true);
AtomicString string1("AtomicString", AtomicString::ConstructFromLiteral);
const AtomicString string2("const AtomicString", AtomicString::ConstructFromLiteral);
logger->logAlways(TestChannel1, string1, " and ", string2);
EXPECT_TRUE(output().contains("AtomicString and const AtomicString", false));

String string3("String");
const String string4("const String");
logger->logAlways(TestChannel1, string3, " and ", string4);
EXPECT_TRUE(output().contains("String and const String", false));
}

TEST_F(LoggingTest, LogHelper)
{
EXPECT_TRUE(logger().enabled());

StringBuilder builder;
builder.appendLiteral("LoggingTest::TestBody(0x");
appendUnsigned64AsHex(reinterpret_cast<uintptr_t>(this), builder);
builder.appendLiteral(")");
String signature = builder.toString();

ALWAYS_LOG();
EXPECT_TRUE(this->output().contains(signature, false));

ALWAYS_LOG("Welcome back", " my friends", " to the show", " that never ends");
String result = this->output();
EXPECT_TRUE(result.contains(signature, false));
EXPECT_TRUE(result.contains("to the show that never", false));

WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelWarning);
EXPECT_TRUE(willLog(WTFLogLevelWarning));

ERROR_LOG("We're so glad you could attend");
EXPECT_TRUE(output().contains("We're so glad you could attend", false));

WARNING_LOG("Come inside! ", "Come inside!");
EXPECT_TRUE(output().contains("Come inside! Come inside!", false));

NOTICE_LOG("There behind a glass is a real blade of grass");
EXPECT_EQ(0u, output().length());

INFO_LOG("be careful as you pass.");
EXPECT_EQ(0u, output().length());

DEBUG_LOG("Move along! Move along!");
EXPECT_EQ(0u, output().length());
}

class LogObserver : public Logger::Observer {
public:
LogObserver() = default;

String log()
{
String log = m_logBuffer.toString();
m_logBuffer.clear();

return log;
}

WTFLogChannel channel() const { return m_lastChannel; }

private:
void didLogMessage(const WTFLogChannel& channel, const String& logMessage) final
{
m_logBuffer.append(logMessage);
m_lastChannel = channel;
}

StringBuilder m_logBuffer;
WTFLogChannel m_lastChannel;
};

TEST_F(LoggingTest, LogObserver)
{
LogObserver observer;

EXPECT_TRUE(logger().enabled());

logger().addObserver(observer);
ALWAYS_LOG("testing 1, 2, 3");
EXPECT_TRUE(this->output().contains("testing 1, 2, 3", false));
EXPECT_TRUE(observer.log().contains("testing 1, 2, 3", false));
EXPECT_STREQ(observer.channel().name, logChannel().name);

logger().removeObserver(observer);
ALWAYS_LOG("testing ", 1, ", ", 2, ", 3");
EXPECT_TRUE(this->output().contains("testing 1, 2, 3", false));
EXPECT_EQ(0u, observer.log().length());
}

#endif

} // namespace TestWebKitAPI

0 comments on commit 8241cd7

Please sign in to comment.