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

Last change on this file since 12279 was 11624, checked in by stoecker, 7 years ago

fix core java bug report

  • Property svn:eol-style set to native
File size: 11.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 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 a warning message if logging is on.
116 * @param message The message to print.
117 */
118 public static void warn(String message) {
119 logPrivate(LEVEL_WARN, message);
120 }
121
122 /**
123 * Prints a formatted warning message if logging is on. Calls {@link MessageFormat#format}
124 * function to format text.
125 * @param pattern The formatted message to print.
126 * @param args The objects to insert into format string.
127 */
128 public static void warn(String pattern, Object... args) {
129 logPrivate(LEVEL_WARN, pattern, args);
130 }
131
132 /**
133 * Prints a info message if logging is on.
134 * @param message The message to print.
135 */
136 public static void info(String message) {
137 logPrivate(LEVEL_INFO, message);
138 }
139
140 /**
141 * Prints a formatted info message if logging is on. Calls {@link MessageFormat#format}
142 * function to format text.
143 * @param pattern The formatted message to print.
144 * @param args The objects to insert into format string.
145 */
146 public static void info(String pattern, Object... args) {
147 logPrivate(LEVEL_INFO, pattern, args);
148 }
149
150 /**
151 * Prints a debug message if logging is on.
152 * @param message The message to print.
153 */
154 public static void debug(String message) {
155 logPrivate(LEVEL_DEBUG, message);
156 }
157
158 /**
159 * Prints a formatted debug 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 debug(String pattern, Object... args) {
165 logPrivate(LEVEL_DEBUG, pattern, args);
166 }
167
168 /**
169 * Prints a trace message if logging is on.
170 * @param message The message to print.
171 */
172 public static void trace(String message) {
173 logPrivate(LEVEL_TRACE, message);
174 }
175
176 /**
177 * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format}
178 * function to format text.
179 * @param pattern The formatted message to print.
180 * @param args The objects to insert into format string.
181 */
182 public static void trace(String pattern, Object... args) {
183 logPrivate(LEVEL_TRACE, pattern, args);
184 }
185
186 /**
187 * Logs a throwable that happened.
188 * @param level The level.
189 * @param t The throwable that should be logged.
190 */
191 public static void log(Level level, Throwable t) {
192 logPrivate(level, () -> getErrorLog(null, t));
193 }
194
195 /**
196 * Logs a throwable that happened.
197 * @param level The level.
198 * @param message An additional error message
199 * @param t The throwable that caused the message
200 */
201 public static void log(Level level, String message, Throwable t) {
202 logPrivate(level, () -> getErrorLog(message, t));
203 }
204
205 /**
206 * Logs a throwable that happened. Adds the stack trace to the log.
207 * @param level The level.
208 * @param t The throwable that should be logged.
209 */
210 public static void logWithStackTrace(Level level, Throwable t) {
211 logPrivate(level, () -> getErrorLogWithStack(null, t));
212 }
213
214 /**
215 * Logs a throwable that happened. Adds the stack trace to the log.
216 * @param level The level.
217 * @param message An additional error message
218 * @param t The throwable that should be logged.
219 */
220 public static void logWithStackTrace(Level level, String message, Throwable t) {
221 logPrivate(level, () -> getErrorLogWithStack(message, t));
222 }
223
224 private static void logPrivate(Level level, String pattern, Object... args) {
225 logPrivate(level, () -> MessageFormat.format(pattern, args));
226 }
227
228 private static void logPrivate(Level level, String message) {
229 logPrivate(level, () -> message);
230 }
231
232 private static void logPrivate(Level level, Supplier<String> supplier) {
233 // all log methods immeadiately call one of the logPrivate methods.
234 if (LOGGER.isLoggable(level)) {
235 StackTraceElement callingMethod = BugReport.getCallingMethod(1, Logging.class.getName(), name -> !"logPrivate".equals(name));
236 LOGGER.logp(level, callingMethod.getClassName(), callingMethod.getMethodName(), supplier);
237 }
238 }
239
240 /**
241 * Tests if a given log level is enabled. This can be used to avoid constructing debug data if required.
242 *
243 * For formatting text, you should use the {@link #debug(String, Object...)} message
244 * @param level A lvele constant. You can e.g. use {@link Logging#LEVEL_ERROR}
245 * @return <code>true</code> if debug is enabled.
246 */
247 public static boolean isLoggingEnabled(Level level) {
248 return LOGGER.isLoggable(level);
249 }
250
251 private static String getErrorLog(String message, Throwable t) {
252 StringBuilder sb = new StringBuilder();
253 if (message != null) {
254 sb.append(message).append(": ");
255 }
256 sb.append(getErrorMessage(t));
257 return sb.toString();
258 }
259
260 private static String getErrorLogWithStack(String message, Throwable t) {
261 StringWriter sb = new StringWriter();
262 sb.append(getErrorLog(message, t));
263 if (t != null) {
264 sb.append('\n');
265 t.printStackTrace(new PrintWriter(sb));
266 }
267 return sb.toString();
268 }
269
270 /**
271 * Returns a human-readable message of error, also usable for developers.
272 * @param t The error
273 * @return The human-readable error message
274 */
275 public static String getErrorMessage(Throwable t) {
276 if (t == null) {
277 return "(no error)";
278 }
279 StringBuilder sb = new StringBuilder(t.getClass().getName());
280 String msg = t.getMessage();
281 if (msg != null) {
282 sb.append(": ").append(msg.trim());
283 }
284 Throwable cause = t.getCause();
285 if (cause != null && !cause.equals(t)) {
286 // this may cause infinite loops in the unlikely case that there is a loop in the causes.
287 sb.append(". ").append(tr("Cause: ")).append(getErrorMessage(cause));
288 }
289 return sb.toString();
290 }
291
292 /**
293 * Clear the list of last warnings
294 */
295 public static void clearLastErrorAndWarnings() {
296 WARNINGS.clear();
297 }
298
299 /**
300 * Get the last error and warning messages in the order in which they were received.
301 * @return The last errors and warnings.
302 */
303 public static List<String> getLastErrorAndWarnings() {
304 return WARNINGS.getMessages();
305 }
306
307 /**
308 * Provides direct access to the logger used. Use of methods like {@link #warn(String)} is prefered.
309 * @return The logger
310 */
311 public static Logger getLogger() {
312 return LOGGER;
313 }
314
315 private static class RememberWarningHandler extends Handler {
316 private final String[] log = new String[10];
317 private int messagesLogged;
318
319 RememberWarningHandler() {
320 setLevel(LEVEL_WARN);
321 }
322
323 synchronized void clear() {
324 messagesLogged = 0;
325 Arrays.fill(log, null);
326 }
327
328 @Override
329 public synchronized void publish(LogRecord record) {
330 if (!isLoggable(record)) {
331 return;
332 }
333
334 String msg = getPrefix(record) + record.getMessage();
335
336 // Only remember first line of message
337 int idx = msg.indexOf('\n');
338 if (idx > 0) {
339 msg = msg.substring(0, idx);
340 }
341 log[messagesLogged % log.length] = msg;
342 messagesLogged++;
343 }
344
345 private static String getPrefix(LogRecord record) {
346 if (record.getLevel().equals(LEVEL_WARN)) {
347 return "W: ";
348 } else {
349 // worse than warn
350 return "E: ";
351 }
352 }
353
354 synchronized List<String> getMessages() {
355 List<String> logged = Arrays.asList(log);
356 ArrayList<String> res = new ArrayList<>();
357 int logOffset = messagesLogged % log.length;
358 if (messagesLogged > logOffset) {
359 res.addAll(logged.subList(logOffset, log.length));
360 }
361 res.addAll(logged.subList(0, logOffset));
362 return res;
363 }
364
365 @Override
366 public synchronized void flush() {
367 // nothing to do
368 }
369
370 @Override
371 public void close() {
372 // nothing to do
373 }
374 }
375}
Note: See TracBrowser for help on using the repository browser.