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

Last change on this file since 11098 was 10899, checked in by Don-vip, 8 years ago

fix #13318 - Clean up program startup (parameters, logging) - patch by michael2402 - gsoc-core

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