001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.corpus; 015 016import java.util.List; 017import java.util.Random; 018 019import ch.qos.logback.classic.Level; 020import ch.qos.logback.classic.LoggerContext; 021import ch.qos.logback.classic.spi.ClassPackagingData; 022import ch.qos.logback.classic.spi.LoggerContextVO; 023import ch.qos.logback.classic.spi.StackTraceElementProxy; 024import ch.qos.logback.classic.spi.ThrowableProxy; 025import ch.qos.logback.classic.spi.ThrowableProxyVO; 026 027/** 028 * Models the corpus. 029 * 030 * <p>This contains the probability distributions of levels, logger names, 031 * messages, message arguments. 032 * 033 * @author Ceki Gülcü 034 * 035 */ 036public class CorpusModel { 037 038 // N(u,s) denotes a random variable normally distributed with mean u and 039 // variance sqrt(s), where sqrt() is the square root function. For an 040 // explanation of normal distribution please see 041 // http://en.wikipedia.org/wiki/Normal_distribution 042 043 // It is assumed that the number of parts in a logger name is a random 044 // variable normally distributed with mean AVERAGE_LOGGER_NAME_PARTS and 045 // standard deviation STD_DEV_FOR_LOGGER_NAME_PARTS 046 static final int AVERAGE_LOGGER_NAME_PARTS = 6; 047 static final int STD_DEV_FOR_LOGGER_NAME_PARTS = 3; 048 049 // It is assumed that there are LOGGER_POOL_SIZE logger names 050 // in our model corpus. 051 static final int LOGGER_POOL_SIZE = 1000; 052 053 // It is assumed that there are LOG_STATEMENT_POOL_SIZE log statements 054 // in our model corpus. 055 static final int LOG_STATEMENT_POOL_SIZE = LOGGER_POOL_SIZE * 8; 056 057 // level distribution is determined by the following table 058 // It corresponds to TRACE 3%, DEBUG 30%, INFO 30%, WARN 5%, 059 // ERROR 5%. See also getRandomLevel() method. 060 static final double[] LEVEL_DISTRIBUTION = new double[] { .3, .3, .9, .95 }; 061 062 // It is assumed that the number of words in the message (contained in a log 063 // statement) is a random variable normally distributed with mean 064 // AVERAGE_MESSAGE_WORDS and standard deviation STD_DEV_FOR_MESSAGE_WORDS 065 static final int AVERAGE_MESSAGE_WORDS = 8; 066 static final int STD_DEV_FOR_MESSAGE_WORDS = 4; 067 068 // messages will have no arguments 80% of the time, one argument in 8%, two 069 // arguments in 7% and three arguments in 5% of cases 070 static final double[] ARGUMENT_DISTRIBUTION = new double[] { .80, .88, 0.95 }; 071 072 static final double THROWABLE_PROPABILITY_FOR_WARNING = .1; 073 static final double THROWABLE_PROPABILITY_FOR_ERRORS = .3; 074 // .5 of throwables are nested once 075 static final double NESTING_PROBABILITY = .5; 076 077 // For each logging event the timer is incremented by a certain value. it is 078 // assumed that this value is a random variable normally distributed with mean 079 // AVERAGE_MILLIS_INCREMENT and standard deviation 080 // STD_DEV_FOR_MILLIS_INCREMENT 081 static final int AVERAGE_MILLIS_INCREMENT = 10; 082 static final int STD_DEV_FOR_MILLIS_INCREMENT = 5; 083 084 // assume that there are THREAD_POOL_SIZE threads in the corpus 085 static final int THREAD_POOL_SIZE = 10; 086 087 final Random random; 088 final List<String> worldList; 089 String[] threadNamePool; 090 LogStatement[] logStatementPool; 091 String[] loggerNamePool; 092 093 // 2009-03-06 13:08 GMT 094 long lastTimeStamp = 1236344888578L; 095 096 public CorpusModel(long seed, List<String> worldList) { 097 random = new Random(seed); 098 this.worldList = worldList; 099 buildThreadNamePool(); 100 buildLoggerNamePool(); 101 buildLogStatementPool(); 102 } 103 104 private void buildThreadNamePool() { 105 threadNamePool = new String[THREAD_POOL_SIZE]; 106 for (int i = 0; i < THREAD_POOL_SIZE; i++) { 107 threadNamePool[i] = "CorpusMakerThread-" + i; 108 } 109 } 110 111 private void buildLoggerNamePool() { 112 loggerNamePool = new String[LOGGER_POOL_SIZE]; 113 for (int i = 0; i < LOGGER_POOL_SIZE; i++) { 114 loggerNamePool[i] = makeRandomLoggerName(); 115 } 116 } 117 118 private void buildLogStatementPool() { 119 logStatementPool = new LogStatement[LOG_STATEMENT_POOL_SIZE]; 120 for (int i = 0; i < LOG_STATEMENT_POOL_SIZE; i++) { 121 logStatementPool[i] = makeRandomLogStatement(loggerNamePool); 122 } 123 } 124 125 private int[] getRandomAnchorPositions(int wordCount, int numAnchors) { 126 // note that the same position may appear multiple times in 127 // positionsIndex, but without serious consequences 128 int[] positionsIndex = new int[numAnchors]; 129 for (int i = 0; i < numAnchors; i++) { 130 positionsIndex[i] = random.nextInt(wordCount); 131 } 132 return positionsIndex; 133 } 134 135 private String[] getRandomWords(int n) { 136 String[] wordArray = new String[n]; 137 for (int i = 0; i < n; i++) { 138 wordArray[i] = getRandomWord(); 139 } 140 return wordArray; 141 } 142 143 public long getRandomLong() { 144 return random.nextLong(); 145 } 146 147 public String getRandomThreadNameFromPool() { 148 int index = random.nextInt(THREAD_POOL_SIZE); 149 return threadNamePool[index]; 150 } 151 152 public LogStatement getRandomLogStatementFromPool() { 153 int index = random.nextInt(logStatementPool.length); 154 return logStatementPool[index]; 155 } 156 157 private String getRandomLoggerNameFromPool(String[] loggerNamePool) { 158 int index = random.nextInt(loggerNamePool.length); 159 return loggerNamePool[index]; 160 } 161 162 public long getRandomTimeStamp() { 163 // subtract 1 so that 0 is allowed 164 lastTimeStamp += RandomUtil.gaussianAsPositiveInt(random, AVERAGE_MILLIS_INCREMENT, STD_DEV_FOR_MILLIS_INCREMENT) - 1; 165 return lastTimeStamp; 166 } 167 168 LoggerContextVO getRandomlyNamedLoggerContextVO() { 169 LoggerContext lc = new LoggerContext(); 170 lc.setName(getRandomJavaIdentifier()); 171 return new LoggerContextVO(lc); 172 } 173 174 String getRandomWord() { 175 int size = worldList.size(); 176 int randomIndex = random.nextInt(size); 177 return worldList.get(randomIndex); 178 } 179 180 String extractLastPart(String loggerName) { 181 int i = loggerName.lastIndexOf('.'); 182 if (i == -1) { 183 return loggerName; 184 } else { 185 return loggerName.substring(i + 1); 186 } 187 } 188 189 public StackTraceElement[] getRandomCallerData(int depth, String loggerName) { 190 StackTraceElement[] cda = new StackTraceElement[depth]; 191 StackTraceElement cd = new StackTraceElement(loggerName, getRandomJavaIdentifier(), extractLastPart(loggerName), 0); 192 cda[0] = cd; 193 for (int i = 1; i < depth; i++) { 194 String ln = getRandomLoggerNameFromPool(loggerNamePool); 195 cda[i] = new StackTraceElement(ln, getRandomJavaIdentifier(), extractLastPart(ln), i * 10); 196 } 197 return cda; 198 } 199 200 public Object[] getRandomArgumentArray(int numOfArguments) { 201 if (numOfArguments == 0) { 202 return null; 203 } 204 Object[] argumentArray = new Object[numOfArguments]; 205 for (int i = 0; i < numOfArguments; i++) { 206 argumentArray[i] = Long.valueOf(random.nextLong()); 207 } 208 return argumentArray; 209 } 210 211 private MessageArgumentTuple makeRandomMessageArgumentTuple() { 212 int numOfArguments = getNumberOfMessageArguments(); 213 214 int wordCount = RandomUtil.gaussianAsPositiveInt(random, AVERAGE_MESSAGE_WORDS, STD_DEV_FOR_MESSAGE_WORDS); 215 String[] wordArray = getRandomWords(wordCount); 216 217 int[] anchorPositions = getRandomAnchorPositions(wordCount, numOfArguments); 218 219 for (int anchorIndex : anchorPositions) { 220 wordArray[anchorIndex] = "{}"; 221 } 222 223 StringBuilder sb = new StringBuilder(); 224 for (int i = 1; i < wordCount; i++) { 225 sb.append(wordArray[i]).append(' '); 226 } 227 sb.append(getRandomWord()); 228 return new MessageArgumentTuple(sb.toString(), numOfArguments); 229 } 230 231 private LogStatement makeRandomLogStatement(String[] loggerNamePool) { 232 MessageArgumentTuple mat = makeRandomMessageArgumentTuple(); 233 String loggerName = getRandomLoggerNameFromPool(loggerNamePool); 234 Level randomLevel = getRandomLevel(); 235 Throwable t = getRandomThrowable(randomLevel); 236 ThrowableProxyVO throwableProxy = null; 237 if (t != null) { 238 throwableProxy = ThrowableProxyVO.build(new ThrowableProxy(t)); 239 pupulateWithPackagingData(throwableProxy.getStackTraceElementProxyArray()); 240 } 241 return new LogStatement(loggerName, randomLevel, mat, throwableProxy); 242 } 243 244 private Throwable getRandomThrowable(Level level) { 245 double rn = random.nextDouble(); 246 if ((level == Level.WARN && rn < THROWABLE_PROPABILITY_FOR_WARNING) || (level == Level.ERROR && rn < THROWABLE_PROPABILITY_FOR_ERRORS)) { 247 return ExceptionBuilder.build(random, NESTING_PROBABILITY); 248 } else { 249 return null; 250 } 251 } 252 253 private void pupulateWithPackagingData(StackTraceElementProxy[] stepArray) { 254 int i = 0; 255 for (StackTraceElementProxy step : stepArray) { 256 String identifier = "na"; 257 String version = "na"; 258 if (i++ % 2 == 0) { 259 identifier = getRandomJavaIdentifier(); 260 version = getRandomJavaIdentifier(); 261 } 262 ClassPackagingData cpd = new ClassPackagingData(identifier, version); 263 step.setClassPackagingData(cpd); 264 } 265 } 266 267 private int getNumberOfMessageArguments() { 268 double rn = random.nextDouble(); 269 if (rn < ARGUMENT_DISTRIBUTION[0]) { 270 return 0; 271 } 272 if (rn < ARGUMENT_DISTRIBUTION[1]) { 273 return 1; 274 } 275 if (rn < ARGUMENT_DISTRIBUTION[2]) { 276 return 2; 277 } 278 return 3; 279 } 280 281 String getRandomJavaIdentifier() { 282 String w = getRandomWord(); 283 w = w.replaceAll("\\p{Punct}", ""); 284 return w; 285 } 286 287 private String makeRandomLoggerName() { 288 int parts = RandomUtil.gaussianAsPositiveInt(random, AVERAGE_LOGGER_NAME_PARTS, STD_DEV_FOR_LOGGER_NAME_PARTS); 289 StringBuilder sb = new StringBuilder(); 290 for (int i = 1; i < parts; i++) { 291 sb.append(getRandomJavaIdentifier()).append('.'); 292 } 293 sb.append(getRandomJavaIdentifier()); 294 return sb.toString(); 295 } 296 297 private Level getRandomLevel() { 298 double rn = random.nextDouble(); 299 if (rn < LEVEL_DISTRIBUTION[0]) { 300 return Level.TRACE; 301 } 302 if (rn < LEVEL_DISTRIBUTION[1]) { 303 return Level.DEBUG; 304 } 305 306 if (rn < LEVEL_DISTRIBUTION[2]) { 307 return Level.INFO; 308 } 309 310 if (rn < LEVEL_DISTRIBUTION[3]) { 311 return Level.WARN; 312 } 313 314 return Level.ERROR; 315 } 316}