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 java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.logging.log4j.Logger; 024import org.apache.logging.log4j.Marker; 025import org.apache.logging.log4j.core.LogEvent; 026import org.apache.logging.log4j.core.config.Configuration; 027import org.apache.logging.log4j.core.config.Node; 028import org.apache.logging.log4j.core.config.plugins.Plugin; 029import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 030import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 031import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 032import org.apache.logging.log4j.core.config.plugins.PluginElement; 033import org.apache.logging.log4j.core.impl.LocationAware; 034import org.apache.logging.log4j.core.pattern.PatternFormatter; 035import org.apache.logging.log4j.core.pattern.PatternParser; 036import org.apache.logging.log4j.status.StatusLogger; 037 038/** 039 * Selects the pattern to use based on the Marker in the LogEvent. 040 */ 041@Plugin(name = "MarkerPatternSelector", category = Node.CATEGORY, elementType = PatternSelector.ELEMENT_TYPE, printObject = true) 042public class MarkerPatternSelector implements PatternSelector, LocationAware { 043 044 /** 045 * Custom MarkerPatternSelector builder. Use the {@link MarkerPatternSelector#newBuilder() builder factory method} to create this. 046 */ 047 public static class Builder implements org.apache.logging.log4j.core.util.Builder<MarkerPatternSelector> { 048 049 @PluginElement("PatternMatch") 050 private PatternMatch[] properties; 051 052 @PluginBuilderAttribute("defaultPattern") 053 private String defaultPattern; 054 055 @PluginBuilderAttribute(value = "alwaysWriteExceptions") 056 private boolean alwaysWriteExceptions = true; 057 058 @PluginBuilderAttribute(value = "disableAnsi") 059 private boolean disableAnsi; 060 061 @PluginBuilderAttribute(value = "noConsoleNoAnsi") 062 private boolean noConsoleNoAnsi; 063 064 @PluginConfiguration 065 private Configuration configuration; 066 067 @Override 068 public MarkerPatternSelector build() { 069 if (defaultPattern == null) { 070 defaultPattern = PatternLayout.DEFAULT_CONVERSION_PATTERN; 071 } 072 if (properties == null || properties.length == 0) { 073 LOGGER.warn("No marker patterns were provided with PatternMatch"); 074 return null; 075 } 076 return new MarkerPatternSelector(properties, defaultPattern, alwaysWriteExceptions, disableAnsi, 077 noConsoleNoAnsi, configuration); 078 } 079 080 public Builder setProperties(final PatternMatch[] properties) { 081 this.properties = properties; 082 return this; 083 } 084 085 public Builder setDefaultPattern(final String defaultPattern) { 086 this.defaultPattern = defaultPattern; 087 return this; 088 } 089 090 public Builder setAlwaysWriteExceptions(final boolean alwaysWriteExceptions) { 091 this.alwaysWriteExceptions = alwaysWriteExceptions; 092 return this; 093 } 094 095 public Builder setDisableAnsi(final boolean disableAnsi) { 096 this.disableAnsi = disableAnsi; 097 return this; 098 } 099 100 public Builder setNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { 101 this.noConsoleNoAnsi = noConsoleNoAnsi; 102 return this; 103 } 104 105 public Builder setConfiguration(final Configuration configuration) { 106 this.configuration = configuration; 107 return this; 108 } 109 110 } 111 112 private final Map<String, PatternFormatter[]> formatterMap = new HashMap<>(); 113 114 private final Map<String, String> patternMap = new HashMap<>(); 115 116 private final PatternFormatter[] defaultFormatters; 117 118 private final String defaultPattern; 119 120 private static Logger LOGGER = StatusLogger.getLogger(); 121 122 private final boolean requiresLocation; 123 124 /** 125 * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version. 126 */ 127 @Deprecated 128 public MarkerPatternSelector(final PatternMatch[] properties, final String defaultPattern, 129 final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi, 130 final Configuration config) { 131 this(properties, defaultPattern, alwaysWriteExceptions, false, noConsoleNoAnsi, config); 132 } 133 134 private MarkerPatternSelector(final PatternMatch[] properties, final String defaultPattern, 135 final boolean alwaysWriteExceptions, final boolean disableAnsi, 136 final boolean noConsoleNoAnsi, final Configuration config) { 137 boolean needsLocation = false; 138 final PatternParser parser = PatternLayout.createPatternParser(config); 139 for (final PatternMatch property : properties) { 140 try { 141 final List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions, 142 disableAnsi, noConsoleNoAnsi); 143 PatternFormatter[] formatters = list.toArray(new PatternFormatter[0]); 144 formatterMap.put(property.getKey(), formatters); 145 for (int i = 0; !needsLocation && i < formatters.length; ++i) { 146 needsLocation = formatters[i].requiresLocation(); 147 } 148 149 patternMap.put(property.getKey(), property.getPattern()); 150 } catch (final RuntimeException ex) { 151 throw new IllegalArgumentException("Cannot parse pattern '" + property.getPattern() + "'", ex); 152 } 153 } 154 try { 155 final List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, 156 noConsoleNoAnsi); 157 defaultFormatters = list.toArray(new PatternFormatter[0]); 158 this.defaultPattern = defaultPattern; 159 for (int i = 0; !needsLocation && i < defaultFormatters.length; ++i) { 160 needsLocation = defaultFormatters[i].requiresLocation(); 161 } 162 } catch (final RuntimeException ex) { 163 throw new IllegalArgumentException("Cannot parse pattern '" + defaultPattern + "'", ex); 164 } 165 requiresLocation = needsLocation; 166 } 167 168 @Override 169 public boolean requiresLocation() { 170 return requiresLocation; 171 } 172 173 @Override 174 public PatternFormatter[] getFormatters(final LogEvent event) { 175 final Marker marker = event.getMarker(); 176 if (marker == null) { 177 return defaultFormatters; 178 } 179 for (final String key : formatterMap.keySet()) { 180 if (marker.isInstanceOf(key)) { 181 return formatterMap.get(key); 182 } 183 } 184 return defaultFormatters; 185 } 186 187 /** 188 * Creates a builder for a custom ScriptPatternSelector. 189 * 190 * @return a ScriptPatternSelector builder. 191 */ 192 @PluginBuilderFactory 193 public static Builder newBuilder() { 194 return new Builder(); 195 } 196 197 /** 198 * Deprecated, use {@link #newBuilder()} instead. 199 * @param properties 200 * @param defaultPattern 201 * @param alwaysWriteExceptions 202 * @param noConsoleNoAnsi 203 * @param configuration 204 * @return a new MarkerPatternSelector. 205 * @deprecated Use {@link #newBuilder()} instead. 206 */ 207 @Deprecated 208 public static MarkerPatternSelector createSelector( 209 final PatternMatch[] properties, 210 final String defaultPattern, 211 final boolean alwaysWriteExceptions, 212 final boolean noConsoleNoAnsi, 213 final Configuration configuration) { 214 final Builder builder = newBuilder(); 215 builder.setProperties(properties); 216 builder.setDefaultPattern(defaultPattern); 217 builder.setAlwaysWriteExceptions(alwaysWriteExceptions); 218 builder.setNoConsoleNoAnsi(noConsoleNoAnsi); 219 builder.setConfiguration(configuration); 220 return builder.build(); 221 } 222 223 @Override 224 public String toString() { 225 final StringBuilder sb = new StringBuilder(); 226 boolean first = true; 227 for (final Map.Entry<String, String> entry : patternMap.entrySet()) { 228 if (!first) { 229 sb.append(", "); 230 } 231 sb.append("key=\"").append(entry.getKey()).append("\", pattern=\"").append(entry.getValue()).append("\""); 232 first = false; 233 } 234 if (!first) { 235 sb.append(", "); 236 } 237 sb.append("default=\"").append(defaultPattern).append("\""); 238 return sb.toString(); 239 } 240}