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