source: josm/trunk/src/org/openstreetmap/josm/tools/Logging.java @ 12798

Last change on this file since 12798 was 12798, checked in by Don-vip, 3 months ago

see #14794 - checkstyle

  • Property svn:eol-style set to native
File size: 14.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.OutputStream;
7import java.io.PrintWriter;
8import java.io.StringWriter;
9import java.text.MessageFormat;
10import java.util.ArrayList;
11import java.util.Arrays;
12import java.util.List;
13import java.util.function.Supplier;
14import java.util.logging.ConsoleHandler;
15import java.util.logging.Handler;
16import java.util.logging.Level;
17import java.util.logging.LogRecord;
18import java.util.logging.Logger;
19
20import org.openstreetmap.josm.tools.bugreport.BugReport;
21
22/**
23 * This class contains utility methods to log errors and warnings.
24 * <p>
25 * There are multiple log levels supported.
26 * @author Michael Zangl
27 * @since 10899
28 */
29public final class Logging {
30    /**
31     * The josm internal log level indicating a severe error in the application that usually leads to a crash.
32     */
33    public static final Level LEVEL_ERROR = Level.SEVERE;
34    /**
35     * The josm internal log level to use when something that may lead to a crash or wrong behaviour has happened.
36     */
37    public static final Level LEVEL_WARN = Level.WARNING;
38    /**
39     * The josm internal log level to use for important events that will be useful when debugging problems
40     */
41    public static final Level LEVEL_INFO = Level.INFO;
42    /**
43     * The josm internal log level to print debug output
44     */
45    public static final Level LEVEL_DEBUG = Level.FINE;
46    /**
47     * The finest log level josm supports. This lets josm print a lot of debug output.
48     */
49    public static final Level LEVEL_TRACE = Level.FINEST;
50    private static final Logger LOGGER = Logger.getAnonymousLogger();
51    private static final RememberWarningHandler WARNINGS = new RememberWarningHandler();
52
53    static {
54        LOGGER.setLevel(Level.ALL);
55        LOGGER.setUseParentHandlers(false);
56
57        // for a more concise logging output via java.util.logging.SimpleFormatter
58        Utils.updateSystemProperty("java.util.logging.SimpleFormatter.format", "%1$tF %1$tT.%1$tL %4$s: %5$s%6$s%n");
59
60        ConsoleHandler stderr = new ConsoleHandler();
61        LOGGER.addHandler(stderr);
62        stderr.setLevel(LEVEL_WARN);
63
64        ConsoleHandler stdout = new ConsoleHandler() {
65            @Override
66            protected synchronized void setOutputStream(OutputStream out) {
67                // overwrite output stream.
68                super.setOutputStream(System.out);
69            }
70
71            @Override
72            public synchronized void publish(LogRecord record) {
73                if (!stderr.isLoggable(record)) {
74                    super.publish(record);
75                }
76            }
77        };
78        LOGGER.addHandler(stdout);
79        stdout.setLevel(Level.ALL);
80
81        LOGGER.addHandler(WARNINGS);
82    }
83
84    private Logging() {
85        // hide
86    }
87
88    /**
89     * Set the global log level.
90     * @param level The log level to use
91     */
92    public static void setLogLevel(Level level) {
93        LOGGER.setLevel(level);
94    }
95
96    /**
97     * Prints an error message if logging is on.
98     * @param message The message to print.
99     */
100    public static void error(String message) {
101        logPrivate(LEVEL_ERROR, message);
102    }
103
104    /**
105     * Prints a formatted error message if logging is on. Calls {@link MessageFormat#format}
106     * function to format text.
107     * @param pattern The formatted message to print.
108     * @param args The objects to insert into format string.
109     */
110    public static void error(String pattern, Object... args) {
111        logPrivate(LEVEL_ERROR, pattern, args);
112    }
113
114    /**
115     * Prints an error message for the given Throwable if logging is on.
116     * @param t The throwable object causing the error.
117     * @since 12620
118     */
119    public static void error(Throwable t) {
120        logWithStackTrace(Logging.LEVEL_ERROR, t);
121    }
122
123    /**
124     * Prints a warning message if logging is on.
125     * @param message The message to print.
126     */
127    public static void warn(String message) {
128        logPrivate(LEVEL_WARN, message);
129    }
130
131    /**
132     * Prints a formatted warning message if logging is on. Calls {@link MessageFormat#format}
133     * function to format text.
134     * @param pattern The formatted message to print.
135     * @param args The objects to insert into format string.
136     */
137    public static void warn(String pattern, Object... args) {
138        logPrivate(LEVEL_WARN, pattern, args);
139    }
140
141    /**
142     * Prints a warning message for the given Throwable if logging is on.
143     * @param t The throwable object causing the error.
144     * @since 12620
145     */
146    public static void warn(Throwable t) {
147        logWithStackTrace(Logging.LEVEL_WARN, t);
148    }
149
150    /**
151     * Prints a info message if logging is on.
152     * @param message The message to print.
153     */
154    public static void info(String message) {
155        logPrivate(LEVEL_INFO, message);
156    }
157
158    /**
159     * Prints a formatted info message if logging is on. Calls {@link MessageFormat#format}
160     * function to format text.
161     * @param pattern The formatted message to print.
162     * @param args The objects to insert into format string.
163     */
164    public static void info(String pattern, Object... args) {
165        logPrivate(LEVEL_INFO, pattern, args);
166    }
167
168    /**
169     * Prints a info message for the given Throwable if logging is on.
170     * @param t The throwable object causing the error.
171     * @since 12620
172     */
173    public static void info(Throwable t) {
174        logWithStackTrace(Logging.LEVEL_INFO, t);
175    }
176
177    /**
178     * Prints a debug message if logging is on.
179     * @param message The message to print.
180     */
181    public static void debug(String message) {
182        logPrivate(LEVEL_DEBUG, message);
183    }
184
185    /**
186     * Prints a formatted debug message if logging is on. Calls {@link MessageFormat#format}
187     * function to format text.
188     * @param pattern The formatted message to print.
189     * @param args The objects to insert into format string.
190     */
191    public static void debug(String pattern, Object... args) {
192        logPrivate(LEVEL_DEBUG, pattern, args);
193    }
194
195    /**
196     * Prints a debug message for the given Throwable if logging is on.
197     * @param t The throwable object causing the error.
198     * @since 12620
199     */
200    public static void debug(Throwable t) {
201        log(Logging.LEVEL_DEBUG, t);
202    }
203
204    /**
205     * Prints a trace message if logging is on.
206     * @param message The message to print.
207     */
208    public static void trace(String message) {
209        logPrivate(LEVEL_TRACE, message);
210    }
211
212    /**
213     * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format}
214     * function to format text.
215     * @param pattern The formatted message to print.
216     * @param args The objects to insert into format string.
217     */
218    public static void trace(String pattern, Object... args) {
219        logPrivate(LEVEL_TRACE, pattern, args);
220    }
221
222    /**
223     * Prints a trace message for the given Throwable if logging is on.
224     * @param t The throwable object causing the error.
225     * @since 12620
226     */
227    public static void trace(Throwable t) {
228        log(Logging.LEVEL_TRACE, t);
229    }
230
231    /**
232     * Logs a throwable that happened. The stack trace is not added to the log.
233     * @param level The level.
234     * @param t The throwable that should be logged.
235     * @see #logWithStackTrace(Level, Throwable)
236     */
237    public static void log(Level level, Throwable t) {
238        logPrivate(level, () -> getErrorLog(null, t));
239    }
240
241    /**
242     * Logs a throwable that happened. The stack trace is not added to the log.
243     * @param level The level.
244     * @param message An additional error message
245     * @param t The throwable that caused the message
246     * @see #logWithStackTrace(Level, String, Throwable)
247     */
248    public static void log(Level level, String message, Throwable t) {
249        logPrivate(level, () -> getErrorLog(message, t));
250    }
251
252    /**
253     * Logs a throwable that happened. Adds the stack trace to the log.
254     * @param level The level.
255     * @param t The throwable that should be logged.
256     * @see #log(Level, Throwable)
257     */
258    public static void logWithStackTrace(Level level, Throwable t) {
259        logPrivate(level, () -> getErrorLogWithStack(null, t));
260    }
261
262    /**
263     * Logs a throwable that happened. Adds the stack trace to the log.
264     * @param level The level.
265     * @param message An additional error message
266     * @param t The throwable that should be logged.
267     * @see #logWithStackTrace(Level, Throwable)
268     */
269    public static void logWithStackTrace(Level level, String message, Throwable t) {
270        logPrivate(level, () -> getErrorLogWithStack(message, t));
271    }
272
273    /**
274     * Logs a throwable that happened. Adds the stack trace to the log.
275     * @param level The level.
276     * @param t The throwable that should be logged.
277     * @param pattern The formatted message to print.
278     * @param args The objects to insert into format string
279     * @see #logWithStackTrace(Level, Throwable)
280     */
281    public static void logWithStackTrace(Level level, Throwable t, String pattern, Object... args) {
282        logPrivate(level, () -> getErrorLogWithStack(MessageFormat.format(pattern, args), t));
283    }
284
285    private static void logPrivate(Level level, String pattern, Object... args) {
286        logPrivate(level, () -> MessageFormat.format(pattern, args));
287    }
288
289    private static void logPrivate(Level level, String message) {
290        logPrivate(level, () -> message);
291    }
292
293    private static void logPrivate(Level level, Supplier<String> supplier) {
294        // all log methods immediately call one of the logPrivate methods.
295        if (LOGGER.isLoggable(level)) {
296            StackTraceElement callingMethod = BugReport.getCallingMethod(1, Logging.class.getName(), name -> !"logPrivate".equals(name));
297            LOGGER.logp(level, callingMethod.getClassName(), callingMethod.getMethodName(), supplier);
298        }
299    }
300
301    /**
302     * Tests if a given log level is enabled. This can be used to avoid constructing debug data if required.
303     *
304     * For formatting text, you should use the {@link #debug(String, Object...)} message
305     * @param level A level constant. You can e.g. use {@link Logging#LEVEL_ERROR}
306     * @return <code>true</code> if log level is enabled.
307     */
308    public static boolean isLoggingEnabled(Level level) {
309        return LOGGER.isLoggable(level);
310    }
311
312    /**
313     * Determines if debug log level is enabled.
314     * Useful to avoid costly construction of debug messages when not enabled.
315     * @return {@code true} if log level is at least debug, {@code false} otherwise
316     * @since 12620
317     */
318    public static boolean isDebugEnabled() {
319        return isLoggingEnabled(Logging.LEVEL_DEBUG);
320    }
321
322    /**
323     * Determines if trace log level is enabled.
324     * Useful to avoid costly construction of trace messages when not enabled.
325     * @return {@code true} if log level is at least trace, {@code false} otherwise
326     * @since 12620
327     */
328    public static boolean isTraceEnabled() {
329        return isLoggingEnabled(Logging.LEVEL_TRACE);
330    }
331
332    private static String getErrorLog(String message, Throwable t) {
333        StringBuilder sb = new StringBuilder();
334        if (message != null) {
335            sb.append(message).append(": ");
336        }
337        sb.append(getErrorMessage(t));
338        return sb.toString();
339    }
340
341    private static String getErrorLogWithStack(String message, Throwable t) {
342        StringWriter sb = new StringWriter();
343        sb.append(getErrorLog(message, t));
344        if (t != null) {
345            sb.append('\n');
346            t.printStackTrace(new PrintWriter(sb));
347        }
348        return sb.toString();
349    }
350
351    /**
352     * Returns a human-readable message of error, also usable for developers.
353     * @param t The error
354     * @return The human-readable error message
355     */
356    public static String getErrorMessage(Throwable t) {
357        if (t == null) {
358            return "(no error)";
359        }
360        StringBuilder sb = new StringBuilder(t.getClass().getName());
361        String msg = t.getMessage();
362        if (msg != null) {
363            sb.append(": ").append(msg.trim());
364        }
365        Throwable cause = t.getCause();
366        if (cause != null && !cause.equals(t)) {
367            // this may cause infinite loops in the unlikely case that there is a loop in the causes.
368            sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause));
369        }
370        return sb.toString();
371    }
372
373    /**
374     * Clear the list of last warnings
375     */
376    public static void clearLastErrorAndWarnings() {
377        WARNINGS.clear();
378    }
379
380    /**
381     * Get the last error and warning messages in the order in which they were received.
382     * @return The last errors and warnings.
383     */
384    public static List<String> getLastErrorAndWarnings() {
385        return WARNINGS.getMessages();
386    }
387
388    /**
389     * Provides direct access to the logger used. Use of methods like {@link #warn(String)} is prefered.
390     * @return The logger
391     */
392    public static Logger getLogger() {
393        return LOGGER;
394    }
395
396    private static class RememberWarningHandler extends Handler {
397        private final String[] log = new String[10];
398        private int messagesLogged;
399
400        RememberWarningHandler() {
401            setLevel(LEVEL_WARN);
402        }
403
404        synchronized void clear() {
405            messagesLogged = 0;
406            Arrays.fill(log, null);
407        }
408
409        @Override
410        public synchronized void publish(LogRecord record) {
411            if (!isLoggable(record)) {
412                return;
413            }
414
415            String msg = getPrefix(record) + record.getMessage();
416
417            // Only remember first line of message
418            int idx = msg.indexOf('\n');
419            if (idx > 0) {
420                msg = msg.substring(0, idx);
421            }
422            log[messagesLogged % log.length] = msg;
423            messagesLogged++;
424        }
425
426        private static String getPrefix(LogRecord record) {
427            if (record.getLevel().equals(LEVEL_WARN)) {
428                return "W: ";
429            } else {
430                // worse than warn
431                return "E: ";
432            }
433        }
434
435        synchronized List<String> getMessages() {
436            List<String> logged = Arrays.asList(log);
437            ArrayList<String> res = new ArrayList<>();
438            int logOffset = messagesLogged % log.length;
439            if (messagesLogged > logOffset) {
440                res.addAll(logged.subList(logOffset, log.length));
441            }
442            res.addAll(logged.subList(0, logOffset));
443            return res;
444        }
445
446        @Override
447        public synchronized void flush() {
448            // nothing to do
449        }
450
451        @Override
452        public void close() {
453            // nothing to do
454        }
455    }
456}
Note: See TracBrowser for help on using the repository browser.