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

Last change on this file since 12931 was 12931, checked in by Don-vip, 2 weeks ago

see #14602 - Override digit group separator to be consistent across languages with ISO 80000-1 + checkstyle fixes

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