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