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 java.util.Locale;
20  
21  import org.apache.logging.log4j.core.LogEvent;
22  import org.apache.logging.log4j.core.config.Configuration;
23  import org.apache.logging.log4j.core.config.plugins.Plugin;
24  import org.apache.logging.log4j.core.util.ArrayUtils;
25  import org.apache.logging.log4j.core.util.Loader;
26  import org.apache.logging.log4j.message.Message;
27  import org.apache.logging.log4j.message.MultiformatMessage;
28  import org.apache.logging.log4j.status.StatusLogger;
29  import org.apache.logging.log4j.util.PerformanceSensitive;
30  import org.apache.logging.log4j.util.StringBuilderFormattable;
31  
32  /**
33   * Returns the event's rendered message in a StringBuilder.
34   */
35  @Plugin(name = "MessagePatternConverter", category = PatternConverter.CATEGORY)
36  @ConverterKeys({ "m", "msg", "message" })
37  @PerformanceSensitive("allocation")
38  public final class MessagePatternConverter extends LogEventPatternConverter {
39  
40      private static final String NOLOOKUPS = "nolookups";
41  
42      private final String[] formats;
43      private final Configuration config;
44      private final TextRenderer textRenderer;
45      private final boolean noLookups;
46  
47      /**
48       * Private constructor.
49       *
50       * @param options
51       *            options, may be null.
52       */
53      private MessagePatternConverter(final Configuration config, final String[] options) {
54          super("Message", "message");
55          this.formats = options;
56          this.config = config;
57          final int noLookupsIdx = loadNoLookups(options);
58          this.noLookups = noLookupsIdx >= 0;
59          this.textRenderer = loadMessageRenderer(noLookupsIdx >= 0 ? ArrayUtils.remove(options, noLookupsIdx) : options);
60      }
61  
62      private int loadNoLookups(final String[] options) {
63          if (options != null) {
64              for (int i = 0; i < options.length; i++) {
65                  final String option = options[i];
66                  if (NOLOOKUPS.equalsIgnoreCase(option)) {
67                      return i;
68                  }
69              }
70          }
71          return -1;
72      }
73  
74      private TextRenderer loadMessageRenderer(final String[] options) {
75          if (options != null) {
76              for (final String option : options) {
77                  switch (option.toUpperCase(Locale.ROOT)) {
78                  case "ANSI":
79                      if (Loader.isJansiAvailable()) {
80                          return new JAnsiTextRenderer(options, JAnsiTextRenderer.DefaultMessageStyleMap);
81                      }
82                      StatusLogger.getLogger()
83                              .warn("You requested ANSI message rendering but JANSI is not on the classpath.");
84                      return null;
85                  case "HTML":
86                      return new HtmlTextRenderer(options);
87                  }
88              }
89          }
90          return null;
91      }
92  
93      /**
94       * Obtains an instance of pattern converter.
95       *
96       * @param config
97       *            The Configuration.
98       * @param options
99       *            options, may be null.
100      * @return instance of pattern converter.
101      */
102     public static MessagePatternConverter newInstance(final Configuration config, final String[] options) {
103         return new MessagePatternConverter(config, options);
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     public void format(final LogEvent event, final StringBuilder toAppendTo) {
111         final Message msg = event.getMessage();
112         if (msg instanceof StringBuilderFormattable) {
113 
114             final boolean doRender = textRenderer != null;
115             final StringBuilder workingBuilder = doRender ? new StringBuilder(80) : toAppendTo;
116 
117             final StringBuilderFormattable stringBuilderFormattable = (StringBuilderFormattable) msg;
118             final int offset = workingBuilder.length();
119             stringBuilderFormattable.formatTo(workingBuilder);
120 
121             // TODO can we optimize this?
122             if (config != null && !noLookups) {
123                 for (int i = offset; i < workingBuilder.length() - 1; i++) {
124                     if (workingBuilder.charAt(i) == '$' && workingBuilder.charAt(i + 1) == '{') {
125                         final String value = workingBuilder.substring(offset, workingBuilder.length());
126                         workingBuilder.setLength(offset);
127                         workingBuilder.append(config.getStrSubstitutor().replace(event, value));
128                     }
129                 }
130             }
131             if (doRender) {
132                 textRenderer.render(workingBuilder, toAppendTo);
133             }
134             return;
135         }
136         if (msg != null) {
137             String result;
138             if (msg instanceof MultiformatMessage) {
139                 result = ((MultiformatMessage) msg).getFormattedMessage(formats);
140             } else {
141                 result = msg.getFormattedMessage();
142             }
143             if (result != null) {
144                 toAppendTo.append(config != null && result.contains("${")
145                         ? config.getStrSubstitutor().replace(event, result) : result);
146             } else {
147                 toAppendTo.append("null");
148             }
149         }
150     }
151 }