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