001package io.cui.tools.logging;
002
003import static io.cui.tools.string.MoreStrings.nullToEmpty;
004import static java.util.Objects.requireNonNull;
005
006import java.util.function.Supplier;
007import java.util.logging.Logger;
008import java.util.regex.Pattern;
009
010import io.cui.tools.string.MoreStrings;
011
012/**
013 * <p>
014 * Wrapper around java-util {@link Logger} that simplifies its usage. In
015 * addition it provides a similar api like slf4j. It is not meant to act as logging-facade like
016 * slf4j or jakarta-commons-logging. It only provides a little syntactic sugar for the built-in
017 * logger.
018 * </p>
019 * <h2>Obtaining a logger</h2>
020 * <p>
021 * {@code private static final CuiLogger log = new CuiLogger(SomeClass.class);}
022 * </p>
023 * <p>
024 * {@code private static final CuiLogger log = new CuiLogger("SomeLoggerName");}
025 * </p>
026 * <p>
027 * {@code private static final CuiLogger log = CuiLoggerFactory.getLogger();}
028 * </p>
029 * <h2>Logging</h2>
030 * <p>
031 * {@link CuiLogger} provides an implicit code guard, if used correctly. Used correctly hereby means
032 * to either use formatting with parameter or incorporating {@link Supplier} for generating the
033 * actual log-message. For other means of creating a message you still can use code guards.
034 * </p>
035 * <p>
036 * {@code log.trace("Parameter-type matches exactly '%s'", assignableSource);}
037 * </p>
038 * <p>
039 * {@code log.debug("Adding found method '%s' on class '%s'", name, clazz);}
040 * </p>
041 * <p>
042 * {@code log.info("Starting up application");}
043 * </p>
044 * <p>
045 * {@code // In order not to mess up with the ellipsis parameter}
046 * </p>
047 * <p>
048 * {@code // exceptions must be the first parameter}
049 * </p>
050 * <p>
051 * {@code log.warn(e, "Exception during lenientFormat for '%s'", objectToString); }
052 * </p>
053 * <p>
054 * {@code log.error(e, "Caught an exception"); }
055 * </p>
056 * <p>
057 * {@code log.info(() -> "Supplier can be used as well");}
058 * </p>
059 * <p>
060 * {@code log.error(e, () -> "Even with exceptions");}
061 * </p>
062 * <p>
063 * {@code log.trace(() -> "I will only be evaluated if the trace-level for is enabled");}
064 * </p>
065 * <h2>Formatting</h2>
066 * <p>
067 * Like slf4j there is a simple way of formatting log-messages. In addition to '{}' the
068 * formatting supports '%s' as well. At runtime it replaces the '{}' tokens with
069 * '%s' and passes the data to {@link MoreStrings#lenientFormat(String, Object...)} for
070 * creating the actual log-message. As a variant providing a {@link Supplier} works as well.
071 * </p>
072 *
073 * @author Oliver Wolff
074 *
075 */
076public class CuiLogger {
077
078    private final Logger delegate;
079
080    static final Pattern SLF4J_PATTERN = Pattern.compile(Pattern.quote("{}"));
081
082    /**
083     * @param clazz to be used for acquiring a concrete {@link Logger} instance.
084     *            Must not be null
085     */
086    public CuiLogger(Class<?> clazz) {
087        requireNonNull(clazz);
088        delegate = Logger.getLogger(clazz.getName());
089    }
090
091    /**
092     * @param name to be used for acquiring a concrete {@link Logger} instance.
093     *            Must not be null
094     */
095    public CuiLogger(String name) {
096        requireNonNull(nullToEmpty(name));
097        delegate = Logger.getLogger(name);
098    }
099
100    /**
101     * Is the logger instance enabled for the trace level?
102     *
103     * @return {@code true} if this CuiLogger is enabled for the trace level,
104     *         false otherwise.
105     *
106     */
107    public boolean isTraceEnabled() {
108        return LogLevel.TRACE.isEnabled(delegate);
109    }
110
111    /**
112     * Log a message at the trace level.
113     *
114     * @param msg the message string to be logged
115     *
116     */
117    public void trace(String msg) {
118        LogLevel.TRACE.handleActualLog(delegate, msg, null);
119    }
120
121    /**
122     * Log a message at the trace level.
123     *
124     * @param msg the message string to be logged
125     *
126     */
127    public void trace(Supplier<String> msg) {
128        LogLevel.TRACE.log(delegate, msg, null);
129    }
130
131    /**
132     * Log a message at the trace level.
133     *
134     * @param throwable to be logged
135     * @param msg the message string to be logged
136     *
137     */
138    public void trace(Throwable throwable, Supplier<String> msg) {
139        LogLevel.TRACE.log(delegate, msg, throwable);
140    }
141
142    /**
143     * Log a message at the trace level.
144     *
145     * @param msg the message string to be logged
146     * @param throwable to be logged
147     *
148     */
149    public void trace(String msg, Throwable throwable) {
150        LogLevel.TRACE.handleActualLog(delegate, msg, throwable);
151    }
152
153    /**
154     * Log a message at the trace level.
155     *
156     * @param throwable to be logged
157     * @param template to be used for formatting, see class-documentation for details on formatting
158     * @param parameter to be used for replacing the placeholder
159     *
160     */
161    public void trace(Throwable throwable, String template, Object... parameter) {
162        LogLevel.TRACE.log(delegate, throwable, template, parameter);
163    }
164
165    /**
166     * Log a message at the trace level.
167     *
168     * @param template to be used for formatting, see class-documentation for details on formatting
169     * @param parameter to be used for replacing the placeholder
170     *
171     */
172    public void trace(String template, Object... parameter) {
173        LogLevel.TRACE.log(delegate, template, parameter);
174    }
175
176    /**
177     * Is the logger instance enabled for the debug level?
178     *
179     * @return {@code true} if this CuiLogger is enabled for the debug level,
180     *         false otherwise.
181     *
182     */
183    public boolean isDebugEnabled() {
184        return LogLevel.DEBUG.isEnabled(delegate);
185    }
186
187    /**
188     * Log a message at the debug level.
189     *
190     * @param msg the message string to be logged
191     */
192    public void debug(String msg) {
193        LogLevel.DEBUG.handleActualLog(delegate, msg, null);
194    }
195
196    /**
197     * Log a message at the debug level.
198     *
199     * @param msg the message string to be logged
200     * @param throwable to be logged
201     *
202     */
203    public void debug(String msg, Throwable throwable) {
204        LogLevel.DEBUG.handleActualLog(delegate, msg, throwable);
205    }
206
207    /**
208     * Log a message at the debug level.
209     *
210     * @param msg the message string to be logged
211     *
212     */
213    public void debug(Supplier<String> msg) {
214        LogLevel.DEBUG.log(delegate, msg, null);
215    }
216
217    /**
218     * Log a message at the debug level.
219     *
220     * @param throwable to be logged
221     * @param msg the message string to be logged
222     *
223     */
224    public void debug(Throwable throwable, Supplier<String> msg) {
225        LogLevel.DEBUG.log(delegate, msg, throwable);
226    }
227
228    /**
229     * Log a message at the debug level.
230     *
231     * @param throwable to be logged
232     * @param template to be used for formatting, see class-documentation for details on formatting
233     * @param parameter to be used for replacing the placeholder
234     *
235     */
236    public void debug(Throwable throwable, String template, Object... parameter) {
237        LogLevel.DEBUG.log(delegate, throwable, template, parameter);
238    }
239
240    /**
241     * Log a message at the debug level.
242     *
243     * @param template to be used for formatting, see class-documentation for details on formatting
244     * @param parameter to be used for replacing the placeholder
245     *
246     */
247    public void debug(String template, Object... parameter) {
248        LogLevel.DEBUG.log(delegate, template, parameter);
249    }
250
251    /**
252     * Is the logger instance enabled for the info level?
253     *
254     * @return {@code true} if this CuiLogger is enabled for the info level,
255     *         false otherwise.
256     *
257     */
258    public boolean isInfoEnabled() {
259        return LogLevel.INFO.isEnabled(delegate);
260    }
261
262    /**
263     * Log a message at the info level.
264     *
265     * @param msg the message string to be logged
266     */
267    public void info(String msg) {
268        LogLevel.INFO.handleActualLog(delegate, msg, null);
269    }
270
271    /**
272     * Log a message at the info level.
273     *
274     * @param msg the message string to be logged
275     * @param throwable to be logged
276     *
277     */
278    public void info(String msg, Throwable throwable) {
279        LogLevel.INFO.handleActualLog(delegate, msg, throwable);
280    }
281
282    /**
283     * Log a message at the info level.
284     *
285     * @param msg the message string to be logged
286     *
287     */
288    public void info(Supplier<String> msg) {
289        LogLevel.INFO.log(delegate, msg, null);
290    }
291
292    /**
293     * Log a message at the info level.
294     *
295     * @param throwable to be logged
296     * @param msg the message string to be logged
297     *
298     */
299    public void info(Throwable throwable, Supplier<String> msg) {
300        LogLevel.INFO.log(delegate, msg, throwable);
301    }
302
303    /**
304     * Log a message at the info level.
305     *
306     * @param throwable to be logged
307     * @param template to be used for formatting, see class-documentation for details on formatting
308     * @param parameter to be used for replacing the placeholder
309     *
310     */
311    public void info(Throwable throwable, String template, Object... parameter) {
312        LogLevel.INFO.log(delegate, throwable, template, parameter);
313    }
314
315    /**
316     * Log a message at the info level.
317     *
318     * @param template to be used for formatting, see class-documentation for details on formatting
319     * @param parameter to be used for replacing the placeholder
320     *
321     */
322    public void info(String template, Object... parameter) {
323        LogLevel.INFO.log(delegate, template, parameter);
324    }
325
326    /**
327     * Is the logger instance enabled for the warn level?
328     *
329     * @return {@code true} if this CuiLogger is enabled for the warn level,
330     *         false otherwise.
331     *
332     */
333    public boolean isWarnEnabled() {
334        return LogLevel.WARN.isEnabled(delegate);
335    }
336
337    /**
338     * Log a message at the warn level.
339     *
340     * @param msg the message string to be logged
341     */
342    public void warn(String msg) {
343        LogLevel.WARN.handleActualLog(delegate, msg, null);
344    }
345
346    /**
347     * Log a message at the warn level.
348     *
349     * @param msg the message string to be logged
350     * @param throwable to be logged
351     *
352     */
353    public void warn(String msg, Throwable throwable) {
354        LogLevel.WARN.handleActualLog(delegate, msg, throwable);
355    }
356
357    /**
358     * Log a message at the warn level.
359     *
360     * @param msg the message string to be logged
361     *
362     */
363    public void warn(Supplier<String> msg) {
364        LogLevel.WARN.log(delegate, msg, null);
365    }
366
367    /**
368     * Log a message at the warn level.
369     *
370     * @param throwable to be logged
371     * @param msg the message string to be logged
372     *
373     */
374    public void warn(Throwable throwable, Supplier<String> msg) {
375        LogLevel.WARN.log(delegate, msg, throwable);
376    }
377
378    /**
379     * Log a message at the warn level.
380     *
381     * @param throwable to be logged
382     * @param template to be used for formatting, see class-documentation for details on formatting
383     * @param parameter to be used for replacing the placeholder
384     *
385     */
386    public void warn(Throwable throwable, String template, Object... parameter) {
387        LogLevel.WARN.log(delegate, throwable, template, parameter);
388    }
389
390    /**
391     * Log a message at the warn level.
392     *
393     * @param template to be used for formatting, see class-documentation for details on formatting
394     * @param parameter to be used for replacing the placeholder
395     *
396     */
397    public void warn(String template, Object... parameter) {
398        LogLevel.WARN.log(delegate, template, parameter);
399    }
400
401    /**
402     * Is the logger instance enabled for the error level?
403     *
404     * @return {@code true} if this CuiLogger is enabled for the error level,
405     *         false otherwise.
406     *
407     */
408    public boolean isErrorEnabled() {
409        return LogLevel.ERROR.isEnabled(delegate);
410    }
411
412    /**
413     * Log a message at the error level.
414     *
415     * @param msg the message string to be logged
416     */
417    public void error(String msg) {
418        LogLevel.ERROR.handleActualLog(delegate, msg, null);
419    }
420
421    /**
422     * Log a message at the error level.
423     *
424     * @param msg the message string to be logged
425     * @param throwable to be logged
426     *
427     */
428    public void error(String msg, Throwable throwable) {
429        LogLevel.ERROR.handleActualLog(delegate, msg, throwable);
430    }
431
432    /**
433     * Log a message at the error level.
434     *
435     * @param msg the message string to be logged
436     *
437     */
438    public void error(Supplier<String> msg) {
439        LogLevel.ERROR.log(delegate, msg, null);
440    }
441
442    /**
443     * Log a message at the error level.
444     *
445     * @param throwable to be logged
446     * @param msg the message string to be logged
447     *
448     */
449    public void error(Throwable throwable, Supplier<String> msg) {
450        LogLevel.ERROR.log(delegate, msg, throwable);
451    }
452
453    /**
454     * Log a message at the error level.
455     *
456     * @param throwable to be logged
457     * @param template to be used for formatting, see class-documentation for details on formatting
458     * @param parameter to be used for replacing the placeholder
459     *
460     */
461    public void error(Throwable throwable, String template, Object... parameter) {
462        LogLevel.ERROR.log(delegate, throwable, template, parameter);
463    }
464
465    /**
466     * Log a message at the error level.
467     *
468     * @param template to be used for formatting, see class-documentation for details on formatting
469     * @param parameter to be used for replacing the placeholder
470     *
471     */
472    public void error(String template, Object... parameter) {
473        LogLevel.ERROR.log(delegate, template, parameter);
474    }
475
476    Logger getWrapped() {
477        return delegate;
478    }
479
480    /**
481     * @return the name / class of the underlying logger
482     */
483    public String getName() {
484        return delegate.getName();
485    }
486
487    /**
488     * @return CUI log level derived from JUL log level.
489     *         E.g. FINEST(300) matches TRACE(400), CONFIG(700) matches DEBUG(500), ALL matches
490     *         TRACE.
491     */
492    public LogLevel getLogLevel() {
493        return LogLevel.from(delegate.getLevel());
494    }
495
496}