You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@samza.apache.org by cr...@apache.org on 2014/08/05 18:34:54 UTC

git commit: SAMZA-350; allow dynamic log level toggling

Repository: incubator-samza
Updated Branches:
  refs/heads/master e603a2794 -> 3f3d80eb4


SAMZA-350; allow dynamic log level toggling


Project: http://git-wip-us.apache.org/repos/asf/incubator-samza/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-samza/commit/3f3d80eb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-samza/tree/3f3d80eb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-samza/diff/3f3d80eb

Branch: refs/heads/master
Commit: 3f3d80eb466dc42567f3fbf07834b35bc4f958b6
Parents: e603a27
Author: Chris Riccomini <cr...@criccomi-mn.linkedin.biz>
Authored: Tue Aug 5 09:34:46 2014 -0700
Committer: Chris Riccomini <cr...@criccomi-mn.linkedin.biz>
Committed: Tue Aug 5 09:34:46 2014 -0700

----------------------------------------------------------------------
 build.gradle                                    |   9 ++
 gradle/dependency-versions.gradle               |   1 +
 .../metrics/reporter/TestJmxReporter.scala      |   2 +-
 .../apache/samza/logging/log4j/JmxAppender.java | 123 +++++++++++++++++
 .../samza/logging/log4j/TestJmxAppender.java    | 136 +++++++++++++++++++
 samza-log4j/src/test/resources/log4j.xml        |  35 +++++
 settings.gradle                                 |  15 +-
 7 files changed, 318 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index 93ec947..ff920b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -162,6 +162,15 @@ project(":samza-kafka_$scalaVersion") {
   }
 }
 
+project(':samza-log4j') {
+  apply plugin: 'java'
+
+  dependencies {
+    compile "log4j:log4j:$log4jVersion"
+    testCompile "junit:junit:$junitVersion"
+  }
+}
+
 project(":samza-serializers_$scalaVersion") {
   apply plugin: 'scala'
 

http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/gradle/dependency-versions.gradle
----------------------------------------------------------------------
diff --git a/gradle/dependency-versions.gradle b/gradle/dependency-versions.gradle
index c8bd830..fe2e446 100644
--- a/gradle/dependency-versions.gradle
+++ b/gradle/dependency-versions.gradle
@@ -31,6 +31,7 @@
   leveldbVersion = "1.8"
   yarnVersion = "2.4.0"
   slf4jVersion = "1.6.2"
+  log4jVersion = "1.2.17"
   guavaVersion = "17.0"
   commonsCodecVersion = "1.9"
 }

http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/samza-core/src/test/scala/org/apache/samza/metrics/reporter/TestJmxReporter.scala
----------------------------------------------------------------------
diff --git a/samza-core/src/test/scala/org/apache/samza/metrics/reporter/TestJmxReporter.scala b/samza-core/src/test/scala/org/apache/samza/metrics/reporter/TestJmxReporter.scala
index 357b290..f6c8646 100644
--- a/samza-core/src/test/scala/org/apache/samza/metrics/reporter/TestJmxReporter.scala
+++ b/samza-core/src/test/scala/org/apache/samza/metrics/reporter/TestJmxReporter.scala
@@ -44,7 +44,7 @@ object TestJmxReporter {
 
   @BeforeClass
   def beforeSetupServers {
-    LocateRegistry.createRegistry(4500)
+    LocateRegistry.createRegistry(port)
     val mbs = ManagementFactory.getPlatformMBeanServer()
     cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs)
     cs.start

http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/samza-log4j/src/main/java/org/apache/samza/logging/log4j/JmxAppender.java
----------------------------------------------------------------------
diff --git a/samza-log4j/src/main/java/org/apache/samza/logging/log4j/JmxAppender.java b/samza-log4j/src/main/java/org/apache/samza/logging/log4j/JmxAppender.java
new file mode 100644
index 0000000..27c9a29
--- /dev/null
+++ b/samza-log4j/src/main/java/org/apache/samza/logging/log4j/JmxAppender.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.samza.logging.log4j;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * <p>
+ * JmxAppender is a simple class that exposes Log4J's getLevel and setLevel APIs
+ * through a JMX MBean. To enable this MBean, simply include the appender in
+ * log4j.xml:
+ * </p>
+ *
+ * <code>
+ * <appender name="jmx" class="org.apache.samza.logging.log4j.JmxAppender"/>
+ * </code>
+ *
+ * <p>
+ * And then enable it as a root logger:
+ * </p>
+ *
+ * <code>
+ *   <root>
+ *     <!-- ...other stuff... -->
+ *     <appender-ref ref="jmx" />
+ *   </root>
+ * </code>
+ */
+public class JmxAppender extends AppenderSkeleton {
+  public static final String JMX_OBJECT_DOMAIN = JmxAppender.class.getName();
+  public static final String JMX_OBJECT_TYPE = "jmx-log4j-appender";
+  public static final String JMX_OBJECT_NAME = "jmx-log4j-appender";
+
+  private static final Logger log = Logger.getLogger(JmxAppender.class.getName());
+
+  public JmxAppender() {
+    this(ManagementFactory.getPlatformMBeanServer());
+  }
+
+  /**
+   * Calling the default constructor causes this appender to register JmxLog4J
+   * as a JMX MBean.
+   */
+  public JmxAppender(MBeanServer mbeanServer) {
+    super();
+
+    try {
+      JmxLog4J mbean = new JmxLog4J();
+      ObjectName name = new ObjectName(JMX_OBJECT_DOMAIN + ":type=" + JMX_OBJECT_TYPE + ",name=" + JMX_OBJECT_NAME);
+      mbeanServer.registerMBean(mbean, name);
+    } catch (Exception e) {
+      log.error("Unable to register Log4J MBean.", e);
+    }
+  }
+
+  public void close() {
+    log.debug("Ignoring close call.");
+  }
+
+  public boolean requiresLayout() {
+    log.debug("Ignoring requresLayout call.");
+
+    return false;
+  }
+
+  protected void append(LoggingEvent event) {
+    // No op. We're just using the appender as a convenient way to start the
+    // JmxServer without introducing any dependencies anywhere for Log4J.
+  }
+
+  /**
+   * An MBean to expose Log4J's getLevel and setLevel APIs.
+   */
+  public static interface JmxLog4JMBean {
+    public void setLevel(String level);
+
+    public String getLevel();
+  }
+
+  /**
+   * An implementation of JmxLog4JMBean that calls getLevel and setLevel on the
+   * root logger.
+   */
+  public static class JmxLog4J implements JmxLog4JMBean {
+    public void setLevel(String level) {
+      try {
+        LogManager.getRootLogger().setLevel(Level.toLevel(level));
+      } catch (Exception e) {
+        log.error("Unable to set level to: " + level, e);
+      }
+    }
+
+    public String getLevel() {
+      return LogManager.getRootLogger().getLevel().toString();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/samza-log4j/src/test/java/org/apache/samza/logging/log4j/TestJmxAppender.java
----------------------------------------------------------------------
diff --git a/samza-log4j/src/test/java/org/apache/samza/logging/log4j/TestJmxAppender.java b/samza-log4j/src/test/java/org/apache/samza/logging/log4j/TestJmxAppender.java
new file mode 100644
index 0000000..036d80c
--- /dev/null
+++ b/samza-log4j/src/test/java/org/apache/samza/logging/log4j/TestJmxAppender.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.samza.logging.log4j;
+
+import java.lang.management.ManagementFactory;
+import java.rmi.registry.LocateRegistry;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/*
+ * These tests assume that log4j.xml and log4j are both set on the classpath
+ * with the JmxAppender added as a root-level appender.
+ */
+public class TestJmxAppender {
+  public static final int port = 5500;
+  public static final JMXServiceURL url = getJmxServiceURL();
+  private static JMXConnectorServer cs = null;
+  private static final Logger log = Logger.getLogger(TestJmxAppender.class.getName());
+
+  private static JMXServiceURL getJmxServiceURL() {
+    try {
+      return new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/jmxapitestrmi");
+    } catch(Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @BeforeClass
+  public static void beforeSetupServers() throws Exception {
+    LocateRegistry.createRegistry(port);
+    MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+    cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
+    cs.start();
+  }
+
+  @AfterClass
+  public static void afterCleanLogDirs() throws Exception {
+    if (cs != null) {
+      cs.stop();
+    }
+  }
+
+  @Test
+  public void testJmxAppender() throws Exception {
+    MBeanServerConnection mbserver = JMXConnectorFactory.connect(url).getMBeanServerConnection();
+    ObjectName objectName = new ObjectName(JmxAppender.JMX_OBJECT_DOMAIN + ":type=" + JmxAppender.JMX_OBJECT_TYPE + ",name=" + JmxAppender.JMX_OBJECT_NAME);
+    String level = null;
+    MockAppender mockAppender = new MockAppender();
+    Logger.getRootLogger().addAppender(mockAppender);
+
+    // Check INFO is set (from log4j.xml).
+    level = (String) mbserver.getAttribute(objectName, "Level");
+    assertEquals("INFO", level);
+
+    log.info("info1");
+    log.debug("debug1");
+
+    // Set to debug.
+    mbserver.setAttribute(objectName, new Attribute("Level", "debug"));
+
+    // Check DEBUG is set.
+    level = (String) mbserver.getAttribute(objectName, "Level");
+    assertEquals("DEBUG", level);
+
+    log.info("info2");
+    log.debug("debug2");
+
+    List<LoggingEvent> logLines = mockAppender.getLogLines();
+
+    // Should not have debug1 because log level is info at first.
+    Iterator<LoggingEvent> logLineIterator = logLines.iterator();
+    assertEquals(3, logLines.size());
+    assertEquals("info1", logLineIterator.next().getMessage());
+    assertEquals("info2", logLineIterator.next().getMessage());
+    assertEquals("debug2", logLineIterator.next().getMessage());
+  }
+
+  public static final class MockAppender extends AppenderSkeleton {
+    private final List<LoggingEvent> logLines;
+
+    public MockAppender() {
+      logLines = new ArrayList<LoggingEvent>();
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public boolean requiresLayout() {
+      return false;
+    }
+
+    @Override
+    protected void append(LoggingEvent event) {
+      logLines.add(event);
+    }
+
+    public List<LoggingEvent> getLogLines() {
+      return logLines;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/samza-log4j/src/test/resources/log4j.xml
----------------------------------------------------------------------
diff --git a/samza-log4j/src/test/resources/log4j.xml b/samza-log4j/src/test/resources/log4j.xml
new file mode 100644
index 0000000..acbbc86
--- /dev/null
+++ b/samza-log4j/src/test/resources/log4j.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
+  license agreements. See the NOTICE file distributed with this work for additional
+  information regarding copyright ownership. The ASF licenses this file to
+  you under the Apache License, Version 2.0 (the "License"); you may not use
+  this file except in compliance with the License. You may obtain a copy of
+  the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+  by applicable law or agreed to in writing, software distributed under the
+  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+  OF ANY KIND, either express or implied. See the License for the specific
+  language governing permissions and limitations under the License. -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="jmx" class="org.apache.samza.logging.log4j.JmxAppender">
+  </appender>
+
+  <appender name="console" class="org.apache.log4j.ConsoleAppender">
+    <param name="Target" value="System.out" />
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %c{1} [%p] %m%n" />
+    </layout>
+  </appender>
+
+  <logger name="org.apache.hadoop">
+    <level value="off" />
+  </logger>
+
+  <root>
+    <priority value="info" />
+    <appender-ref ref="console" />
+    <appender-ref ref="jmx" />
+  </root>
+
+</log4j:configuration>

http://git-wip-us.apache.org/repos/asf/incubator-samza/blob/3f3d80eb/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
index db5c32b..325cac2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -16,10 +16,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-include 'samza-api', 'samza-core', 'samza-kafka', 'samza-kv', 'samza-kv-inmemory', 'samza-kv-leveldb', 'samza-serializers', 'samza-shell', 'samza-yarn', 'samza-test'
+include \
+  'samza-api',
+  'samza-core',
+  'samza-kafka',
+  'samza-kv',
+  'samza-kv-inmemory',
+  'samza-kv-leveldb',
+  'samza-log4j',
+  'samza-serializers',
+  'samza-shell',
+  'samza-yarn',
+  'samza-test'
 
 rootProject.children.each {
-  if (it.name != 'samza-api' && it.name != 'samza-shell') {
+  if (it.name != 'samza-api' && it.name != 'samza-shell' && it.name != 'samza-log4j') {
     it.name = it.name + "_" + scalaVersion
   }
 }