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