You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2016/09/13 09:30:37 UTC
logging-log4j2 git commit: [LOG4J2-1578] RoutingAppender can be
configured with scripts. First implementation of the simplest case:
initialize the default route from a script run at appender start time.
Repository: logging-log4j2
Updated Branches:
refs/heads/master 82cd54656 -> 363bc187d
[LOG4J2-1578] RoutingAppender can be configured with scripts. First
implementation of the simplest case: initialize the default route from a
script run at appender start time.
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/363bc187
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/363bc187
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/363bc187
Branch: refs/heads/master
Commit: 363bc187d96dbd67ae0e2db39637e46d3f1d9d1d
Parents: 82cd546
Author: Gary Gregory <gg...@apache.org>
Authored: Tue Sep 13 02:30:32 2016 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Tue Sep 13 02:30:32 2016 -0700
----------------------------------------------------------------------
.../log4j/core/appender/routing/Routes.java | 23 +++-
.../core/appender/routing/RoutingAppender.java | 90 ++++++++++---
.../routing/DefaultRouteScriptAppenderTest.java | 130 +++++++++++++++++++
...og4j-routing-default-route-script-groovy.xml | 43 ++++++
...-routing-default-route-script-javascript.xml | 40 ++++++
5 files changed, 303 insertions(+), 23 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/363bc187/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/Routes.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/Routes.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/Routes.java
index 63a178c..aae4087 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/Routes.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/Routes.java
@@ -16,6 +16,8 @@
*/
package org.apache.logging.log4j.core.appender.routing;
+import java.util.Objects;
+
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
@@ -24,7 +26,7 @@ import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.status.StatusLogger;
/**
- * Used to contain the individual Route elements.
+ * Contains the individual Route elements.
*/
@Plugin(name = "Routes", category = "Core", printObject = true)
public final class Routes {
@@ -32,6 +34,8 @@ public final class Routes {
private static final Logger LOGGER = StatusLogger.getLogger();
private final String pattern;
+
+ // TODO Why not make this a Map or add a Map.
private final Route[] routes;
private Routes(final String pattern, final Route... routes) {
@@ -47,6 +51,16 @@ public final class Routes {
return pattern;
}
+ public Route getRoute(String key) {
+ for (int i = 0; i < routes.length; i++) {
+ final Route route = routes[i];
+ if (Objects.equals(route.getKey(), key)) {
+ return route;
+ }
+ }
+ return null;
+ }
+
/**
* Returns the array of Route elements.
* @return an array of Route elements.
@@ -72,7 +86,7 @@ public final class Routes {
}
/**
- * Create the Routes.
+ * Creates the Routes.
* @param pattern The pattern.
* @param routes An array of Route elements.
* @return The Routes container.
@@ -81,14 +95,11 @@ public final class Routes {
public static Routes createRoutes(
@PluginAttribute("pattern") final String pattern,
@PluginElement("Routes") final Route... routes) {
- if (pattern == null) {
- LOGGER.error("A pattern is required");
- return null;
- }
if (routes == null || routes.length == 0) {
LOGGER.error("No routes configured");
return null;
}
return new Routes(pattern, routes);
}
+
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/363bc187/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java
index 86427ca..4471333 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java
@@ -18,10 +18,13 @@ package org.apache.logging.log4j.core.appender.routing;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
+import javax.script.SimpleBindings;
+
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
@@ -34,6 +37,7 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.script.AbstractScript;
import org.apache.logging.log4j.core.util.Booleans;
/**
@@ -50,6 +54,10 @@ public final class RoutingAppender extends AbstractAppender {
public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
implements org.apache.logging.log4j.core.util.Builder<RoutingAppender> {
+ // Does not work unless the element is called "Script", I wanted "DefaultRounteScript"...
+ @PluginElement("Script")
+ private AbstractScript defaultRouteScript;
+
@PluginElement("Routes")
private Routes routes;
@@ -65,14 +73,15 @@ public final class RoutingAppender extends AbstractAppender {
@Override
public RoutingAppender build() {
if (getName() == null) {
- LOGGER.error("No name provided for RoutingAppender");
+ LOGGER.error("No name defined for RoutingAppender");
return null;
}
if (routes == null) {
LOGGER.error("No routes defined for RoutingAppender");
return null;
}
- return new RoutingAppender(getName(), getFilter(), isIgnoreExceptions(), routes, rewritePolicy, getConfiguration(), purgePolicy);
+ return new RoutingAppender(getName(), getFilter(), isIgnoreExceptions(), routes, rewritePolicy,
+ configuration, purgePolicy, defaultRouteScript);
}
public Routes getRoutes() {
@@ -83,6 +92,10 @@ public final class RoutingAppender extends AbstractAppender {
return configuration;
}
+ public AbstractScript getDefaultRouteScript() {
+ return defaultRouteScript;
+ }
+
public RewritePolicy getRewritePolicy() {
return rewritePolicy;
}
@@ -91,16 +104,24 @@ public final class RoutingAppender extends AbstractAppender {
return purgePolicy;
}
- public void withRoutes(@SuppressWarnings("hiding") final Routes routes) {
+ public B withRoutes(@SuppressWarnings("hiding") final Routes routes) {
this.routes = routes;
+ return asBuilder();
}
- public void withConfiguration(@SuppressWarnings("hiding") final Configuration configuration) {
+ public B withConfiguration(@SuppressWarnings("hiding") final Configuration configuration) {
this.configuration = configuration;
+ return asBuilder();
}
- public void withRewritePolicy(@SuppressWarnings("hiding") final RewritePolicy rewritePolicy) {
+ public B withDefaultRouteScript(@SuppressWarnings("hiding") AbstractScript defaultRouteScript) {
+ this.defaultRouteScript = defaultRouteScript;
+ return asBuilder();
+ }
+
+ public B withRewritePolicy(@SuppressWarnings("hiding") final RewritePolicy rewritePolicy) {
this.rewritePolicy = rewritePolicy;
+ return asBuilder();
}
public void withPurgePolicy(@SuppressWarnings("hiding") final PurgePolicy purgePolicy) {
@@ -116,22 +137,25 @@ public final class RoutingAppender extends AbstractAppender {
private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
private final Routes routes;
- private final Route defaultRoute;
- private final Configuration config;
+ private Route defaultRoute;
+ private final Configuration configuration;
private final ConcurrentMap<String, AppenderControl> appenders = new ConcurrentHashMap<>();
private final RewritePolicy rewritePolicy;
private final PurgePolicy purgePolicy;
+ private final AbstractScript defaultRouteScript;
private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes,
- final RewritePolicy rewritePolicy, final Configuration config, final PurgePolicy purgePolicy) {
+ final RewritePolicy rewritePolicy, final Configuration configuration, final PurgePolicy purgePolicy,
+ AbstractScript defaultRouteScript) {
super(name, filter, null, ignoreExceptions);
this.routes = routes;
- this.config = config;
+ this.configuration = configuration;
this.rewritePolicy = rewritePolicy;
this.purgePolicy = purgePolicy;
if (this.purgePolicy != null) {
this.purgePolicy.initialize(this);
}
+ this.defaultRouteScript = defaultRouteScript;
Route defRoute = null;
for (final Route route : routes.getRoutes()) {
if (route.getKey() == null) {
@@ -147,15 +171,30 @@ public final class RoutingAppender extends AbstractAppender {
@Override
public void start() {
+ if (defaultRouteScript != null) {
+ if (configuration == null) {
+ error("No Configuration defined for RoutingAppender; required for DefaultRouteScript");
+ } else {
+ configuration.getScriptManager().addScript(defaultRouteScript);
+ final SimpleBindings bindings = new SimpleBindings();
+ bindings.put("configuration", configuration);
+ bindings.put("statusLogger", LOGGER);
+ final Object object = configuration.getScriptManager().execute(defaultRouteScript.getName(), bindings);
+ final Route route = routes.getRoute(Objects.toString(object, null));
+ if (route != null) {
+ defaultRoute = route;
+ }
+ }
+ }
// Register all the static routes.
for (final Route route : routes.getRoutes()) {
if (route.getAppenderRef() != null) {
- final Appender appender = config.getAppender(route.getAppenderRef());
+ final Appender appender = configuration.getAppender(route.getAppenderRef());
if (appender != null) {
final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey();
appenders.put(key, new AppenderControl(appender, null, null));
} else {
- LOGGER.error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
+ error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
}
}
}
@@ -166,7 +205,7 @@ public final class RoutingAppender extends AbstractAppender {
public boolean stop(final long timeout, final TimeUnit timeUnit) {
setStopping();
super.stop(timeout, timeUnit, false);
- final Map<String, Appender> map = config.getAppenders();
+ final Map<String, Appender> map = configuration.getAppenders();
for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) {
final String name = entry.getValue().getAppender().getName();
if (!map.containsKey(name)) {
@@ -182,7 +221,8 @@ public final class RoutingAppender extends AbstractAppender {
if (rewritePolicy != null) {
event = rewritePolicy.rewrite(event);
}
- final String key = config.getStrSubstitutor().replace(event, routes.getPattern());
+ final String pattern = routes.getPattern();
+ final String key = pattern != null ? configuration.getStrSubstitutor().replace(event, pattern) : defaultRoute.getKey();
final AppenderControl control = getControl(key, event);
if (control != null) {
control.callAppender(event);
@@ -229,17 +269,17 @@ public final class RoutingAppender extends AbstractAppender {
for (final Node node : routeNode.getChildren()) {
if (node.getType().getElementName().equals("appender")) {
final Node appNode = new Node(node);
- config.createConfiguration(appNode, event);
+ configuration.createConfiguration(appNode, event);
if (appNode.getObject() instanceof Appender) {
final Appender app = appNode.getObject();
app.start();
return app;
}
- LOGGER.error("Unable to create Appender of type " + node.getName());
+ error("Unable to create Appender of type " + node.getName());
return null;
}
}
- LOGGER.error("No Appender was configured for route " + route.getKey());
+ error("No Appender was configured for route " + route.getKey());
return null;
}
@@ -294,6 +334,22 @@ public final class RoutingAppender extends AbstractAppender {
LOGGER.error("No routes defined for RoutingAppender");
return null;
}
- return new RoutingAppender(name, filter, ignoreExceptions, routes, rewritePolicy, config, purgePolicy);
+ return new RoutingAppender(name, filter, ignoreExceptions, routes, rewritePolicy, config, purgePolicy, null);
+ }
+
+ public Route getDefaultRoute() {
+ return defaultRoute;
+ }
+
+ public AbstractScript getDefaultRouteScript() {
+ return defaultRouteScript;
+ }
+
+ public PurgePolicy getPurgePolicy() {
+ return purgePolicy;
+ }
+
+ public RewritePolicy getRewritePolicy() {
+ return rewritePolicy;
}
}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/363bc187/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
new file mode 100644
index 0000000..44fb6de
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.logging.log4j.core.appender.routing;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.EventLogger;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.AppenderControl;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ *
+ */
+@RunWith(Parameterized.class)
+public class DefaultRouteScriptAppenderTest {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static String[] getParameters() {
+ return new String[] {
+ // @Ignore "log4j-routing-default-route-script-groovy.xml",
+ "log4j-routing-default-route-script-javascript.xml" };
+ }
+
+ @Rule
+ public final LoggerContextRule loggerContextRule;
+
+ public DefaultRouteScriptAppenderTest(final String configLocation) {
+ this.loggerContextRule = new LoggerContextRule(configLocation);
+ }
+
+ private ListAppender getListAppender() {
+ final String key = "Service2";
+ final RoutingAppender routingAppender = getRoutingAppender();
+ Assert.assertTrue(routingAppender.isStarted());
+ final Map<String, AppenderControl> appenders = routingAppender.getAppenders();
+ final AppenderControl appenderControl = appenders.get(key);
+ assertNotNull("No appender control generated for '" + key + "'; appenders = " + appenders, appenderControl);
+ final ListAppender listAppender = (ListAppender) appenderControl.getAppender();
+ return listAppender;
+ }
+
+ private RoutingAppender getRoutingAppender() {
+ return loggerContextRule.getRequiredAppender("Routing", RoutingAppender.class);
+ }
+
+ private void logAndCheck() {
+ final StructuredDataMessage msg = new StructuredDataMessage("Test", "This is a test", "Alert");
+ EventLogger.logEvent(msg);
+ final ListAppender listAppender = getListAppender();
+ final List<LogEvent> list = listAppender.getEvents();
+ assertNotNull("No events generated", list);
+ assertTrue("Incorrect number of events. Expected 1, got " + list.size(), list.size() == 1);
+ EventLogger.logEvent(msg);
+ assertTrue("Incorrect number of events. Expected 2, got " + list.size(), list.size() == 2);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void testAppenderAbsence() {
+ loggerContextRule.getListAppender("List1");
+ }
+
+ @Test
+ public void testListAppenderPresence() {
+ // No appender until an event is routed, even thought we initialized the default route on startup.
+ Assert.assertNull("No appender control generated", getRoutingAppender().getAppenders().get("Service2"));
+ }
+
+ @Test
+ public void testNoPurgePolicy() {
+ // No PurgePolicy in this test
+ Assert.assertNull("Unexpected PurgePolicy", getRoutingAppender().getPurgePolicy());
+ }
+
+ @Test
+ public void testNoRewritePolicy() {
+ // No RewritePolicy in this test
+ Assert.assertNull("Unexpected RewritePolicy", getRoutingAppender().getRewritePolicy());
+ }
+
+ @Test
+ public void testRoutingAppenderDefaultRouteKey() {
+ final RoutingAppender routingAppender = getRoutingAppender();
+ Assert.assertNotNull(routingAppender.getDefaultRouteScript());
+ Assert.assertNotNull(routingAppender.getDefaultRoute());
+ Assert.assertEquals("Service2", routingAppender.getDefaultRoute().getKey());
+ }
+
+ @Test
+ public void testRoutingAppenderPresence() {
+ getRoutingAppender();
+ }
+
+ @Test
+ public void testRoutingPresence1() {
+ logAndCheck();
+ }
+
+ @Test
+ //@Ignore
+ public void testRoutingPresence2() {
+ logAndCheck();
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/363bc187/log4j-core/src/test/resources/log4j-routing-default-route-script-groovy.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-routing-default-route-script-groovy.xml b/log4j-core/src/test/resources/log4j-routing-default-route-script-groovy.xml
new file mode 100644
index 0000000..3d371a9
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-routing-default-route-script-groovy.xml
@@ -0,0 +1,43 @@
+<?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.
+
+-->
+<Configuration status="WARN" name="RoutingTest">
+ <Appenders>
+ <Routing name="Routing">
+ <Script name="RoutingInit" language="groovy"><![CDATA[
+ if ("OSNameFoo".contains("Foo")) {
+ return "Service2";
+ }
+ return "Service1";]]>
+ </Script>
+ <Routes>
+ <Route key="Service1">
+ <List name="List1" />
+ </Route>
+ <Route key="Service2">
+ <List name="List2" />
+ </Route>
+ </Routes>
+ </Routing>
+ </Appenders>
+ <Loggers>
+ <Root level="error">
+ <AppenderRef ref="Routing" />
+ </Root>
+ </Loggers>
+</Configuration>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/363bc187/log4j-core/src/test/resources/log4j-routing-default-route-script-javascript.xml
----------------------------------------------------------------------
diff --git a/log4j-core/src/test/resources/log4j-routing-default-route-script-javascript.xml b/log4j-core/src/test/resources/log4j-routing-default-route-script-javascript.xml
new file mode 100644
index 0000000..abe99ea
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-routing-default-route-script-javascript.xml
@@ -0,0 +1,40 @@
+<?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.
+
+-->
+<Configuration status="WARN" name="RoutingTest">
+ <Appenders>
+ <Routing name="Routing">
+ <Script name="RoutingInit" language="JavaScript"><![CDATA[
+ "OSNameFoo".search("Foo") > -1 ? "Service2" : "Service1";]]>
+ </Script>
+ <Routes>
+ <Route key="Service1">
+ <List name="List1" />
+ </Route>
+ <Route key="Service2">
+ <List name="List2" />
+ </Route>
+ </Routes>
+ </Routing>
+ </Appenders>
+ <Loggers>
+ <Root level="error">
+ <AppenderRef ref="Routing" />
+ </Root>
+ </Loggers>
+</Configuration>
\ No newline at end of file