001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.appender.routing;
018
019import static org.apache.logging.log4j.core.appender.routing.RoutingAppender.STATIC_VARIABLES_KEY;
020
021import java.util.Objects;
022import java.util.concurrent.ConcurrentMap;
023
024import javax.script.Bindings;
025
026import org.apache.logging.log4j.Logger;
027import org.apache.logging.log4j.core.Core;
028import org.apache.logging.log4j.core.LogEvent;
029import org.apache.logging.log4j.core.config.Configuration;
030import org.apache.logging.log4j.core.config.plugins.Plugin;
031import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
032import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
033import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
034import org.apache.logging.log4j.core.config.plugins.PluginElement;
035import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
036import org.apache.logging.log4j.core.script.AbstractScript;
037import org.apache.logging.log4j.core.script.ScriptManager;
038import org.apache.logging.log4j.status.StatusLogger;
039
040/**
041 * Contains the individual Route elements.
042 */
043@Plugin(name = "Routes", category = Core.CATEGORY_NAME, printObject = true)
044public final class Routes {
045
046    private static final String LOG_EVENT_KEY = "logEvent";
047
048    public static class Builder implements org.apache.logging.log4j.core.util.Builder<Routes>  {
049
050        @PluginConfiguration
051        private Configuration configuration;
052
053        @PluginAttribute("pattern")
054        private String pattern;
055
056        @PluginElement("Script")
057        private AbstractScript patternScript;
058
059        @PluginElement("Routes")
060        @Required
061        private Route[] routes;
062
063        @Override
064        public Routes build() {
065            if (routes == null || routes.length == 0) {
066                LOGGER.error("No Routes configured.");
067                return null;
068            }
069            if (patternScript != null && pattern != null) {
070                LOGGER.warn("In a Routes element, you must configure either a Script element or a pattern attribute.");
071            }
072            if (patternScript != null) {
073                if (configuration == null) {
074                    LOGGER.error("No Configuration defined for Routes; required for Script");
075                } else {
076                    configuration.getScriptManager().addScript(patternScript);
077                }
078            }
079            return new Routes(configuration, patternScript, pattern, routes);
080        }
081
082        public Configuration getConfiguration() {
083            return configuration;
084        }
085
086        public String getPattern() {
087            return pattern;
088        }
089
090        public AbstractScript getPatternScript() {
091            return patternScript;
092        }
093
094        public Route[] getRoutes() {
095            return routes;
096        }
097
098        public Builder withConfiguration(@SuppressWarnings("hiding") final Configuration configuration) {
099            this.configuration = configuration;
100            return this;
101        }
102
103        public Builder withPattern(@SuppressWarnings("hiding") final String pattern) {
104            this.pattern = pattern;
105            return this;
106        }
107
108        public Builder withPatternScript(@SuppressWarnings("hiding") final AbstractScript patternScript) {
109            this.patternScript = patternScript;
110            return this;
111        }
112
113        public Builder withRoutes(@SuppressWarnings("hiding") final Route[] routes) {
114            this.routes = routes;
115            return this;
116        }
117
118    }
119
120    private static final Logger LOGGER = StatusLogger.getLogger();
121
122    /**
123     * Creates the Routes.
124     * @param pattern The pattern.
125     * @param routes An array of Route elements.
126     * @return The Routes container.
127     * @deprecated since 2.7; use {@link #newBuilder()}.
128     */
129    @Deprecated
130    public static Routes createRoutes(
131            final String pattern,
132            final Route... routes) {
133        if (routes == null || routes.length == 0) {
134            LOGGER.error("No routes configured");
135            return null;
136        }
137        return new Routes(null, null, pattern, routes);
138    }
139
140    @PluginBuilderFactory
141    public static Builder newBuilder() {
142        return new Builder();
143    }
144
145    private final Configuration configuration;
146
147    private final String pattern;
148
149    private final AbstractScript patternScript;
150
151    // TODO Why not make this a Map or add a Map.
152    private final Route[] routes;
153
154    private Routes(final Configuration configuration, final AbstractScript patternScript, final String pattern, final Route... routes) {
155        this.configuration = configuration;
156        this.patternScript = patternScript;
157        this.pattern = pattern;
158        this.routes = routes;
159    }
160
161    /**
162     * Returns the pattern.
163     * @param event The log event passed to the script (if there is a script.)
164     * @param scriptStaticVariables The script's static variables.
165     * @return the pattern.
166     */
167    public String getPattern(final LogEvent event, final ConcurrentMap<Object, Object> scriptStaticVariables) {
168        if (patternScript != null) {
169            final ScriptManager scriptManager = configuration.getScriptManager();
170            final Bindings bindings = scriptManager.createBindings(patternScript);
171            bindings.put(STATIC_VARIABLES_KEY, scriptStaticVariables);
172            bindings.put(LOG_EVENT_KEY, event);
173            final Object object = scriptManager.execute(patternScript.getName(), bindings);
174            bindings.remove(LOG_EVENT_KEY);
175            return Objects.toString(object, null);
176        }
177        return pattern;
178    }
179
180    /**
181     * Gets the optional script that decides which route to pick.
182     * @return the optional script that decides which route to pick. May be null.
183     */
184    public AbstractScript getPatternScript() {
185        return patternScript;
186    }
187
188    public Route getRoute(final String key) {
189        for (final Route route : routes) {
190            if (Objects.equals(route.getKey(), key)) {
191                return route;
192            }
193        }
194        return null;
195    }
196
197    /**
198     * Returns the array of Route elements.
199     * @return an array of Route elements.
200     */
201    public Route[] getRoutes() {
202        return routes;
203    }
204
205    @Override
206    public String toString() {
207        final StringBuilder sb = new StringBuilder("{");
208        boolean first = true;
209        for (final Route route : routes) {
210            if (!first) {
211                sb.append(',');
212            }
213            first = false;
214            sb.append(route.toString());
215        }
216        sb.append('}');
217        return sb.toString();
218
219    }
220
221}