Repository: logging-log4j2
Updated Branches:
  refs/heads/master 329cd77fb -> 361785ab7


[LOG4J2-1578] RoutingAppender can be configured with scripts. 

Add a "logEvent" variable as input to the Routes script.

Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/361785ab
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/361785ab
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/361785ab

Branch: refs/heads/master
Commit: 361785ab72419c8409f79d417f51982374ad7c97
Parents: 329cd77
Author: ggregory <[email protected]>
Authored: Wed Sep 14 14:19:52 2016 -0700
Committer: ggregory <[email protected]>
Committed: Wed Sep 14 14:19:52 2016 -0700

----------------------------------------------------------------------
 .../log4j/core/appender/routing/Routes.java     | 448 +++++------
 .../core/appender/routing/RoutingAppender.java  | 742 +++++++++----------
 .../routing/DefaultRouteScriptAppenderTest.java |   6 +
 .../routing/RoutesScriptAppenderTest.java       |   2 +-
 .../log4j-routing-script-staticvars-groovy.xml  |   3 +
 ...g4j-routing-script-staticvars-javascript.xml |   3 +
 6 files changed, 611 insertions(+), 593 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/361785ab/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 4a62542..0fbbdc4 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
@@ -1,221 +1,227 @@
-/*
- * 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 java.util.Objects;
-
-import javax.script.Bindings;
-
-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.core.script.ScriptManager;
-import org.apache.logging.log4j.status.StatusLogger;
-
-/**
- * Contains the individual Route elements.
- */
-@Plugin(name = "Routes", category = "Core", printObject = true)
-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("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.");
-                return null;
-            }
-            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 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 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 Bindings bindings;
-    
-    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 Configuration configuration, final AbstractScript 
patternScript, final String pattern, final Route... routes) {
-        this.configuration = configuration;
-        this.patternScript = patternScript;
-        this.pattern = pattern;
-        this.routes = routes;
-    }
-
-    public Bindings getBindings() {
-        return bindings;
-    }
-
-    /**
-     * Returns the pattern.
-     * @return the pattern.
-     */
-    public String getPattern() {
-        if (patternScript != null) {
-            final ScriptManager scriptManager = 
configuration.getScriptManager();
-            if (bindings == null) {
-                bindings = scriptManager.createBindings(patternScript);
-            }
-            final Object object = 
scriptManager.execute(patternScript.getName(), bindings);
-            return Objects.toString(object, null);
-        }
-        return pattern;
-    }
-
-    /**
-     * 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;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the array of Route elements.
-     * @return an array of Route elements.
-     */
-    public Route[] getRoutes() {
-        return routes;
-    }
-
-    public void setBindings(final Bindings bindings) {
-        this.bindings = bindings;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder("{");
-        boolean first = true;
-        for (final Route route : routes) {
-            if (!first) {
-                sb.append(',');
-            }
-            first = false;
-            sb.append(route.toString());
-        }
-        sb.append('}');
-        return sb.toString();
-
-    }
-
-}
+/*
+ * 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 java.util.Objects;
+
+import javax.script.Bindings;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.LogEvent;
+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.core.script.ScriptManager;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Contains the individual Route elements.
+ */
+@Plugin(name = "Routes", category = "Core", printObject = true)
+public final class Routes {
+
+    private static final String LOG_EVENT_KEY = "logEvent";
+
+    public static class Builder implements 
org.apache.logging.log4j.core.util.Builder<Routes>  {
+
+        @PluginConfiguration 
+        private Configuration configuration;
+
+        @PluginAttribute("pattern") 
+        private String pattern;
+        
+        @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.");
+                return null;
+            }
+            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 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 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 Bindings bindings;
+    
+    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 Configuration configuration, final AbstractScript 
patternScript, final String pattern, final Route... routes) {
+        this.configuration = configuration;
+        this.patternScript = patternScript;
+        this.pattern = pattern;
+        this.routes = routes;
+    }
+
+    public Bindings getBindings() {
+        return bindings;
+    }
+
+    /**
+     * Returns the pattern.
+     * @param event The log event passed to the script (if there is a script.)
+     * @return the pattern.
+     */
+    public String getPattern(final LogEvent event) {
+        if (patternScript != null) {
+            final ScriptManager scriptManager = 
configuration.getScriptManager();
+            if (bindings == null) {
+                bindings = scriptManager.createBindings(patternScript);
+            }
+            bindings.put(LOG_EVENT_KEY, event);
+            final Object object = 
scriptManager.execute(patternScript.getName(), bindings);
+            bindings.remove(LOG_EVENT_KEY);
+            return Objects.toString(object, null);
+        }
+        return pattern;
+    }
+
+    /**
+     * 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;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the array of Route elements.
+     * @return an array of Route elements.
+     */
+    public Route[] getRoutes() {
+        return routes;
+    }
+
+    public void setBindings(final Bindings bindings) {
+        this.bindings = bindings;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("{");
+        boolean first = true;
+        for (final Route route : routes) {
+            if (!first) {
+                sb.append(',');
+            }
+            first = false;
+            sb.append(route.toString());
+        }
+        sb.append('}');
+        return sb.toString();
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/361785ab/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 bedd8bf..e823484 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
@@ -1,371 +1,371 @@
-/*
- * 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 java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-
-import javax.script.Bindings;
-
-import org.apache.logging.log4j.core.Appender;
-import org.apache.logging.log4j.core.Filter;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
-import org.apache.logging.log4j.core.config.AppenderControl;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.Node;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
-import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.script.AbstractScript;
-import org.apache.logging.log4j.core.script.ScriptManager;
-import org.apache.logging.log4j.core.util.Booleans;
-
-/**
- * This Appender "routes" between various Appenders, some of which can be 
references to
- * Appenders defined earlier in the configuration while others can be 
dynamically created
- * within this Appender as required. Routing is achieved by specifying a 
pattern on
- * the Routing appender declaration. The pattern should contain one or more 
substitution patterns of
- * the form "$${[key:]token}". The pattern will be resolved each time the 
Appender is called using
- * the built in StrSubstitutor and the StrLookup plugin that matches the 
specified key.
- */
-@Plugin(name = "Routing", category = "Core", elementType = "appender", 
printObject = true)
-public final class RoutingAppender extends AbstractAppender {
-    
-    public static class Builder<B extends Builder<B>> extends 
AbstractAppender.Builder<B>
-            implements 
org.apache.logging.log4j.core.util.Builder<RoutingAppender> {
-                
-        // Does not work unless the element is called "Script", I wanted 
"DefaultRounteScript"...
-        @PluginElement("Script")
-        private AbstractScript defaultRouteScript;
-        
-        @PluginElement("Routes") 
-        private Routes routes;
-        
-        @PluginConfiguration 
-        private Configuration configuration;
-        
-        @PluginElement("RewritePolicy") 
-        private RewritePolicy rewritePolicy;
-        
-        @PluginElement("PurgePolicy") 
-        private PurgePolicy purgePolicy;
-        
-        @Override
-        public RoutingAppender build() {
-            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 {}", name);
-                return null;
-            }
-            return new RoutingAppender(name, getFilter(), 
isIgnoreExceptions(), routes, rewritePolicy,
-                    configuration, purgePolicy, defaultRouteScript);
-        }
-
-        public Routes getRoutes() {
-            return routes;
-        }
-
-        public Configuration getConfiguration() {
-            return configuration;
-        }
-
-        public AbstractScript getDefaultRouteScript() {
-            return defaultRouteScript;
-        }
-
-        public RewritePolicy getRewritePolicy() {
-            return rewritePolicy;
-        }
-
-        public PurgePolicy getPurgePolicy() {
-            return purgePolicy;
-        }
-
-        public B withRoutes(@SuppressWarnings("hiding") final Routes routes) {
-            this.routes = routes;
-            return asBuilder();
-        }
-
-        public B withConfiguration(@SuppressWarnings("hiding") final 
Configuration configuration) {
-            this.configuration = configuration;
-            return asBuilder();
-        }
-
-        public B withDefaultRouteScript(@SuppressWarnings("hiding") 
AbstractScript defaultRouteScript) {
-            this.defaultRouteScript = defaultRouteScript;
-            return asBuilder();
-        }
-        
-        public B withRewritePolicy(@SuppressWarnings("hiding") final 
RewritePolicy rewritePolicy) {
-            this.rewritePolicy = rewritePolicy;
-            return asBuilder();
-        }
-
-        public void withPurgePolicy(@SuppressWarnings("hiding") final 
PurgePolicy purgePolicy) {
-            this.purgePolicy = purgePolicy;
-        }
-
-    }
-    
-    @PluginBuilderFactory
-    public static <B extends Builder<B>> B newBuilder() {
-        return new Builder<B>().asBuilder();
-    }
-
-    private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
-    
-    private final Routes routes;
-    private Route defaultRoute;
-    private final Configuration configuration;
-    private final ConcurrentMap<String, AppenderControl> appenders = new 
ConcurrentHashMap<>();
-    private final RewritePolicy rewritePolicy;
-    private final PurgePolicy purgePolicy;
-    private final AbstractScript defaultRouteScript;
-    private Bindings bindings;
-    
-    private RoutingAppender(final String name, final Filter filter, final 
boolean ignoreExceptions, final Routes routes,
-            final RewritePolicy rewritePolicy, final Configuration 
configuration, final PurgePolicy purgePolicy,
-            AbstractScript defaultRouteScript) {
-        super(name, filter, null, ignoreExceptions);
-        this.routes = routes;
-        this.configuration = configuration;
-        this.rewritePolicy = rewritePolicy;
-        this.purgePolicy = purgePolicy;
-        if (this.purgePolicy != null) {
-            this.purgePolicy.initialize(this);
-        }
-        this.defaultRouteScript = defaultRouteScript;
-        Route defRoute = null;
-        for (final Route route : routes.getRoutes()) {
-            if (route.getKey() == null) {
-                if (defRoute == null) {
-                    defRoute = route;
-                } else {
-                    error("Multiple default routes. Route " + route.toString() 
+ " will be ignored");
-                }
-            }
-        }
-        defaultRoute = defRoute;
-    }
-
-    @Override
-    public void start() {
-        if (defaultRouteScript != null) {
-            if (configuration == null) {
-                error("No Configuration defined for RoutingAppender; required 
for Script element.");
-            } else {
-                final ScriptManager scriptManager = 
configuration.getScriptManager();
-                scriptManager.addScript(defaultRouteScript);
-                bindings = scriptManager.createBindings(defaultRouteScript);
-                routes.setBindings(bindings);
-                final Object object = 
scriptManager.execute(defaultRouteScript.getName(), bindings);
-                final Route route = routes.getRoute(Objects.toString(object, 
null));
-                if (route != null) {
-                    defaultRoute = route;
-                }
-            }
-        }
-        // Register all the static routes.
-        for (final Route route : routes.getRoutes()) {
-            if (route.getAppenderRef() != null) {
-                final Appender appender = 
configuration.getAppender(route.getAppenderRef());
-                if (appender != null) {
-                    final String key = route == defaultRoute ? DEFAULT_KEY : 
route.getKey();
-                    appenders.put(key, new AppenderControl(appender, null, 
null));
-                } else {
-                    error("Appender " + route.getAppenderRef() + " cannot be 
located. Route ignored");
-                }
-            }
-        }
-        super.start();
-    }
-
-    @Override
-    public boolean stop(final long timeout, final TimeUnit timeUnit) {
-        setStopping();
-        super.stop(timeout, timeUnit, false);
-        final Map<String, Appender> map = configuration.getAppenders();
-        for (final Map.Entry<String, AppenderControl> entry : 
appenders.entrySet()) {
-            final String name = entry.getValue().getAppender().getName();
-            if (!map.containsKey(name)) {
-                entry.getValue().getAppender().stop(timeout, timeUnit);
-            }
-        }
-        setStopped();
-        return true;
-    }
-
-    @Override
-    public void append(LogEvent event) {
-        if (rewritePolicy != null) {
-            event = rewritePolicy.rewrite(event);
-        }
-        final String pattern = routes.getPattern();
-        final String key = pattern != null ? 
configuration.getStrSubstitutor().replace(event, pattern) : 
defaultRoute.getKey();
-        final AppenderControl control = getControl(key, event);
-        if (control != null) {
-            control.callAppender(event);
-        }
-
-        if (purgePolicy != null) {
-            purgePolicy.update(key, event);
-        }
-    }
-
-    private synchronized AppenderControl getControl(final String key, final 
LogEvent event) {
-        AppenderControl control = appenders.get(key);
-        if (control != null) {
-            return control;
-        }
-        Route route = null;
-        for (final Route r : routes.getRoutes()) {
-            if (r.getAppenderRef() == null && key.equals(r.getKey())) {
-                route = r;
-                break;
-            }
-        }
-        if (route == null) {
-            route = defaultRoute;
-            control = appenders.get(DEFAULT_KEY);
-            if (control != null) {
-                return control;
-            }
-        }
-        if (route != null) {
-            final Appender app = createAppender(route, event);
-            if (app == null) {
-                return null;
-            }
-            control = new AppenderControl(app, null, null);
-            appenders.put(key, control);
-        }
-
-        return control;
-    }
-
-    private Appender createAppender(final Route route, final LogEvent event) {
-        final Node routeNode = route.getNode();
-        for (final Node node : routeNode.getChildren()) {
-            if (node.getType().getElementName().equals("appender")) {
-                final Node appNode = new Node(node);
-                configuration.createConfiguration(appNode, event);
-                if (appNode.getObject() instanceof Appender) {
-                    final Appender app = appNode.getObject();
-                    app.start();
-                    return app;
-                }
-                error("Unable to create Appender of type " + node.getName());
-                return null;
-            }
-        }
-        error("No Appender was configured for route " + route.getKey());
-        return null;
-    }
-
-    public Map<String, AppenderControl> getAppenders() {
-        return Collections.unmodifiableMap(appenders);
-    }
-
-    /**
-     * Deletes the specified appender.
-     *
-     * @param key The appender's key
-     */
-    public void deleteAppender(final String key) {
-        LOGGER.debug("Deleting route with " + key + " key ");
-        final AppenderControl control = appenders.remove(key);
-        if (null != control) {
-            LOGGER.debug("Stopping route with " + key + " key");
-            control.getAppender().stop();
-        } else {
-            LOGGER.debug("Route with " + key + " key already deleted");
-        }
-    }
-
-    /**
-     * Creates a RoutingAppender.
-     * @param name The name of the Appender.
-     * @param ignore If {@code "true"} (default) exceptions encountered when 
appending events are logged; otherwise
-     *               they are propagated to the caller.
-     * @param routes The routing definitions.
-     * @param config The Configuration (automatically added by the 
Configuration).
-     * @param rewritePolicy A RewritePolicy, if any.
-     * @param filter A Filter to restrict events processed by the Appender or 
null.
-     * @return The RoutingAppender
-     * @deprecated Since 2.7; use {@link #newBuilder()}
-     */
-    @Deprecated
-    public static RoutingAppender createAppender(
-            final String name,
-            final String ignore,
-            final Routes routes,
-            final Configuration config,
-            final RewritePolicy rewritePolicy,
-            final PurgePolicy purgePolicy,
-            final Filter filter) {
-
-        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
-        if (name == null) {
-            LOGGER.error("No name provided for RoutingAppender");
-            return null;
-        }
-        if (routes == null) {
-            LOGGER.error("No routes defined for RoutingAppender");
-            return null;
-        }
-        return new RoutingAppender(name, filter, ignoreExceptions, routes, 
rewritePolicy, config, purgePolicy, null);
-    }
-
-    public Route getDefaultRoute() {
-        return defaultRoute;
-    }
-
-    public AbstractScript getDefaultRouteScript() {
-        return defaultRouteScript;
-    }
-
-    public PurgePolicy getPurgePolicy() {
-        return purgePolicy;
-    }
-
-    public RewritePolicy getRewritePolicy() {
-        return rewritePolicy;
-    }
-
-    public Routes getRoutes() {
-        return routes;
-    }
-
-    public Configuration getConfiguration() {
-        return configuration;
-    }
-
-    public Bindings getBindings() {
-        return bindings;
-    }
-}
+/*
+ * 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 java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import javax.script.Bindings;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.logging.log4j.core.config.AppenderControl;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.script.AbstractScript;
+import org.apache.logging.log4j.core.script.ScriptManager;
+import org.apache.logging.log4j.core.util.Booleans;
+
+/**
+ * This Appender "routes" between various Appenders, some of which can be 
references to
+ * Appenders defined earlier in the configuration while others can be 
dynamically created
+ * within this Appender as required. Routing is achieved by specifying a 
pattern on
+ * the Routing appender declaration. The pattern should contain one or more 
substitution patterns of
+ * the form "$${[key:]token}". The pattern will be resolved each time the 
Appender is called using
+ * the built in StrSubstitutor and the StrLookup plugin that matches the 
specified key.
+ */
+@Plugin(name = "Routing", category = "Core", elementType = "appender", 
printObject = true)
+public final class RoutingAppender extends AbstractAppender {
+    
+    public static class Builder<B extends Builder<B>> extends 
AbstractAppender.Builder<B>
+            implements 
org.apache.logging.log4j.core.util.Builder<RoutingAppender> {
+                
+        // Does not work unless the element is called "Script", I wanted 
"DefaultRounteScript"...
+        @PluginElement("Script")
+        private AbstractScript defaultRouteScript;
+        
+        @PluginElement("Routes") 
+        private Routes routes;
+        
+        @PluginConfiguration 
+        private Configuration configuration;
+        
+        @PluginElement("RewritePolicy") 
+        private RewritePolicy rewritePolicy;
+        
+        @PluginElement("PurgePolicy") 
+        private PurgePolicy purgePolicy;
+        
+        @Override
+        public RoutingAppender build() {
+            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 {}", name);
+                return null;
+            }
+            return new RoutingAppender(name, getFilter(), 
isIgnoreExceptions(), routes, rewritePolicy,
+                    configuration, purgePolicy, defaultRouteScript);
+        }
+
+        public Routes getRoutes() {
+            return routes;
+        }
+
+        public Configuration getConfiguration() {
+            return configuration;
+        }
+
+        public AbstractScript getDefaultRouteScript() {
+            return defaultRouteScript;
+        }
+
+        public RewritePolicy getRewritePolicy() {
+            return rewritePolicy;
+        }
+
+        public PurgePolicy getPurgePolicy() {
+            return purgePolicy;
+        }
+
+        public B withRoutes(@SuppressWarnings("hiding") final Routes routes) {
+            this.routes = routes;
+            return asBuilder();
+        }
+
+        public B withConfiguration(@SuppressWarnings("hiding") final 
Configuration configuration) {
+            this.configuration = configuration;
+            return asBuilder();
+        }
+
+        public B withDefaultRouteScript(@SuppressWarnings("hiding") final 
AbstractScript defaultRouteScript) {
+            this.defaultRouteScript = defaultRouteScript;
+            return asBuilder();
+        }
+        
+        public B withRewritePolicy(@SuppressWarnings("hiding") final 
RewritePolicy rewritePolicy) {
+            this.rewritePolicy = rewritePolicy;
+            return asBuilder();
+        }
+
+        public void withPurgePolicy(@SuppressWarnings("hiding") final 
PurgePolicy purgePolicy) {
+            this.purgePolicy = purgePolicy;
+        }
+
+    }
+    
+    @PluginBuilderFactory
+    public static <B extends Builder<B>> B newBuilder() {
+        return new Builder<B>().asBuilder();
+    }
+
+    private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
+    
+    private final Routes routes;
+    private Route defaultRoute;
+    private final Configuration configuration;
+    private final ConcurrentMap<String, AppenderControl> appenders = new 
ConcurrentHashMap<>();
+    private final RewritePolicy rewritePolicy;
+    private final PurgePolicy purgePolicy;
+    private final AbstractScript defaultRouteScript;
+    private Bindings bindings;
+    
+    private RoutingAppender(final String name, final Filter filter, final 
boolean ignoreExceptions, final Routes routes,
+            final RewritePolicy rewritePolicy, final Configuration 
configuration, final PurgePolicy purgePolicy,
+            final AbstractScript defaultRouteScript) {
+        super(name, filter, null, ignoreExceptions);
+        this.routes = routes;
+        this.configuration = configuration;
+        this.rewritePolicy = rewritePolicy;
+        this.purgePolicy = purgePolicy;
+        if (this.purgePolicy != null) {
+            this.purgePolicy.initialize(this);
+        }
+        this.defaultRouteScript = defaultRouteScript;
+        Route defRoute = null;
+        for (final Route route : routes.getRoutes()) {
+            if (route.getKey() == null) {
+                if (defRoute == null) {
+                    defRoute = route;
+                } else {
+                    error("Multiple default routes. Route " + route.toString() 
+ " will be ignored");
+                }
+            }
+        }
+        defaultRoute = defRoute;
+    }
+
+    @Override
+    public void start() {
+        if (defaultRouteScript != null) {
+            if (configuration == null) {
+                error("No Configuration defined for RoutingAppender; required 
for Script element.");
+            } else {
+                final ScriptManager scriptManager = 
configuration.getScriptManager();
+                scriptManager.addScript(defaultRouteScript);
+                bindings = scriptManager.createBindings(defaultRouteScript);
+                routes.setBindings(bindings);
+                final Object object = 
scriptManager.execute(defaultRouteScript.getName(), bindings);
+                final Route route = routes.getRoute(Objects.toString(object, 
null));
+                if (route != null) {
+                    defaultRoute = route;
+                }
+            }
+        }
+        // Register all the static routes.
+        for (final Route route : routes.getRoutes()) {
+            if (route.getAppenderRef() != null) {
+                final Appender appender = 
configuration.getAppender(route.getAppenderRef());
+                if (appender != null) {
+                    final String key = route == defaultRoute ? DEFAULT_KEY : 
route.getKey();
+                    appenders.put(key, new AppenderControl(appender, null, 
null));
+                } else {
+                    error("Appender " + route.getAppenderRef() + " cannot be 
located. Route ignored");
+                }
+            }
+        }
+        super.start();
+    }
+
+    @Override
+    public boolean stop(final long timeout, final TimeUnit timeUnit) {
+        setStopping();
+        super.stop(timeout, timeUnit, false);
+        final Map<String, Appender> map = configuration.getAppenders();
+        for (final Map.Entry<String, AppenderControl> entry : 
appenders.entrySet()) {
+            final String name = entry.getValue().getAppender().getName();
+            if (!map.containsKey(name)) {
+                entry.getValue().getAppender().stop(timeout, timeUnit);
+            }
+        }
+        setStopped();
+        return true;
+    }
+
+    @Override
+    public void append(LogEvent event) {
+        if (rewritePolicy != null) {
+            event = rewritePolicy.rewrite(event);
+        }
+        final String pattern = routes.getPattern(event);
+        final String key = pattern != null ? 
configuration.getStrSubstitutor().replace(event, pattern) : 
defaultRoute.getKey();
+        final AppenderControl control = getControl(key, event);
+        if (control != null) {
+            control.callAppender(event);
+        }
+
+        if (purgePolicy != null) {
+            purgePolicy.update(key, event);
+        }
+    }
+
+    private synchronized AppenderControl getControl(final String key, final 
LogEvent event) {
+        AppenderControl control = appenders.get(key);
+        if (control != null) {
+            return control;
+        }
+        Route route = null;
+        for (final Route r : routes.getRoutes()) {
+            if (r.getAppenderRef() == null && key.equals(r.getKey())) {
+                route = r;
+                break;
+            }
+        }
+        if (route == null) {
+            route = defaultRoute;
+            control = appenders.get(DEFAULT_KEY);
+            if (control != null) {
+                return control;
+            }
+        }
+        if (route != null) {
+            final Appender app = createAppender(route, event);
+            if (app == null) {
+                return null;
+            }
+            control = new AppenderControl(app, null, null);
+            appenders.put(key, control);
+        }
+
+        return control;
+    }
+
+    private Appender createAppender(final Route route, final LogEvent event) {
+        final Node routeNode = route.getNode();
+        for (final Node node : routeNode.getChildren()) {
+            if (node.getType().getElementName().equals("appender")) {
+                final Node appNode = new Node(node);
+                configuration.createConfiguration(appNode, event);
+                if (appNode.getObject() instanceof Appender) {
+                    final Appender app = appNode.getObject();
+                    app.start();
+                    return app;
+                }
+                error("Unable to create Appender of type " + node.getName());
+                return null;
+            }
+        }
+        error("No Appender was configured for route " + route.getKey());
+        return null;
+    }
+
+    public Map<String, AppenderControl> getAppenders() {
+        return Collections.unmodifiableMap(appenders);
+    }
+
+    /**
+     * Deletes the specified appender.
+     *
+     * @param key The appender's key
+     */
+    public void deleteAppender(final String key) {
+        LOGGER.debug("Deleting route with " + key + " key ");
+        final AppenderControl control = appenders.remove(key);
+        if (null != control) {
+            LOGGER.debug("Stopping route with " + key + " key");
+            control.getAppender().stop();
+        } else {
+            LOGGER.debug("Route with " + key + " key already deleted");
+        }
+    }
+
+    /**
+     * Creates a RoutingAppender.
+     * @param name The name of the Appender.
+     * @param ignore If {@code "true"} (default) exceptions encountered when 
appending events are logged; otherwise
+     *               they are propagated to the caller.
+     * @param routes The routing definitions.
+     * @param config The Configuration (automatically added by the 
Configuration).
+     * @param rewritePolicy A RewritePolicy, if any.
+     * @param filter A Filter to restrict events processed by the Appender or 
null.
+     * @return The RoutingAppender
+     * @deprecated Since 2.7; use {@link #newBuilder()}
+     */
+    @Deprecated
+    public static RoutingAppender createAppender(
+            final String name,
+            final String ignore,
+            final Routes routes,
+            final Configuration config,
+            final RewritePolicy rewritePolicy,
+            final PurgePolicy purgePolicy,
+            final Filter filter) {
+
+        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
+        if (name == null) {
+            LOGGER.error("No name provided for RoutingAppender");
+            return null;
+        }
+        if (routes == null) {
+            LOGGER.error("No routes defined for RoutingAppender");
+            return null;
+        }
+        return new RoutingAppender(name, filter, ignoreExceptions, routes, 
rewritePolicy, config, purgePolicy, null);
+    }
+
+    public Route getDefaultRoute() {
+        return defaultRoute;
+    }
+
+    public AbstractScript getDefaultRouteScript() {
+        return defaultRouteScript;
+    }
+
+    public PurgePolicy getPurgePolicy() {
+        return purgePolicy;
+    }
+
+    public RewritePolicy getRewritePolicy() {
+        return rewritePolicy;
+    }
+
+    public Routes getRoutes() {
+        return routes;
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    public Bindings getBindings() {
+        return bindings;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/361785ab/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
index f3f5ad3..670395b 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/DefaultRouteScriptAppenderTest.java
@@ -24,6 +24,8 @@ import java.util.Map;
 
 import javax.script.Bindings;
 
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.Logger;
 import org.apache.logging.log4j.core.config.AppenderControl;
@@ -68,6 +70,7 @@ public class DefaultRouteScriptAppenderTest {
         final Bindings bindings = routingAppender.getBindings();
         if (expectBindingEntries) {
             Assert.assertEquals("TestValue2", ((Map<?, ?>) 
bindings.get("staticVariables")).get("TestKey"));
+            Assert.assertEquals("HEXDUMP", ((Map<?, ?>) 
bindings.get("staticVariables")).get("MarkerName"));
         }
     }
 
@@ -87,6 +90,7 @@ public class DefaultRouteScriptAppenderTest {
     }
 
     private void logAndCheck() {
+        Marker marker = MarkerManager.getMarker("HEXDUMP");
         final Logger logger = 
loggerContextRule.getLogger(DefaultRouteScriptAppenderTest.class);
         logger.error("Hello");
         final ListAppender listAppender = getListAppender();
@@ -95,6 +99,8 @@ public class DefaultRouteScriptAppenderTest {
         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);
+        logger.error(marker, "DEADBEEF");
+        assertTrue("Incorrect number of events. Expected 3, got " + 
list.size(), list.size() == 3);
     }
 
     @Test(expected = AssertionError.class)

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/361785ab/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
index 7d90f6b..84d13a1 100644
--- 
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
@@ -110,7 +110,7 @@ public class RoutesScriptAppenderTest {
         final Routes routes = routingAppender.getRoutes();
         Assert.assertNotNull(routes);
         Assert.assertNotNull(routes.getPatternScript());
-        Assert.assertEquals("Service2", routes.getPattern());
+        Assert.assertEquals("Service2", routes.getPattern(null));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/361785ab/log4j-core/src/test/resources/log4j-routing-script-staticvars-groovy.xml
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/resources/log4j-routing-script-staticvars-groovy.xml 
b/log4j-core/src/test/resources/log4j-routing-script-staticvars-groovy.xml
index 1c16152..4b63fa0 100644
--- a/log4j-core/src/test/resources/log4j-routing-script-staticvars-groovy.xml
+++ b/log4j-core/src/test/resources/log4j-routing-script-staticvars-groovy.xml
@@ -28,6 +28,9 @@
           if (staticVariables.containsKey("TestKey")) {
             staticVariables.put("TestKey", "TestValue2");
           }
+          if (logEvent.getMarker() != null) {
+            staticVariables.put("MarkerName", logEvent.getMarker().getName());
+          }
           return "OSNameFoo".contains("Foo") ? "Service2": "Service1";]]>
         </Script>
         <Route key="Service1">

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/361785ab/log4j-core/src/test/resources/log4j-routing-script-staticvars-javascript.xml
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/resources/log4j-routing-script-staticvars-javascript.xml 
b/log4j-core/src/test/resources/log4j-routing-script-staticvars-javascript.xml
index d3493ad..c81ab9f 100644
--- 
a/log4j-core/src/test/resources/log4j-routing-script-staticvars-javascript.xml
+++ 
b/log4j-core/src/test/resources/log4j-routing-script-staticvars-javascript.xml
@@ -28,6 +28,9 @@
           if (staticVariables.containsKey("TestKey")) {
             staticVariables.put("TestKey", "TestValue2");
           }
+          if (logEvent.getMarker() != null) {
+            staticVariables.put("MarkerName", logEvent.getMarker().getName());
+          }
           "OSNameFoo".search("Foo") > -1 ? "Service2" : "Service1";]]>
         </Script>
         <Route key="Service1">

Reply via email to