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, Clearable {
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                      // Do not retain references to objects in the reusable params array.
77                      for (int i = 0; i < argCount; i++) {
78                          params[i] = null;
79                      }
80                      result = emptyReplacement;
81                  } else {
82                      // replacement array is too small for current content and future content: discard it
83                      params = new Object[MAX_PARMS];
84                  }
85              }
86          } else {
87              // The returned array will be reused by the caller in future swapParameter() calls.
88              // Therefore we want to avoid returning arrays with less than 10 elements.
89              // If the vararg array is less than 10 params we just copy its content into the specified array
90              // and return it. This helps the caller to retain a reusable array of at least 10 elements.
91              // NOTE: LOG4J2-1688 unearthed the use case that an application array (not a varargs array) is passed
92              // as the argument array. This array should not be modified, so it cannot be passed to the caller
93              // who will at some point null out the elements in the array).
94              if (argCount <= emptyReplacement.length) {
95                  result = emptyReplacement;
96              } else {
97                  result = new Object[argCount]; // LOG4J2-1688
98              }
99              // copy params into the specified replacement array and return that
100             System.arraycopy(varargs, 0, result, 0, argCount);
101         }
102         return result;
103     }
104 
105     // see interface javadoc
106     @Override
107     public short getParameterCount() {
108         return (short) argCount;
109     }
110 
111     @Override
112     public <S> void forEachParameter(final ParameterConsumer<S> action, final S state) {
113         final Object[] parameters = getParams();
114         for (short i = 0; i < argCount; i++) {
115             action.accept(parameters[i], i, state);
116         }
117     }
118 
119     @Override
120     public Message memento() {
121         return new ParameterizedMessage(messagePattern, getTrimmedParams());
122     }
123 
124     private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
125         this.varargs = null;
126         this.messagePattern = messagePattern;
127         this.argCount = argCount;
128         final int placeholderCount = count(messagePattern, indices);
129         initThrowable(paramArray, argCount, placeholderCount);
130         this.usedCount = Math.min(placeholderCount, argCount);
131     }
132 
133     private static int count(final String messagePattern, final int[] indices) {
134         try {
135             // try the fast path first
136             return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
137         } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
138             return ParameterFormatter.countArgumentPlaceholders(messagePattern);
139         }
140     }
141 
142     private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
143         if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
144             this.throwable = (Throwable) params[argCount - 1];
145         } else {
146             this.throwable = null;
147         }
148     }
149 
150     ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
151         init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
152         varargs = arguments;
153         return this;
154     }
155 
156     ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
157         params[0] = p0;
158         init(messagePattern, 1, params);
159         return this;
160     }
161 
162     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
163         params[0] = p0;
164         params[1] = p1;
165         init(messagePattern, 2, params);
166         return this;
167     }
168 
169     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
170         params[0] = p0;
171         params[1] = p1;
172         params[2] = p2;
173         init(messagePattern, 3, params);
174         return this;
175     }
176 
177     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
178         params[0] = p0;
179         params[1] = p1;
180         params[2] = p2;
181         params[3] = p3;
182         init(messagePattern, 4, params);
183         return this;
184     }
185 
186     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
187         params[0] = p0;
188         params[1] = p1;
189         params[2] = p2;
190         params[3] = p3;
191         params[4] = p4;
192         init(messagePattern, 5, params);
193         return this;
194     }
195 
196     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
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         init(messagePattern, 6, params);
204         return this;
205     }
206 
207     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
208             final Object p6) {
209         params[0] = p0;
210         params[1] = p1;
211         params[2] = p2;
212         params[3] = p3;
213         params[4] = p4;
214         params[5] = p5;
215         params[6] = p6;
216         init(messagePattern, 7, params);
217         return this;
218     }
219 
220     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
221             final Object p6, final Object p7) {
222         params[0] = p0;
223         params[1] = p1;
224         params[2] = p2;
225         params[3] = p3;
226         params[4] = p4;
227         params[5] = p5;
228         params[6] = p6;
229         params[7] = p7;
230         init(messagePattern, 8, params);
231         return this;
232     }
233 
234     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
235             final Object p6, final Object p7, final Object p8) {
236         params[0] = p0;
237         params[1] = p1;
238         params[2] = p2;
239         params[3] = p3;
240         params[4] = p4;
241         params[5] = p5;
242         params[6] = p6;
243         params[7] = p7;
244         params[8] = p8;
245         init(messagePattern, 9, params);
246         return this;
247     }
248 
249     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
250             final Object p6, final Object p7, final Object p8, final Object p9) {
251         params[0] = p0;
252         params[1] = p1;
253         params[2] = p2;
254         params[3] = p3;
255         params[4] = p4;
256         params[5] = p5;
257         params[6] = p6;
258         params[7] = p7;
259         params[8] = p8;
260         params[9] = p9;
261         init(messagePattern, 10, params);
262         return this;
263     }
264 
265     /**
266      * Returns the message pattern.
267      * @return the message pattern.
268      */
269     @Override
270     public String getFormat() {
271         return messagePattern;
272     }
273 
274     /**
275      * Returns the message parameters.
276      * @return the message parameters.
277      */
278     @Override
279     public Object[] getParameters() {
280         return getTrimmedParams();
281     }
282 
283     /**
284      * Returns the Throwable that was given as the last argument, if any.
285      * It will not survive serialization. The Throwable exists as part of the message
286      * primarily so that it can be extracted from the end of the list of parameters
287      * and then be added to the LogEvent. As such, the Throwable in the event should
288      * not be used once the LogEvent has been constructed.
289      *
290      * @return the Throwable, if any.
291      */
292     @Override
293     public Throwable getThrowable() {
294         return throwable;
295     }
296 
297     /**
298      * Returns the formatted message.
299      * @return the formatted message.
300      */
301     @Override
302     public String getFormattedMessage() {
303         final StringBuilder sb = getBuffer();
304         formatTo(sb);
305         final String result = sb.toString();
306         StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE);
307         return result;
308     }
309 
310     private StringBuilder getBuffer() {
311         if (buffer == null) {
312             buffer = new ThreadLocal<>();
313         }
314         StringBuilder result = buffer.get();
315         if (result == null) {
316             final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
317             result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2));
318             buffer.set(result);
319         }
320         result.setLength(0);
321         return result;
322     }
323 
324     @Override
325     public void formatTo(final StringBuilder builder) {
326         if (indices[0] < 0) {
327             ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
328         } else {
329             ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
330         }
331     }
332 
333     /**
334      * Sets the reserved flag to true and returns this object.
335      * @return this object
336      * @since 2.7
337      */
338     ReusableParameterizedMessage reserve() {
339         reserved = true;
340         return this;
341     }
342 
343     @Override
344     public String toString() {
345         return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
346                 Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
347     }
348 
349     @Override
350     public void clear() { // LOG4J2-1583
351         // This method does not clear parameter values, those are expected to be swapped to a
352         // reusable message, which is responsible for clearing references.
353         reserved = false;
354         varargs = null;
355         messagePattern = null;
356         throwable = null;
357     }
358 }