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 <[email protected]> Authored: Tue Aug 5 09:34:46 2014 -0700 Committer: Chris Riccomini <[email protected]> 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 } }
