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.layout;
018
019import org.apache.logging.log4j.Logger;
020import org.apache.logging.log4j.core.LogEvent;
021import org.apache.logging.log4j.core.config.Configuration;
022import org.apache.logging.log4j.core.config.Node;
023import org.apache.logging.log4j.core.config.plugins.Plugin;
024import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
025import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
026import org.apache.logging.log4j.core.config.plugins.PluginElement;
027import org.apache.logging.log4j.core.config.plugins.PluginFactory;
028import org.apache.logging.log4j.core.pattern.PatternFormatter;
029import org.apache.logging.log4j.core.pattern.PatternParser;
030import org.apache.logging.log4j.core.script.AbstractScript;
031import org.apache.logging.log4j.core.script.ScriptRef;
032import org.apache.logging.log4j.status.StatusLogger;
033
034import javax.script.SimpleBindings;
035import java.util.HashMap;
036import java.util.List;
037import java.util.Map;
038
039/**
040 * Selects the pattern to use based on the Marker in the LogEvent.
041 */
042@Plugin(name = "ScriptPatternSelector", category = Node.CATEGORY, elementType = PatternSelector.ELEMENT_TYPE, printObject = true)
043public class ScriptPatternSelector implements PatternSelector {
044
045    private final Map<String, PatternFormatter[]> formatterMap = new HashMap<>();
046
047    private final Map<String, String> patternMap = new HashMap<>();
048
049    private final PatternFormatter[] defaultFormatters;
050
051    private final String defaultPattern;
052
053    private static Logger LOGGER = StatusLogger.getLogger();
054    private final AbstractScript script;
055    private final Configuration configuration;
056
057
058    public ScriptPatternSelector(final AbstractScript script, final PatternMatch[] properties, final String defaultPattern,
059                                 final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
060                                 final Configuration config) {
061        this.script = script;
062        this.configuration = config;
063        if (!(script instanceof ScriptRef)) {
064            config.getScriptManager().addScript(script);
065        }
066        final PatternParser parser = PatternLayout.createPatternParser(config);
067        for (PatternMatch property : properties) {
068            try {
069                List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, noConsoleNoAnsi);
070                formatterMap.put(property.getKey(), list.toArray(new PatternFormatter[list.size()]));
071                patternMap.put(property.getKey(), property.getPattern());
072            } catch (RuntimeException ex) {
073                throw new IllegalArgumentException("Cannot parse pattern '" + property.getPattern() + "'", ex);
074            }
075        }
076        try {
077            List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi);
078            defaultFormatters = list.toArray(new PatternFormatter[list.size()]);
079            this.defaultPattern = defaultPattern;
080        } catch (RuntimeException ex) {
081            throw new IllegalArgumentException("Cannot parse pattern '" + defaultPattern + "'", ex);
082        }
083    }
084
085    @Override
086    public PatternFormatter[] getFormatters(LogEvent event) {
087        SimpleBindings bindings = new SimpleBindings();
088        bindings.putAll(configuration.getProperties());
089        bindings.put("substitutor", configuration.getStrSubstitutor());
090        bindings.put("logEvent", event);
091        Object object = configuration.getScriptManager().execute(script.getName(), bindings);
092        if (object == null) {
093            return defaultFormatters;
094        }
095        PatternFormatter[] patternFormatter = formatterMap.get(object.toString());
096
097        return patternFormatter == null ? defaultFormatters : patternFormatter;
098    }
099
100
101    @PluginFactory
102    public static ScriptPatternSelector createSelector(@PluginElement("Script") AbstractScript script,
103                                                       @PluginElement("PatternMatch") final PatternMatch[] properties,
104                                                       @PluginAttribute("defaultPattern") String defaultPattern,
105                                                       @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
106                                                       @PluginAttribute(value = "noConsoleNoAnsi", defaultBoolean = false) final boolean noConsoleNoAnsi,
107                                                       @PluginConfiguration final Configuration config) {
108        if (script == null) {
109            LOGGER.error("A Script, ScriptFile or ScriptRef element must be provided for this ScriptFilter");
110            return null;
111        }
112        if (script instanceof ScriptRef) {
113            if (config.getScriptManager().getScript(script.getName()) == null) {
114                LOGGER.error("No script with name {} has been declared.", script.getName());
115                return null;
116            }
117        }
118        if (defaultPattern == null) {
119            defaultPattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
120        }
121        if (properties == null || properties.length == 0) {
122            LOGGER.warn("No marker patterns were provided");
123        }
124        return new ScriptPatternSelector(script, properties, defaultPattern, alwaysWriteExceptions, noConsoleNoAnsi, config);
125    }
126
127    @Override
128    public String toString() {
129        StringBuilder sb = new StringBuilder();
130        boolean first = true;
131        for (Map.Entry<String, String> entry : patternMap.entrySet()) {
132            if (!first) {
133                sb.append(", ");
134            }
135            sb.append("key=\"").append(entry.getKey()).append("\", pattern=\"").append(entry.getValue()).append("\"");
136            first = false;
137        }
138        if (!first) {
139            sb.append(", ");
140        }
141        sb.append("default=\"").append(defaultPattern).append("\"");
142        return sb.toString();
143    }
144}