And obviously the Bindings variable should not be a class member. Ralph
> On Sep 14, 2016, at 4:33 PM, Ralph Goers <[email protected]> wrote: > > Oops. Never mind. You have if (bindings == null) … Don’t do that. > > Ralph > >> On Sep 14, 2016, at 4:29 PM, Gary Gregory <[email protected] >> <mailto:[email protected]>> wrote: >> >> That's not how I have now. I'll revisit... >> >> >> On Sep 14, 2016 4:27 PM, "Ralph Goers" <[email protected] >> <mailto:[email protected]>> wrote: >> A new Binding is created for every script execution so thread safety is not >> a problem. >> >> Ralph >> >>> On Sep 14, 2016, at 4:09 PM, Remko Popma <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> If the Binding is shared between threads (and therefore not thread-safe), >>> you could put the LogEvent in a ThreadLocal. >>> >>> Sent from my iPhone >>> >>> On 2016/09/15, at 6:24, Gary Gregory <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>>> Ralph: Thank you for the guidance on this topic. >>>> >>>> I'll tackle the documentation update tonight. >>>> >>>> WRT Lookups, that's YAGNI for me ATM. If you have a solution path on that, >>>> I can take a look. >>>> >>>> I am wondering in general about the performance difference from an app POV >>>> between a plain appender and one appender nested in a RoutingAppender. >>>> >>>> I am also wondering about any concurrency issue passing a LogEvent to a >>>> Script. Can a LogEvent be skipped when multiple threads use the same >>>> RoutingAppender? >>>> >>>> Gary >>>> >>>> On Wed, Sep 14, 2016 at 12:50 PM, Ralph Goers <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> Sounds good. >>>> >>>> When documenting that you should make it clear that the Map is shared so >>>> that every thread is seeing the same Map. Users need to be aware that they >>>> cannot put things in the map and expect it to only be available for that >>>> single event. >>>> >>>> The last, and probably trickiest part, is going to be making it so >>>> variables in the Map can be accessed via a Lookup. To be honest, I haven’t >>>> really figured out how to do that. >>>> >>>> Ralph >>>> >>>>> On Sep 14, 2016, at 12:34 PM, Gary Gregory <[email protected] >>>>> <mailto:[email protected]>> wrote: >>>>> >>>>> The RoutingAppender Scripts now share a Bindings instance which contains >>>>> a ConcurrentMap keyed under "staticVariables". The Bindings instance is >>>>> tracked as a RoutingAppender and Routes ivar. >>>>> >>>>> I created an abstract superclass for (private) ScriptRunner >>>>> implementations which holds on to the ConcurrentMap. The map can act as a >>>>> set of static/global variables for that script and can be shared through >>>>> a Bindings instance. The private ScriptRunner has new method >>>>> ScriptManager.ScriptRunner.createBindings(). Right now there is no script >>>>> specific data added to the Bindings, but there could be in the future. >>>>> >>>>> I'll add LogEvent support next... >>>>> >>>>> Gary >>>>> >>>>> On Wed, Sep 14, 2016 at 6:42 AM, Ralph Goers <[email protected] >>>>> <mailto:[email protected]>> wrote: >>>>> OK - It wasn’t in there when I looked last night. >>>>> >>>>> A couple other things. A ConcurrentMap should be created and passed to >>>>> the init script and the routing script so that the init script can pass >>>>> variables to the routing script. Also, the routing script really needs to >>>>> be passed the logEvent so it can route based on data within it. >>>>> >>>>> Ralph >>>>> >>>>>> On Sep 13, 2016, at 10:49 PM, Gary Gregory <[email protected] >>>>>> <mailto:[email protected]>> wrote: >>>>>> >>>>>> On Tue, Sep 13, 2016 at 10:33 PM, Ralph Goers >>>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>>> Gary, >>>>>> >>>>>> RoutingAppender calls routes.getPattern(). Wouldn’t it make sense for >>>>>> the Routes class to execute the script in the call to getPattern and >>>>>> return the result if there is a script? >>>>>> >>>>>> That's what Routes.getPattern() does (see this very commit thread): >>>>>> >>>>>> @@ -90,12 +155,26 @@ public final class Routes { >>>>>> * @return the pattern. >>>>>> */ >>>>>> public String getPattern() { >>>>>> + if (patternScript != null) { >>>>>> + final SimpleBindings bindings = new SimpleBindings(); >>>>>> + bindings.put("configuration", configuration); >>>>>> + bindings.put("statusLogger", LOGGER); >>>>>> + final Object object = >>>>>> configuration.getScriptManager().execute(patternScript.getName(), >>>>>> bindings); >>>>>> + return Objects.toString(object, null); >>>>>> + } >>>>>> return pattern; >>>>>> } >>>>>> >>>>>> Gary >>>>>> >>>>>> >>>>>> Ralph >>>>>> >>>>>> > On Sep 13, 2016, at 10:00 PM, [email protected] >>>>>> > <mailto:[email protected]> wrote: >>>>>> > >>>>>> > Repository: logging-log4j2 >>>>>> > Updated Branches: >>>>>> > refs/heads/master 3846e2a87 -> e0f29d9ad >>>>>> > >>>>>> > >>>>>> > [LOG4J2-1578] RoutingAppender can be configured with scripts. Add >>>>>> > Script >>>>>> > in a Routes element. >>>>>> > >>>>>> > Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo> >>>>>> > Commit: >>>>>> > http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e0f29d9a >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/e0f29d9a> >>>>>> > Tree: >>>>>> > http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e0f29d9a >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/e0f29d9a> >>>>>> > Diff: >>>>>> > http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e0f29d9a >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/e0f29d9a> >>>>>> > >>>>>> > Branch: refs/heads/master >>>>>> > Commit: e0f29d9ad73aa4579cdc2d71ae5e8c01dd4f9f9c >>>>>> > Parents: 3846e2a >>>>>> > Author: Gary Gregory <[email protected] <mailto:[email protected]>> >>>>>> > Authored: Tue Sep 13 21:59:59 2016 -0700 >>>>>> > Committer: Gary Gregory <[email protected] >>>>>> > <mailto:[email protected]>> >>>>>> > Committed: Tue Sep 13 21:59:59 2016 -0700 >>>>>> > >>>>>> > ---------------------------------------------------------------------- >>>>>> > .../log4j/core/appender/routing/Routes.java | 121 ++++++++++++----- >>>>>> > .../core/appender/routing/RoutingAppender.java | 19 ++- >>>>>> > .../routing/RoutesScriptAppenderTest.java | 130 >>>>>> > +++++++++++++++++++ >>>>>> > .../log4j-routing-routes-script-groovy.xml | 43 ++++++ >>>>>> > .../log4j-routing-routes-script-javascript.xml | 40 ++++++ >>>>>> > src/site/xdoc/manual/appenders.xml | 27 +++- >>>>>> > 6 files changed, 340 insertions(+), 40 deletions(-) >>>>>> > ---------------------------------------------------------------------- >>>>>> > >>>>>> > >>>>>> > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/Routes.java >>>>>> > >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/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 c95b64a..33fccd7 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 >>>>>> > @@ -18,11 +18,17 @@ package >>>>>> > org.apache.logging.log4j.core.appender.routing; >>>>>> > >>>>>> > import java.util.Objects; >>>>>> > >>>>>> > +import javax.script.SimpleBindings; >>>>>> > + >>>>>> > import org.apache.logging.log4j.Logger; >>>>>> > +import org.apache.logging.log4j.core.config.Configuration; >>>>>> > import org.apache.logging.log4j.core.config.plugins.Plugin; >>>>>> > import org.apache.logging.log4j.core.config.plugins.PluginAttribute; >>>>>> > 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.config.plugins.validation.constraints.Required; >>>>>> > +import org.apache.logging.log4j.core.script.AbstractScript; >>>>>> > import org.apache.logging.log4j.status.StatusLogger; >>>>>> > >>>>>> > /** >>>>>> > @@ -33,54 +39,113 @@ public final class Routes { >>>>>> > >>>>>> > public static class Builder implements >>>>>> > org.apache.logging.log4j.core.util.Builder<Routes> { >>>>>> > >>>>>> > + @PluginConfiguration >>>>>> > + private Configuration configuration; >>>>>> > + >>>>>> > @PluginAttribute("pattern") >>>>>> > private String pattern; >>>>>> > >>>>>> > - @PluginElement("Routes") >>>>>> > + @PluginElement("Script") >>>>>> > + private AbstractScript patternScript; >>>>>> > + >>>>>> > + @PluginElement("Routes") >>>>>> > + @Required >>>>>> > private Route[] routes; >>>>>> > >>>>>> > @Override >>>>>> > public Routes build() { >>>>>> > if (routes == null || routes.length == 0) { >>>>>> > - LOGGER.error("No routes configured"); >>>>>> > + LOGGER.error("No Routes configured."); >>>>>> > return null; >>>>>> > } >>>>>> > - return new Routes(pattern, routes); >>>>>> > + if (patternScript != null && pattern != null) { >>>>>> > + LOGGER.warn("In a Routes element, you must configure >>>>>> > either a Script element or a pattern attribute."); >>>>>> > + } >>>>>> > + if (patternScript != null) { >>>>>> > + if (configuration == null) { >>>>>> > + LOGGER.error("No Configuration defined for >>>>>> > Routes; required for Script"); >>>>>> > + } else { >>>>>> > + >>>>>> > configuration.getScriptManager().addScript(patternScript); >>>>>> > + } >>>>>> > + } >>>>>> > + return new Routes(configuration, patternScript, pattern, >>>>>> > routes); >>>>>> > + } >>>>>> > + >>>>>> > + public Configuration getConfiguration() { >>>>>> > + return configuration; >>>>>> > } >>>>>> > >>>>>> > public String getPattern() { >>>>>> > return pattern; >>>>>> > } >>>>>> > >>>>>> > + public AbstractScript getPatternScript() { >>>>>> > + return patternScript; >>>>>> > + } >>>>>> > + >>>>>> > public Route[] getRoutes() { >>>>>> > return routes; >>>>>> > } >>>>>> > >>>>>> > - public Builder withPattern(@SuppressWarnings("hiding") String >>>>>> > pattern) { >>>>>> > + public Builder withConfiguration(@SuppressWarnings("hiding") >>>>>> > final Configuration configuration) { >>>>>> > + this.configuration = configuration; >>>>>> > + return this; >>>>>> > + } >>>>>> > + >>>>>> > + public Builder withPattern(@SuppressWarnings("hiding") final >>>>>> > String pattern) { >>>>>> > this.pattern = pattern; >>>>>> > return this; >>>>>> > } >>>>>> > >>>>>> > - public Builder withRoutes(@SuppressWarnings("hiding") Route[] >>>>>> > routes) { >>>>>> > + public Builder withPatternScript(@SuppressWarnings("hiding") >>>>>> > final AbstractScript patternScript) { >>>>>> > + this.patternScript = patternScript; >>>>>> > + return this; >>>>>> > + } >>>>>> > + >>>>>> > + public Builder withRoutes(@SuppressWarnings("hiding") final >>>>>> > Route[] routes) { >>>>>> > this.routes = routes; >>>>>> > return this; >>>>>> > } >>>>>> > >>>>>> > } >>>>>> > >>>>>> > + private static final Logger LOGGER = StatusLogger.getLogger(); >>>>>> > + >>>>>> > + /** >>>>>> > + * Creates the Routes. >>>>>> > + * @param pattern The pattern. >>>>>> > + * @param routes An array of Route elements. >>>>>> > + * @return The Routes container. >>>>>> > + * @deprecated since 2.7; use {@link #newBuilder()}. >>>>>> > + */ >>>>>> > + @Deprecated >>>>>> > + public static Routes createRoutes( >>>>>> > + final String pattern, >>>>>> > + final Route... routes) { >>>>>> > + if (routes == null || routes.length == 0) { >>>>>> > + LOGGER.error("No routes configured"); >>>>>> > + return null; >>>>>> > + } >>>>>> > + return new Routes(null, null, pattern, routes); >>>>>> > + } >>>>>> > + >>>>>> > @PluginBuilderFactory >>>>>> > public static Builder newBuilder() { >>>>>> > return new Builder(); >>>>>> > } >>>>>> > - >>>>>> > - private static final Logger LOGGER = StatusLogger.getLogger(); >>>>>> > - >>>>>> > + >>>>>> > + private final Configuration configuration; >>>>>> > + >>>>>> > private final String pattern; >>>>>> > >>>>>> > + private final AbstractScript patternScript; >>>>>> > + >>>>>> > // TODO Why not make this a Map or add a Map. >>>>>> > private final Route[] routes; >>>>>> > >>>>>> > - private Routes(final String pattern, final Route... routes) { >>>>>> > + private Routes(final Configuration configuration, final >>>>>> > AbstractScript patternScript, final String pattern, final Route... >>>>>> > routes) { >>>>>> > + this.configuration = configuration; >>>>>> > + this.patternScript = patternScript; >>>>>> > this.pattern = pattern; >>>>>> > this.routes = routes; >>>>>> > } >>>>>> > @@ -90,12 +155,26 @@ public final class Routes { >>>>>> > * @return the pattern. >>>>>> > */ >>>>>> > public String getPattern() { >>>>>> > + if (patternScript != null) { >>>>>> > + final SimpleBindings bindings = new SimpleBindings(); >>>>>> > + bindings.put("configuration", configuration); >>>>>> > + bindings.put("statusLogger", LOGGER); >>>>>> > + final Object object = >>>>>> > configuration.getScriptManager().execute(patternScript.getName(), >>>>>> > bindings); >>>>>> > + return Objects.toString(object, null); >>>>>> > + } >>>>>> > return pattern; >>>>>> > } >>>>>> > >>>>>> > - public Route getRoute(String key) { >>>>>> > - for (int i = 0; i < routes.length; i++) { >>>>>> > - final Route route = routes[i]; >>>>>> > + /** >>>>>> > + * Gets the optional script that decides which route to pick. >>>>>> > + * @return the optional script that decides which route to pick. >>>>>> > May be null. >>>>>> > + */ >>>>>> > + public AbstractScript getPatternScript() { >>>>>> > + return patternScript; >>>>>> > + } >>>>>> > + >>>>>> > + public Route getRoute(final String key) { >>>>>> > + for (final Route route : routes) { >>>>>> > if (Objects.equals(route.getKey(), key)) { >>>>>> > return route; >>>>>> > } >>>>>> > @@ -127,22 +206,4 @@ public final class Routes { >>>>>> > >>>>>> > } >>>>>> > >>>>>> > - /** >>>>>> > - * Creates the Routes. >>>>>> > - * @param pattern The pattern. >>>>>> > - * @param routes An array of Route elements. >>>>>> > - * @return The Routes container. >>>>>> > - * @deprecated since 2.7; use {@link #newBuilder()}. >>>>>> > - */ >>>>>> > - @Deprecated >>>>>> > - public static Routes createRoutes( >>>>>> > - final String pattern, >>>>>> > - final Route... routes) { >>>>>> > - 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/e0f29d9a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java >>>>>> > >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/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 4471333..78fddbc 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 >>>>>> > @@ -72,15 +72,16 @@ public final class RoutingAppender extends >>>>>> > AbstractAppender { >>>>>> > >>>>>> > @Override >>>>>> > public RoutingAppender build() { >>>>>> > - if (getName() == null) { >>>>>> > - LOGGER.error("No name defined for RoutingAppender"); >>>>>> > + final String name = getName(); >>>>>> > + if (name == null) { >>>>>> > + LOGGER.error("No name defined for this >>>>>> > RoutingAppender"); >>>>>> > return null; >>>>>> > } >>>>>> > if (routes == null) { >>>>>> > - LOGGER.error("No routes defined for RoutingAppender"); >>>>>> > + LOGGER.error("No routes defined for RoutingAppender >>>>>> > {}", name); >>>>>> > return null; >>>>>> > } >>>>>> > - return new RoutingAppender(getName(), getFilter(), >>>>>> > isIgnoreExceptions(), routes, rewritePolicy, >>>>>> > + return new RoutingAppender(name, getFilter(), >>>>>> > isIgnoreExceptions(), routes, rewritePolicy, >>>>>> > configuration, purgePolicy, defaultRouteScript); >>>>>> > } >>>>>> > >>>>>> > @@ -173,7 +174,7 @@ public final class RoutingAppender extends >>>>>> > AbstractAppender { >>>>>> > public void start() { >>>>>> > if (defaultRouteScript != null) { >>>>>> > if (configuration == null) { >>>>>> > - error("No Configuration defined for RoutingAppender; >>>>>> > required for DefaultRouteScript"); >>>>>> > + error("No Configuration defined for RoutingAppender; >>>>>> > required for Script element."); >>>>>> > } else { >>>>>> > >>>>>> > configuration.getScriptManager().addScript(defaultRouteScript); >>>>>> > final SimpleBindings bindings = new SimpleBindings(); >>>>>> > @@ -352,4 +353,12 @@ public final class RoutingAppender extends >>>>>> > AbstractAppender { >>>>>> > public RewritePolicy getRewritePolicy() { >>>>>> > return rewritePolicy; >>>>>> > } >>>>>> > + >>>>>> > + public Routes getRoutes() { >>>>>> > + return routes; >>>>>> > + } >>>>>> > + >>>>>> > + public Configuration getConfiguration() { >>>>>> > + return configuration; >>>>>> > + } >>>>>> > } >>>>>> > >>>>>> > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutesScriptAppenderTest.java >>>>>> > >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutesScriptAppenderTest.java> >>>>>> > ---------------------------------------------------------------------- >>>>>> > diff --git >>>>>> > a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutesScriptAppenderTest.java >>>>>> > >>>>>> > b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutesScriptAppenderTest.java >>>>>> > new file mode 100644 >>>>>> > index 0000000..7d90f6b >>>>>> > --- /dev/null >>>>>> > +++ >>>>>> > b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutesScriptAppenderTest.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 >>>>>> > <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.core.LogEvent; >>>>>> > +import org.apache.logging.log4j.core.Logger; >>>>>> > +import org.apache.logging.log4j.core.config.AppenderControl; >>>>>> > +import org.apache.logging.log4j.junit.LoggerContextRule; >>>>>> > +import org.apache.logging.log4j.test.appender.ListAppender; >>>>>> > +import org.junit.Assert; >>>>>> > +import org.junit.Rule; >>>>>> > +import org.junit.Test; >>>>>> > +import org.junit.runner.RunWith; >>>>>> > +import org.junit.runners.Parameterized; >>>>>> > + >>>>>> > +/** >>>>>> > + * >>>>>> > + */ >>>>>> > +@RunWith(Parameterized.class) >>>>>> > +public class RoutesScriptAppenderTest { >>>>>> > + >>>>>> > + @Parameterized.Parameters(name = "{0}") >>>>>> > + public static String[] getParameters() { >>>>>> > + return new String[] { >>>>>> > + "log4j-routing-routes-script-groovy.xml", >>>>>> > + "log4j-routing-routes-script-javascript.xml" }; >>>>>> > + } >>>>>> > + >>>>>> > + @Rule >>>>>> > + public final LoggerContextRule loggerContextRule; >>>>>> > + >>>>>> > + public RoutesScriptAppenderTest(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 Logger logger = >>>>>> > loggerContextRule.getLogger(RoutesScriptAppenderTest.class); >>>>>> > + logger.error("Hello"); >>>>>> > + 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); >>>>>> > + logger.error("World"); >>>>>> > + 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 testRoutingAppenderRoutes() { >>>>>> > + final RoutingAppender routingAppender = getRoutingAppender(); >>>>>> > + Assert.assertNull(routingAppender.getDefaultRouteScript()); >>>>>> > + Assert.assertNull(routingAppender.getDefaultRoute()); >>>>>> > + final Routes routes = routingAppender.getRoutes(); >>>>>> > + Assert.assertNotNull(routes); >>>>>> > + Assert.assertNotNull(routes.ge >>>>>> > <http://routes.ge/>tPatternScript()); >>>>>> > + Assert.assertEquals("Service2", routes.getPattern()); >>>>>> > + } >>>>>> > + >>>>>> > + @Test >>>>>> > + public void testRoutingAppenderPresence() { >>>>>> > + getRoutingAppender(); >>>>>> > + } >>>>>> > + >>>>>> > + @Test >>>>>> > + public void testRoutingPresence1() { >>>>>> > + logAndCheck(); >>>>>> > + } >>>>>> > + >>>>>> > + @Test >>>>>> > + public void testRoutingPresence2() { >>>>>> > + logAndCheck(); >>>>>> > + } >>>>>> > +} >>>>>> > >>>>>> > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/log4j-core/src/test/resources/log4j-routing-routes-script-groovy.xml >>>>>> > >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/log4j-core/src/test/resources/log4j-routing-routes-script-groovy.xml> >>>>>> > ---------------------------------------------------------------------- >>>>>> > diff --git >>>>>> > a/log4j-core/src/test/resources/log4j-routing-routes-script-groovy.xml >>>>>> > b/log4j-core/src/test/resources/log4j-routing-routes-script-groovy.xml >>>>>> > new file mode 100644 >>>>>> > index 0000000..83121ea >>>>>> > --- /dev/null >>>>>> > +++ >>>>>> > b/log4j-core/src/test/resources/log4j-routing-routes-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 >>>>>> > <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"> >>>>>> > + <Routes> >>>>>> > + <Script name="RoutingInit" language="groovy"><![CDATA[ >>>>>> > + if ("OSNameFoo".contains("Foo")) { >>>>>> > + return "Service2"; >>>>>> > + } >>>>>> > + return "Service1";]]> >>>>>> > + </Script> >>>>>> > + <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/e0f29d9a/log4j-core/src/test/resources/log4j-routing-routes-script-javascript.xml >>>>>> > >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/log4j-core/src/test/resources/log4j-routing-routes-script-javascript.xml> >>>>>> > ---------------------------------------------------------------------- >>>>>> > diff --git >>>>>> > a/log4j-core/src/test/resources/log4j-routing-routes-script-javascript.xml >>>>>> > >>>>>> > b/log4j-core/src/test/resources/log4j-routing-routes-script-javascript.xml >>>>>> > new file mode 100644 >>>>>> > index 0000000..e672aea >>>>>> > --- /dev/null >>>>>> > +++ >>>>>> > b/log4j-core/src/test/resources/log4j-routing-routes-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 >>>>>> > <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"> >>>>>> > + <Routes> >>>>>> > + <Script name="RoutingInit" language="JavaScript"><![CDATA[ >>>>>> > + "OSNameFoo".search("Foo") > -1 ? "Service2" : "Service1";]]> >>>>>> > + </Script> >>>>>> > + <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/e0f29d9a/src/site/xdoc/manual/appenders.xml >>>>>> > >>>>>> > <http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/e0f29d9a/src/site/xdoc/manual/appenders.xml> >>>>>> > ---------------------------------------------------------------------- >>>>>> > diff --git a/src/site/xdoc/manual/appenders.xml >>>>>> > b/src/site/xdoc/manual/appenders.xml >>>>>> > index 2d3d361..a87d9b7 100644 >>>>>> > --- a/src/site/xdoc/manual/appenders.xml >>>>>> > +++ b/src/site/xdoc/manual/appenders.xml >>>>>> > @@ -3279,7 +3279,11 @@ public class JpaLogEntity extends >>>>>> > AbstractLogEventWrapperEntity { >>>>>> > Appender may be an appender previously configured and may >>>>>> > be referenced by its name or the >>>>>> > Appender can be dynamically created as needed. The >>>>>> > RoutingAppender should be configured after any >>>>>> > Appenders it references to allow it to shut down properly. >>>>>> > - </p> >>>>>> > + </p> >>>>>> > + <p> >>>>>> > + You can also configure a RoutingAppender with scripts: >>>>>> > you can run a script when the appender starts >>>>>> > + and when a route is chosen for an log event. >>>>>> > + </p> >>>>>> > <table> >>>>>> > <caption align="top">RoutingAppender Parameters</caption> >>>>>> > <tr> >>>>>> > @@ -3288,7 +3292,7 @@ public class JpaLogEntity extends >>>>>> > AbstractLogEventWrapperEntity { >>>>>> > <th>Description</th> >>>>>> > </tr> >>>>>> > <tr> >>>>>> > - <td>filter</td> >>>>>> > + <td>Filter</td> >>>>>> > <td>Filter</td> >>>>>> > <td>A Filter to determine if the event should be handled >>>>>> > by this Appender. More than one Filter >>>>>> > may be used by using a CompositeFilter.</td> >>>>>> > @@ -3299,16 +3303,22 @@ public class JpaLogEntity extends >>>>>> > AbstractLogEventWrapperEntity { >>>>>> > <td>The name of the Appender.</td> >>>>>> > </tr> >>>>>> > <tr> >>>>>> > - <td>rewritePolicy</td> >>>>>> > + <td>RewritePolicy</td> >>>>>> > <td>RewritePolicy</td> >>>>>> > <td>The RewritePolicy that will manipulate the >>>>>> > LogEvent.</td> >>>>>> > </tr> >>>>>> > <tr> >>>>>> > - <td>routes</td> >>>>>> > + <td>Routes</td> >>>>>> > <td>Routes</td> >>>>>> > <td>Contains one or more Route declarations to identify >>>>>> > the criteria for choosing Appenders.</td> >>>>>> > </tr> >>>>>> > <tr> >>>>>> > + <td>Script</td> >>>>>> > + <td>Script</td> >>>>>> > + <td>This Script runs when Log4j starts the >>>>>> > RoutingAppender and returns a String Route key to determine >>>>>> > + the default Route.</td> >>>>>> > + </tr> >>>>>> > + <tr> >>>>>> > <td>ignoreExceptions</td> >>>>>> > <td>boolean</td> >>>>>> > <td>The default is <code>true</code>, causing exceptions >>>>>> > encountered while appending events to be >>>>>> > @@ -3319,13 +3329,20 @@ public class JpaLogEntity extends >>>>>> > AbstractLogEventWrapperEntity { >>>>>> > </table> >>>>>> > <h4>Routes</h4> >>>>>> > <p> >>>>>> > - The Routes element accepts a single, required attribute >>>>>> > named "pattern". The pattern is evaluated >>>>>> > + The Routes element accepts a single attribute named >>>>>> > "pattern". The pattern is evaluated >>>>>> > against all the registered Lookups and the result is >>>>>> > used to select a Route. Each Route may be >>>>>> > configured with a key. If the key matches the result of >>>>>> > evaluating the pattern then that Route >>>>>> > will be selected. If no key is specified on a Route then >>>>>> > that Route is the default. Only one Route >>>>>> > can be configured as the default. >>>>>> > </p> >>>>>> > <p> >>>>>> > + The Routes element may contain a Script child element. >>>>>> > If specified, the Script is run for each >>>>>> > + log event and returns the String Route key to use. >>>>>> > + </p> >>>>>> > + <p> >>>>>> > + You must specify either the pattern attribute or the >>>>>> > Script element, but not both. >>>>>> > + </p> >>>>>> > + <p> >>>>>> > Each Route must reference an Appender. If the Route >>>>>> > contains a ref attribute then the >>>>>> > Route will reference an Appender that was defined in the >>>>>> > configuration. If the Route contains an >>>>>> > Appender definition then an Appender will be created >>>>>> > within the context of the RoutingAppender and >>>>>> > >>>>>> > >>>>>> >>>>>> >>>>>> >>>>>> --------------------------------------------------------------------- >>>>>> To unsubscribe, e-mail: [email protected] >>>>>> <mailto:[email protected]> >>>>>> For additional commands, e-mail: [email protected] >>>>>> <mailto:[email protected]> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> -- >>>>>> E-Mail: [email protected] <mailto:[email protected]> | >>>>>> [email protected] <mailto:[email protected]> >>>>>> Java Persistence with Hibernate, Second Edition >>>>>> <http://www.manning.com/bauer3/> >>>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>>> Blog: http://garygregory.wordpress.com >>>>>> <http://garygregory.wordpress.com/> >>>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>>>> >>>>> >>>>> >>>>> -- >>>>> E-Mail: [email protected] <mailto:[email protected]> | >>>>> [email protected] <mailto:[email protected]> >>>>> Java Persistence with Hibernate, Second Edition >>>>> <http://www.manning.com/bauer3/> >>>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>>> Spring Batch in Action <http://www.manning.com/templier/> >>>>> Blog: http://garygregory.wordpress.com >>>>> <http://garygregory.wordpress.com/> >>>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >>>> >>>> >>>> >>>> -- >>>> E-Mail: [email protected] <mailto:[email protected]> | >>>> [email protected] <mailto:[email protected]> >>>> Java Persistence with Hibernate, Second Edition >>>> <http://www.manning.com/bauer3/> >>>> JUnit in Action, Second Edition <http://www.manning.com/tahchiev/> >>>> Spring Batch in Action <http://www.manning.com/templier/> >>>> Blog: http://garygregory.wordpress.com <http://garygregory.wordpress.com/> >>>> Home: http://garygregory.com/ <http://garygregory.com/> >>>> Tweet! http://twitter.com/GaryGregory <http://twitter.com/GaryGregory> >
