Add WTFLogChannel level to allow runtime log filtering
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Aug 2017 22:46:17 +0000 (22:46 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Aug 2017 22:46:17 +0000 (22:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175731
<rdar://problem/33967234>

Reviewed by Jer Noble.
Source/WTF:

Add WTFLog*, LOG, and RELEASE_LOG variants that take a "log level" parameter so code
can include logging statements that are only conditionally emitted.

* wtf/Assertions.cpp:
* wtf/Assertions.h:
* wtf/MemoryPressureHandler.cpp:
* wtf/RefCountedLeakCounter.cpp:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/Logging.cpp: Added.
(TestWebKitAPI::LoggingTest::output):
(TestWebKitAPI::TEST_F):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220938 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WTF/ChangeLog
Source/WTF/wtf/Assertions.cpp
Source/WTF/wtf/Assertions.h
Source/WTF/wtf/MemoryPressureHandler.cpp
Source/WTF/wtf/RefCountedLeakCounter.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebCore/Logging.cpp [new file with mode: 0644]

index d98619a..d8fd93a 100644 (file)
@@ -1,3 +1,19 @@
+2017-08-18  Eric Carlson  <eric.carlson@apple.com>
+
+        Add WTFLogChannel level to allow runtime log filtering
+        https://bugs.webkit.org/show_bug.cgi?id=175731
+        <rdar://problem/33967234>
+
+        Reviewed by Jer Noble.
+        
+        Add WTFLog*, LOG, and RELEASE_LOG variants that take a "log level" parameter so code
+        can include logging statements that are only conditionally emitted.
+
+        * wtf/Assertions.cpp:
+        * wtf/Assertions.h:
+        * wtf/MemoryPressureHandler.cpp:
+        * wtf/RefCountedLeakCounter.cpp:
+
 2017-08-18  Per Arne Vollan  <pvollan@apple.com>
 
         Implement 32-bit MacroAssembler::probe support for Windows.
index 86ae545..f77fd18 100644 (file)
@@ -410,6 +410,40 @@ static WTFLoggingAccumulator& loggingAccumulator()
     return *accumulator;
 }
 
+void WTFSetLogChannelLevel(WTFLogChannel* channel, WTFLogLevel level)
+{
+    channel->level = level;
+    WTFLog(channel, "Channel \"%s\" level set to %i", channel->name, level);
+}
+
+bool WTFWillLogWithLevel(WTFLogChannel* channel, WTFLogLevel level)
+{
+    return channel->level >= level && channel->state != WTFLogChannelOff;
+}
+
+void WTFLogWithLevel(WTFLogChannel* channel, WTFLogLevel level, const char* format, ...)
+{
+    if (channel->level < level)
+        return;
+
+    if (channel->state == WTFLogChannelOff)
+        return;
+
+    va_list args;
+    va_start(args, format);
+
+#if COMPILER(CLANG)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+    WTFLog(channel, format, args);
+#if COMPILER(CLANG)
+#pragma clang diagnostic pop
+#endif
+
+    va_end(args);
+}
+
 void WTFLog(WTFLogChannel* channel, const char* format, ...)
 {
     if (channel->state == WTFLogChannelOff)
@@ -522,7 +556,9 @@ void WTFInitializeLogChannelStatesFromString(WTFLogChannel* channels[], size_t c
     logLevelString.split(',', components);
 
     for (size_t i = 0; i < components.size(); ++i) {
-        String component = components[i];
+        Vector<String> componentInfo;
+        components[i].split('=', componentInfo);
+        String component = componentInfo[0].stripWhiteSpace();
 
         WTFLogChannelState logChannelState = WTFLogChannelOn;
         if (component.startsWith('-')) {
@@ -535,9 +571,25 @@ void WTFInitializeLogChannelStatesFromString(WTFLogChannel* channels[], size_t c
             continue;
         }
 
-        if (WTFLogChannel* channel = WTFLogChannelByName(channels, count, component.utf8().data()))
+        WTFLogLevel logChannelLevel = WTFLogLevelError;
+        if (componentInfo.size() > 1) {
+            String level = componentInfo[1].stripWhiteSpace();
+            if (equalLettersIgnoringASCIICase(level, "error"))
+                logChannelLevel = WTFLogLevelError;
+            else if (equalLettersIgnoringASCIICase(level, "warning"))
+                logChannelLevel = WTFLogLevelWarning;
+            else if (equalLettersIgnoringASCIICase(level, "info"))
+                logChannelLevel = WTFLogLevelInfo;
+            else if (equalLettersIgnoringASCIICase(level, "debug"))
+                logChannelLevel = WTFLogLevelDebug;
+            else
+                WTFLogAlways("Unknown logging level: %s", level.utf8().data());
+        }
+
+        if (WTFLogChannel* channel = WTFLogChannelByName(channels, count, component.utf8().data())) {
             channel->state = logChannelState;
-        else
+            channel->level = logChannelLevel;
+        } else
             WTFLogAlways("Unknown logging channel: %s", component.utf8().data());
     }
 }
index 2191d96..81f4bfb 100644 (file)
@@ -148,10 +148,12 @@ extern "C" {
 #endif
 
 typedef enum { WTFLogChannelOff, WTFLogChannelOn, WTFLogChannelOnWithAccumulation } WTFLogChannelState;
+typedef enum { WTFLogLevelError, WTFLogLevelWarning, WTFLogLevelInfo, WTFLogLevelDebug } WTFLogLevel;
 
 typedef struct {
     WTFLogChannelState state;
     const char* name;
+    WTFLogLevel level;
 #if !RELEASE_LOG_DISABLED
     const char* subsystem;
     __unsafe_unretained os_log_t osLogChannel;
@@ -171,10 +173,10 @@ typedef struct {
 #if !defined(DEFINE_LOG_CHANNEL)
 #if RELEASE_LOG_DISABLED
 #define DEFINE_LOG_CHANNEL(name, subsystem) \
-    WTFLogChannel LOG_CHANNEL(name) = { WTFLogChannelOff, #name };
+    WTFLogChannel LOG_CHANNEL(name) = { WTFLogChannelOff, #name, WTFLogLevelError };
 #else
 #define DEFINE_LOG_CHANNEL(name, subsystem) \
-    WTFLogChannel LOG_CHANNEL(name) = { WTFLogChannelOff, #name, subsystem, OS_LOG_DEFAULT };
+    WTFLogChannel LOG_CHANNEL(name) = { WTFLogChannelOff, #name, WTFLogLevelError, subsystem, OS_LOG_DEFAULT };
 #endif
 #endif
 
@@ -190,6 +192,9 @@ WTF_EXPORT_PRIVATE void WTFLogAlways(const char* format, ...) WTF_ATTRIBUTE_PRIN
 WTF_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH void WTFLogAlwaysAndCrash(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2);
 WTF_EXPORT_PRIVATE WTFLogChannel* WTFLogChannelByName(WTFLogChannel*[], size_t count, const char*);
 WTF_EXPORT_PRIVATE void WTFInitializeLogChannelStatesFromString(WTFLogChannel*[], size_t count, const char*);
+WTF_EXPORT_PRIVATE void WTFLogWithLevel(WTFLogChannel*, WTFLogLevel, const char* format, ...) WTF_ATTRIBUTE_PRINTF(3, 4);
+WTF_EXPORT_PRIVATE void WTFSetLogChannelLevel(WTFLogChannel*, WTFLogLevel);
+WTF_EXPORT_PRIVATE bool WTFWillLogWithLevel(WTFLogChannel*, WTFLogLevel);
 
 WTF_EXPORT_PRIVATE void WTFGetBacktrace(void** stack, int* size);
 WTF_EXPORT_PRIVATE void WTFReportBacktrace();
@@ -427,6 +432,14 @@ WTF_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH void WTFCrashWithSecurityImplication()
 #define LOG_VERBOSE(channel, ...) WTFLogVerbose(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, &LOG_CHANNEL(channel), __VA_ARGS__)
 #endif
 
+/* LOG_WITH_LEVEL */
+
+#if LOG_DISABLED
+#define LOG_WITH_LEVEL(channel, level, ...) ((void)0)
+#else
+#define LOG_WITH_LEVEL(channel, level, ...) WTFLogWithLevel(&LOG_CHANNEL(channel), level, __VA_ARGS__)
+#endif
+
 /* RELEASE_LOG */
 
 #if RELEASE_LOG_DISABLED
@@ -435,12 +448,25 @@ WTF_EXPORT_PRIVATE NO_RETURN_DUE_TO_CRASH void WTFCrashWithSecurityImplication()
 
 #define RELEASE_LOG_IF(      isAllowed, channel, format, ...) ((void)0)
 #define RELEASE_LOG_ERROR_IF(isAllowed, channel, format, ...) do { if (isAllowed) RELEASE_LOG_ERROR(channel, format, ##__VA_ARGS__); } while (0)
+
+#define RELEASE_LOG_WITH_LEVEL(              channel, level, format, ...) ((void)0)
+#define RELEASE_LOG_WITH_LEVEL_IF(isAllowed, channel, level, format, ...) do { if (isAllowed) RELEASE_LOG_WITH_LEVEL(channel, level, format, ##__VA_ARGS__); } while (0)
 #else
 #define RELEASE_LOG(      channel, format, ...) os_log(      LOG_CHANNEL(channel).osLogChannel, format, ##__VA_ARGS__)
 #define RELEASE_LOG_ERROR(channel, format, ...) os_log_error(LOG_CHANNEL(channel).osLogChannel, format, ##__VA_ARGS__)
 
 #define RELEASE_LOG_IF(      isAllowed, channel, format, ...) do { if (isAllowed) RELEASE_LOG(      channel, format, ##__VA_ARGS__); } while (0)
 #define RELEASE_LOG_ERROR_IF(isAllowed, channel, format, ...) do { if (isAllowed) RELEASE_LOG_ERROR(channel, format, ##__VA_ARGS__); } while (0)
+
+#define RELEASE_LOG_WITH_LEVEL(channel, logLevel, format, ...) do { \
+    if (LOG_CHANNEL(channel).level >= (logLevel)) \
+        os_log(LOG_CHANNEL(channel).osLogChannel, format, ##__VA_ARGS__); \
+} while (0)
+
+#define RELEASE_LOG_WITH_LEVEL_IF(isAllowed, channel, logLevel, format, ...) do { \
+    if ((isAllowed) && LOG_CHANNEL(channel).level >= (logLevel)) \
+        os_log(LOG_CHANNEL(channel).osLogChannel, format, ##__VA_ARGS__); \
+} while (0)
 #endif
 
 
index 3616286..0e1d1a6 100644 (file)
@@ -34,9 +34,9 @@
 namespace WTF {
 
 #if RELEASE_LOG_DISABLED
-WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure" };
+WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", WTFLogLevelError };
 #else
-WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
+WTFLogChannel LogMemoryPressure = { WTFLogChannelOn, "MemoryPressure", WTFLogLevelError, LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
 #endif
 
 WTF_EXPORT_PRIVATE bool MemoryPressureHandler::ReliefLogger::s_loggingEnabled = false;
index be87266..dd008bb 100644 (file)
@@ -40,9 +40,9 @@ void RefCountedLeakCounter::decrement() { }
 
 #define LOG_CHANNEL_PREFIX Log
 #if RELEASE_LOG_DISABLED
-static WTFLogChannel LogRefCountedLeaks = { WTFLogChannelOn, "RefCountedLeaks" };
+static WTFLogChannel LogRefCountedLeaks = { WTFLogChannelOn, "RefCountedLeaks", WTFLogLevelError };
 #else
-static WTFLogChannel LogRefCountedLeaks = { WTFLogChannelOn, "RefCountedLeaks", LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
+static WTFLogChannel LogRefCountedLeaks = { WTFLogChannelOn, "RefCountedLeaks", WTFLogLevelError, LOG_CHANNEL_WEBKIT_SUBSYSTEM, OS_LOG_DEFAULT };
 #endif
 
 typedef HashCountedSet<const char*, PtrHash<const char*>> ReasonSet;
index c6217f6..8b768dc 100644 (file)
@@ -1,3 +1,16 @@
+2017-08-18  Eric Carlson  <eric.carlson@apple.com>
+
+        Add WTFLogChannel level to allow runtime log filtering
+        https://bugs.webkit.org/show_bug.cgi?id=175731
+        <rdar://problem/33967234>
+
+        Reviewed by Jer Noble.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/Logging.cpp: Added.
+        (TestWebKitAPI::LoggingTest::output):
+        (TestWebKitAPI::TEST_F):
+
 2017-08-18  Alexey Proskuryakov  <ap@apple.com>
 
         Add Dean Johnson to contributors.json for Bugzilla autocomplete.
index 5696fae..52128d4 100644 (file)
@@ -25,6 +25,7 @@
                07492B3B1DF8B14C00633DE1 /* EnumerateMediaDevices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07492B3A1DF8AE2D00633DE1 /* EnumerateMediaDevices.cpp */; };
                07492B3C1DF8B86600633DE1 /* enumerateMediaDevices.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 07492B391DF8ADA400633DE1 /* enumerateMediaDevices.html */; };
                074994421EA5034B000DA44E /* getUserMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 4A410F4D19AF7BEF002EBAB5 /* getUserMedia.html */; };
+               076E507F1F4513D6006E9F5A /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 076E507E1F45031E006E9F5A /* Logging.cpp */; };
                0799C3491EBA2D7B003B7532 /* UserMediaDisabled.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07EDEFAC1EB9400C00D43292 /* UserMediaDisabled.mm */; };
                0799C34B1EBA3301003B7532 /* disableGetUserMedia.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */; };
                07C046CA1E4262A8007201E7 /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07C046C91E42573E007201E7 /* CARingBuffer.cpp */; };
                07492B391DF8ADA400633DE1 /* enumerateMediaDevices.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = enumerateMediaDevices.html; sourceTree = "<group>"; };
                07492B3A1DF8AE2D00633DE1 /* EnumerateMediaDevices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EnumerateMediaDevices.cpp; sourceTree = "<group>"; };
                0766DD1F1A5AD5200023E3BB /* PendingAPIRequestURL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PendingAPIRequestURL.cpp; sourceTree = "<group>"; };
+               076E507E1F45031E006E9F5A /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Logging.cpp; sourceTree = "<group>"; };
                0799C34A1EBA32F4003B7532 /* disableGetUserMedia.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = disableGetUserMedia.html; sourceTree = "<group>"; };
                07C046C91E42573E007201E7 /* CARingBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CARingBuffer.cpp; sourceTree = "<group>"; };
                07CE1CF21F06A7E000BF89F5 /* GetUserMediaNavigation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GetUserMediaNavigation.mm; sourceTree = "<group>"; };
                                7A909A741D877475007E10F8 /* IntRect.cpp */,
                                7A909A751D877475007E10F8 /* IntSize.cpp */,
                                14464012167A8305000BD218 /* LayoutUnit.cpp */,
+                               076E507E1F45031E006E9F5A /* Logging.cpp */,
                                CD225C071C45A69200140761 /* ParsedContentRange.cpp */,
                                CDCFA7A91E45122F00C2433D /* SampleMap.cpp */,
                                CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */,
                                A1EC11881F42541200D0146E /* PreviewLoader.cpp in Sources */,
                                7CCE7F0C1A411AE600447C4C /* PrivateBrowsingPushStateNoHistoryCallback.cpp in Sources */,
                                4647B1261EBA3B850041D7EF /* ProcessDidTerminate.cpp in Sources */,
+                               076E507F1F4513D6006E9F5A /* Logging.cpp in Sources */,
                                7C83E0C11D0A652F00FEBCF3 /* ProvisionalURLNotChange.mm in Sources */,
                                7CCE7EC81A411A7E00447C4C /* PublicSuffix.mm in Sources */,
                                7C83E0C21D0A653500FEBCF3 /* QuickLook.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/Logging.cpp b/Tools/TestWebKitAPI/Tests/WebCore/Logging.cpp
new file mode 100644 (file)
index 0000000..cfb42f9
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WTFStringUtilities.h"
+#include <wtf/Assertions.h>
+#include <wtf/MainThread.h>
+
+#define LOG_CHANNEL_PREFIX Test
+
+using namespace WTF;
+
+const char* logTestingSubsystem = "com.webkit.testing";
+
+DEFINE_LOG_CHANNEL(Channel1, logTestingSubsystem);
+DEFINE_LOG_CHANNEL(Channel2, logTestingSubsystem);
+DEFINE_LOG_CHANNEL(Channel3, logTestingSubsystem);
+DEFINE_LOG_CHANNEL(Channel4, logTestingSubsystem);
+
+static WTFLogChannel* testLogChannels[] = {
+    &TestChannel1,
+    &TestChannel2,
+    &TestChannel3,
+    &TestChannel4,
+};
+static const size_t logChannelCount = sizeof(testLogChannels) / sizeof(testLogChannels[0]);
+
+namespace TestWebKitAPI {
+
+class LoggingTest : public testing::Test {
+public:
+    void SetUp() final
+    {
+        WTF::initializeMainThread();
+
+        // Replace stderr with a nonblocking pipe that we can read from.
+        pipe(m_descriptors);
+        fcntl(m_descriptors[0], F_SETFL, fcntl(m_descriptors[0], F_GETFL, 0) | O_NONBLOCK);
+        dup2(m_descriptors[1], STDERR_FILENO);
+        close(m_descriptors[1]);
+
+        m_stderr = fdopen(m_descriptors[0], "r");
+
+        WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "all");
+        WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelError);
+        WTFSetLogChannelLevel(&TestChannel2, WTFLogLevelError);
+        WTFSetLogChannelLevel(&TestChannel3, WTFLogLevelError);
+        WTFSetLogChannelLevel(&TestChannel4, WTFLogLevelError);
+    }
+
+    void TearDown() override
+    {
+        close(m_descriptors[0]);
+        fclose(m_stderr);
+    }
+
+    String output()
+    {
+        char buffer[1024];
+        StringBuilder result;
+        char* line;
+
+        while ((line = fgets(buffer, sizeof(buffer), m_stderr)))
+            result.append(line);
+
+        return result.toString();
+    }
+
+private:
+    int m_descriptors[2];
+    FILE* m_stderr;
+};
+
+TEST_F(LoggingTest, Initialization)
+{
+    EXPECT_EQ(TestChannel1.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel2.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel3.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel4.state, WTFLogChannelOn);
+
+    EXPECT_EQ(TestChannel1.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel2.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel3.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel4.level, WTFLogLevelError);
+
+    TestChannel1.state = WTFLogChannelOff;
+    WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel1");
+    EXPECT_EQ(TestChannel1.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel1.state, WTFLogChannelOn);
+
+    TestChannel1.state = WTFLogChannelOff;
+    WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel1=foo");
+    EXPECT_EQ(TestChannel1.level, WTFLogLevelError);
+    EXPECT_TRUE(output().contains("Unknown logging level: foo", false));
+
+    WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel1=warning");
+    EXPECT_EQ(TestChannel1.level, WTFLogLevelWarning);
+    EXPECT_EQ(TestChannel2.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel3.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel4.level, WTFLogLevelError);
+
+    WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel4=   debug, Channel3 = info");
+    EXPECT_EQ(TestChannel1.level, WTFLogLevelWarning);
+    EXPECT_EQ(TestChannel2.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel3.level, WTFLogLevelInfo);
+    EXPECT_EQ(TestChannel4.level, WTFLogLevelDebug);
+
+    WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelError);
+    EXPECT_TRUE(output().contains("Channel \"Channel1\" level set to 0", false));
+
+    WTFSetLogChannelLevel(&TestChannel2, WTFLogLevelWarning);
+    EXPECT_TRUE(output().contains("Channel \"Channel2\" level set to 1", false));
+
+    WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "-all");
+    EXPECT_EQ(TestChannel1.state, WTFLogChannelOff);
+    EXPECT_EQ(TestChannel2.state, WTFLogChannelOff);
+    EXPECT_EQ(TestChannel3.state, WTFLogChannelOff);
+    EXPECT_EQ(TestChannel4.state, WTFLogChannelOff);
+
+    WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "all");
+    EXPECT_EQ(TestChannel1.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel2.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel3.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel4.state, WTFLogChannelOn);
+}
+
+TEST_F(LoggingTest, WTFWillLogWithLevel)
+{
+    EXPECT_EQ(TestChannel1.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel2.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel3.state, WTFLogChannelOn);
+    EXPECT_EQ(TestChannel4.state, WTFLogChannelOn);
+
+    EXPECT_EQ(TestChannel1.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel2.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel3.level, WTFLogLevelError);
+    EXPECT_EQ(TestChannel4.level, WTFLogLevelError);
+
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelError));
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel2, WTFLogLevelError));
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel3, WTFLogLevelError));
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel4, WTFLogLevelError));
+
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelInfo));
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel2, WTFLogLevelInfo));
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel3, WTFLogLevelInfo));
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel4, WTFLogLevelInfo));
+
+    TestChannel1.state = WTFLogChannelOff;
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelError));
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelInfo));
+
+    TestChannel1.state = WTFLogChannelOn;
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelError));
+    EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelInfo));
+
+    TestChannel1.level = WTFLogLevelInfo;
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelError));
+    EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevelInfo));
+}
+
+TEST_F(LoggingTest, LOG)
+{
+    LOG(Channel1, "Log message.");
+    EXPECT_TRUE(output().contains("Log Message.", false));
+}
+
+TEST_F(LoggingTest, LOG_WITH_LEVEL)
+{
+    LOG_WITH_LEVEL(Channel1, WTFLogLevelError, "Go and boil your bottoms, you sons of a silly person.");
+    EXPECT_TRUE(output().contains("sons of a silly person.", false));
+
+    LOG_WITH_LEVEL(Channel1, WTFLogLevelWarning, "You don't frighten us, English pig dogs.");
+    EXPECT_EQ(0u, output().length());
+
+    WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelInfo);
+    LOG_WITH_LEVEL(Channel1, WTFLogLevelWarning, "I'm French. Why do you think I have this outrageous accent, you silly king?");
+    EXPECT_TRUE(output().contains("outrageous accent", false));
+
+    LOG_WITH_LEVEL(Channel1, WTFLogLevelDebug, "You don't frighten us with your silly knees-bent running around advancing behavior!");
+    EXPECT_EQ(0u, output().length());
+
+    WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelDebug);
+    LOG_WITH_LEVEL(Channel1, WTFLogLevelDebug, "Go and tell your master that we have been charged by God with a sacred quest.");
+    EXPECT_TRUE(output().contains("sacred quest", false));
+}
+
+TEST_F(LoggingTest, RELEASE_LOG)
+{
+    RELEASE_LOG(Channel1, "Log message.");
+    EXPECT_TRUE(output().contains("Log Message.", false));
+}
+
+TEST_F(LoggingTest, RELEASE_LOG_IF)
+{
+    bool enabled = true;
+    RELEASE_LOG_IF(enabled, Channel1, "Your mother was a hamster,");
+    EXPECT_TRUE(output().contains("hamster,", false));
+
+    enabled = false;
+    RELEASE_LOG_IF(enabled, Channel1, "and your father smelt of elderberries ...");
+    EXPECT_EQ(0u, output().length());
+}
+
+TEST_F(LoggingTest, RELEASE_LOG_WITH_LEVEL)
+{
+    RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevelError, "You don't frighten us, English pig dogs.");
+    EXPECT_TRUE(output().contains("pig dogs.", false));
+
+    RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevelWarning, "Go and boil your bottoms, you sons of a silly person.");
+    EXPECT_EQ(0u, output().length());
+
+    WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelInfo);
+    RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevelWarning, "I'm French. Why do you think I have this outrageous accent, you silly king?");
+    EXPECT_TRUE(output().contains("outrageous accent", false));
+
+    RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevelDebug, "You don't frighten us with your silly knees-bent running around advancing behavior!");
+    EXPECT_EQ(0u, output().length());
+
+    WTFSetLogChannelLevel(&TestChannel1, WTFLogLevelDebug);
+    RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevelDebug, "Go and tell your master that we have been charged by God with a sacred quest.");
+    EXPECT_TRUE(output().contains("sacred quest", false));
+}
+
+TEST_F(LoggingTest, RELEASE_LOG_WITH_LEVEL_IF)
+{
+    bool enabled = true;
+    RELEASE_LOG_WITH_LEVEL_IF(enabled, Channel1, WTFLogLevelError, "Is there someone else up there that we can talk to?");
+    EXPECT_TRUE(output().contains("someone else", false));
+
+    RELEASE_LOG_WITH_LEVEL_IF(enabled, Channel1, WTFLogLevelDebug, "No, now go away");
+    EXPECT_EQ(0u, output().length());
+
+    enabled = false;
+    RELEASE_LOG_WITH_LEVEL_IF(enabled, Channel1, WTFLogLevelWarning, "or I shall taunt you a second time!");
+    EXPECT_EQ(0u, output().length());
+}
+} // namespace TestWebKitAPI