001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.message; 018 019import java.util.Arrays; 020 021import org.apache.logging.log4j.util.Constants; 022import org.apache.logging.log4j.util.PerformanceSensitive; 023import org.apache.logging.log4j.util.StringBuilders; 024 025/** 026 * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple 027 * threads concurrently. 028 * 029 * @see ParameterizedMessage 030 * @since 2.6 031 */ 032@PerformanceSensitive("allocation") 033public class ReusableParameterizedMessage implements ReusableMessage, ParameterVisitable, Clearable { 034 035 private static final int MIN_BUILDER_SIZE = 512; 036 private static final int MAX_PARMS = 10; 037 private static final long serialVersionUID = 7800075879295123856L; 038 private transient ThreadLocal<StringBuilder> buffer; // non-static: LOG4J2-1583 039 040 private String messagePattern; 041 private int argCount; 042 private int usedCount; 043 private final int[] indices = new int[256]; 044 private transient Object[] varargs; 045 private transient Object[] params = new Object[MAX_PARMS]; 046 private transient Throwable throwable; 047 transient boolean reserved = false; // LOG4J2-1583 prevent scrambled logs with nested logging calls 048 049 /** 050 * Creates a reusable message. 051 */ 052 public ReusableParameterizedMessage() { 053 } 054 055 private Object[] getTrimmedParams() { 056 return varargs == null ? Arrays.copyOf(params, argCount) : varargs; 057 } 058 059 private Object[] getParams() { 060 return varargs == null ? params : varargs; 061 } 062 063 // see interface javadoc 064 @Override 065 public Object[] swapParameters(final Object[] emptyReplacement) { 066 Object[] result; 067 if (varargs == null) { 068 result = params; 069 if (emptyReplacement.length >= MAX_PARMS) { 070 params = emptyReplacement; 071 } else if (argCount <= emptyReplacement.length) { 072 // Bad replacement! Too small, may blow up future 10-arg messages. 073 // copy params into the specified replacement array and return that 074 System.arraycopy(params, 0, emptyReplacement, 0, argCount); 075 // Do not retain references to objects in the reusable params array. 076 for (int i = 0; i < argCount; i++) { 077 params[i] = null; 078 } 079 result = emptyReplacement; 080 } else { 081 // replacement array is too small for current content and future content: discard it 082 params = new Object[MAX_PARMS]; 083 } 084 } else { 085 // The returned array will be reused by the caller in future swapParameter() calls. 086 // Therefore we want to avoid returning arrays with less than 10 elements. 087 // If the vararg array is less than 10 params we just copy its content into the specified array 088 // and return it. This helps the caller to retain a reusable array of at least 10 elements. 089 // NOTE: LOG4J2-1688 unearthed the use case that an application array (not a varargs array) is passed 090 // as the argument array. This array should not be modified, so it cannot be passed to the caller 091 // who will at some point null out the elements in the array). 092 if (argCount <= emptyReplacement.length) { 093 result = emptyReplacement; 094 } else { 095 result = new Object[argCount]; // LOG4J2-1688 096 } 097 // copy params into the specified replacement array and return that 098 System.arraycopy(varargs, 0, result, 0, argCount); 099 } 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}