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, ParameterVisitable {
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 <S> void forEachParameter(ParameterConsumer<S> action, S state) {
109         Object[] parameters = getParams();
110         for (short i = 0; i < argCount; i++) {
111             action.accept(parameters[i], i, state);
112         }
113     }
114 
115     @Override
116     public Message memento() {
117         return new ParameterizedMessage(messagePattern, getTrimmedParams());
118     }
119 
120     private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
121         this.varargs = null;
122         this.messagePattern = messagePattern;
123         this.argCount = argCount;
124         final int placeholderCount = count(messagePattern, indices);
125         initThrowable(paramArray, argCount, placeholderCount);
126         this.usedCount = Math.min(placeholderCount, argCount);
127     }
128 
129     private static int count(final String messagePattern, final int[] indices) {
130         try {
131             // try the fast path first
132             return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
133         } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
134             return ParameterFormatter.countArgumentPlaceholders(messagePattern);
135         }
136     }
137 
138     private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
139         if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
140             this.throwable = (Throwable) params[argCount - 1];
141         } else {
142             this.throwable = null;
143         }
144     }
145 
146     ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
147         init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
148         varargs = arguments;
149         return this;
150     }
151 
152     ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
153         params[0] = p0;
154         init(messagePattern, 1, params);
155         return this;
156     }
157 
158     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
159         params[0] = p0;
160         params[1] = p1;
161         init(messagePattern, 2, params);
162         return this;
163     }
164 
165     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
166         params[0] = p0;
167         params[1] = p1;
168         params[2] = p2;
169         init(messagePattern, 3, params);
170         return this;
171     }
172 
173     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
174         params[0] = p0;
175         params[1] = p1;
176         params[2] = p2;
177         params[3] = p3;
178         init(messagePattern, 4, params);
179         return this;
180     }
181 
182     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
183         params[0] = p0;
184         params[1] = p1;
185         params[2] = p2;
186         params[3] = p3;
187         params[4] = p4;
188         init(messagePattern, 5, params);
189         return this;
190     }
191 
192     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
193         params[0] = p0;
194         params[1] = p1;
195         params[2] = p2;
196         params[3] = p3;
197         params[4] = p4;
198         params[5] = p5;
199         init(messagePattern, 6, params);
200         return this;
201     }
202 
203     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
204             final Object p6) {
205         params[0] = p0;
206         params[1] = p1;
207         params[2] = p2;
208         params[3] = p3;
209         params[4] = p4;
210         params[5] = p5;
211         params[6] = p6;
212         init(messagePattern, 7, params);
213         return this;
214     }
215 
216     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
217             final Object p6, final Object p7) {
218         params[0] = p0;
219         params[1] = p1;
220         params[2] = p2;
221         params[3] = p3;
222         params[4] = p4;
223         params[5] = p5;
224         params[6] = p6;
225         params[7] = p7;
226         init(messagePattern, 8, params);
227         return this;
228     }
229 
230     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
231             final Object p6, final Object p7, final Object p8) {
232         params[0] = p0;
233         params[1] = p1;
234         params[2] = p2;
235         params[3] = p3;
236         params[4] = p4;
237         params[5] = p5;
238         params[6] = p6;
239         params[7] = p7;
240         params[8] = p8;
241         init(messagePattern, 9, params);
242         return this;
243     }
244 
245     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
246             final Object p6, final Object p7, final Object p8, final Object p9) {
247         params[0] = p0;
248         params[1] = p1;
249         params[2] = p2;
250         params[3] = p3;
251         params[4] = p4;
252         params[5] = p5;
253         params[6] = p6;
254         params[7] = p7;
255         params[8] = p8;
256         params[9] = p9;
257         init(messagePattern, 10, params);
258         return this;
259     }
260 
261     /**
262      * Returns the message pattern.
263      * @return the message pattern.
264      */
265     @Override
266     public String getFormat() {
267         return messagePattern;
268     }
269 
270     /**
271      * Returns the message parameters.
272      * @return the message parameters.
273      */
274     @Override
275     public Object[] getParameters() {
276         return getTrimmedParams();
277     }
278 
279     /**
280      * Returns the Throwable that was given as the last argument, if any.
281      * It will not survive serialization. The Throwable exists as part of the message
282      * primarily so that it can be extracted from the end of the list of parameters
283      * and then be added to the LogEvent. As such, the Throwable in the event should
284      * not be used once the LogEvent has been constructed.
285      *
286      * @return the Throwable, if any.
287      */
288     @Override
289     public Throwable getThrowable() {
290         return throwable;
291     }
292 
293     /**
294      * Returns the formatted message.
295      * @return the formatted message.
296      */
297     @Override
298     public String getFormattedMessage() {
299         final StringBuilder sb = getBuffer();
300         formatTo(sb);
301         final String result = sb.toString();
302         StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
303         return result;
304     }
305 
306     private StringBuilder getBuffer() {
307         if (buffer == null) {
308             buffer = new ThreadLocal<>();
309         }
310         StringBuilder result = buffer.get();
311         if (result == null) {
312             final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
313             result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2));
314             buffer.set(result);
315         }
316         result.setLength(0);
317         return result;
318     }
319 
320     @Override
321     public void formatTo(final StringBuilder builder) {
322         if (indices[0] < 0) {
323             ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
324         } else {
325             ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
326         }
327     }
328 
329     /**
330      * Sets the reserved flag to true and returns this object.
331      * @return this object
332      * @since 2.7
333      */
334     ReusableParameterizedMessage reserve() {
335         reserved = true;
336         return this;
337     }
338 
339     @Override
340     public String toString() {
341         return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
342                 Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
343     }
344 }