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.PerformanceSensitive; 022 023/** 024 * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple 025 * threads concurrently. 026 * 027 * @see ParameterizedMessage 028 * @since 2.6 029 */ 030@PerformanceSensitive("allocation") 031public class ReusableParameterizedMessage implements ReusableMessage { 032 033 private static final int MIN_BUILDER_SIZE = 512; 034 private static final int MAX_PARMS = 10; 035 private static final long serialVersionUID = 7800075879295123856L; 036 private static ThreadLocal<StringBuilder> buffer = new ThreadLocal<>(); 037 038 private String messagePattern; 039 private int argCount; 040 private int usedCount; 041 private final int[] indices = new int[256]; 042 private transient Object[] varargs; 043 private transient Object[] params = new Object[MAX_PARMS]; 044 private transient Throwable throwable; 045 046 /** 047 * Creates a reusable message. 048 */ 049 public ReusableParameterizedMessage() { 050 } 051 052 private Object[] getTrimmedParams() { 053 return varargs == null ? Arrays.copyOf(params, argCount) : varargs; 054 } 055 056 private Object[] getParams() { 057 return varargs == null ? params : varargs; 058 } 059 060 // see interface javadoc 061 @Override 062 public Object[] swapParameters(final Object[] emptyReplacement) { 063 Object[] result; 064 if (varargs == null) { 065 result = params; 066 if (emptyReplacement.length >= MAX_PARMS) { 067 params = emptyReplacement; 068 } else { 069 // Bad replacement! Too small, may blow up future 10-arg messages. 070 if (argCount <= emptyReplacement.length) { 071 // copy params into the specified replacement array and return that 072 System.arraycopy(params, 0, emptyReplacement, 0, argCount); 073 result = emptyReplacement; 074 } else { 075 // replacement array is too small for current content and future content: discard it 076 params = new Object[MAX_PARMS]; 077 } 078 } 079 } else { 080 // The returned array will be reused by the caller in future swapParameter() calls. 081 // Therefore we want to avoid returning arrays with less than 10 elements. 082 // If the vararg array is less than 10 params we just copy its content into the specified array 083 // and return it. This helps the caller to retain a reusable array of at least 10 elements. 084 if (argCount <= emptyReplacement.length) { 085 // copy params into the specified replacement array and return that 086 System.arraycopy(varargs, 0, emptyReplacement, 0, argCount); 087 result = emptyReplacement; 088 } else { 089 result = varargs; 090 varargs = emptyReplacement; 091 } 092 } 093 return result; 094 } 095 096 // see interface javadoc 097 @Override 098 public short getParameterCount() { 099 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}