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; 018 019import java.io.ObjectStreamException; 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.logging.log4j.Level; 027import org.apache.logging.log4j.Marker; 028import org.apache.logging.log4j.core.config.Configuration; 029import org.apache.logging.log4j.core.config.LoggerConfig; 030import org.apache.logging.log4j.core.config.ReliabilityStrategy; 031import org.apache.logging.log4j.core.filter.CompositeFilter; 032import org.apache.logging.log4j.message.Message; 033import org.apache.logging.log4j.message.MessageFactory; 034import org.apache.logging.log4j.message.SimpleMessage; 035import org.apache.logging.log4j.spi.AbstractLogger; 036import org.apache.logging.log4j.util.Strings; 037import org.apache.logging.log4j.util.Supplier; 038 039/** 040 * The core implementation of the {@link org.apache.logging.log4j.Logger} interface. Besides providing an implementation 041 * of all the Logger methods, this class also provides some convenience methods for Log4j 1.x compatibility as well as 042 * access to the {@link org.apache.logging.log4j.core.Filter Filters} and {@link org.apache.logging.log4j.core.Appender 043 * Appenders} associated with this Logger. Note that access to these underlying objects is provided primarily for use in 044 * unit tests or bridging legacy Log4j 1.x code. Future versions of this class may or may not include the various 045 * methods that are noted as not being part of the public API. 046 * 047 * TODO All the isEnabled methods could be pushed into a filter interface. Not sure of the utility of having isEnabled 048 * be able to examine the message pattern and parameters. (RG) Moving the isEnabled methods out of Logger noticeably 049 * impacts performance. The message pattern and parameters are required so that they can be used in global filters. 050 */ 051public class Logger extends AbstractLogger implements Supplier<LoggerConfig> { 052 053 private static final long serialVersionUID = 1L; 054 055 /** 056 * Config should be consistent across threads. 057 */ 058 protected volatile PrivateConfig privateConfig; 059 060 // FIXME: ditto to the above 061 private final LoggerContext context; 062 063 /** 064 * The constructor. 065 * 066 * @param context The LoggerContext this Logger is associated with. 067 * @param messageFactory The message factory. 068 * @param name The name of the Logger. 069 */ 070 protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) { 071 super(name, messageFactory); 072 this.context = context; 073 privateConfig = new PrivateConfig(context.getConfiguration(), this); 074 } 075 076 protected Object writeReplace() throws ObjectStreamException { 077 return new LoggerProxy(getName(), getMessageFactory()); 078 } 079 080 /** 081 * This method is only used for 1.x compatibility. Returns the parent of this Logger. If it doesn't already exist 082 * return a temporary Logger. 083 * 084 * @return The parent Logger. 085 */ 086 public Logger getParent() { 087 final LoggerConfig lc = privateConfig.loggerConfig.getName().equals(getName()) ? privateConfig.loggerConfig 088 .getParent() : privateConfig.loggerConfig; 089 if (lc == null) { 090 return null; 091 } 092 final String lcName = lc.getName(); 093 final MessageFactory messageFactory = getMessageFactory(); 094 if (context.hasLogger(lcName, messageFactory)) { 095 return context.getLogger(lcName, messageFactory); 096 } 097 return new Logger(context, lcName, messageFactory); 098 } 099 100 /** 101 * Returns the LoggerContext this Logger is associated with. 102 * 103 * @return the LoggerContext. 104 */ 105 public LoggerContext getContext() { 106 return context; 107 } 108 109 /** 110 * This method is not exposed through the public API and is provided primarily for unit testing. 111 * <p> 112 * If the new level is null, this logger inherits the level from its parent. 113 * </p> 114 * 115 * @param level The Level to use on this Logger, may be null. 116 */ 117 public synchronized void setLevel(final Level level) { 118 if (level == getLevel()) { 119 return; 120 } 121 Level actualLevel; 122 if (level != null) { 123 actualLevel = level; 124 } else { 125 final Logger parent = getParent(); 126 actualLevel = parent != null ? parent.getLevel() : privateConfig.loggerConfigLevel; 127 } 128 privateConfig = new PrivateConfig(privateConfig, actualLevel); 129 } 130 131 /* 132 * (non-Javadoc) 133 * 134 * @see org.apache.logging.log4j.util.Supplier#get() 135 */ 136 @Override 137 public LoggerConfig get() { 138 return privateConfig.loggerConfig; 139 } 140 141 @Override 142 public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, 143 final Throwable t) { 144 final Message msg = message == null ? new SimpleMessage(Strings.EMPTY) : message; 145 final ReliabilityStrategy strategy = privateConfig.loggerConfig.getReliabilityStrategy(); 146 strategy.log(this, getName(), fqcn, marker, level, msg, t); 147 } 148 149 @Override 150 public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) { 151 return privateConfig.filter(level, marker, message, t); 152 } 153 154 @Override 155 public boolean isEnabled(final Level level, final Marker marker, final String message) { 156 return privateConfig.filter(level, marker, message); 157 } 158 159 @Override 160 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) { 161 return privateConfig.filter(level, marker, message, params); 162 } 163 164 @Override 165 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) { 166 return privateConfig.filter(level, marker, message, p0); 167 } 168 169 @Override 170 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 171 final Object p1) { 172 return privateConfig.filter(level, marker, message, p0, p1); 173 } 174 175 @Override 176 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 177 final Object p1, final Object p2) { 178 return privateConfig.filter(level, marker, message, p0, p1, p2); 179 } 180 181 @Override 182 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 183 final Object p1, final Object p2, final Object p3) { 184 return privateConfig.filter(level, marker, message, p0, p1, p2, p3); 185 } 186 187 @Override 188 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 189 final Object p1, final Object p2, final Object p3, 190 final Object p4) { 191 return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4); 192 } 193 194 @Override 195 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 196 final Object p1, final Object p2, final Object p3, 197 final Object p4, final Object p5) { 198 return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5); 199 } 200 201 @Override 202 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 203 final Object p1, final Object p2, final Object p3, 204 final Object p4, final Object p5, final Object p6) { 205 return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6); 206 } 207 208 @Override 209 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 210 final Object p1, final Object p2, final Object p3, 211 final Object p4, final Object p5, final Object p6, 212 final Object p7) { 213 return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7); 214 } 215 216 @Override 217 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 218 final Object p1, final Object p2, final Object p3, 219 final Object p4, final Object p5, final Object p6, 220 final Object p7, final Object p8) { 221 return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); 222 } 223 224 @Override 225 public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0, 226 final Object p1, final Object p2, final Object p3, 227 final Object p4, final Object p5, final Object p6, 228 final Object p7, final Object p8, final Object p9) { 229 return privateConfig.filter(level, marker, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); 230 } 231 232 @Override 233 public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) { 234 return privateConfig.filter(level, marker, message, t); 235 } 236 237 @Override 238 public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) { 239 return privateConfig.filter(level, marker, message, t); 240 } 241 242 @Override 243 public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) { 244 return privateConfig.filter(level, marker, message, t); 245 } 246 247 /** 248 * This method is not exposed through the public API and is used primarily for unit testing. 249 * 250 * @param appender The Appender to add to the Logger. 251 */ 252 public void addAppender(final Appender appender) { 253 privateConfig.config.addLoggerAppender(this, appender); 254 } 255 256 /** 257 * This method is not exposed through the public API and is used primarily for unit testing. 258 * 259 * @param appender The Appender to remove from the Logger. 260 */ 261 public void removeAppender(final Appender appender) { 262 privateConfig.loggerConfig.removeAppender(appender.getName()); 263 } 264 265 /** 266 * This method is not exposed through the public API and is used primarily for unit testing. 267 * 268 * @return A Map containing the Appender's name as the key and the Appender as the value. 269 */ 270 public Map<String, Appender> getAppenders() { 271 return privateConfig.loggerConfig.getAppenders(); 272 } 273 274 /** 275 * This method is not exposed through the public API and is used primarily for unit testing. 276 * 277 * @return An Iterator over all the Filters associated with the Logger. 278 */ 279 // FIXME: this really ought to be an Iterable instead of an Iterator 280 public Iterator<Filter> getFilters() { 281 final Filter filter = privateConfig.loggerConfig.getFilter(); 282 if (filter == null) { 283 return new ArrayList<Filter>().iterator(); 284 } else if (filter instanceof CompositeFilter) { 285 return ((CompositeFilter) filter).iterator(); 286 } else { 287 final List<Filter> filters = new ArrayList<>(); 288 filters.add(filter); 289 return filters.iterator(); 290 } 291 } 292 293 /** 294 * Gets the Level associated with the Logger. 295 * 296 * @return the Level associate with the Logger. 297 */ 298 @Override 299 public Level getLevel() { 300 return privateConfig.loggerConfigLevel; 301 } 302 303 /** 304 * This method is not exposed through the public API and is used primarily for unit testing. 305 * 306 * @return The number of Filters associated with the Logger. 307 */ 308 public int filterCount() { 309 final Filter filter = privateConfig.loggerConfig.getFilter(); 310 if (filter == null) { 311 return 0; 312 } else if (filter instanceof CompositeFilter) { 313 return ((CompositeFilter) filter).size(); 314 } 315 return 1; 316 } 317 318 /** 319 * This method is not exposed through the public API and is used primarily for unit testing. 320 * 321 * @param filter The Filter to add. 322 */ 323 public void addFilter(final Filter filter) { 324 privateConfig.config.addLoggerFilter(this, filter); 325 } 326 327 /** 328 * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility 329 * bridge. 330 * 331 * @return true if the associated LoggerConfig is additive, false otherwise. 332 */ 333 public boolean isAdditive() { 334 return privateConfig.loggerConfig.isAdditive(); 335 } 336 337 /** 338 * This method is not exposed through the public API and is present only to support the Log4j 1.2 compatibility 339 * bridge. 340 * 341 * @param additive Boolean value to indicate whether the Logger is additive or not. 342 */ 343 public void setAdditive(final boolean additive) { 344 privateConfig.config.setLoggerAdditive(this, additive); 345 } 346 347 /** 348 * Associates this Logger with a new Configuration. This method is not 349 * exposed through the public API. 350 * <p> 351 * There are two ways this could be used to guarantee all threads are aware 352 * of changes to config. 353 * <ol> 354 * <li>Synchronize this method. Accessors don't need to be synchronized as 355 * Java will treat all variables within a synchronized block as volatile. 356 * </li> 357 * <li>Declare the variable volatile. Option 2 is used here as the 358 * performance cost is very low and it does a better job at documenting how 359 * it is used.</li> 360 * 361 * @param newConfig 362 * The new Configuration. 363 */ 364 protected void updateConfiguration(final Configuration newConfig) { 365 this.privateConfig = new PrivateConfig(newConfig, this); 366 } 367 368 /** 369 * The binding between a Logger and its configuration. 370 */ 371 protected class PrivateConfig { 372 // config fields are public to make them visible to Logger subclasses 373 /** LoggerConfig to delegate the actual logging to. */ 374 public final LoggerConfig loggerConfig; // SUPPRESS CHECKSTYLE 375 /** The current Configuration associated with the LoggerConfig. */ 376 public final Configuration config; // SUPPRESS CHECKSTYLE 377 private final Level loggerConfigLevel; 378 private final int intLevel; 379 private final Logger logger; 380 381 public PrivateConfig(final Configuration config, final Logger logger) { 382 this.config = config; 383 this.loggerConfig = config.getLoggerConfig(getName()); 384 this.loggerConfigLevel = this.loggerConfig.getLevel(); 385 this.intLevel = this.loggerConfigLevel.intLevel(); 386 this.logger = logger; 387 } 388 389 public PrivateConfig(final PrivateConfig pc, final Level level) { 390 this.config = pc.config; 391 this.loggerConfig = pc.loggerConfig; 392 this.loggerConfigLevel = level; 393 this.intLevel = this.loggerConfigLevel.intLevel(); 394 this.logger = pc.logger; 395 } 396 397 public PrivateConfig(final PrivateConfig pc, final LoggerConfig lc) { 398 this.config = pc.config; 399 this.loggerConfig = lc; 400 this.loggerConfigLevel = lc.getLevel(); 401 this.intLevel = this.loggerConfigLevel.intLevel(); 402 this.logger = pc.logger; 403 } 404 405 // LOG4J2-151: changed visibility to public 406 public void logEvent(final LogEvent event) { 407 loggerConfig.log(event); 408 } 409 410 boolean filter(final Level level, final Marker marker, final String msg) { 411 final Filter filter = config.getFilter(); 412 if (filter != null) { 413 final Filter.Result r = filter.filter(logger, level, marker, msg); 414 if (r != Filter.Result.NEUTRAL) { 415 return r == Filter.Result.ACCEPT; 416 } 417 } 418 return level != null && intLevel >= level.intLevel(); 419 } 420 421 boolean filter(final Level level, final Marker marker, final String msg, final Throwable t) { 422 final Filter filter = config.getFilter(); 423 if (filter != null) { 424 final Filter.Result r = filter.filter(logger, level, marker, (Object) msg, t); 425 if (r != Filter.Result.NEUTRAL) { 426 return r == Filter.Result.ACCEPT; 427 } 428 } 429 return level != null && intLevel >= level.intLevel(); 430 } 431 432 boolean filter(final Level level, final Marker marker, final String msg, final Object... p1) { 433 final Filter filter = config.getFilter(); 434 if (filter != null) { 435 final Filter.Result r = filter.filter(logger, level, marker, msg, p1); 436 if (r != Filter.Result.NEUTRAL) { 437 return r == Filter.Result.ACCEPT; 438 } 439 } 440 return level != null && intLevel >= level.intLevel(); 441 } 442 443 boolean filter(final Level level, final Marker marker, final String msg, final Object p0) { 444 final Filter filter = config.getFilter(); 445 if (filter != null) { 446 final Filter.Result r = filter.filter(logger, level, marker, msg, p0); 447 if (r != Filter.Result.NEUTRAL) { 448 return r == Filter.Result.ACCEPT; 449 } 450 } 451 return level != null && intLevel >= level.intLevel(); 452 } 453 454 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 455 final Object p1) { 456 final Filter filter = config.getFilter(); 457 if (filter != null) { 458 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1); 459 if (r != Filter.Result.NEUTRAL) { 460 return r == Filter.Result.ACCEPT; 461 } 462 } 463 return level != null && intLevel >= level.intLevel(); 464 } 465 466 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 467 final Object p1, final Object p2) { 468 final Filter filter = config.getFilter(); 469 if (filter != null) { 470 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2); 471 if (r != Filter.Result.NEUTRAL) { 472 return r == Filter.Result.ACCEPT; 473 } 474 } 475 return level != null && intLevel >= level.intLevel(); 476 } 477 478 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 479 final Object p1, final Object p2, final Object p3) { 480 final Filter filter = config.getFilter(); 481 if (filter != null) { 482 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3); 483 if (r != Filter.Result.NEUTRAL) { 484 return r == Filter.Result.ACCEPT; 485 } 486 } 487 return level != null && intLevel >= level.intLevel(); 488 } 489 490 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 491 final Object p1, final Object p2, final Object p3, 492 final Object p4) { 493 final Filter filter = config.getFilter(); 494 if (filter != null) { 495 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4); 496 if (r != Filter.Result.NEUTRAL) { 497 return r == Filter.Result.ACCEPT; 498 } 499 } 500 return level != null && intLevel >= level.intLevel(); 501 } 502 503 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 504 final Object p1, final Object p2, final Object p3, 505 final Object p4, final Object p5) { 506 final Filter filter = config.getFilter(); 507 if (filter != null) { 508 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5); 509 if (r != Filter.Result.NEUTRAL) { 510 return r == Filter.Result.ACCEPT; 511 } 512 } 513 return level != null && intLevel >= level.intLevel(); 514 } 515 516 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 517 final Object p1, final Object p2, final Object p3, 518 final Object p4, final Object p5, final Object p6) { 519 final Filter filter = config.getFilter(); 520 if (filter != null) { 521 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6); 522 if (r != Filter.Result.NEUTRAL) { 523 return r == Filter.Result.ACCEPT; 524 } 525 } 526 return level != null && intLevel >= level.intLevel(); 527 } 528 529 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 530 final Object p1, final Object p2, final Object p3, 531 final Object p4, final Object p5, final Object p6, 532 final Object p7) { 533 final Filter filter = config.getFilter(); 534 if (filter != null) { 535 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7); 536 if (r != Filter.Result.NEUTRAL) { 537 return r == Filter.Result.ACCEPT; 538 } 539 } 540 return level != null && intLevel >= level.intLevel(); 541 } 542 543 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 544 final Object p1, final Object p2, final Object p3, 545 final Object p4, final Object p5, final Object p6, 546 final Object p7, final Object p8) { 547 final Filter filter = config.getFilter(); 548 if (filter != null) { 549 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8); 550 if (r != Filter.Result.NEUTRAL) { 551 return r == Filter.Result.ACCEPT; 552 } 553 } 554 return level != null && intLevel >= level.intLevel(); 555 } 556 557 boolean filter(final Level level, final Marker marker, final String msg, final Object p0, 558 final Object p1, final Object p2, final Object p3, 559 final Object p4, final Object p5, final Object p6, 560 final Object p7, final Object p8, final Object p9) { 561 final Filter filter = config.getFilter(); 562 if (filter != null) { 563 final Filter.Result r = filter.filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8, 564 p9); 565 if (r != Filter.Result.NEUTRAL) { 566 return r == Filter.Result.ACCEPT; 567 } 568 } 569 return level != null && intLevel >= level.intLevel(); 570 } 571 572 boolean filter(final Level level, final Marker marker, final CharSequence msg, final Throwable t) { 573 final Filter filter = config.getFilter(); 574 if (filter != null) { 575 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 576 if (r != Filter.Result.NEUTRAL) { 577 return r == Filter.Result.ACCEPT; 578 } 579 } 580 return level != null && intLevel >= level.intLevel(); 581 } 582 583 boolean filter(final Level level, final Marker marker, final Object msg, final Throwable t) { 584 final Filter filter = config.getFilter(); 585 if (filter != null) { 586 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 587 if (r != Filter.Result.NEUTRAL) { 588 return r == Filter.Result.ACCEPT; 589 } 590 } 591 return level != null && intLevel >= level.intLevel(); 592 } 593 594 boolean filter(final Level level, final Marker marker, final Message msg, final Throwable t) { 595 final Filter filter = config.getFilter(); 596 if (filter != null) { 597 final Filter.Result r = filter.filter(logger, level, marker, msg, t); 598 if (r != Filter.Result.NEUTRAL) { 599 return r == Filter.Result.ACCEPT; 600 } 601 } 602 return level != null && intLevel >= level.intLevel(); 603 } 604 605 @Override 606 public String toString() { 607 StringBuilder builder = new StringBuilder(); 608 builder.append("PrivateConfig [loggerConfig="); 609 builder.append(loggerConfig); 610 builder.append(", config="); 611 builder.append(config); 612 builder.append(", loggerConfigLevel="); 613 builder.append(loggerConfigLevel); 614 builder.append(", intLevel="); 615 builder.append(intLevel); 616 builder.append(", logger="); 617 builder.append(logger); 618 builder.append("]"); 619 return builder.toString(); 620 } 621 } 622 623 /** 624 * Serialization proxy class for Logger. Since the LoggerContext and config information can be reconstructed on the 625 * fly, the only information needed for a Logger are what's available in AbstractLogger. 626 * 627 * @since 2.5 628 */ 629 protected static class LoggerProxy implements Serializable { 630 private static final long serialVersionUID = 1L; 631 632 private final String name; 633 private final MessageFactory messageFactory; 634 635 public LoggerProxy(String name, MessageFactory messageFactory) { 636 this.name = name; 637 this.messageFactory = messageFactory; 638 } 639 640 protected Object readResolve() throws ObjectStreamException { 641 return new Logger(LoggerContext.getContext(), name, messageFactory); 642 } 643 } 644 645 /** 646 * Returns a String representation of this instance in the form {@code "name:level[ in context_name]"}. 647 * 648 * @return A String describing this Logger instance. 649 */ 650 @Override 651 public String toString() { 652 final String nameLevel = Strings.EMPTY + getName() + ':' + getLevel(); 653 if (context == null) { 654 return nameLevel; 655 } 656 final String contextName = context.getName(); 657 return contextName == null ? nameLevel : nameLevel + " in " + contextName; 658 } 659 660 @Override 661 public boolean equals(final Object o) { 662 if (this == o) { 663 return true; 664 } 665 if (o == null || getClass() != o.getClass()) { 666 return false; 667 } 668 final Logger that = (Logger) o; 669 return getName().equals(that.getName()); 670 } 671 672 @Override 673 public int hashCode() { 674 return getName().hashCode(); 675 } 676}