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 */ 017 package org.apache.logging.log4j.core.config; 018 019 import org.apache.logging.log4j.Level; 020 import org.apache.logging.log4j.LogManager; 021 import org.apache.logging.log4j.Logger; 022 import org.apache.logging.log4j.Marker; 023 import org.apache.logging.log4j.core.Appender; 024 import org.apache.logging.log4j.core.Filter; 025 import org.apache.logging.log4j.core.LifeCycle; 026 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 027 import org.apache.logging.log4j.core.filter.AbstractFilterable; 028 import org.apache.logging.log4j.core.impl.Log4jLogEvent; 029 import org.apache.logging.log4j.core.LogEvent; 030 import org.apache.logging.log4j.core.impl.LogEventFactory; 031 import org.apache.logging.log4j.core.config.plugins.Plugin; 032 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 033 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 034 import org.apache.logging.log4j.core.config.plugins.PluginElement; 035 import org.apache.logging.log4j.status.StatusLogger; 036 import org.apache.logging.log4j.message.Message; 037 038 import java.util.ArrayList; 039 import java.util.Arrays; 040 import java.util.Collection; 041 import java.util.HashMap; 042 import java.util.Iterator; 043 import java.util.List; 044 import java.util.Locale; 045 import java.util.Map; 046 import java.util.concurrent.ConcurrentHashMap; 047 import java.util.concurrent.atomic.AtomicInteger; 048 049 /** 050 * Logger object that is created via configuration. 051 */ 052 @Plugin(name = "logger", type = "Core", printObject = true) 053 public class LoggerConfig extends AbstractFilterable implements LogEventFactory { 054 055 private static final Logger LOGGER = StatusLogger.getLogger(); 056 private static final int MAX_RETRIES = 3; 057 private static final long WAIT_TIME = 1000; 058 059 private List<AppenderRef> appenderRefs = new ArrayList<AppenderRef>(); 060 private Map<String, AppenderControl> appenders = new ConcurrentHashMap<String, AppenderControl>(); 061 private final String name; 062 private LogEventFactory logEventFactory; 063 private Level level; 064 private boolean additive = true; 065 private LoggerConfig parent; 066 private AtomicInteger counter = new AtomicInteger(); 067 private boolean shutdown = false; 068 private final Map<Property, Boolean> properties; 069 private final Configuration config; 070 071 072 /** 073 * Default constructor. 074 */ 075 public LoggerConfig() { 076 this.logEventFactory = this; 077 this.level = Level.ERROR; 078 this.name = ""; 079 this.properties = null; 080 this.config = null; 081 } 082 083 /** 084 * Constructor that sets the name, level and additive values. 085 * @param name The Logger name. 086 * @param level The Level. 087 * @param additive true if the Logger is additive, false otherwise. 088 */ 089 public LoggerConfig(String name, Level level, boolean additive) { 090 this.logEventFactory = this; 091 this.name = name; 092 this.level = level; 093 this.additive = additive; 094 this.properties = null; 095 this.config = null; 096 } 097 098 protected LoggerConfig(String name, List<AppenderRef> appenders, Filter filter, Level level, 099 boolean additive, Property[] properties, Configuration config) { 100 super(filter); 101 this.logEventFactory = this; 102 this.name = name; 103 this.appenderRefs = appenders; 104 this.level = level; 105 this.additive = additive; 106 this.config = config; 107 if (properties != null && properties.length > 0) { 108 this.properties = new HashMap<Property, Boolean>(properties.length); 109 for (Property prop : properties) { 110 boolean interpolate = prop.getValue().contains("${"); 111 this.properties.put(prop, interpolate); 112 } 113 } else { 114 this.properties = null; 115 } 116 } 117 118 @Override 119 public Filter getFilter() { 120 return super.getFilter(); 121 } 122 123 /** 124 * Returns the name of the LoggerConfig. 125 * @return the name of the LoggerConfig. 126 */ 127 public String getName() { 128 return name; 129 } 130 131 /** 132 * Sets the parent of this LoggerConfig. 133 * @param parent the parent LoggerConfig. 134 */ 135 public void setParent(LoggerConfig parent) { 136 this.parent = parent; 137 } 138 139 /** 140 * Returns the parent of this LoggerConfig. 141 * @return the LoggerConfig that is the parent of this one. 142 */ 143 public LoggerConfig getParent() { 144 return this.parent; 145 } 146 147 /** 148 * Adds an Appender to the LoggerConfig. 149 * @param appender The Appender to add. 150 * @param level The Level to use. 151 * @param filter A Filter for the Appender reference. 152 */ 153 public void addAppender(Appender appender, Level level, Filter filter) { 154 appenders.put(appender.getName(), new AppenderControl(appender, level, filter)); 155 } 156 157 /** 158 * Removes the Appender with the specific name. 159 * @param name The name of the Appender. 160 */ 161 public void removeAppender(String name) { 162 AppenderControl ctl = appenders.remove(name); 163 if (ctl != null) { 164 cleanupFilter(ctl); 165 } 166 } 167 168 /** 169 * Returns all Appenders as a Map. 170 * @return a Map with the Appender name as the key and the Appender as the value. 171 */ 172 public Map<String, Appender> getAppenders() { 173 Map<String, Appender> map = new HashMap<String, Appender>(); 174 for (Map.Entry<String, AppenderControl> entry : appenders.entrySet()) { 175 map.put(entry.getKey(), entry.getValue().getAppender()); 176 } 177 return map; 178 } 179 180 /** 181 * Removes all Appenders. 182 */ 183 protected void clearAppenders() { 184 waitForCompletion(); 185 Collection<AppenderControl> controls = appenders.values(); 186 Iterator<AppenderControl> iterator = controls.iterator(); 187 while (iterator.hasNext()) { 188 AppenderControl ctl = iterator.next(); 189 iterator.remove(); 190 cleanupFilter(ctl); 191 } 192 } 193 194 private void cleanupFilter(AppenderControl ctl) { 195 Filter filter = ctl.getFilter(); 196 if (filter != null) { 197 ctl.removeFilter(filter); 198 if (filter instanceof LifeCycle) { 199 ((LifeCycle) filter).stop(); 200 } 201 } 202 } 203 204 /** 205 * Returns the Appender references. 206 * @return a List of all the Appender names attached to this LoggerConfig. 207 */ 208 public List<AppenderRef> getAppenderRefs() { 209 return appenderRefs; 210 } 211 212 /** 213 * Sets the logging Level. 214 * @param level The logging Level. 215 */ 216 public void setLevel(Level level) { 217 this.level = level; 218 } 219 220 /** 221 * Returns the logging Level. 222 * @return the logging Level. 223 */ 224 public Level getLevel() { 225 return level; 226 } 227 228 /** 229 * Returns the LogEventFactory. 230 * @return the LogEventFactory. 231 */ 232 public LogEventFactory getLogEventFactory() { 233 return logEventFactory; 234 } 235 236 /** 237 * Sets the LogEventFactory. Usually the LogEventFactory will be this LoggerConfig. 238 * @param logEventFactory the LogEventFactory. 239 */ 240 public void setLogEventFactory(LogEventFactory logEventFactory) { 241 this.logEventFactory = logEventFactory; 242 } 243 244 /** 245 * Returns the valid of the additive flag. 246 * @return true if the LoggerConfig is additive, false otherwise. 247 */ 248 public boolean isAdditive() { 249 return additive; 250 } 251 252 /** 253 * Sets the additive setting. 254 * @param additive true if thee LoggerConfig should be additive, false otherwise. 255 */ 256 public void setAdditive(boolean additive) { 257 this.additive = additive; 258 } 259 260 /** 261 * Logs an event. 262 * @param loggerName The name of the Logger. 263 * @param marker A Marker or null if none is present. 264 * @param fqcn The fully qualified class name of the caller. 265 * @param level The event Level. 266 * @param data The Message. 267 * @param t A Throwable or null. 268 */ 269 public void log(String loggerName, Marker marker, String fqcn, Level level, Message data, Throwable t) { 270 List<Property> props = null; 271 if (properties != null) { 272 props = new ArrayList<Property>(properties.size()); 273 274 for (Map.Entry<Property, Boolean> entry : properties.entrySet()) { 275 Property prop = entry.getKey(); 276 String value = entry.getValue() ? config.getSubst().replace(prop.getValue()) : prop.getValue(); 277 props.add(Property.createProperty(prop.getName(), value)); 278 } 279 } 280 LogEvent event = logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t); 281 log(event); 282 } 283 284 /** 285 * Waits for all log events to complete before shutting down this loggerConfig. 286 */ 287 private synchronized void waitForCompletion() { 288 if (shutdown) { 289 return; 290 } 291 shutdown = true; 292 int retries = 0; 293 while (counter.get() > 0) { 294 try { 295 wait(WAIT_TIME * (retries + 1)); 296 } catch (InterruptedException ie) { 297 if (++retries > MAX_RETRIES) { 298 break; 299 } 300 } 301 } 302 } 303 304 /** 305 * Logs an event. 306 * @param event The log event. 307 */ 308 public void log(LogEvent event) { 309 310 counter.incrementAndGet(); 311 try { 312 if (isFiltered(event)) { 313 return; 314 } 315 316 callAppenders(event); 317 318 if (additive && parent != null) { 319 parent.log(event); 320 } 321 } finally { 322 if (counter.decrementAndGet() == 0) { 323 synchronized (this) { 324 if (shutdown) { 325 notifyAll(); 326 } 327 } 328 329 } 330 } 331 } 332 333 private void callAppenders(LogEvent event) { 334 for (AppenderControl control : appenders.values()) { 335 control.callAppender(event); 336 } 337 } 338 339 /** 340 * Creates a log event. 341 * @param loggerName The name of the Logger. 342 * @param marker An optional Marker. 343 * @param fqcn The fully qualified class name of the caller. 344 * @param level The event Level. 345 * @param data The Message. 346 * @param t An optional Throwable. 347 * @return The LogEvent. 348 */ 349 public LogEvent createEvent(String loggerName, Marker marker, String fqcn, Level level, Message data, 350 List<Property> properties, Throwable t) { 351 return new Log4jLogEvent(loggerName, marker, fqcn, level, data, properties, t); 352 } 353 354 @Override 355 public String toString() { 356 return name == null || name.length() == 0 ? "root" : name; 357 } 358 359 /** 360 * Factory method to create a LoggerConfig. 361 * @param additivity True if additive, false otherwise. 362 * @param levelName The Level to be associated with the Logger. 363 * @param loggerName The name of the Logger. 364 * @param refs An array of Appender names. 365 * @param filter A Filter. 366 * @return A new LoggerConfig. 367 */ 368 @PluginFactory 369 public static LoggerConfig createLogger(@PluginAttr("additivity") String additivity, 370 @PluginAttr("level") String levelName, 371 @PluginAttr("name") String loggerName, 372 @PluginElement("appender-ref") AppenderRef[] refs, 373 @PluginElement("properties") Property[] properties, 374 @PluginConfiguration Configuration config, 375 @PluginElement("filters") Filter filter) { 376 if (loggerName == null) { 377 LOGGER.error("Loggers cannot be configured without a name"); 378 return null; 379 } 380 381 List<AppenderRef> appenderRefs = Arrays.asList(refs); 382 Level level; 383 try { 384 level = Level.toLevel(levelName, Level.ERROR); 385 } catch (Exception ex) { 386 LOGGER.error("Invalid Log level specified: {}. Defaulting to Error", levelName); 387 level = Level.ERROR; 388 } 389 String name = loggerName.equals("root") ? "" : loggerName; 390 boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity); 391 392 return new LoggerConfig(name, appenderRefs, filter, level, additive, properties, config); 393 } 394 395 /** 396 * The root Logger. 397 */ 398 @Plugin(name = "root", type = "Core", printObject = true) 399 public static class RootLogger extends LoggerConfig { 400 401 @PluginFactory 402 public static LoggerConfig createLogger(@PluginAttr("additivity") String additivity, 403 @PluginAttr("level") String levelName, 404 @PluginElement("appender-ref") AppenderRef[] refs, 405 @PluginElement("properties") Property[] properties, 406 @PluginConfiguration Configuration config, 407 @PluginElement("filters") Filter filter) { 408 List<AppenderRef> appenderRefs = Arrays.asList(refs); 409 Level level; 410 try { 411 level = Level.toLevel(levelName, Level.ERROR); 412 } catch (Exception ex) { 413 LOGGER.error("Invalid Log level specified: {}. Defaulting to Error", levelName); 414 level = Level.ERROR; 415 } 416 boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity); 417 418 return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additive, properties, 419 config); 420 } 421 } 422 423 }