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 static ThreadLocal<StringBuilder> buffer = new ThreadLocal<>();
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  
46      /**
47       * Creates a reusable message.
48       */
49      public ReusableParameterizedMessage() {
50      }
51  
52      private Object[] getTrimmedParams() {
53          return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
54      }
55  
56      private Object[] getParams() {
57          return varargs == null ? params : varargs;
58      }
59  
60      // see interface javadoc
61      @Override
62      public Object[] swapParameters(final Object[] emptyReplacement) {
63          Object[] result;
64          if (varargs == null) {
65              result = params;
66              if (emptyReplacement.length >= MAX_PARMS) {
67                  params = emptyReplacement;
68              } else {
69                  // Bad replacement! Too small, may blow up future 10-arg messages.
70                  if (argCount <= emptyReplacement.length) {
71                      // copy params into the specified replacement array and return that
72                      System.arraycopy(params, 0, emptyReplacement, 0, argCount);
73                      result = emptyReplacement;
74                  } else {
75                      // replacement array is too small for current content and future content: discard it
76                      params = new Object[MAX_PARMS];
77                  }
78              }
79          } else {
80              // The returned array will be reused by the caller in future swapParameter() calls.
81              // Therefore we want to avoid returning arrays with less than 10 elements.
82              // If the vararg array is less than 10 params we just copy its content into the specified array
83              // and return it. This helps the caller to retain a reusable array of at least 10 elements.
84              if (argCount <= emptyReplacement.length) {
85                  // copy params into the specified replacement array and return that
86                  System.arraycopy(varargs, 0, emptyReplacement, 0, argCount);
87                  result = emptyReplacement;
88              } else {
89                  result = varargs;
90                  varargs = emptyReplacement;
91              }
92          }
93          return result;
94      }
95  
96      // see interface javadoc
97      @Override
98      public short getParameterCount() {
99          return (short) argCount;
100     }
101 
102     @Override
103     public Message memento() {
104         return new ParameterizedMessage(messagePattern, getTrimmedParams());
105     }
106 
107     private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
108         this.varargs = null;
109         this.messagePattern = messagePattern;
110         this.argCount = argCount;
111         final int placeholderCount = count(messagePattern, indices);
112         initThrowable(paramArray, argCount, placeholderCount);
113         this.usedCount = Math.min(placeholderCount, argCount);
114     }
115 
116     private static int count(final String messagePattern, final int[] indices) {
117         try {
118             // try the fast path first
119             return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
120         } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
121             return ParameterFormatter.countArgumentPlaceholders(messagePattern);
122         }
123     }
124 
125     private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
126         if (usedParams < argCount && params[argCount - 1] instanceof Throwable) {
127             this.throwable = (Throwable) params[argCount - 1];
128         } else {
129             this.throwable = null;
130         }
131     }
132 
133     ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) {
134         init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
135         varargs = arguments;
136         return this;
137     }
138 
139     ReusableParameterizedMessage set(final String messagePattern, final Object p0) {
140         params[0] = p0;
141         init(messagePattern, 1, params);
142         return this;
143     }
144 
145     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) {
146         params[0] = p0;
147         params[1] = p1;
148         init(messagePattern, 2, params);
149         return this;
150     }
151 
152     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) {
153         params[0] = p0;
154         params[1] = p1;
155         params[2] = p2;
156         init(messagePattern, 3, params);
157         return this;
158     }
159 
160     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) {
161         params[0] = p0;
162         params[1] = p1;
163         params[2] = p2;
164         params[3] = p3;
165         init(messagePattern, 4, params);
166         return this;
167     }
168 
169     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
170         params[0] = p0;
171         params[1] = p1;
172         params[2] = p2;
173         params[3] = p3;
174         params[4] = p4;
175         init(messagePattern, 5, params);
176         return this;
177     }
178 
179     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
180         params[0] = p0;
181         params[1] = p1;
182         params[2] = p2;
183         params[3] = p3;
184         params[4] = p4;
185         params[5] = p5;
186         init(messagePattern, 6, params);
187         return this;
188     }
189 
190     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
191             final Object p6) {
192         params[0] = p0;
193         params[1] = p1;
194         params[2] = p2;
195         params[3] = p3;
196         params[4] = p4;
197         params[5] = p5;
198         params[6] = p6;
199         init(messagePattern, 7, 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, final Object p7) {
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         params[7] = p7;
213         init(messagePattern, 8, params);
214         return this;
215     }
216 
217     ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
218             final Object p6, final Object p7, final Object p8) {
219         params[0] = p0;
220         params[1] = p1;
221         params[2] = p2;
222         params[3] = p3;
223         params[4] = p4;
224         params[5] = p5;
225         params[6] = p6;
226         params[7] = p7;
227         params[8] = p8;
228         init(messagePattern, 9, 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, final Object p9) {
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         params[9] = p9;
244         init(messagePattern, 10, params);
245         return this;
246     }
247 
248     /**
249      * Returns the message pattern.
250      * @return the message pattern.
251      */
252     @Override
253     public String getFormat() {
254         return messagePattern;
255     }
256 
257     /**
258      * Returns the message parameters.
259      * @return the message parameters.
260      */
261     @Override
262     public Object[] getParameters() {
263         return getTrimmedParams();
264     }
265 
266     /**
267      * Returns the Throwable that was given as the last argument, if any.
268      * It will not survive serialization. The Throwable exists as part of the message
269      * primarily so that it can be extracted from the end of the list of parameters
270      * and then be added to the LogEvent. As such, the Throwable in the event should
271      * not be used once the LogEvent has been constructed.
272      *
273      * @return the Throwable, if any.
274      */
275     @Override
276     public Throwable getThrowable() {
277         return throwable;
278     }
279 
280     /**
281      * Returns the formatted message.
282      * @return the formatted message.
283      */
284     @Override
285     public String getFormattedMessage() {
286         final StringBuilder sb = getBuffer();
287         formatTo(sb);
288         return sb.toString();
289     }
290 
291     private StringBuilder getBuffer() {
292         StringBuilder result = buffer.get();
293         if (result == null) {
294             final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
295             result = new StringBuilder(Math.min(MIN_BUILDER_SIZE, currentPatternLength * 2));
296             buffer.set(result);
297         }
298         result.setLength(0);
299         return result;
300     }
301 
302     @Override
303     public void formatTo(final StringBuilder builder) {
304         if (indices[0] < 0) {
305             ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount);
306         } else {
307             ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices);
308         }
309     }
310 
311 
312     @Override
313     public String toString() {
314         return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
315                 Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
316     }
317 }