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