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 { 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 Message memento() { 109 return new ParameterizedMessage(messagePattern, getTrimmedParams()); 110 } 111 112 private void init(final String messagePattern, final int argCount, final Object[] paramArray) { 113 this.varargs = null; 114 this.messagePattern = messagePattern; 115 this.argCount = argCount; 116 final int placeholderCount = count(messagePattern, indices); 117 initThrowable(paramArray, argCount, placeholderCount); 118 this.usedCount = Math.min(placeholderCount, argCount); 119 } 120 121 private static int count(final String messagePattern, final int[] indices) { 122 try { 123 // try the fast path first 124 return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices); 125 } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders 126 return ParameterFormatter.countArgumentPlaceholders(messagePattern); 127 } 128 } 129 130 private void initThrowable(final Object[] params, final int argCount, final int usedParams) { 131 if (usedParams < argCount && params[argCount - 1] instanceof Throwable) { 132 this.throwable = (Throwable) params[argCount - 1]; 133 } else { 134 this.throwable = null; 135 } 136 } 137 138 ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) { 139 init(messagePattern, arguments == null ? 0 : arguments.length, arguments); 140 varargs = arguments; 141 return this; 142 } 143 144 ReusableParameterizedMessage set(final String messagePattern, final Object p0) { 145 params[0] = p0; 146 init(messagePattern, 1, params); 147 return this; 148 } 149 150 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) { 151 params[0] = p0; 152 params[1] = p1; 153 init(messagePattern, 2, params); 154 return this; 155 } 156 157 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) { 158 params[0] = p0; 159 params[1] = p1; 160 params[2] = p2; 161 init(messagePattern, 3, params); 162 return this; 163 } 164 165 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) { 166 params[0] = p0; 167 params[1] = p1; 168 params[2] = p2; 169 params[3] = p3; 170 init(messagePattern, 4, params); 171 return this; 172 } 173 174 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { 175 params[0] = p0; 176 params[1] = p1; 177 params[2] = p2; 178 params[3] = p3; 179 params[4] = p4; 180 init(messagePattern, 5, 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, final Object p5) { 185 params[0] = p0; 186 params[1] = p1; 187 params[2] = p2; 188 params[3] = p3; 189 params[4] = p4; 190 params[5] = p5; 191 init(messagePattern, 6, params); 192 return this; 193 } 194 195 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, 196 final Object p6) { 197 params[0] = p0; 198 params[1] = p1; 199 params[2] = p2; 200 params[3] = p3; 201 params[4] = p4; 202 params[5] = p5; 203 params[6] = p6; 204 init(messagePattern, 7, params); 205 return this; 206 } 207 208 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, 209 final Object p6, final Object p7) { 210 params[0] = p0; 211 params[1] = p1; 212 params[2] = p2; 213 params[3] = p3; 214 params[4] = p4; 215 params[5] = p5; 216 params[6] = p6; 217 params[7] = p7; 218 init(messagePattern, 8, params); 219 return this; 220 } 221 222 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, 223 final Object p6, final Object p7, final Object p8) { 224 params[0] = p0; 225 params[1] = p1; 226 params[2] = p2; 227 params[3] = p3; 228 params[4] = p4; 229 params[5] = p5; 230 params[6] = p6; 231 params[7] = p7; 232 params[8] = p8; 233 init(messagePattern, 9, params); 234 return this; 235 } 236 237 ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, 238 final Object p6, final Object p7, final Object p8, final Object p9) { 239 params[0] = p0; 240 params[1] = p1; 241 params[2] = p2; 242 params[3] = p3; 243 params[4] = p4; 244 params[5] = p5; 245 params[6] = p6; 246 params[7] = p7; 247 params[8] = p8; 248 params[9] = p9; 249 init(messagePattern, 10, params); 250 return this; 251 } 252 253 /** 254 * Returns the message pattern. 255 * @return the message pattern. 256 */ 257 @Override 258 public String getFormat() { 259 return messagePattern; 260 } 261 262 /** 263 * Returns the message parameters. 264 * @return the message parameters. 265 */ 266 @Override 267 public Object[] getParameters() { 268 return getTrimmedParams(); 269 } 270 271 /** 272 * Returns the Throwable that was given as the last argument, if any. 273 * It will not survive serialization. The Throwable exists as part of the message 274 * primarily so that it can be extracted from the end of the list of parameters 275 * and then be added to the LogEvent. As such, the Throwable in the event should 276 * not be used once the LogEvent has been constructed. 277 * 278 * @return the Throwable, if any. 279 */ 280 @Override 281 public Throwable getThrowable() { 282 return throwable; 283 } 284 285 /** 286 * Returns the formatted message. 287 * @return the formatted message. 288 */ 289 @Override 290 public String getFormattedMessage() { 291 final StringBuilder sb = getBuffer(); 292 formatTo(sb); 293 final String result = sb.toString(); 294 StringBuilders.trimToMaxSize(sb, Constants.MAX_REUSABLE_MESSAGE_SIZE); 295 return result; 296 } 297 298 private StringBuilder getBuffer() { 299 if (buffer == null) { 300 buffer = new ThreadLocal<>(); 301 } 302 StringBuilder result = buffer.get(); 303 if (result == null) { 304 final int currentPatternLength = messagePattern == null ? 0 : messagePattern.length(); 305 result = new StringBuilder(Math.max(MIN_BUILDER_SIZE, currentPatternLength * 2)); 306 buffer.set(result); 307 } 308 result.setLength(0); 309 return result; 310 } 311 312 @Override 313 public void formatTo(final StringBuilder builder) { 314 if (indices[0] < 0) { 315 ParameterFormatter.formatMessage(builder, messagePattern, getParams(), argCount); 316 } else { 317 ParameterFormatter.formatMessage2(builder, messagePattern, getParams(), usedCount, indices); 318 } 319 } 320 321 /** 322 * Sets the reserved flag to true and returns this object. 323 * @return this object 324 * @since 2.7 325 */ 326 ReusableParameterizedMessage reserve() { 327 reserved = true; 328 return this; 329 } 330 331 @Override 332 public String toString() { 333 return "ReusableParameterizedMessage[messagePattern=" + getFormat() + ", stringArgs=" + 334 Arrays.toString(getParameters()) + ", throwable=" + getThrowable() + ']'; 335 } 336}