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 */ 017 018 package org.apache.logging.log4j.core.tools; 019 020 import java.io.PrintStream; 021 import java.util.ArrayList; 022 import java.util.Arrays; 023 import java.util.List; 024 025 /** 026 * Generates source code for custom or extended logger wrappers. 027 * <p> 028 * Usage: 029 * <p> 030 * To generate source code for an extended logger that adds custom log levels to the existing ones: <br> 031 * {@code java org.apache.logging.log4j.core.tools.Generate$ExtendedLogger <logger.class.name> <CUSTOMLEVEL>=<WEIGHT> 032 * [CUSTOMLEVEL2=WEIGHT2 [CUSTOMLEVEL3=WEIGHT3] ...]} 033 * <p> 034 * Example of creating an extended logger:<br> 035 * {@code java org.apache.logging.log4j.core.tools.Generate$ExtendedLogger com.mycomp.ExtLogger DIAG=350 NOTICE=450 VERBOSE=550} 036 * <p> 037 * To generate source code for a custom logger that replaces the existing log levels with custom ones: <br> 038 * {@code java org.apache.logging.log4j.core.tools.Generate$CustomLogger <logger.class.name> <CUSTOMLEVEL>=<WEIGHT> 039 * [CUSTOMLEVEL2=WEIGHT2 [CUSTOMLEVEL3=WEIGHT3] ...]} 040 * <p> 041 * Example of creating a custom logger:<br> 042 * {@code java org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550} 043 */ 044 public final class Generate { 045 046 static final String PACKAGE_DECLARATION = "package %s;%n%n"; 047 048 static enum Type { 049 CUSTOM { 050 String imports() { 051 return "" // 052 + "import java.io.Serializable;%n" // 053 + "import org.apache.logging.log4j.Level;%n" // 054 + "import org.apache.logging.log4j.LogManager;%n" + "import org.apache.logging.log4j.Logger;%n" // 055 + "import org.apache.logging.log4j.Marker;%n" // 056 + "import org.apache.logging.log4j.message.Message;%n" // 057 + "import org.apache.logging.log4j.message.MessageFactory;%n" // 058 + "import org.apache.logging.log4j.spi.AbstractLogger;%n" // 059 + "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n" // 060 + "%n"; 061 } 062 063 String declaration() { 064 return "" // 065 + "/**%n" // 066 + " * Custom Logger interface with convenience methods for%n" // 067 + " * %s%n" // 068 + " */%n" // 069 + "public final class %s implements Serializable {%n" // 070 + " private static final long serialVersionUID = " + System.nanoTime() + "L;%n" // 071 + " private final ExtendedLoggerWrapper logger;%n" // 072 + "%n" // 073 ; 074 } 075 076 String constructor() { 077 return "" // 078 + "%n" // 079 + " private %s(final Logger logger) {%n" // 080 + " this.logger = new ExtendedLoggerWrapper((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());%n" // 081 + " }%n" // 082 ; 083 } 084 085 Class<?> generator() { 086 return CustomLogger.class; 087 } 088 }, 089 EXTEND { 090 String imports() { 091 return "" // 092 + "import org.apache.logging.log4j.Level;%n" // 093 + "import org.apache.logging.log4j.LogManager;%n" + "import org.apache.logging.log4j.Logger;%n" // 094 + "import org.apache.logging.log4j.Marker;%n" // 095 + "import org.apache.logging.log4j.message.Message;%n" // 096 + "import org.apache.logging.log4j.message.MessageFactory;%n" // 097 + "import org.apache.logging.log4j.spi.AbstractLogger;%n" // 098 + "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n" // 099 + "%n"; 100 } 101 102 String declaration() { 103 return "" // 104 + "/**%n" // 105 + " * Extended Logger interface with convenience methods for%n" // 106 + " * %s%n" // 107 + " */%n" // 108 + "public final class %s extends ExtendedLoggerWrapper {%n" // 109 + " private static final long serialVersionUID = " + System.nanoTime() + "L;%n" // 110 + " private final ExtendedLoggerWrapper logger;%n" // 111 + "%n" // 112 ; 113 } 114 115 String constructor() { 116 return "" // 117 + "%n" // 118 + " private %s(final Logger logger) {%n" // 119 + " super((AbstractLogger) logger, logger.getName(), logger.getMessageFactory());%n" // 120 + " this.logger = this;%n" // 121 + " }%n" // 122 ; 123 } 124 125 Class<?> generator() { 126 return ExtendedLogger.class; 127 } 128 }; 129 abstract String imports(); 130 131 abstract String declaration(); 132 133 abstract String constructor(); 134 135 abstract Class<?> generator(); 136 } 137 138 static final String FQCN_FIELD = "" // 139 + " private static final String FQCN = %s.class.getName();%n"; 140 141 static final String LEVEL_FIELD = "" // 142 + " private static final Level %s = Level.forName(\"%s\", %d);%n"; 143 144 static final String FACTORY_METHODS = "" // 145 + "%n" // 146 + " /**%n" // 147 + " * Returns a custom Logger with the name of the calling class.%n" // 148 + " * %n" // 149 + " * @return The custom Logger for the calling class.%n" // 150 + " */%n" // 151 + " public static CLASSNAME create() {%n" // 152 + " final Logger wrapped = LogManager.getLogger();%n" // 153 + " return new CLASSNAME(wrapped);%n" // 154 + " }%n" // 155 + "%n" // 156 + " /**%n" // 157 + " * Returns a custom Logger using the fully qualified name of the Class as%n" // 158 + " * the Logger name.%n" // 159 + " * %n" // 160 + " * @param loggerName The Class whose name should be used as the Logger name.%n" // 161 + " * If null it will default to the calling class.%n" // 162 + " * @return The custom Logger.%n" // 163 + " */%n" // 164 + " public static CLASSNAME create(final Class<?> loggerName) {%n" // 165 + " final Logger wrapped = LogManager.getLogger(loggerName);%n" // 166 + " return new CLASSNAME(wrapped);%n" // 167 + " }%n" // 168 + "%n" // 169 + " /**%n" // 170 + " * Returns a custom Logger using the fully qualified name of the Class as%n" // 171 + " * the Logger name.%n" // 172 + " * %n" // 173 + " * @param loggerName The Class whose name should be used as the Logger name.%n" // 174 + " * If null it will default to the calling class.%n" // 175 + " * @param messageFactory The message factory is used only when creating a%n" // 176 + " * logger, subsequent use does not change the logger but will log%n" // 177 + " * a warning if mismatched.%n" // 178 + " * @return The custom Logger.%n" // 179 + " */%n" // 180 + " public static CLASSNAME create(final Class<?> loggerName, final MessageFactory factory) {%n" // 181 + " final Logger wrapped = LogManager.getLogger(loggerName, factory);%n" // 182 + " return new CLASSNAME(wrapped);%n" // 183 + " }%n" // 184 + "%n" // 185 + " /**%n" // 186 + " * Returns a custom Logger using the fully qualified class name of the value%n" // 187 + " * as the Logger name.%n" // 188 + " * %n" // 189 + " * @param value The value whose class name should be used as the Logger%n" // 190 + " * name. If null the name of the calling class will be used as%n" // 191 + " * the logger name.%n" // 192 + " * @return The custom Logger.%n" // 193 + " */%n" // 194 + " public static CLASSNAME create(final Object value) {%n" // 195 + " final Logger wrapped = LogManager.getLogger(value);%n" // 196 + " return new CLASSNAME(wrapped);%n" // 197 + " }%n" // 198 + "%n" // 199 + " /**%n" // 200 + " * Returns a custom Logger using the fully qualified class name of the value%n" // 201 + " * as the Logger name.%n" // 202 + " * %n" // 203 + " * @param value The value whose class name should be used as the Logger%n" // 204 + " * name. If null the name of the calling class will be used as%n" // 205 + " * the logger name.%n" // 206 + " * @param messageFactory The message factory is used only when creating a%n" // 207 + " * logger, subsequent use does not change the logger but will log%n" // 208 + " * a warning if mismatched.%n" // 209 + " * @return The custom Logger.%n" // 210 + " */%n" // 211 + " public static CLASSNAME create(final Object value, final MessageFactory factory) {%n" // 212 + " final Logger wrapped = LogManager.getLogger(value, factory);%n" // 213 + " return new CLASSNAME(wrapped);%n" // 214 + " }%n" // 215 + "%n" // 216 + " /**%n" // 217 + " * Returns a custom Logger with the specified name.%n" // 218 + " * %n" // 219 + " * @param name The logger name. If null the name of the calling class will%n" // 220 + " * be used.%n" // 221 + " * @return The custom Logger.%n" // 222 + " */%n" // 223 + " public static CLASSNAME create(final String name) {%n" // 224 + " final Logger wrapped = LogManager.getLogger(name);%n" // 225 + " return new CLASSNAME(wrapped);%n" // 226 + " }%n" // 227 + "%n" // 228 + " /**%n" // 229 + " * Returns a custom Logger with the specified name.%n" // 230 + " * %n" // 231 + " * @param name The logger name. If null the name of the calling class will%n" // 232 + " * be used.%n" // 233 + " * @param messageFactory The message factory is used only when creating a%n" // 234 + " * logger, subsequent use does not change the logger but will log%n" // 235 + " * a warning if mismatched.%n" // 236 + " * @return The custom Logger.%n" // 237 + " */%n" // 238 + " public static CLASSNAME create(final String name, final MessageFactory factory) {%n" // 239 + " final Logger wrapped = LogManager.getLogger(name, factory);%n" // 240 + " return new CLASSNAME(wrapped);%n" // 241 + " }%n" // 242 ; 243 static final String METHODS = "" // 244 + "%n" // 245 + " /**%n" // 246 + " * Logs a message with the specific Marker at the {@code CUSTOM_LEVEL} level.%n" // 247 + " * %n" // 248 + " * @param marker the marker data specific to this log statement%n" // 249 + " * @param msg the message string to be logged%n" // 250 + " */%n" // 251 + " public void methodName(final Marker marker, final Message msg) {%n" // 252 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, msg, (Throwable) null);%n" // 253 + " }%n" // 254 + "%n" // 255 + " /**%n" // 256 + " * Logs a message with the specific Marker at the {@code CUSTOM_LEVEL} level.%n" // 257 + " * %n" // 258 + " * @param marker the marker data specific to this log statement%n" // 259 + " * @param msg the message string to be logged%n" // 260 + " * @param t A Throwable or null.%n" // 261 + " */%n" // 262 + " public void methodName(final Marker marker, final Message msg, final Throwable t) {%n" // 263 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, msg, t);%n" // 264 + " }%n" // 265 + "%n" // 266 + " /**%n" // 267 + " * Logs a message object with the {@code CUSTOM_LEVEL} level.%n" // 268 + " * %n" // 269 + " * @param marker the marker data specific to this log statement%n" // 270 + " * @param message the message object to log.%n" // 271 + " */%n" // 272 + " public void methodName(final Marker marker, final Object message) {%n" // 273 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, message, (Throwable) null);%n" // 274 + " }%n" // 275 + "%n" // 276 + " /**%n" // 277 + " * Logs a message at the {@code CUSTOM_LEVEL} level including the stack trace of%n" // 278 + " * the {@link Throwable} {@code t} passed as parameter.%n" // 279 + " * %n" // 280 + " * @param marker the marker data specific to this log statement%n" // 281 + " * @param message the message to log.%n" // 282 + " * @param t the exception to log, including its stack trace.%n" // 283 + " */%n" // 284 + " public void methodName(final Marker marker, final Object message, final Throwable t) {%n" // 285 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, message, t);%n" // 286 + " }%n" // 287 + "%n" // 288 + " /**%n" // 289 + " * Logs a message object with the {@code CUSTOM_LEVEL} level.%n" // 290 + " * %n" // 291 + " * @param marker the marker data specific to this log statement%n" // 292 + " * @param message the message object to log.%n" // 293 + " */%n" // 294 + " public void methodName(final Marker marker, final String message) {%n" // 295 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, message, (Throwable) null);%n" // 296 + " }%n" // 297 + "%n" // 298 + " /**%n" // 299 + " * Logs a message with parameters at the {@code CUSTOM_LEVEL} level.%n" // 300 + " * %n" // 301 + " * @param marker the marker data specific to this log statement%n" // 302 + " * @param message the message to log; the format depends on the message factory.%n" // 303 + " * @param params parameters to the message.%n" // 304 + " * @see #getMessageFactory()%n" // 305 + " */%n" // 306 + " public void methodName(final Marker marker, final String message, final Object... params) {%n" // 307 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, message, params);%n" // 308 + " }%n" // 309 + "%n" // 310 + " /**%n" // 311 + " * Logs a message at the {@code CUSTOM_LEVEL} level including the stack trace of%n" // 312 + " * the {@link Throwable} {@code t} passed as parameter.%n" // 313 + " * %n" // 314 + " * @param marker the marker data specific to this log statement%n" // 315 + " * @param message the message to log.%n" // 316 + " * @param t the exception to log, including its stack trace.%n" // 317 + " */%n" // 318 + " public void methodName(final Marker marker, final String message, final Throwable t) {%n" // 319 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, marker, message, t);%n" // 320 + " }%n" // 321 + "%n" // 322 + " /**%n" // 323 + " * Logs the specified Message at the {@code CUSTOM_LEVEL} level.%n" // 324 + " * %n" // 325 + " * @param msg the message string to be logged%n" // 326 + " */%n" // 327 + " public void methodName(final Message msg) {%n" // 328 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, msg, (Throwable) null);%n" // 329 + " }%n" // 330 + "%n" // 331 + " /**%n" // 332 + " * Logs the specified Message at the {@code CUSTOM_LEVEL} level.%n" // 333 + " * %n" // 334 + " * @param msg the message string to be logged%n" // 335 + " * @param t A Throwable or null.%n" // 336 + " */%n" // 337 + " public void methodName(final Message msg, final Throwable t) {%n" // 338 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, msg, t);%n" // 339 + " }%n" // 340 + "%n" // 341 + " /**%n" // 342 + " * Logs a message object with the {@code CUSTOM_LEVEL} level.%n" // 343 + " * %n" // 344 + " * @param message the message object to log.%n" // 345 + " */%n" // 346 + " public void methodName(final Object message) {%n" // 347 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, message, (Throwable) null);%n" // 348 + " }%n" // 349 + "%n" // 350 + " /**%n" // 351 + " * Logs a message at the {@code CUSTOM_LEVEL} level including the stack trace of%n" // 352 + " * the {@link Throwable} {@code t} passed as parameter.%n" // 353 + " * %n" // 354 + " * @param message the message to log.%n" // 355 + " * @param t the exception to log, including its stack trace.%n" // 356 + " */%n" // 357 + " public void methodName(final Object message, final Throwable t) {%n" // 358 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, message, t);%n" // 359 + " }%n" // 360 + "%n" // 361 + " /**%n" // 362 + " * Logs a message object with the {@code CUSTOM_LEVEL} level.%n" // 363 + " * %n" // 364 + " * @param message the message object to log.%n" // 365 + " */%n" // 366 + " public void methodName(final String message) {%n" // 367 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, message, (Throwable) null);%n" // 368 + " }%n" // 369 + "%n" // 370 + " /**%n" // 371 + " * Logs a message with parameters at the {@code CUSTOM_LEVEL} level.%n" // 372 + " * %n" // 373 + " * @param message the message to log; the format depends on the message factory.%n" // 374 + " * @param params parameters to the message.%n" // 375 + " * @see #getMessageFactory()%n" // 376 + " */%n" // 377 + " public void methodName(final String message, final Object... params) {%n" // 378 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, message, params);%n" // 379 + " }%n" // 380 + "%n" // 381 + " /**%n" // 382 + " * Logs a message at the {@code CUSTOM_LEVEL} level including the stack trace of%n" // 383 + " * the {@link Throwable} {@code t} passed as parameter.%n" // 384 + " * %n" // 385 + " * @param message the message to log.%n" // 386 + " * @param t the exception to log, including its stack trace.%n" // 387 + " */%n" // 388 + " public void methodName(final String message, final Throwable t) {%n" // 389 + " logger.logIfEnabled(FQCN, CUSTOM_LEVEL, null, message, t);%n" // 390 + " }%n" // 391 ; 392 393 private Generate() { 394 } 395 396 /** 397 * Generates source code for custom logger wrappers that only provide convenience methods for the specified custom 398 * levels, not for the standard built-in levels. 399 */ 400 public static final class CustomLogger { 401 /** 402 * Generates source code for custom logger wrappers that only provide convenience methods for the specified 403 * custom levels, not for the standard built-in levels. 404 * 405 * @param args className of the custom logger to generate, followed by a NAME=intLevel pair for each custom log 406 * level to generate convenience methods for 407 */ 408 public static void main(final String[] args) { 409 generate(args, Type.CUSTOM); 410 } 411 412 private CustomLogger() { 413 } 414 } 415 416 /** 417 * Generates source code for extended logger wrappers that provide convenience methods for the specified custom 418 * levels, and by extending {@code org.apache.logging.log4j.spi.ExtendedLoggerWrapper}, inherit the convenience 419 * methods for the built-in levels provided by the {@code Logger} interface. 420 */ 421 public static final class ExtendedLogger { 422 /** 423 * Generates source code for extended logger wrappers that provide convenience methods for the specified custom 424 * levels. 425 * 426 * @param args className of the custom logger to generate, followed by a NAME=intLevel pair for each custom log 427 * level to generate convenience methods for 428 */ 429 public static void main(final String[] args) { 430 generate(args, Type.EXTEND); 431 } 432 433 private ExtendedLogger() { 434 } 435 } 436 437 static class LevelInfo { 438 final String name; 439 final int intLevel; 440 441 LevelInfo(final String description) { 442 final String[] parts = description.split("="); 443 name = parts[0]; 444 intLevel = Integer.parseInt(parts[1]); 445 } 446 447 public static List<LevelInfo> parse(final List<String> values, final Class<?> generator) { 448 final List<LevelInfo> result = new ArrayList<Generate.LevelInfo>(values.size()); 449 for (int i = 0; i < values.size(); i++) { 450 try { 451 result.add(new LevelInfo(values.get(i))); 452 } catch (Exception ex) { 453 System.err.println("Cannot parse custom level '" + values.get(i) + "': " + ex.toString()); 454 usage(System.err, generator); 455 System.exit(-1); 456 } 457 } 458 return result; 459 } 460 } 461 462 private static void generate(final String[] args, final Type type) { 463 generate(args, type, System.out); 464 } 465 466 static void generate(final String[] args, final Type type, final PrintStream printStream) { 467 if (!validate(args)) { 468 usage(printStream, type.generator()); 469 System.exit(-1); 470 } 471 final List<String> values = new ArrayList<String>(Arrays.asList(args)); 472 final String classFQN = values.remove(0); 473 final List<LevelInfo> levels = LevelInfo.parse(values, type.generator()); 474 printStream.println(generateSource(classFQN, levels, type)); 475 } 476 477 static boolean validate(final String[] args) { 478 if (args.length < 2) { 479 return false; 480 } 481 return true; 482 } 483 484 private static void usage(final PrintStream out, final Class<?> generator) { 485 out.println("Usage: java " + generator.getName() + " className LEVEL1=intLevel1 [LEVEL2=intLevel2...]"); 486 out.println(" Where className is the fully qualified class name of the custom/extended logger to generate,"); 487 out.println(" followed by a space-separated list of custom log levels."); 488 out.println(" For each custom log level, specify NAME=intLevel (without spaces)."); 489 } 490 491 static String generateSource(final String classNameFQN, final List<LevelInfo> levels, final Type type) { 492 final StringBuilder sb = new StringBuilder(10000 * levels.size()); 493 final int lastDot = classNameFQN.lastIndexOf('.'); 494 final String pkg = classNameFQN.substring(0, lastDot >= 0 ? lastDot : 0); 495 if (!pkg.isEmpty()) { 496 sb.append(String.format(PACKAGE_DECLARATION, pkg)); 497 } 498 sb.append(String.format(type.imports(), "")); 499 final String className = classNameFQN.substring(classNameFQN.lastIndexOf('.') + 1); 500 final String javadocDescr = javadocDescription(levels); 501 sb.append(String.format(type.declaration(), javadocDescr, className)); 502 sb.append(String.format(FQCN_FIELD, className)); 503 for (LevelInfo level : levels) { 504 sb.append(String.format(LEVEL_FIELD, level.name, level.name, level.intLevel)); 505 } 506 sb.append(String.format(type.constructor(), className)); 507 sb.append(String.format(FACTORY_METHODS.replaceAll("CLASSNAME", className), "")); 508 for (LevelInfo level : levels) { 509 final String methodName = camelCase(level.name); 510 final String phase1 = METHODS.replaceAll("CUSTOM_LEVEL", level.name); 511 final String phase2 = phase1.replaceAll("methodName", methodName); 512 sb.append(String.format(phase2, "")); 513 } 514 515 sb.append(String.format("}%n", "")); 516 return sb.toString(); 517 } 518 519 static String javadocDescription(final List<LevelInfo> levels) { 520 if (levels.size() == 1) { 521 return "the " + levels.get(0).name + " custom log level."; 522 } 523 final StringBuilder sb = new StringBuilder(512); 524 sb.append("the "); 525 String sep = ""; 526 for (int i = 0; i < levels.size(); i++) { 527 sb.append(sep); 528 sb.append(levels.get(i).name); 529 sep = (i == levels.size() - 2) ? " and " : ", "; 530 } 531 sb.append(" custom log levels."); 532 return sb.toString(); 533 } 534 535 static String camelCase(final String customLevel) { 536 final StringBuilder sb = new StringBuilder(customLevel.length()); 537 boolean lower = true; 538 for (char ch : customLevel.toCharArray()) { 539 if (ch == '_') { 540 lower = false; 541 continue; 542 } 543 sb.append(lower ? Character.toLowerCase(ch) : Character.toUpperCase(ch)); 544 lower = true; 545 } 546 return sb.toString(); 547 } 548 }