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