And I am not worried about concurrency around the log event. Gary
On Wed, Sep 14, 2016 at 6:59 PM, Gary Gregory <[email protected]> wrote: > 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.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 >>>>>> > ------------------------------------------------------------ >>>>>> ---------- >>>>>> > 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(routingAppen >>>>>> der.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 > -- 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
