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

Last change on this file since 12987 was 12931, checked in by Don-vip, 7 years 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.