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.nio.charset.Charset; 020import java.util.Arrays; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.logging.log4j.core.Layout; 026import org.apache.logging.log4j.core.LogEvent; 027import org.apache.logging.log4j.core.config.Configuration; 028import org.apache.logging.log4j.core.config.DefaultConfiguration; 029import org.apache.logging.log4j.core.config.Node; 030import org.apache.logging.log4j.core.config.plugins.Plugin; 031import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 032import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 033import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 034import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 035import org.apache.logging.log4j.core.config.plugins.PluginElement; 036import org.apache.logging.log4j.core.config.plugins.PluginFactory; 037import org.apache.logging.log4j.core.pattern.LogEventPatternConverter; 038import org.apache.logging.log4j.core.pattern.PatternFormatter; 039import org.apache.logging.log4j.core.pattern.PatternParser; 040import org.apache.logging.log4j.core.pattern.RegexReplacement; 041import org.apache.logging.log4j.util.PropertiesUtil; 042import org.apache.logging.log4j.util.Strings; 043 044/** 045 * A flexible layout configurable with pattern string. 046 * <p> 047 * The goal of this class is to {@link org.apache.logging.log4j.core.Layout#toByteArray format} a {@link LogEvent} and 048 * return the results. The format of the result depends on the <em>conversion pattern</em>. 049 * </p> 050 * <p> 051 * The conversion pattern is closely related to the conversion pattern of the printf function in C. A conversion pattern 052 * is composed of literal text and format control expressions called <em>conversion specifiers</em>. 053 * </p> 054 * <p> 055 * See the Log4j Manual for details on the supported pattern converters. 056 * </p> 057 */ 058@Plugin(name = "PatternLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true) 059public final class PatternLayout extends AbstractStringLayout { 060 061 /** 062 * Default pattern string for log output. Currently set to the string <b>"%m%n"</b> which just prints the 063 * application supplied message. 064 */ 065 public static final String DEFAULT_CONVERSION_PATTERN = "%m%n"; 066 067 /** 068 * A conversion pattern equivalent to the TTCCLayout. Current value is <b>%r [%t] %p %c %notEmpty{%x }- %m%n</b>. 069 */ 070 public static final String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %notEmpty{%x }- %m%n"; 071 072 /** 073 * A simple pattern. Current value is <b>%d [%t] %p %c - %m%n</b>. 074 */ 075 public static final String SIMPLE_CONVERSION_PATTERN = "%d [%t] %p %c - %m%n"; 076 077 /** Key to identify pattern converters. */ 078 public static final String KEY = "Converter"; 079 080 /** 081 * Conversion pattern. 082 */ 083 private final String conversionPattern; 084 private final PatternSelector patternSelector; 085 private final Serializer eventSerializer; 086 087 /** 088 * Constructs a PatternLayout using the supplied conversion pattern. 089 * 090 * @param config The Configuration. 091 * @param replace The regular expression to match. 092 * @param eventPattern conversion pattern. 093 * @param patternSelector The PatternSelector. 094 * @param charset The character set. 095 * @param alwaysWriteExceptions Whether or not exceptions should always be handled in this pattern (if {@code true}, 096 * exceptions will be written even if the pattern does not specify so). 097 * @param disableAnsi 098 * If {@code "true"}, do not output ANSI escape codes 099 * @param noConsoleNoAnsi 100 * If {@code "true"} (default) and {@link System#console()} is null, do not output ANSI escape codes 101 * @param headerPattern header conversion pattern. 102 * @param footerPattern footer conversion pattern. 103 */ 104 private PatternLayout(final Configuration config, final RegexReplacement replace, final String eventPattern, 105 final PatternSelector patternSelector, final Charset charset, final boolean alwaysWriteExceptions, 106 final boolean disableAnsi, final boolean noConsoleNoAnsi, final String headerPattern, 107 final String footerPattern) { 108 super(config, charset, 109 newSerializerBuilder() 110 .setConfiguration(config) 111 .setReplace(replace) 112 .setPatternSelector(patternSelector) 113 .setAlwaysWriteExceptions(alwaysWriteExceptions) 114 .setDisableAnsi(disableAnsi) 115 .setNoConsoleNoAnsi(noConsoleNoAnsi) 116 .setPattern(headerPattern) 117 .build(), 118 newSerializerBuilder() 119 .setConfiguration(config) 120 .setReplace(replace) 121 .setPatternSelector(patternSelector) 122 .setAlwaysWriteExceptions(alwaysWriteExceptions) 123 .setDisableAnsi(disableAnsi) 124 .setNoConsoleNoAnsi(noConsoleNoAnsi) 125 .setPattern(footerPattern) 126 .build()); 127 this.conversionPattern = eventPattern; 128 this.patternSelector = patternSelector; 129 this.eventSerializer = newSerializerBuilder() 130 .setConfiguration(config) 131 .setReplace(replace) 132 .setPatternSelector(patternSelector) 133 .setAlwaysWriteExceptions(alwaysWriteExceptions) 134 .setDisableAnsi(disableAnsi) 135 .setNoConsoleNoAnsi(noConsoleNoAnsi) 136 .setPattern(eventPattern) 137 .setDefaultPattern(DEFAULT_CONVERSION_PATTERN) 138 .build(); 139 } 140 141 public static SerializerBuilder newSerializerBuilder() { 142 return new SerializerBuilder(); 143 } 144 145 /** 146 * Deprecated, use {@link #newSerializerBuilder()} instead. 147 * 148 * @param configuration 149 * @param replace 150 * @param pattern 151 * @param defaultPattern 152 * @param patternSelector 153 * @param alwaysWriteExceptions 154 * @param noConsoleNoAnsi 155 * @return a new Serializer. 156 * @deprecated Use {@link #newSerializerBuilder()} instead. 157 */ 158 @Deprecated 159 public static Serializer createSerializer(final Configuration configuration, final RegexReplacement replace, 160 final String pattern, final String defaultPattern, final PatternSelector patternSelector, 161 final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi) { 162 final SerializerBuilder builder = newSerializerBuilder(); 163 builder.setAlwaysWriteExceptions(alwaysWriteExceptions); 164 builder.setConfiguration(configuration); 165 builder.setDefaultPattern(defaultPattern); 166 builder.setNoConsoleNoAnsi(noConsoleNoAnsi); 167 builder.setPattern(pattern); 168 builder.setPatternSelector(patternSelector); 169 builder.setReplace(replace); 170 return builder.build(); 171 } 172 173 /** 174 * Gets the conversion pattern. 175 * 176 * @return the conversion pattern. 177 */ 178 public String getConversionPattern() { 179 return conversionPattern; 180 } 181 182 /** 183 * Gets this PatternLayout's content format. Specified by: 184 * <ul> 185 * <li>Key: "structured" Value: "false"</li> 186 * <li>Key: "formatType" Value: "conversion" (format uses the keywords supported by OptionConverter)</li> 187 * <li>Key: "format" Value: provided "conversionPattern" param</li> 188 * </ul> 189 * 190 * @return Map of content format keys supporting PatternLayout 191 */ 192 @Override 193 public Map<String, String> getContentFormat() { 194 final Map<String, String> result = new HashMap<>(); 195 result.put("structured", "false"); 196 result.put("formatType", "conversion"); 197 result.put("format", conversionPattern); 198 return result; 199 } 200 201 /** 202 * Formats a logging event to a writer. 203 * 204 * @param event logging event to be formatted. 205 * @return The event formatted as a String. 206 */ 207 @Override 208 public String toSerializable(final LogEvent event) { 209 return eventSerializer.toSerializable(event); 210 } 211 212 @Override 213 public void encode(final LogEvent event, final ByteBufferDestination destination) { 214 if (!(eventSerializer instanceof Serializer2)) { 215 super.encode(event, destination); 216 return; 217 } 218 final StringBuilder text = toText((Serializer2) eventSerializer, event, getStringBuilder()); 219 final Encoder<StringBuilder> encoder = getStringBuilderEncoder(); 220 encoder.encode(text, destination); 221 trimToMaxSize(text); 222 } 223 224 /** 225 * Creates a text representation of the specified log event 226 * and writes it into the specified StringBuilder. 227 * <p> 228 * Implementations are free to return a new StringBuilder if they can 229 * detect in advance that the specified StringBuilder is too small. 230 */ 231 private StringBuilder toText(final Serializer2 serializer, final LogEvent event, 232 final StringBuilder destination) { 233 return serializer.toSerializable(event, destination); 234 } 235 236 /** 237 * Creates a PatternParser. 238 * @param config The Configuration. 239 * @return The PatternParser. 240 */ 241 public static PatternParser createPatternParser(final Configuration config) { 242 if (config == null) { 243 return new PatternParser(config, KEY, LogEventPatternConverter.class); 244 } 245 PatternParser parser = config.getComponent(KEY); 246 if (parser == null) { 247 parser = new PatternParser(config, KEY, LogEventPatternConverter.class); 248 config.addComponent(KEY, parser); 249 parser = config.getComponent(KEY); 250 } 251 return parser; 252 } 253 254 @Override 255 public String toString() { 256 return patternSelector == null ? conversionPattern : patternSelector.toString(); 257 } 258 259 /** 260 * Creates a pattern layout. 261 * 262 * @param pattern 263 * The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN. 264 * @param patternSelector 265 * Allows different patterns to be used based on some selection criteria. 266 * @param config 267 * The Configuration. Some Converters require access to the Interpolator. 268 * @param replace 269 * A Regex replacement String. 270 * @param charset 271 * The character set. The platform default is used if not specified. 272 * @param alwaysWriteExceptions 273 * If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens. 274 * @param noConsoleNoAnsi 275 * If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes 276 * @param headerPattern 277 * The footer to place at the top of the document, once. 278 * @param footerPattern 279 * The footer to place at the bottom of the document, once. 280 * @return The PatternLayout. 281 * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version. 282 */ 283 @PluginFactory 284 @Deprecated 285 public static PatternLayout createLayout( 286 @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern, 287 @PluginElement("PatternSelector") final PatternSelector patternSelector, 288 @PluginConfiguration final Configuration config, 289 @PluginElement("Replace") final RegexReplacement replace, 290 // LOG4J2-783 use platform default by default, so do not specify defaultString for charset 291 @PluginAttribute(value = "charset") final Charset charset, 292 @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions, 293 @PluginAttribute(value = "noConsoleNoAnsi") final boolean noConsoleNoAnsi, 294 @PluginAttribute("header") final String headerPattern, 295 @PluginAttribute("footer") final String footerPattern) { 296 return newBuilder() 297 .withPattern(pattern) 298 .withPatternSelector(patternSelector) 299 .withConfiguration(config) 300 .withRegexReplacement(replace) 301 .withCharset(charset) 302 .withAlwaysWriteExceptions(alwaysWriteExceptions) 303 .withNoConsoleNoAnsi(noConsoleNoAnsi) 304 .withHeader(headerPattern) 305 .withFooter(footerPattern) 306 .build(); 307 } 308 309 private static class PatternSerializer implements Serializer, Serializer2 { 310 311 private final PatternFormatter[] formatters; 312 private final RegexReplacement replace; 313 314 private PatternSerializer(final PatternFormatter[] formatters, final RegexReplacement replace) { 315 super(); 316 this.formatters = formatters; 317 this.replace = replace; 318 } 319 320 @Override 321 public String toSerializable(final LogEvent event) { 322 final StringBuilder sb = getStringBuilder(); 323 try { 324 return toSerializable(event, sb).toString(); 325 } finally { 326 trimToMaxSize(sb); 327 } 328 } 329 330 @Override 331 public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) { 332 final int len = formatters.length; 333 for (int i = 0; i < len; i++) { 334 formatters[i].format(event, buffer); 335 } 336 if (replace != null) { // creates temporary objects 337 String str = buffer.toString(); 338 str = replace.format(str); 339 buffer.setLength(0); 340 buffer.append(str); 341 } 342 return buffer; 343 } 344 345 @Override 346 public String toString() { 347 final StringBuilder builder = new StringBuilder(); 348 builder.append(super.toString()); 349 builder.append("[formatters="); 350 builder.append(Arrays.toString(formatters)); 351 builder.append(", replace="); 352 builder.append(replace); 353 builder.append("]"); 354 return builder.toString(); 355 } 356 } 357 358 public static class SerializerBuilder implements org.apache.logging.log4j.core.util.Builder<Serializer> { 359 360 private Configuration configuration; 361 private RegexReplacement replace; 362 private String pattern; 363 private String defaultPattern; 364 private PatternSelector patternSelector; 365 private boolean alwaysWriteExceptions; 366 private boolean disableAnsi; 367 private boolean noConsoleNoAnsi; 368 369 @Override 370 public Serializer build() { 371 if (Strings.isEmpty(pattern) && Strings.isEmpty(defaultPattern)) { 372 return null; 373 } 374 if (patternSelector == null) { 375 try { 376 final PatternParser parser = createPatternParser(configuration); 377 final List<PatternFormatter> list = parser.parse(pattern == null ? defaultPattern : pattern, 378 alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi); 379 final PatternFormatter[] formatters = list.toArray(new PatternFormatter[0]); 380 return new PatternSerializer(formatters, replace); 381 } catch (final RuntimeException ex) { 382 throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex); 383 } 384 } 385 return new PatternSelectorSerializer(patternSelector, replace); 386 } 387 388 public SerializerBuilder setConfiguration(final Configuration configuration) { 389 this.configuration = configuration; 390 return this; 391 } 392 393 public SerializerBuilder setReplace(final RegexReplacement replace) { 394 this.replace = replace; 395 return this; 396 } 397 398 public SerializerBuilder setPattern(final String pattern) { 399 this.pattern = pattern; 400 return this; 401 } 402 403 public SerializerBuilder setDefaultPattern(final String defaultPattern) { 404 this.defaultPattern = defaultPattern; 405 return this; 406 } 407 408 public SerializerBuilder setPatternSelector(final PatternSelector patternSelector) { 409 this.patternSelector = patternSelector; 410 return this; 411 } 412 413 public SerializerBuilder setAlwaysWriteExceptions(final boolean alwaysWriteExceptions) { 414 this.alwaysWriteExceptions = alwaysWriteExceptions; 415 return this; 416 } 417 418 public SerializerBuilder setDisableAnsi(final boolean disableAnsi) { 419 this.disableAnsi = disableAnsi; 420 return this; 421 } 422 423 public SerializerBuilder setNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { 424 this.noConsoleNoAnsi = noConsoleNoAnsi; 425 return this; 426 } 427 428 } 429 430 private static class PatternSelectorSerializer implements Serializer, Serializer2 { 431 432 private final PatternSelector patternSelector; 433 private final RegexReplacement replace; 434 435 private PatternSelectorSerializer(final PatternSelector patternSelector, final RegexReplacement replace) { 436 super(); 437 this.patternSelector = patternSelector; 438 this.replace = replace; 439 } 440 441 @Override 442 public String toSerializable(final LogEvent event) { 443 final StringBuilder sb = getStringBuilder(); 444 try { 445 return toSerializable(event, sb).toString(); 446 } finally { 447 trimToMaxSize(sb); 448 } 449 } 450 451 @Override 452 public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) { 453 final PatternFormatter[] formatters = patternSelector.getFormatters(event); 454 final int len = formatters.length; 455 for (int i = 0; i < len; i++) { 456 formatters[i].format(event, buffer); 457 } 458 if (replace != null) { // creates temporary objects 459 String str = buffer.toString(); 460 str = replace.format(str); 461 buffer.setLength(0); 462 buffer.append(str); 463 } 464 return buffer; 465 } 466 467 @Override 468 public String toString() { 469 final StringBuilder builder = new StringBuilder(); 470 builder.append(super.toString()); 471 builder.append("[patternSelector="); 472 builder.append(patternSelector); 473 builder.append(", replace="); 474 builder.append(replace); 475 builder.append("]"); 476 return builder.toString(); 477 } 478 } 479 480 /** 481 * Creates a PatternLayout using the default options. These options include using UTF-8, the default conversion 482 * pattern, exceptions being written, and with ANSI escape codes. 483 * 484 * @return the PatternLayout. 485 * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern 486 */ 487 public static PatternLayout createDefaultLayout() { 488 return newBuilder().build(); 489 } 490 491 /** 492 * Creates a PatternLayout using the default options and the given configuration. These options include using UTF-8, 493 * the default conversion pattern, exceptions being written, and with ANSI escape codes. 494 * 495 * @param configuration The Configuration. 496 * 497 * @return the PatternLayout. 498 * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern 499 */ 500 public static PatternLayout createDefaultLayout(final Configuration configuration) { 501 return newBuilder().withConfiguration(configuration).build(); 502 } 503 504 /** 505 * Creates a builder for a custom PatternLayout. 506 * 507 * @return a PatternLayout builder. 508 */ 509 @PluginBuilderFactory 510 public static Builder newBuilder() { 511 return new Builder(); 512 } 513 514 /** 515 * Custom PatternLayout builder. Use the {@link PatternLayout#newBuilder() builder factory method} to create this. 516 */ 517 public static class Builder implements org.apache.logging.log4j.core.util.Builder<PatternLayout> { 518 519 @PluginBuilderAttribute 520 private String pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN; 521 522 @PluginElement("PatternSelector") 523 private PatternSelector patternSelector; 524 525 @PluginConfiguration 526 private Configuration configuration; 527 528 @PluginElement("Replace") 529 private RegexReplacement regexReplacement; 530 531 // LOG4J2-783 use platform default by default 532 @PluginBuilderAttribute 533 private Charset charset = Charset.defaultCharset(); 534 535 @PluginBuilderAttribute 536 private boolean alwaysWriteExceptions = true; 537 538 @PluginBuilderAttribute 539 private boolean disableAnsi = !useAnsiEscapeCodes(); 540 541 @PluginBuilderAttribute 542 private boolean noConsoleNoAnsi; 543 544 @PluginBuilderAttribute 545 private String header; 546 547 @PluginBuilderAttribute 548 private String footer; 549 550 private Builder() { 551 } 552 553 private boolean useAnsiEscapeCodes() { 554 PropertiesUtil propertiesUtil = PropertiesUtil.getProperties(); 555 boolean isPlatformSupportsAnsi = !propertiesUtil.isOsWindows(); 556 boolean isJansiRequested = !propertiesUtil.getBooleanProperty("log4j.skipJansi", true); 557 return isPlatformSupportsAnsi || isJansiRequested; 558 } 559 560 /** 561 * @param pattern 562 * The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN. 563 */ 564 public Builder withPattern(final String pattern) { 565 this.pattern = pattern; 566 return this; 567 } 568 569 /** 570 * @param patternSelector 571 * Allows different patterns to be used based on some selection criteria. 572 */ 573 public Builder withPatternSelector(final PatternSelector patternSelector) { 574 this.patternSelector = patternSelector; 575 return this; 576 } 577 578 /** 579 * @param configuration 580 * The Configuration. Some Converters require access to the Interpolator. 581 */ 582 public Builder withConfiguration(final Configuration configuration) { 583 this.configuration = configuration; 584 return this; 585 } 586 587 /** 588 * @param regexReplacement 589 * A Regex replacement 590 */ 591 public Builder withRegexReplacement(final RegexReplacement regexReplacement) { 592 this.regexReplacement = regexReplacement; 593 return this; 594 } 595 596 /** 597 * @param charset 598 * The character set. The platform default is used if not specified. 599 */ 600 public Builder withCharset(final Charset charset) { 601 // LOG4J2-783 if null, use platform default by default 602 if (charset != null) { 603 this.charset = charset; 604 } 605 return this; 606 } 607 608 /** 609 * @param alwaysWriteExceptions 610 * If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens. 611 */ 612 public Builder withAlwaysWriteExceptions(final boolean alwaysWriteExceptions) { 613 this.alwaysWriteExceptions = alwaysWriteExceptions; 614 return this; 615 } 616 617 /** 618 * @param disableAnsi 619 * If {@code "true"} (default is value of system property `log4j.skipJansi`, or `true` if undefined), 620 * do not output ANSI escape codes 621 */ 622 public Builder withDisableAnsi(final boolean disableAnsi) { 623 this.disableAnsi = disableAnsi; 624 return this; 625 } 626 627 /** 628 * @param noConsoleNoAnsi 629 * If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes 630 */ 631 public Builder withNoConsoleNoAnsi(final boolean noConsoleNoAnsi) { 632 this.noConsoleNoAnsi = noConsoleNoAnsi; 633 return this; 634 } 635 636 /** 637 * @param header 638 * The footer to place at the top of the document, once. 639 */ 640 public Builder withHeader(final String header) { 641 this.header = header; 642 return this; 643 } 644 645 /** 646 * @param footer 647 * The footer to place at the bottom of the document, once. 648 */ 649 public Builder withFooter(final String footer) { 650 this.footer = footer; 651 return this; 652 } 653 654 @Override 655 public PatternLayout build() { 656 // fall back to DefaultConfiguration 657 if (configuration == null) { 658 configuration = new DefaultConfiguration(); 659 } 660 return new PatternLayout(configuration, regexReplacement, pattern, patternSelector, charset, 661 alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi, header, footer); 662 } 663 } 664 665 public Serializer getEventSerializer() { 666 return eventSerializer; 667 } 668}