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.message;
18  
19  import java.util.Arrays;
20  
21  import org.apache.logging.log4j.util.Constants;
22  import org.apache.logging.log4j.util.PerformanceSensitive;
23  import org.apache.logging.log4j.util.StringBuilders;
24  
25  /**
26   * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple
27   * threads concurrently.
28   *
29   * @see ParameterizedMessage
30   * @since 2.6
31   */
32  @PerformanceSensitive("allocation")
33  public class ReusableParameterizedMessage implements ReusableMessage {
34  
35      private static final int MIN_BUILDER_SIZE = 512;
36      private static final int MAX_PARMS = 10;
37      private static final long serialVersionUID = 7800075879295123856L;
38      private transient ThreadLocal<StringBuilder> buffer; // non-static: LOG4J2-1583
39  
40      private String messagePattern;
41      private int argCount;
42      private int usedCount;
43      private final int[] indices = new int[256];
44      private transient Object[] varargs;
45      private transient Object[] params = new Object[MAX_PARMS];
46      private transient Throwable throwable;
47      transient boolean reserved = false; // LOG4J2-1583 prevent scrambled logs with nested logging calls
48  
49      /**
50       * Creates a reusable message.
51       */
52      public ReusableParameterizedMessage() {
53      }
54  
55      private Object[] getTrimmedParams() {
56          return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
57      }
58  
59      private Object[] getParams() {
60          return varargs == null ? params : varargs;
61      }
62  
63      // see interface javadoc
64      @Override
65      public Object[] swapParameters(final Object[] emptyReplacement) {
66          Object[] result;
67          if (varargs == null) {
68              result = params;
69              if (emptyReplacement.length >= MAX_PARMS) {
70                  params = emptyReplacement;
71              } else {
72                  // Bad replacement! Too small, may blow up future 10-arg messages.
73                  if (argCount <= emptyReplacement.length) {
74                      // copy params into the specified replacement array and return that
75                      System.arraycopy(params, 0, emptyReplacement, 0, argCount);
76                      result = emptyReplacement;
77                  } else {
78                      // replacement array is too small for current content and future content: discard it
79                      params = new Object[MAX_PARMS];
80                  }
81              }
82          } else {
83              // The returned array will be reused by the caller in future swapParameter() calls.
84              // Therefore we want to avoid returning arrays with less than 10 elements.
85              // If the vararg array is less than 10 params we just copy its content into the specified array
86              // and return it. This helps the caller to retain a reusable array of at least 10 elements.
87              // NOTE: LOG4J2-1688 unearthed the use case that an application array (not a varargs array) is passed
88              // as the argument array. This array should not be modified, so it cannot be passed to the caller
89              // who will at some point null out the elements in the array).
90              if (argCount <= emptyReplacement.length) {
91                  result = emptyReplacement;
92              } else {
93                  result = new Object[argCount]; // LOG4J2-1688
94              }
95              // copy params into the specified replacement array and return that
96              System.arraycopy(varargs, 0, result, 0, argCount);
97          }
98          return result;
99      }
100 
101     // see interface javadoc
102     @Override
103     public short getParameterCount() {
104         return (short) argCount;
105     }
106 
107     @Override
108     public Message memento() {
109         return new ParameterizedMessage(messagePattern, getTrimmedParams());
110     }
111 
112     private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
113         this.varargs = null;
114         this.messagePattern = messagePattern;
115         this.argCount = argCount;
116         final int placeholderCount = count(messagePattern, indices);
117         initThrowable(paramArray, argCount, placeholderCount);
118         this.usedCount = Math.min(placeholderCount, argCount);
119     }
120 
121     private static int count(final String messagePattern, final int[] indices) {
122         try {
123             // try the fast path first
124             return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
125         } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
126             return ParameterFormatter.countArgumentPlaceholders(messagePattern);
127         }
128     }
129 
130     private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
131         if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
132             this.throwable = (Throwable) params[argCount - 1];
133         } else {
134             this.throwable = null;
135         }
136     }
137 
138     ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
139         init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
140         varargs = arguments;
141         return this;
142     }
143 
144     ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
145         params[0] = p0;
146         init(messagePattern, 1, params);
147         return this;
148     }
149 
150     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
151         params[0] = p0;
152         params[1] = p1;
153         init(messagePattern, 2, params);
154         return this;
155     }
156 
157     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
158         params[0] = p0;
159         params[1] = p1;
160         params[2] = p2;
161         init(messagePattern, 3, params);
162         return this;
163     }
164 
165     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
166         params[0] = p0;
167         params[1] = p1;
168         params[2] = p2;
169         params[3] = p3;
170         init(messagePattern, 4, params);
171         return this;
172     }
173 
174     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
175         params[0] = p0;
176         params[1] = p1;
177         params[2] = p2;
178         params[3] = p3;
179         params[4] = p4;
180         init(messagePattern, 5, params);
181         return this;
182     }
183 
184     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
185         params[0] = p0;
186         params[1] = p1;
187         params[2] = p2;
188         params[3] = p3;
189         params[4] = p4;
190         params[5] = p5;
191         init(messagePattern, 6, params);
192         return this;
193     }
194 
195     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
196             final Object p6) {
197         params[0] = p0;
198         params[1] = p1;
199         params[2] = p2;
200         params[3] = p3;
201         params[4] = p4;
202         params[5] = p5;
203         params[6] = p6;
204         init(messagePattern, 7, params);
205         return this;
206     }
207 
208     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
209             final Object p6, final Object p7) {
210         params[0] = p0;
211         params[1] = p1;
212         params[2] = p2;
213         params[3] = p3;
214         params[4] = p4;
215         params[5] = p5;
216         params[6] = p6;
217         params[7] = p7;
218         init(messagePattern, 8, params);
219         return this;
220     }
221 
222     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
223             final Object p6, final Object p7, final Object p8) {
224         params[0] = p0;
225         params[1] = p1;
226         params[2] = p2;
227         params[3] = p3;
228         params[4] = p4;
229         params[5] = p5;
230         params[6] = p6;
231         params[7] = p7;
232         params[8] = p8;
233         init(messagePattern, 9, params);
234         return this;
235     }
236 
237     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
238             final Object p6, final Object p7, final Object p8, final Object p9) {
239         params[0] = p0;
240         params[1] = p1;
241         params[2] = p2;
242         params[3] = p3;
243         params[4] = p4;
244         params[5] = p5;
245         params[6] = p6;
246         params[7] = p7;
247         params[8] = p8;
248         params[9] = p9;
249         init(messagePattern, 10, params);
250         return this;
251     }
252 
253     /**
254      * Returns the message pattern.
255      * @return the message pattern.
256      */
257     @Override
258     public String getFormat() {
259         return messagePattern;
260     }
261 
262     /**
263      * Returns the message parameters.
264      * @return the message parameters.
265      */
266     @Override
267     public Object[] getParameters() {
268         return getTrimmedParams();
269     }
270 
271     /**
272      * Returns the Throwable that was given as the last argument, if any.
273      * It will not survive serialization. The Throwable exists as part of the message
274      * primarily so that it can be extracted from the end of the list of parameters
275      * and then be added to the LogEvent. As such, the Throwable in the event should
276      * not be used once the LogEvent has been constructed.
277      *
278      * @return the Throwable, if any.
279      */
280     @Override
281     public Throwable getThrowable() {
282         return throwable;
283     }
284 
285     /**
286      * Returns the formatted message.
287      * @return the formatted message.
288      */
289     @Override
290     public String getFormattedMessage() {
291         final StringBuilder sb = getBuffer();
292         formatTo(sb);
293         final String result = sb.toString();
294         StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
295         return result;
296     }
297 
298     private StringBuilder getBuffer() {
299         if (buffer == null) {
300             buffer = new ThreadLocal<>();
301         }
302         StringBuilder result = buffer.get();
303         if (result == null) {
304             final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
305             result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2));
306             buffer.set(result);
307         }
308         result.setLength(0);
309         return result;
310     }
311 
312     @Override
313     public void formatTo(final StringBuilder builder) {
314         if (indices[0] < 0) {
315             ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
316         } else {
317             ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
318         }
319     }
320 
321     /**
322      * Sets the reserved flag to true and returns this object.
323      * @return this object
324      * @since 2.7
325      */
326     ReusableParameterizedMessage reserve() {
327         reserved = true;
328         return this;
329     }
330 
331     @Override
332     public String toString() {
333         return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
334                 Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
335     }
336 }