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; 020import java.util.Objects; 021 022import org.apache.logging.log4j.util.PerformanceSensitive; 023 024/** 025 * Reusable parameterized message. This message is mutable and is not safe to be accessed or modified by multiple 026 * threads concurrently. 027 * 028 * @see ParameterizedMessage 029 * @since 2.6 030 */ 031@PerformanceSensitive("allocation") 032public class ReusableParameterizedMessage implements ReusableMessage { 033 034 private static final long serialVersionUID = 7800075879295123856L; 035 private static ThreadLocal<StringBuilder> buffer = new ThreadLocal<>(); 036 037 private String messagePattern; 038 private int argCount; 039 private int usedCount; 040 private final int[] indices = new int[256]; 041 private transient Object[] varargs; 042 private transient Object[] params = new Object[10]; 043 private transient Throwable throwable; 044 045 /** 046 * Creates a reusable message. 047 */ 048 public ReusableParameterizedMessage() { 049 } 050 051 private Object[] getTrimmedParams() { 052 return varargs == null ? Arrays.copyOf(params, argCount) : varargs; 053 } 054 055 private Object[] getParams() { 056 return varargs == null ? params : varargs; 057 } 058 059 // see interface javadoc 060 @Override 061 public Object[] swapParameters(final Object[] emptyReplacement) { 062 Object[] result; 063 if (varargs == null) { 064 result = params; 065 params = Objects.requireNonNull(emptyReplacement); 066 } else { 067 result = varargs; 068 varargs = Objects.requireNonNull(emptyReplacement); 069 } 070 return result; 071 } 072 073 // see interface javadoc 074 @Override 075 public short getParameterCount() { 076 return (short) argCount; 077 } 078 079 @Override 080 public Message memento() { 081 return new ParameterizedMessage(messagePattern, getTrimmedParams()); 082 } 083 084 private void init(final String messagePattern, final int argCount, final Object[] paramArray) { 085 this.varargs = null; 086 this.messagePattern = messagePattern; 087 this.argCount = argCount; 088 int placeholderCount = count(messagePattern, indices); 089 initThrowable(paramArray, argCount, placeholderCount); 090 this.usedCount = Math.min(placeholderCount, argCount); 091 } 092 093 private static int count(final String messagePattern, final int[] indices) { 094 try { 095 // try the fast path first 096 return ParameterFormatter.countArgumentPlaceholders2(messagePattern, indices); 097 } catch (final Exception ex) { // fallback if more than int[] length (256) parameter placeholders 098 return ParameterFormatter.countArgumentPlaceholders(messagePattern); 099 } 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}