View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.pattern;
18  
19  import org.apache.logging.log4j.util.PerformanceSensitive;
20  import org.apache.logging.log4j.util.ReadOnlyStringMap;
21  import org.apache.logging.log4j.core.LogEvent;
22  import org.apache.logging.log4j.core.config.plugins.Plugin;
23  import org.apache.logging.log4j.util.TriConsumer;
24  import org.apache.logging.log4j.util.StringBuilders;
25  
26  /**
27   * Able to handle the contents of the LogEvent's MDC and either
28   * output the entire contents of the properties in a similar format to the
29   * java.util.Hashtable.toString(), or to output the value of a specific key
30   * within the property bundle
31   * when this pattern converter has the option set.
32   */
33  @Plugin(name = "MdcPatternConverter", category = PatternConverter.CATEGORY)
34  @ConverterKeys({ "X", "mdc", "MDC" })
35  @PerformanceSensitive("allocation")
36  public final class MdcPatternConverter extends LogEventPatternConverter {
37  
38      /**
39       * Name of property to output.
40       */
41      private final String key;
42      private final String[] keys;
43      private final boolean full;
44  
45      /**
46       * Private constructor.
47       *
48       * @param options options, may be null.
49       */
50      private MdcPatternConverter(final String[] options) {
51          super(options != null && options.length > 0 ? "MDC{" + options[0] + '}' : "MDC", "mdc");
52          if (options != null && options.length > 0) {
53              full = false;
54              if (options[0].indexOf(',') > 0) {
55                  keys = options[0].split(",");
56                  for (int i = 0; i < keys.length; i++) {
57                      keys[i] = keys[i].trim();
58                  }
59                  key = null;
60              } else {
61                  keys = null;
62                  key = options[0];
63              }
64          } else {
65              full = true;
66              key = null;
67              keys = null;
68          }
69      }
70  
71      /**
72       * Obtains an instance of PropertiesPatternConverter.
73       *
74       * @param options options, may be null or first element contains name of property to format.
75       * @return instance of PropertiesPatternConverter.
76       */
77      public static MdcPatternConverter newInstance(final String[] options) {
78          return new MdcPatternConverter(options);
79      }
80  
81      private static final TriConsumer<String, Object, StringBuilder> WRITE_KEY_VALUES_INTO = new TriConsumer<String, Object, StringBuilder>() {
82          @Override
83          public void accept(final String key, final Object value, final StringBuilder sb) {
84              sb.append(key).append('=');
85              StringBuilders.appendValue(sb, value);
86              sb.append(", ");
87          }
88      };
89  
90      /**
91       * {@inheritDoc}
92       */
93      @Override
94      public void format(final LogEvent event, final StringBuilder toAppendTo) {
95          final ReadOnlyStringMap contextData = event.getContextData();
96          // if there is no additional options, we output every single
97          // Key/Value pair for the MDC in a similar format to Hashtable.toString()
98          if (full) {
99              if (contextData == null || contextData.size() == 0) {
100                 toAppendTo.append("{}");
101                 return;
102             }
103             appendFully(contextData, toAppendTo);
104         } else {
105             if (keys != null) {
106                 if (contextData == null || contextData.size() == 0) {
107                     toAppendTo.append("{}");
108                     return;
109                 }
110                 appendSelectedKeys(keys, contextData, toAppendTo);
111             } else if (contextData != null){
112                 // otherwise they just want a single key output
113                 final Object value = contextData.getValue(key);
114                 if (value != null) {
115                     StringBuilders.appendValue(toAppendTo, value);
116                 }
117             }
118         }
119     }
120 
121     private static void appendFully(final ReadOnlyStringMap contextData, final StringBuilder toAppendTo) {
122         toAppendTo.append("{");
123         final int start = toAppendTo.length();
124         contextData.forEach(WRITE_KEY_VALUES_INTO, toAppendTo);
125         final int end = toAppendTo.length();
126         if (end > start) {
127             toAppendTo.setCharAt(end - 2, '}');
128             toAppendTo.deleteCharAt(end - 1);
129         } else {
130             toAppendTo.append('}');
131         }
132     }
133 
134     private static void appendSelectedKeys(final String[] keys, final ReadOnlyStringMap contextData, final StringBuilder sb) {
135         // Print all the keys in the array that have a value.
136         final int start = sb.length();
137         sb.append('{');
138         for (int i = 0; i < keys.length; i++) {
139             final String theKey = keys[i];
140             final Object value = contextData.getValue(theKey);
141             if (value != null) { // !contextData.containskey(theKey)
142                 if (sb.length() - start > 1) {
143                     sb.append(", ");
144                 }
145                 sb.append(theKey).append('=');
146                 StringBuilders.appendValue(sb, value);
147             }
148         }
149         sb.append('}');
150     }
151 }