View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.logging.log4j.core.tools;
19  
20  import java.io.PrintStream;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.List;
24  
25  /**
26   * Generates source code for custom or extended logger wrappers.
27   * <p>
28   * Usage:
29   * <p>
30   * To generate source code for an extended logger that adds custom log levels to the existing ones: <br>
31   * {@code java org.apache.logging.log4j.core.tools.Generate$ExtendedLogger <logger.class.name> <CUSTOMLEVEL>=<WEIGHT>
32   * [CUSTOMLEVEL2=WEIGHT2 [CUSTOMLEVEL3=WEIGHT3] ...]}
33   * <p>
34   * Example of creating an extended logger:<br>
35   * {@code java org.apache.logging.log4j.core.tools.Generate$ExtendedLogger com.mycomp.ExtLogger DIAG=350 NOTICE=450
36   * VERBOSE=550}
37   * <p>
38   * To generate source code for a custom logger that replaces the existing log levels with custom ones: <br>
39   * {@code java org.apache.logging.log4j.core.tools.Generate$CustomLogger <logger.class.name> <CUSTOMLEVEL>=<WEIGHT>
40   * [CUSTOMLEVEL2=WEIGHT2 [CUSTOMLEVEL3=WEIGHT3] ...]}
41   * <p>
42   * Example of creating a custom logger:<br>
43   * {@code java org.apache.logging.log4j.core.tools.Generate$CustomLogger com.mycomp.MyLogger DEFCON1=350 DEFCON2=450
44   * DEFCON3=550}
45   */
46  public final class Generate {
47  
48      static final String PACKAGE_DECLARATION = "package %s;%n%n";
49  
50      static enum Type {
51          CUSTOM {
52              @Override
53              String imports() {
54                  return "" //
55                          + "import java.io.Serializable;%n" //
56                          + "import org.apache.logging.log4j.Level;%n" //
57                          + "import org.apache.logging.log4j.LogManager;%n" //
58                          + "import org.apache.logging.log4j.Logger;%n" //
59                          + "import org.apache.logging.log4j.Marker;%n" //
60                          + "import org.apache.logging.log4j.message.Message;%n" //
61                          + "import org.apache.logging.log4j.message.MessageFactory;%n" //
62                          + "import org.apache.logging.log4j.spi.AbstractLogger;%n" //
63                          + "import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;%n" //
64                          + "import org.apache.logging.log4j.util.MessageSupplier;%n" //
65                          + "import org.apache.logging.log4j.util.Supplier;%n"
66                          + "%n";
67              }
68  
69              @Override
70              String declaration() {
71                  return "" //
72                          + "/**%n" //
73                          + " * Custom Logger interface with convenience methods for%n" //
74                          + " * %s%n" //
75                          + " */%n" //
76                          + "public final class %s implements Serializable {%n" //
77                          + "    private static final long serialVersionUID = " + System.nanoTime() + "L;%n" //
78                          + "    private final ExtendedLoggerWrapper logger;%n" //
79                          + "%n";
80              }
81  
82              @Override
83              String constructor() {
84                  return "" //
85                          + "%n" //
86                          + "    private %s(final Logger logger) {%n" //
87                          + "        this.logger = new ExtendedLoggerWrapper((AbstractLogger) logger, logger.getName(), "
88                          + "logger.getMessageFactory());%n" //
89                          + "    }%n";
90              }
91  
92              @Override
93              Class<?> generator() {
94                  return CustomLogger.class;
95              }
96          },
97          EXTEND {
98              @Override
99              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 }