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  import java.util.Objects;
21  
22  import org.apache.logging.log4j.util.PerformanceSensitive;
23  
24  /**
25   * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple
26   * threads concurrently.
27   *
28   * @see ParameterizedMessage
29   * @since 2.6
30   */
31  @PerformanceSensitive("allocation")
32  public class ReusableParameterizedMessage implements ReusableMessage {
33  
34      private static final long serialVersionUID = 7800075879295123856L;
35      private static ThreadLocal<StringBuilder> buffer = new ThreadLocal<>();
36  
37      private String messagePattern;
38      private int argCount;
39      private int usedCount;
40      private final int[] indices = new int[256];
41      private transient Object[] varargs;
42      private transient Object[] params = new Object[10];
43      private transient Throwable throwable;
44  
45      /**
46       * Creates a reusable message.
47       */
48      public ReusableParameterizedMessage() {
49      }
50  
51      private Object[] getTrimmedParams() {
52          return varargs == null ? Arrays.copyOf(params, argCount) : varargs;
53      }
54  
55      private Object[] getParams() {
56          return varargs == null ? params : varargs;
57      }
58  
59      // see interface javadoc
60      @Override
61      public Object[] swapParameters(final Object[] emptyReplacement) {
62          Object[] result;
63          if (varargs == null) {
64              result = params;
65              params = Objects.requireNonNull(emptyReplacement);
66          } else {
67              result = varargs;
68              varargs = Objects.requireNonNull(emptyReplacement);
69          }
70          return result;
71      }
72  
73      // see interface javadoc
74      @Override
75      public short getParameterCount() {
76          return (short) argCount;
77      }
78  
79      @Override
80      public Message memento() {
81          return new ParameterizedMessage(messagePattern, getTrimmedParams());
82      }
83  
84      private void init(final String messagePattern, final int argCount, final Object[] paramArray) {
85          this.varargs = null;
86          this.messagePattern = messagePattern;
87          this.argCount = argCount;
88          int placeholderCount = count(messagePattern, indices);
89          initThrowable(paramArray, argCount, placeholderCount);
90          this.usedCount = Math.min(placeholderCount, argCount);
91      }
92  
93      private static int count(final String messagePattern, final int[] indices) {
94          try {
95              // try the fast path first
96              return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices);
97          } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders
98              return ParameterFormatter.countArgumentPlaceholders(messagePattern);
99          }
100     }
101 
102     private void initThrowable(final Object[] params, final int argCount, final int usedParams) {
103         if (usedParams < argCount && this.throwable == null && params[argCount - 1] instanceof Throwable) {
104             this.throwable = (Throwable) params[argCount - 1];
105         }
106     }
107 
108     ReusableParameterizedMessage set(String messagePattern, Object... arguments) {
109         init(messagePattern, arguments == null ? 0 : arguments.length, arguments);
110         varargs = arguments;
111         return this;
112     }
113 
114     ReusableParameterizedMessage set(String messagePattern, Object p0) {
115         params[0] = p0;
116         init(messagePattern, 1, params);
117         return this;
118     }
119 
120     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1) {
121         params[0] = p0;
122         params[1] = p1;
123         init(messagePattern, 2, params);
124         return this;
125     }
126 
127     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2) {
128         params[0] = p0;
129         params[1] = p1;
130         params[2] = p2;
131         init(messagePattern, 3, params);
132         return this;
133     }
134 
135     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3) {
136         params[0] = p0;
137         params[1] = p1;
138         params[2] = p2;
139         params[3] = p3;
140         init(messagePattern, 4, params);
141         return this;
142     }
143 
144     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4) {
145         params[0] = p0;
146         params[1] = p1;
147         params[2] = p2;
148         params[3] = p3;
149         params[4] = p4;
150         init(messagePattern, 5, params);
151         return this;
152     }
153 
154     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
155         params[0] = p0;
156         params[1] = p1;
157         params[2] = p2;
158         params[3] = p3;
159         params[4] = p4;
160         params[5] = p5;
161         init(messagePattern, 6, params);
162         return this;
163     }
164 
165     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
166             Object p6) {
167         params[0] = p0;
168         params[1] = p1;
169         params[2] = p2;
170         params[3] = p3;
171         params[4] = p4;
172         params[5] = p5;
173         params[6] = p6;
174         init(messagePattern, 7, params);
175         return this;
176     }
177 
178     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
179             Object p6, Object p7) {
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         params[6] = p6;
187         params[7] = p7;
188         init(messagePattern, 8, params);
189         return this;
190     }
191 
192     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
193             Object p6, Object p7, Object p8) {
194         params[0] = p0;
195         params[1] = p1;
196         params[2] = p2;
197         params[3] = p3;
198         params[4] = p4;
199         params[5] = p5;
200         params[6] = p6;
201         params[7] = p7;
202         params[8] = p8;
203         init(messagePattern, 9, params);
204         return this;
205     }
206 
207     ReusableParameterizedMessage set(String messagePattern, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5,
208             Object p6, Object p7, Object p8, Object p9) {
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         params[7] = p7;
217         params[8] = p8;
218         params[9] = p9;
219         init(messagePattern, 10, params);
220         return this;
221     }
222 
223     /**
224      * Returns the message pattern.
225      * @return the message pattern.
226      */
227     @Override
228     public String getFormat() {
229         return messagePattern;
230     }
231 
232     /**
233      * Returns the message parameters.
234      * @return the message parameters.
235      */
236     @Override
237     public Object[] getParameters() {
238         return getTrimmedParams();
239     }
240 
241     /**
242      * Returns the Throwable that was given as the last argument, if any.
243      * It will not survive serialization. The Throwable exists as part of the message
244      * primarily so that it can be extracted from the end of the list of parameters
245      * and then be added to the LogEvent. As such, the Throwable in the event should
246      * not be used once the LogEvent has been constructed.
247      *
248      * @return the Throwable, if any.
249      */
250     @Override
251     public Throwable getThrowable() {
252         return throwable;
253     }
254 
255     /**
256      * Returns the formatted message.
257      * @return the formatted message.
258      */
259     @Override
260     public String getFormattedMessage() {
261         final StringBuilder sb = getBuffer();
262         formatTo(sb);
263         return sb.toString();
264     }
265 
266     private StringBuilder getBuffer() {
267         StringBuilder result = buffer.get();
268         if (result == null) {
269             final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length();
270             result = new StringBuilder(Math.min(512, currentPatternLength * 2));
271             buffer.set(result);
272         }
273         result.setLength(0);
274         return result;
275     }
276 
277     @Override
278     public void formatTo(final StringBuilder buffer) {
279         if (indices[0] < 0) {
280             ParameterFormatter.formatMessage(buffer, messagePattern, getParams(), argCount);
281         } else {
282             ParameterFormatter.formatMessage2(buffer, messagePattern, getParams(), usedCount, indices);
283         }
284     }
285 
286 
287     @Override
288     public String toString() {
289         return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" +
290                 Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']';
291     }
292 }