I just looked at the code. Sure looks ok to me. I’ll look again….

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] 
> <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>

Reply via email to