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, 7 years ago

see #14794 - checkstyle

  • Property svn:eol-style set to native
File size: 14.6 KB
RevLine 
[10899]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
[11165]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
[10899]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
[11624]72 public synchronized void publish(LogRecord record) {
[10899]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 /**
[12620]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 /**
[10899]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 /**
[12620]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 /**
[10899]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 /**
[12620]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 /**
[10899]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 /**
[12620]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) {
[12621]201 log(Logging.LEVEL_DEBUG, t);
[12620]202 }
203
204 /**
[10899]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 /**
[12620]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) {
[12621]228 log(Logging.LEVEL_TRACE, t);
[12620]229 }
230
231 /**
232 * Logs a throwable that happened. The stack trace is not added to the log.
[10899]233 * @param level The level.
234 * @param t The throwable that should be logged.
[12620]235 * @see #logWithStackTrace(Level, Throwable)
[10899]236 */
237 public static void log(Level level, Throwable t) {
238 logPrivate(level, () -> getErrorLog(null, t));
239 }
240
241 /**
[12620]242 * Logs a throwable that happened. The stack trace is not added to the log.
[10899]243 * @param level The level.
244 * @param message An additional error message
245 * @param t The throwable that caused the message
[12620]246 * @see #logWithStackTrace(Level, String, Throwable)
[10899]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.
[12620]256 * @see #log(Level, Throwable)
[10899]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.
[12620]267 * @see #logWithStackTrace(Level, Throwable)
[10899]268 */
269 public static void logWithStackTrace(Level level, String message, Throwable t) {
270 logPrivate(level, () -> getErrorLogWithStack(message, t));
271 }
272
[12765]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) {
[12798]282 logPrivate(level, () -> getErrorLogWithStack(MessageFormat.format(pattern, args), t));
[12765]283 }
284
[10899]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) {
[12620]294 // all log methods immediately call one of the logPrivate methods.
[10899]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
[12620]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.
[10899]307 */
308 public static boolean isLoggingEnabled(Level level) {
309 return LOGGER.isLoggable(level);
310 }
311
[12620]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
[10899]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));
[11397]344 if (t != null) {
345 sb.append('\n');
346 t.printStackTrace(new PrintWriter(sb));
347 }
[10899]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.