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.ArrayList; 021import java.util.Calendar; 022import java.util.GregorianCalendar; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.SortedMap; 027import java.util.TreeMap; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031import org.apache.logging.log4j.Level; 032import org.apache.logging.log4j.LoggingException; 033import org.apache.logging.log4j.core.LogEvent; 034import org.apache.logging.log4j.core.appender.TLSSyslogFrame; 035import org.apache.logging.log4j.core.config.Configuration; 036import org.apache.logging.log4j.core.config.plugins.Plugin; 037import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 038import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 039import org.apache.logging.log4j.core.config.plugins.PluginElement; 040import org.apache.logging.log4j.core.config.plugins.PluginFactory; 041import org.apache.logging.log4j.core.helpers.Booleans; 042import org.apache.logging.log4j.core.helpers.Charsets; 043import org.apache.logging.log4j.core.helpers.Integers; 044import org.apache.logging.log4j.core.helpers.NetUtils; 045import org.apache.logging.log4j.core.helpers.Strings; 046import org.apache.logging.log4j.core.net.Facility; 047import org.apache.logging.log4j.core.net.Priority; 048import org.apache.logging.log4j.core.pattern.LogEventPatternConverter; 049import org.apache.logging.log4j.core.pattern.PatternConverter; 050import org.apache.logging.log4j.core.pattern.PatternFormatter; 051import org.apache.logging.log4j.core.pattern.PatternParser; 052import org.apache.logging.log4j.core.pattern.ThrowablePatternConverter; 053import org.apache.logging.log4j.message.Message; 054import org.apache.logging.log4j.message.StructuredDataId; 055import org.apache.logging.log4j.message.StructuredDataMessage; 056 057 058/** 059 * Formats a log event in accordance with RFC 5424. 060 * 061 * @see <a href="https://tools.ietf.org/html/rfc5424">RFC 5424</a> 062 */ 063@Plugin(name = "RFC5424Layout", category = "Core", elementType = "layout", printObject = true) 064public class RFC5424Layout extends AbstractStringLayout { 065 066 private static final String LF = "\n"; 067 068 /** 069 * Not a very good default - it is the Apache Software Foundation's enterprise number. 070 */ 071 public static final int DEFAULT_ENTERPRISE_NUMBER = 18060; 072 /** 073 * The default event id. 074 */ 075 public static final String DEFAULT_ID = "Audit"; 076 /** 077 * Match newlines in a platform-independent manner. 078 */ 079 public static final Pattern NEWLINE_PATTERN = Pattern.compile("\\r?\\n"); 080 /** 081 * Match characters which require escaping 082 */ 083 public static final Pattern PARAM_VALUE_ESCAPE_PATTERN = Pattern.compile("[\\\"\\]\\\\]"); 084 085 protected static final String DEFAULT_MDCID = "mdc"; 086 private static final int TWO_DIGITS = 10; 087 private static final int THREE_DIGITS = 100; 088 private static final int MILLIS_PER_MINUTE = 60000; 089 private static final int MINUTES_PER_HOUR = 60; 090 091 private static final String COMPONENT_KEY = "RFC5424-Converter"; 092 093 private final Facility facility; 094 private final String defaultId; 095 private final int enterpriseNumber; 096 private final boolean includeMDC; 097 private final String mdcId; 098 private final StructuredDataId mdcSDID; 099 private final String localHostName; 100 private final String appName; 101 private final String messageId; 102 private final String configName; 103 private final String mdcPrefix; 104 private final String eventPrefix; 105 private final List<String> mdcExcludes; 106 private final List<String> mdcIncludes; 107 private final List<String> mdcRequired; 108 private final ListChecker checker; 109 private final ListChecker noopChecker = new NoopChecker(); 110 private final boolean includeNewLine; 111 private final String escapeNewLine; 112 private final boolean useTLSMessageFormat; 113 114 private long lastTimestamp = -1; 115 private String timestamppStr; 116 117 private final List<PatternFormatter> exceptionFormatters; 118 private final Map<String, FieldFormatter> fieldFormatters; 119 120 private RFC5424Layout(final Configuration config, final Facility facility, final String id, final int ein, 121 final boolean includeMDC, final boolean includeNL, final String escapeNL, final String mdcId, 122 final String mdcPrefix, final String eventPrefix, 123 final String appName, final String messageId, final String excludes, final String includes, 124 final String required, final Charset charset, final String exceptionPattern, 125 final boolean useTLSMessageFormat, final LoggerFields[] loggerFields) { 126 super(charset); 127 final PatternParser exceptionParser = createPatternParser(config, ThrowablePatternConverter.class); 128 exceptionFormatters = exceptionPattern == null ? null : exceptionParser.parse(exceptionPattern, false, false); 129 this.facility = facility; 130 this.defaultId = id == null ? DEFAULT_ID : id; 131 this.enterpriseNumber = ein; 132 this.includeMDC = includeMDC; 133 this.includeNewLine = includeNL; 134 this.escapeNewLine = escapeNL == null ? null : Matcher.quoteReplacement(escapeNL); 135 this.mdcId = mdcId; 136 this.mdcSDID = new StructuredDataId(mdcId, enterpriseNumber, null, null); 137 this.mdcPrefix = mdcPrefix; 138 this.eventPrefix = eventPrefix; 139 this.appName = appName; 140 this.messageId = messageId; 141 this.useTLSMessageFormat = useTLSMessageFormat; 142 this.localHostName = NetUtils.getLocalHostname(); 143 ListChecker c = null; 144 if (excludes != null) { 145 final String[] array = excludes.split(","); 146 if (array.length > 0) { 147 c = new ExcludeChecker(); 148 mdcExcludes = new ArrayList<String>(array.length); 149 for (final String str : array) { 150 mdcExcludes.add(str.trim()); 151 } 152 } else { 153 mdcExcludes = null; 154 } 155 } else { 156 mdcExcludes = null; 157 } 158 if (includes != null) { 159 final String[] array = includes.split(","); 160 if (array.length > 0) { 161 c = new IncludeChecker(); 162 mdcIncludes = new ArrayList<String>(array.length); 163 for (final String str : array) { 164 mdcIncludes.add(str.trim()); 165 } 166 } else { 167 mdcIncludes = null; 168 } 169 } else { 170 mdcIncludes = null; 171 } 172 if (required != null) { 173 final String[] array = required.split(","); 174 if (array.length > 0) { 175 mdcRequired = new ArrayList<String>(array.length); 176 for (final String str : array) { 177 mdcRequired.add(str.trim()); 178 } 179 } else { 180 mdcRequired = null; 181 } 182 183 } else { 184 mdcRequired = null; 185 } 186 this.checker = c != null ? c : noopChecker; 187 final String name = config == null ? null : config.getName(); 188 configName = name != null && name.length() > 0 ? name : null; 189 this.fieldFormatters = createFieldFormatters(loggerFields, config); 190 } 191 192 private Map<String, FieldFormatter> createFieldFormatters(final LoggerFields[] loggerFields, 193 final Configuration config) { 194 final Map<String, FieldFormatter> sdIdMap = new HashMap<String, FieldFormatter>(); 195 196 if (loggerFields != null) { 197 for (final LoggerFields lField : loggerFields) { 198 final StructuredDataId key = lField.getSdId() == null ? mdcSDID : lField.getSdId(); 199 final Map<String, List<PatternFormatter>> sdParams = new HashMap<String, List<PatternFormatter>>(); 200 final Map<String, String> fields = lField.getMap(); 201 if (!fields.isEmpty()) { 202 final PatternParser fieldParser = createPatternParser(config, null); 203 204 for (final Map.Entry<String, String> entry : fields.entrySet()) { 205 final List<PatternFormatter> formatters = fieldParser.parse(entry.getValue(), false, false); 206 sdParams.put(entry.getKey(), formatters); 207 } 208 final FieldFormatter fieldFormatter = new FieldFormatter(sdParams, 209 lField.getDiscardIfAllFieldsAreEmpty()); 210 sdIdMap.put(key.toString(), fieldFormatter); 211 } 212 } 213 } 214 return sdIdMap.size() > 0 ? sdIdMap : null; 215 } 216 217 /** 218 * Create a PatternParser. 219 * 220 * @param config The Configuration. 221 * @param filterClass Filter the returned plugins after calling the plugin manager. 222 * @return The PatternParser. 223 */ 224 private static PatternParser createPatternParser(final Configuration config, 225 final Class<? extends PatternConverter> filterClass) { 226 if (config == null) { 227 return new PatternParser(config, PatternLayout.KEY, LogEventPatternConverter.class, filterClass); 228 } 229 PatternParser parser = config.getComponent(COMPONENT_KEY); 230 if (parser == null) { 231 parser = new PatternParser(config, PatternLayout.KEY, ThrowablePatternConverter.class); 232 config.addComponent(COMPONENT_KEY, parser); 233 parser = (PatternParser) config.getComponent(COMPONENT_KEY); 234 } 235 return parser; 236 } 237 238 /** 239 * RFC5424Layout's content format is specified by:<p/> 240 * Key: "structured" Value: "true"<p/> 241 * Key: "format" Value: "RFC5424"<p/> 242 * 243 * @return Map of content format keys supporting RFC5424Layout 244 */ 245 @Override 246 public Map<String, String> getContentFormat() { 247 final Map<String, String> result = new HashMap<String, String>(); 248 result.put("structured", "true"); 249 result.put("formatType", "RFC5424"); 250 return result; 251 } 252 253 /** 254 * Formats a {@link org.apache.logging.log4j.core.LogEvent} in conformance with the RFC 5424 Syslog specification. 255 * 256 * @param event The LogEvent. 257 * @return The RFC 5424 String representation of the LogEvent. 258 */ 259 @Override 260 public String toSerializable(final LogEvent event) { 261 final StringBuilder buf = new StringBuilder(); 262 appendPriority(buf, event.getLevel()); 263 appendTimestamp(buf, event.getMillis()); 264 appendSpace(buf); 265 appendHostName(buf); 266 appendSpace(buf); 267 appendAppName(buf); 268 appendSpace(buf); 269 appendProcessId(buf); 270 appendSpace(buf); 271 appendMessageId(buf, event.getMessage()); 272 appendSpace(buf); 273 appendStructuredElements(buf, event); 274 appendMessage(buf, event); 275 if (useTLSMessageFormat) { 276 return new TLSSyslogFrame(buf.toString()).toString(); 277 } 278 return buf.toString(); 279 } 280 281 private void appendPriority(final StringBuilder buffer, final Level logLevel) { 282 buffer.append("<"); 283 buffer.append(Priority.getPriority(facility, logLevel)); 284 buffer.append(">1 "); 285 } 286 287 private void appendTimestamp(final StringBuilder buffer, final long milliseconds) { 288 buffer.append(computeTimeStampString(milliseconds)); 289 } 290 291 private void appendSpace(final StringBuilder buffer) { 292 buffer.append(" "); 293 } 294 295 private void appendHostName(final StringBuilder buffer) { 296 buffer.append(localHostName); 297 } 298 299 private void appendAppName(final StringBuilder buffer) { 300 if (appName != null) { 301 buffer.append(appName); 302 } else if (configName != null) { 303 buffer.append(configName); 304 } else { 305 buffer.append("-"); 306 } 307 } 308 309 private void appendProcessId(final StringBuilder buffer) { 310 buffer.append(getProcId()); 311 } 312 313 private void appendMessageId(final StringBuilder buffer, final Message message) { 314 final boolean isStructured = message instanceof StructuredDataMessage; 315 final String type = isStructured ? ((StructuredDataMessage) message).getType() : null; 316 if (type != null) { 317 buffer.append(type); 318 } else if (messageId != null) { 319 buffer.append(messageId); 320 } else { 321 buffer.append("-"); 322 } 323 } 324 325 private void appendMessage(final StringBuilder buffer, final LogEvent event) { 326 final Message message = event.getMessage(); 327 // This layout formats StructuredDataMessages instead of delegating to the Message itself. 328 final String text = (message instanceof StructuredDataMessage) ? message.getFormat() : message.getFormattedMessage(); 329 330 if (text != null && text.length() > 0) { 331 buffer.append(" ").append(escapeNewlines(text, escapeNewLine)); 332 } 333 334 if (exceptionFormatters != null && event.getThrown() != null) { 335 final StringBuilder exception = new StringBuilder(LF); 336 for (final PatternFormatter formatter : exceptionFormatters) { 337 formatter.format(event, exception); 338 } 339 buffer.append(escapeNewlines(exception.toString(), escapeNewLine)); 340 } 341 if (includeNewLine) { 342 buffer.append(LF); 343 } 344 } 345 346 private void appendStructuredElements(final StringBuilder buffer, final LogEvent event) { 347 final Message message = event.getMessage(); 348 final boolean isStructured = message instanceof StructuredDataMessage; 349 350 if (!isStructured && (fieldFormatters!= null && fieldFormatters.size() == 0) && !includeMDC) { 351 buffer.append("-"); 352 return; 353 } 354 355 final Map<String, StructuredDataElement> sdElements = new HashMap<String, StructuredDataElement>(); 356 final Map<String, String> contextMap = event.getContextMap(); 357 358 if (mdcRequired != null) { 359 checkRequired(contextMap); 360 } 361 362 if (fieldFormatters != null) { 363 for (final Map.Entry<String, FieldFormatter> sdElement: fieldFormatters.entrySet()) { 364 final String sdId = sdElement.getKey(); 365 final StructuredDataElement elem = sdElement.getValue().format(event); 366 sdElements.put(sdId, elem); 367 } 368 } 369 370 if (includeMDC && contextMap.size() > 0) { 371 if (sdElements.containsKey(mdcSDID.toString())) { 372 final StructuredDataElement union = sdElements.get(mdcSDID.toString()); 373 union.union(contextMap); 374 sdElements.put(mdcSDID.toString(), union); 375 } else { 376 final StructuredDataElement formattedContextMap = new StructuredDataElement(contextMap, false); 377 sdElements.put(mdcSDID.toString(), formattedContextMap); 378 } 379 } 380 381 if (isStructured) { 382 final StructuredDataMessage data = (StructuredDataMessage) message; 383 final Map<String, String> map = data.getData(); 384 final StructuredDataId id = data.getId(); 385 final String sdId = getId(id); 386 387 if (sdElements.containsKey(sdId)) { 388 final StructuredDataElement union = sdElements.get(id.toString()); 389 union.union(map); 390 sdElements.put(sdId, union); 391 } else { 392 final StructuredDataElement formattedData = new StructuredDataElement(map, false); 393 sdElements.put(sdId, formattedData); 394 } 395 } 396 397 if (sdElements.size() == 0) { 398 buffer.append("-"); 399 return; 400 } 401 402 for (final Map.Entry<String, StructuredDataElement> entry: sdElements.entrySet()) { 403 formatStructuredElement(entry.getKey(), mdcPrefix, entry.getValue(), buffer, checker); 404 } 405 } 406 407 private String escapeNewlines(final String text, final String escapeNewLine) { 408 if (null == escapeNewLine) { 409 return text; 410 } 411 return NEWLINE_PATTERN.matcher(text).replaceAll(escapeNewLine); 412 } 413 414 protected String getProcId() { 415 return "-"; 416 } 417 418 protected List<String> getMdcExcludes() { 419 return mdcExcludes; 420 } 421 422 protected List<String> getMdcIncludes() { 423 return mdcIncludes; 424 } 425 426 private String computeTimeStampString(final long now) { 427 long last; 428 synchronized (this) { 429 last = lastTimestamp; 430 if (now == lastTimestamp) { 431 return timestamppStr; 432 } 433 } 434 435 final StringBuilder buffer = new StringBuilder(); 436 final Calendar cal = new GregorianCalendar(); 437 cal.setTimeInMillis(now); 438 buffer.append(Integer.toString(cal.get(Calendar.YEAR))); 439 buffer.append("-"); 440 pad(cal.get(Calendar.MONTH) + 1, TWO_DIGITS, buffer); 441 buffer.append("-"); 442 pad(cal.get(Calendar.DAY_OF_MONTH), TWO_DIGITS, buffer); 443 buffer.append("T"); 444 pad(cal.get(Calendar.HOUR_OF_DAY), TWO_DIGITS, buffer); 445 buffer.append(":"); 446 pad(cal.get(Calendar.MINUTE), TWO_DIGITS, buffer); 447 buffer.append(":"); 448 pad(cal.get(Calendar.SECOND), TWO_DIGITS, buffer); 449 450 final int millis = cal.get(Calendar.MILLISECOND); 451 if (millis != 0) { 452 buffer.append('.'); 453 pad(millis, THREE_DIGITS, buffer); 454 } 455 456 int tzmin = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / MILLIS_PER_MINUTE; 457 if (tzmin == 0) { 458 buffer.append("Z"); 459 } else { 460 if (tzmin < 0) { 461 tzmin = -tzmin; 462 buffer.append("-"); 463 } else { 464 buffer.append("+"); 465 } 466 final int tzhour = tzmin / MINUTES_PER_HOUR; 467 tzmin -= tzhour * MINUTES_PER_HOUR; 468 pad(tzhour, TWO_DIGITS, buffer); 469 buffer.append(":"); 470 pad(tzmin, TWO_DIGITS, buffer); 471 } 472 synchronized (this) { 473 if (last == lastTimestamp) { 474 lastTimestamp = now; 475 timestamppStr = buffer.toString(); 476 } 477 } 478 return buffer.toString(); 479 } 480 481 private void pad(final int val, int max, final StringBuilder buf) { 482 while (max > 1) { 483 if (val < max) { 484 buf.append("0"); 485 } 486 max = max / TWO_DIGITS; 487 } 488 buf.append(Integer.toString(val)); 489 } 490 491 private void formatStructuredElement(final String id, final String prefix, final StructuredDataElement data, 492 final StringBuilder sb, final ListChecker checker) { 493 if ((id == null && defaultId == null) || data.discard()) { 494 return; 495 } 496 497 sb.append("["); 498 sb.append(id); 499 if (!mdcSDID.toString().equals(id)) { 500 appendMap(prefix, data.getFields(), sb, noopChecker); 501 } else { 502 appendMap(prefix, data.getFields(), sb, checker); 503 } 504 sb.append("]"); 505 } 506 507 private String getId(final StructuredDataId id) { 508 final StringBuilder sb = new StringBuilder(); 509 if (id == null || id.getName() == null) { 510 sb.append(defaultId); 511 } else { 512 sb.append(id.getName()); 513 } 514 int ein = id != null ? id.getEnterpriseNumber() : enterpriseNumber; 515 if (ein < 0) { 516 ein = enterpriseNumber; 517 } 518 if (ein >= 0) { 519 sb.append("@").append(ein); 520 } 521 return sb.toString(); 522 } 523 524 private void checkRequired(final Map<String, String> map) { 525 for (final String key : mdcRequired) { 526 final String value = map.get(key); 527 if (value == null) { 528 throw new LoggingException("Required key " + key + " is missing from the " + mdcId); 529 } 530 } 531 } 532 533 private void appendMap(final String prefix, final Map<String, String> map, final StringBuilder sb, 534 final ListChecker checker) { 535 final SortedMap<String, String> sorted = new TreeMap<String, String>(map); 536 for (final Map.Entry<String, String> entry : sorted.entrySet()) { 537 if (checker.check(entry.getKey()) && entry.getValue() != null) { 538 sb.append(" "); 539 if (prefix != null) { 540 sb.append(prefix); 541 } 542 sb.append(escapeNewlines(escapeSDParams(entry.getKey()), escapeNewLine)).append("=\"") 543 .append(escapeNewlines(escapeSDParams(entry.getValue()), escapeNewLine)).append("\""); 544 } 545 } 546 } 547 548 private String escapeSDParams(final String value) { 549 return PARAM_VALUE_ESCAPE_PATTERN.matcher(value).replaceAll("\\\\$0"); 550 } 551 552 /** 553 * Interface used to check keys in a Map. 554 */ 555 private interface ListChecker { 556 boolean check(String key); 557 } 558 559 /** 560 * Includes only the listed keys. 561 */ 562 private class IncludeChecker implements ListChecker { 563 @Override 564 public boolean check(final String key) { 565 return mdcIncludes.contains(key); 566 } 567 } 568 569 /** 570 * Excludes the listed keys. 571 */ 572 private class ExcludeChecker implements ListChecker { 573 @Override 574 public boolean check(final String key) { 575 return !mdcExcludes.contains(key); 576 } 577 } 578 579 /** 580 * Does nothing. 581 */ 582 private class NoopChecker implements ListChecker { 583 @Override 584 public boolean check(final String key) { 585 return true; 586 } 587 } 588 589 @Override 590 public String toString() { 591 final StringBuilder sb = new StringBuilder(); 592 sb.append("facility=").append(facility.name()); 593 sb.append(" appName=").append(appName); 594 sb.append(" defaultId=").append(defaultId); 595 sb.append(" enterpriseNumber=").append(enterpriseNumber); 596 sb.append(" newLine=").append(includeNewLine); 597 sb.append(" includeMDC=").append(includeMDC); 598 sb.append(" messageId=").append(messageId); 599 return sb.toString(); 600 } 601 602 /** 603 * Create the RFC 5424 Layout. 604 * 605 * @param facility The Facility is used to try to classify the message. 606 * @param id The default structured data id to use when formatting according to RFC 5424. 607 * @param ein The IANA enterprise number. 608 * @param includeMDC Indicates whether data from the ThreadContextMap will be included in the RFC 5424 Syslog 609 * record. Defaults to "true:. 610 * @param mdcId The id to use for the MDC Structured Data Element. 611 * @param mdcPrefix The prefix to add to MDC key names. 612 * @param eventPrefix The prefix to add to event key names. 613 * @param includeNL If true, a newline will be appended to the end of the syslog record. The default is false. 614 * @param escapeNL String that should be used to replace newlines within the message text. 615 * @param appName The value to use as the APP-NAME in the RFC 5424 syslog record. 616 * @param msgId The default value to be used in the MSGID field of RFC 5424 syslog records. 617 * @param excludes A comma separated list of MDC keys that should be excluded from the LogEvent. 618 * @param includes A comma separated list of MDC keys that should be included in the FlumeEvent. 619 * @param required A comma separated list of MDC keys that must be present in the MDC. 620 * @param exceptionPattern The pattern for formatting exceptions. 621 * @param useTLSMessageFormat If true the message will be formatted according to RFC 5425. 622 * @param loggerFields Container for the KeyValuePairs containing the patterns 623 * @param config The Configuration. Some Converters require access to the Interpolator. 624 * @return An RFC5424Layout. 625 */ 626 @PluginFactory 627 public static RFC5424Layout createLayout( 628 @PluginAttribute("facility") final String facility, 629 @PluginAttribute("id") final String id, 630 @PluginAttribute("enterpriseNumber") final String ein, 631 @PluginAttribute("includeMDC") final String includeMDC, 632 @PluginAttribute("mdcId") String mdcId, 633 @PluginAttribute("mdcPrefix") final String mdcPrefix, 634 @PluginAttribute("eventPrefix") final String eventPrefix, 635 @PluginAttribute("newLine") final String includeNL, 636 @PluginAttribute("newLineEscape") final String escapeNL, 637 @PluginAttribute("appName") final String appName, 638 @PluginAttribute("messageId") final String msgId, 639 @PluginAttribute("mdcExcludes") final String excludes, 640 @PluginAttribute("mdcIncludes") String includes, 641 @PluginAttribute("mdcRequired") final String required, 642 @PluginAttribute("exceptionPattern") final String exceptionPattern, 643 @PluginAttribute("useTLSMessageFormat") final String useTLSMessageFormat, // RFC 5425 644 @PluginElement("LoggerFields") final LoggerFields[] loggerFields, 645 @PluginConfiguration final Configuration config) { 646 final Charset charset = Charsets.UTF_8; 647 if (includes != null && excludes != null) { 648 LOGGER.error("mdcIncludes and mdcExcludes are mutually exclusive. Includes wil be ignored"); 649 includes = null; 650 } 651 final Facility f = Facility.toFacility(facility, Facility.LOCAL0); 652 final int enterpriseNumber = Integers.parseInt(ein, DEFAULT_ENTERPRISE_NUMBER); 653 final boolean isMdc = Booleans.parseBoolean(includeMDC, true); 654 final boolean includeNewLine = Boolean.parseBoolean(includeNL); 655 final boolean useTlsMessageFormat = Booleans.parseBoolean(useTLSMessageFormat, false); 656 if (mdcId == null) { 657 mdcId = DEFAULT_MDCID; 658 } 659 660 return new RFC5424Layout(config, f, id, enterpriseNumber, isMdc, includeNewLine, escapeNL, mdcId, mdcPrefix, 661 eventPrefix, appName, msgId, excludes, includes, required, charset, exceptionPattern, 662 useTlsMessageFormat, loggerFields); 663 } 664 665 private class FieldFormatter { 666 667 private final Map<String, List<PatternFormatter>> delegateMap; 668 private final boolean discardIfEmpty; 669 670 public FieldFormatter(final Map<String, List<PatternFormatter>> fieldMap, final boolean discardIfEmpty) { 671 this.discardIfEmpty = discardIfEmpty; 672 this.delegateMap = fieldMap; 673 } 674 675 public StructuredDataElement format(final LogEvent event) { 676 final Map<String, String> map = new HashMap<String, String>(); 677 678 for (final Map.Entry<String, List<PatternFormatter>> entry : delegateMap.entrySet()) { 679 final StringBuilder buffer = new StringBuilder(); 680 for (final PatternFormatter formatter : entry.getValue()) { 681 formatter.format(event, buffer); 682 } 683 map.put(entry.getKey(), buffer.toString()); 684 } 685 return new StructuredDataElement(map, discardIfEmpty); 686 } 687 } 688 689 private class StructuredDataElement { 690 691 private final Map<String, String> fields; 692 private final boolean discardIfEmpty; 693 694 public StructuredDataElement(final Map<String, String> fields, final boolean discardIfEmpty) { 695 this.discardIfEmpty = discardIfEmpty; 696 this.fields = fields; 697 } 698 699 boolean discard() { 700 if (discardIfEmpty == false) { 701 return false; 702 } 703 boolean foundNotEmptyValue = false; 704 for (final Map.Entry<String, String> entry : fields.entrySet()) { 705 if (Strings.isNotEmpty(entry.getValue())) { 706 foundNotEmptyValue = true; 707 break; 708 } 709 } 710 return !foundNotEmptyValue; 711 } 712 713 void union(final Map<String, String> fields) { 714 this.fields.putAll(fields); 715 } 716 717 Map<String, String> getFields() { 718 return this.fields; 719 } 720 } 721}