OK, I've just reworked the code. Bindings are no longer shared, only the ConcurrentMap is shared. Please review.
Gary On Wed, Sep 14, 2016 at 4:38 PM, Ralph Goers <[email protected]> wrote: > 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]> wrote: > > That's not how I have now. I'll revisit... > > On Sep 14, 2016 4:27 PM, "Ralph Goers" <[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]> 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]> 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] >> > 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]> >>> 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] >>> > 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]> >>>> wrote: >>>> >>>> On Tue, Sep 13, 2016 at 10:33 PM, Ralph Goers <ralph.goers@dslextreme. >>>> com> 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] 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 >>>>> > Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j >>>>> 2/commit/e0f29d9a >>>>> > Tree: 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 >>>>> > >>>>> > Branch: refs/heads/master >>>>> > Commit: e0f29d9ad73aa4579cdc2d71ae5e8c01dd4f9f9c >>>>> > Parents: 3846e2a >>>>> > Author: Gary Gregory <[email protected]> >>>>> > Authored: Tue Sep 13 21:59:59 2016 -0700 >>>>> > Committer: Gary Gregory <[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 >>>>> > ------------------------------------------------------------ >>>>> ---------- >>>>> > 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/app >>>>> ender/routing/Routes.java >>>>> > index c95b64a..33fccd7 100644 >>>>> > --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/app >>>>> ender/routing/Routes.java >>>>> > +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/app >>>>> ender/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.PluginBuilderFa >>>>> ctory; >>>>> > +import org.apache.logging.log4j.core.config.plugins.PluginConfigura >>>>> tion; >>>>> > import org.apache.logging.log4j.core.config.plugins.PluginElement; >>>>> > +import org.apache.logging.log4j.core.config.plugins.validation.cons >>>>> traints.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 >>>>> > ------------------------------------------------------------ >>>>> ---------- >>>>> > 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/app >>>>> ender/routing/RoutingAppender.java >>>>> > index 4471333..78fddbc 100644 >>>>> > --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/app >>>>> ender/routing/RoutingAppender.java >>>>> > +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/app >>>>> ender/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.getScriptManage >>>>> r().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 >>>>> > ------------------------------------------------------------ >>>>> ---------- >>>>> > 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/app >>>>> ender/routing/RoutesScriptAppenderTest.java >>>>> > new file mode 100644 >>>>> > index 0000000..7d90f6b >>>>> > --- /dev/null >>>>> > +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/app >>>>> ender/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 >>>>> > + * >>>>> > + * 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(configLocati >>>>> on); >>>>> > + } >>>>> > + >>>>> > + 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(Ro >>>>> utesScriptAppenderTest.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.getPatternScript()); >>>>> > + 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 >>>>> > ------------------------------------------------------------ >>>>> ---------- >>>>> > diff --git a/log4j-core/src/test/resource >>>>> s/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 >>>>> > + >>>>> > + 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 >>>>> > ------------------------------------------------------------ >>>>> ---------- >>>>> > diff --git a/log4j-core/src/test/resource >>>>> s/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 >>>>> > + >>>>> > + 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 >>>>> > ------------------------------------------------------------ >>>>> ---------- >>>>> > 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] >>>>> For additional commands, e-mail: [email protected] >>>>> >>>>> >>>> >>>> >>>> -- >>>> E-Mail: [email protected] | [email protected] >>>> <[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 >>>> Home: http://garygregory.com/ >>>> Tweet! http://twitter.com/GaryGregory >>>> >>>> >>>> >>> >>> >>> -- >>> E-Mail: [email protected] | [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 >>> Home: http://garygregory.com/ >>> Tweet! http://twitter.com/GaryGregory >>> >>> >>> >> >> >> -- >> E-Mail: [email protected] | [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 >> Home: http://garygregory.com/ >> Tweet! http://twitter.com/GaryGregory >> >> >> > > -- E-Mail: [email protected] | [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 Home: http://garygregory.com/ Tweet! http://twitter.com/GaryGregory
