Skip to content

Package: CompactFormatter

CompactFormatter

nameinstructionbranchcomplexitylinemethod
CompactFormatter()
M: 13 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
CompactFormatter(String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
apply(Throwable)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
defaultIgnore(StackTraceElement)
M: 16 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
findAndFormat(StackTraceElement[])
M: 57 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
format(LogRecord)
M: 132 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 17 C: 0
0%
M: 1 C: 0
0%
formatBackTrace(LogRecord)
M: 66 C: 0
0%
M: 16 C: 0
0%
M: 9 C: 0
0%
M: 20 C: 0
0%
M: 1 C: 0
0%
formatError(LogRecord)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
formatLevel(LogRecord)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
formatLoggerName(LogRecord)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
formatMessage(LogRecord)
M: 52 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 16 C: 0
0%
M: 1 C: 0
0%
formatMessage(Throwable)
M: 84 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 18 C: 0
0%
M: 1 C: 0
0%
formatSource(LogRecord)
M: 32 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
formatStackTraceElement(StackTraceElement)
M: 29 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
formatThreadID(LogRecord)
M: 12 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
formatThrown(LogRecord)
M: 37 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 7 C: 0
0%
M: 1 C: 0
0%
formatZonedDateTime(LogRecord)
M: 13 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
getFormat()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getLocale(LogRecord)
M: 10 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
ignore(StackTraceElement)
M: 12 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isNullOrSpaces(String)
M: 10 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isReflection(StackTraceElement)
M: 19 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
isStaticUtility(StackTraceElement)
M: 28 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
isSynthetic(StackTraceElement)
M: 10 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isUnknown(StackTraceElement)
M: 7 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
loadDeclaredClasses()
M: 7 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
replaceClassName(String, Object[])
M: 35 C: 0
0%
M: 8 C: 0
0%
M: 5 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
replaceClassName(String, Throwable)
M: 30 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
setFormat(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
setFormat0(String)
M: 9 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
simpleClassName(Class)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
simpleClassName(String)
M: 79 C: 0
0%
M: 22 C: 0
0%
M: 12 C: 0
0%
M: 20 C: 0
0%
M: 1 C: 0
0%
simpleFileName(String)
M: 18 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
toAlternate(String)
M: 9 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2013, 2024 Oracle and/or its affiliates. All rights reserved.
3: * Copyright (c) 2013, 2024 Jason Mehrens. All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17: package org.eclipse.angus.mail.util.logging;
18:
19: import java.io.IOException;
20: import java.io.UncheckedIOException;
21: import java.util.Date;
22: import java.util.Locale;
23: import java.util.ResourceBundle;
24: import java.util.logging.LogRecord;
25: import static org.eclipse.angus.mail.util.logging.LogManagerProperties.fromLogManager;
26:
27: /**
28: * A plain text formatter that can produce fixed width output. By default this
29: * formatter will produce output no greater than 160 characters
30: * (Unicode code points) wide plus the separator and newline characters. Only
31: * specified fields support an
32: * {@linkplain #toAlternate(java.lang.String) alternate} fixed width format.
33: * <p>
34: * By default each <code>CompactFormatter</code> is initialized using the
35: * following LogManager configuration properties where
36: * <code><formatter-name></code> refers to the fully qualified class name
37: * or the fully qualified derived class name of the formatter. If properties are
38: * not defined, or contain invalid values, then the specified default values are
39: * used.
40: * <ul>
41: * <li><formatter-name>.format - the {@linkplain java.util.Formatter
42: * format} string used to transform the output. The arguments are explained
43: * in detail in the {@linkplain #format(java.util.logging.LogRecord) format}
44: * documentation. The format string can be used to fix the output size.
45: * (defaults to <code>%7$#.160s%n</code>)</li>
46: * </ul>
47: *
48: * @author Jason Mehrens
49: * @since JavaMail 1.5.2
50: */
51: public class CompactFormatter extends java.util.logging.Formatter {
52:
53: /**
54: * Load any declared classes to workaround GLASSFISH-21258.
55: */
56: static {
57: loadDeclaredClasses();
58: }
59:
60: /**
61: * Used to load declared classes encase class loader doesn't allow loading
62: * during JVM termination. This method is used with unit testing.
63: *
64: * @return an array of classes never null.
65: */
66: private static Class<?>[] loadDeclaredClasses() {
67: return new Class<?>[]{Alternate.class};
68: }
69:
70: /**
71: * Holds the java.util.Formatter pattern.
72: */
73: private volatile String fmt;
74:
75: /**
76: * Creates an instance with a default format pattern.
77: */
78: public CompactFormatter() {
79: String p = getClass().getName();
80: setFormat0(fromLogManager(p.concat(".format")));
81: }
82:
83: /**
84: * Creates an instance with the given format pattern.
85: *
86: * @param format the {@linkplain java.util.Formatter pattern} or null to use
87: * the LogManager default. The arguments are described in the
88: * {@linkplain #format(java.util.logging.LogRecord) format} method.
89: */
90: public CompactFormatter(final String format) {
91: setFormat0(format);
92: }
93:
94: /**
95: * Format the given log record and returns the formatted string. The
96: * {@linkplain java.util.Formatter#format(java.lang.String, java.lang.Object...)
97: * java.util} argument indexes are assigned to the following properties:
98: *
99: * <ol start='0'>
100: * <li>{@code format} - the {@linkplain java.util.Formatter
101: * java.util.Formatter} format string specified in the
102: * <formatter-name>.format property or the format that was given when
103: * this formatter was created.</li>
104: * <li>{@code date} - if the log record supports nanoseconds then a
105: * ZonedDateTime object representing the event time of the log record in the
106: * system time zone. Otherwise, a {@linkplain Date} object representing
107: * {@linkplain LogRecord#getMillis event time} of the log record.</li>
108: * <li>{@code source} - a string representing the caller, if available;
109: * otherwise, the logger's name.</li>
110: * <li>{@code logger} - the logger's
111: * {@linkplain Class#getSimpleName() simple}
112: * {@linkplain LogRecord#getLoggerName() name}.</li>
113: * <li>{@code level} - the
114: * {@linkplain java.util.logging.Level#getLocalizedName log level}.</li>
115: * <li>{@code message} - the formatted log message returned from the
116: * {@linkplain #formatMessage(LogRecord)} method.</li>
117: * <li>{@code thrown} - a string representing the
118: * {@linkplain LogRecord#getThrown throwable} associated with the log record
119: * and a relevant stack trace element if available; otherwise, an empty
120: * string is used.</li>
121: * <li>{@code message|thrown} The message and the thrown properties joined
122: * as one parameter. Width and precision are by Unicode code points. This
123: * parameter supports
124: * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
125: * <li>{@code thrown|message} The thrown and message properties joined as
126: * one parameter. Width and precision are by Unicode code points. This
127: * parameter supports
128: * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
129: * <li>{@code sequence} the
130: * {@linkplain LogRecord#getSequenceNumber() sequence number} if the given
131: * log record.</li>
132: * <li>{@code thread id} the {@linkplain LogRecord#getThreadID() thread id}
133: * of the given log record. By default this is formatted as a {@code long}
134: * by an unsigned conversion.</li>
135: * <li>{@code error} the throwable
136: * {@linkplain Class#getSimpleName() simple class name} and
137: * {@linkplain #formatError(LogRecord) error message} without any stack
138: * trace.</li>
139: * <li>{@code message|error} The message and error properties joined as one
140: * parameter. Width and precision are by Unicode code points. This parameter
141: * supports
142: * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
143: * <li>{@code error|message} The error and message properties joined as one
144: * parameter. Width and precision are by Unicode code points. This parameter
145: * supports
146: * {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
147: * <li>{@code backtrace} only the
148: * {@linkplain #formatBackTrace(LogRecord) stack trace} of the given
149: * throwable.</li>
150: * <li>{@code bundlename} the resource bundle
151: * {@linkplain LogRecord#getResourceBundleName() name} of the given log
152: * record.</li>
153: * <li>{@code key} the {@linkplain LogRecord#getMessage() raw message}
154: * before localization or formatting.</li>
155: * </ol>
156: *
157: * <p>
158: * Some example formats:<br>
159: * <ul>
160: * <li>{@code org.eclipse.angus.mail.util.logging.CompactFormatter.format=%7$#.160s%n}
161: * <p>
162: * This prints only 160 characters (Unicode code points) of the
163: * message|thrown ({@code 7$}) using the
164: * {@linkplain #toAlternate(java.lang.String) alternate} form. The separator
165: * is not included as part of the total width.
166: * <pre>
167: * Encoding failed.|NullPointerException: null String.getBytes(:913)
168: * </pre>
169: *
170: * <li>{@code org.eclipse.angus.mail.util.logging.CompactFormatter.format=%7$#.20s%n}
171: * <p>
172: * This prints only 20 characters (Unicode code points) of the
173: * message|thrown ({@code 7$}) using the
174: * {@linkplain #toAlternate(java.lang.String) alternate} form. This will
175: * perform a weighted truncation of both the message and thrown properties
176: * of the log record. The separator is not included as part of the total
177: * width.
178: * <pre>
179: * Encoding|NullPointerE
180: * </pre>
181: *
182: * <li>{@code org.eclipse.angus.mail.util.logging.CompactFormatter.format=%1$tc %2$s%n%4$s: %5$s%6$s%n}
183: * <p>
184: * This prints the timestamp ({@code 1$}) and the source ({@code 2$}) on the
185: * first line. The second line is the log level ({@code 4$}), log message
186: * ({@code 5$}), and the throwable with a relevant stack trace element
187: * ({@code 6$}) if one is available.
188: * <pre>
189: * Fri Nov 20 07:29:24 CST 2009 MyClass fatal
190: * SEVERE: Encoding failed.NullPointerException: null String.getBytes(:913)
191: * </pre>
192: *
193: * <li>{@code org.eclipse.angus.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n}
194: * <p>
195: * This prints the log level ({@code 4$}) and only 160 characters
196: * (Unicode code points) of the message|error ({@code 12$}) using the
197: * alternate form.
198: * <pre>
199: * SEVERE: Unable to send notification.|SocketException: Permission denied: connect
200: * </pre>
201: *
202: * <li>{@code org.eclipse.angus.mail.util.logging.CompactFormatter.format=[%9$d][%1$tT][%10$d][%2$s] %5$s%n%6$s%n}
203: * <p>
204: * This prints the sequence ({@code 9$}), event time ({@code 1$}) as 24 hour
205: * time, thread id ({@code 10$}), source ({@code 2$}), log message
206: * ({@code 5$}), and the throwable with back trace ({@code 6$}).
207: * <pre>
208: * [125][14:11:42][38][MyClass fatal] Unable to send notification.
209: * SocketException: Permission denied: connect SMTPTransport.openServer(:1949)
210: * </pre>
211: *
212: * </ul>
213: *
214: * @param record the log record to format.
215: * @return the formatted record.
216: * @throws NullPointerException if the given record is null.
217: */
218: @Override
219: public String format(final LogRecord record) {
220: //LogRecord is mutable so define local vars.
221: Locale l = getLocale(record);
222: String msg = formatMessage(record);
223: String thrown = formatThrown(record);
224: String err = formatError(record);
225: Object[] params = {
226: formatZonedDateTime(record),
227: formatSource(record),
228: formatLoggerName(record),
229: formatLevel(record),
230: msg,
231: thrown,
232: new Alternate(msg, thrown),
233: new Alternate(thrown, msg),
234: record.getSequenceNumber(),
235: formatThreadID(record),
236: err,
237: new Alternate(msg, err),
238: new Alternate(err, msg),
239: formatBackTrace(record),
240: record.getResourceBundleName(),
241: record.getMessage()};
242:
243:• if (l == null) { //BUG ID 6282094
244: return String.format(fmt, params);
245: } else {
246: return String.format(l, fmt, params);
247: }
248: }
249:
250: /**
251: * Formats message for the log record. This method removes any fully
252: * qualified throwable class names from the message.
253: *
254: * @param record the log record.
255: * @return the formatted message string.
256: */
257: @Override
258: public String formatMessage(final LogRecord record) {
259: String msg;
260: try {
261: msg = super.formatMessage(record);
262: } catch (RuntimeException ignore) {
263: msg = record.getMessage();
264: }
265:
266: //Render any String.format patterns in the message.
267: final Object[] params = record.getParameters();
268:• if (msg != null && ((params != null && params.length != 0)
269:• || msg.contains("%n"))) {
270: Locale l = getLocale(record);
271: try {
272:• if (l == null) { //BUG ID 6282094
273: msg = String.format(msg, params);
274: } else {
275: msg = String.format(l, msg, params);
276: }
277: } catch (RuntimeException ignore) {
278: }
279: msg = replaceClassName(msg, params);
280: }
281: msg = replaceClassName(msg, record.getThrown());
282: return msg;
283: }
284:
285: /**
286: * Formats the message from the thrown property of the log record. This
287: * method replaces fully qualified throwable class names from the message
288: * cause chain with simple class names.
289: *
290: * @param t the throwable to format or null.
291: * @return the empty string if null was given or the formatted message
292: * string from the throwable which may be null.
293: */
294: public String formatMessage(final Throwable t) {
295: String r;
296:• if (t != null) {
297: final Throwable apply = apply(t);
298: final String m = apply.getLocalizedMessage();
299: final String s = apply.toString();
300: final String sn = simpleClassName(apply.getClass());
301:• if (!isNullOrSpaces(m)) {
302:• if (s.contains(m)) {
303:• if (s.startsWith(apply.getClass().getName())
304:• || s.startsWith(sn)) {
305: r = replaceClassName(m, t);
306: } else {
307: r = replaceClassName(simpleClassName(s), t);
308: }
309: } else {
310: r = replaceClassName(simpleClassName(s) + ": " + m, t);
311: }
312: } else {
313: r = replaceClassName(simpleClassName(s), t);
314: }
315:
316:• if (!r.contains(sn)) {
317: r = sn + ": " + r;
318: }
319: } else {
320: r = "";
321: }
322: return r;
323: }
324:
325: /**
326: * Formats the level property of the given log record.
327: *
328: * @param record the record.
329: * @return the formatted logger name.
330: * @throws NullPointerException if the given record is null.
331: */
332: public String formatLevel(final LogRecord record) {
333: return record.getLevel().getLocalizedName();
334: }
335:
336: /**
337: * Formats the source from the given log record.
338: *
339: * @param record the record.
340: * @return the formatted source of the log record.
341: * @throws NullPointerException if the given record is null.
342: */
343: public String formatSource(final LogRecord record) {
344: String source = record.getSourceClassName();
345:• if (source != null) {
346:• if (record.getSourceMethodName() != null) {
347: source = simpleClassName(source) + " "
348: + record.getSourceMethodName();
349: } else {
350: source = simpleClassName(source);
351: }
352: } else {
353: source = simpleClassName(record.getLoggerName());
354: }
355: return source;
356: }
357:
358: /**
359: * Formats the logger name property of the given log record.
360: *
361: * @param record the record.
362: * @return the formatted logger name.
363: * @throws NullPointerException if the given record is null.
364: */
365: public String formatLoggerName(final LogRecord record) {
366: return simpleClassName(record.getLoggerName());
367: }
368:
369: /**
370: * Formats the thread id property of the given log record. Long thread ids
371: * are preferred if supported. Otherwise, the integer thread id is
372: * formatted as a {@code long} by an unsigned conversion.
373: *
374: * @param record the record.
375: * @return the formatted thread id as a number.
376: * @throws NullPointerException if the given record is null.
377: * @since JavaMail 1.5.4
378: */
379: @SuppressWarnings("deprecation") //See JDK-8245302
380: public Number formatThreadID(final LogRecord record) {
381: Long id = LogManagerProperties.getLongThreadID(record);
382:• if (id == null) {
383: id = Integer.toUnsignedLong(record.getThreadID());
384: }
385: return id;
386: }
387:
388: /**
389: * Formats the thrown property of a LogRecord. The returned string will
390: * contain a throwable message with a back trace.
391: *
392: * @param record the record.
393: * @return empty string if nothing was thrown or formatted string.
394: * @throws NullPointerException if the given record is null.
395: * @see #apply(java.lang.Throwable)
396: * @see #formatBackTrace(java.util.logging.LogRecord)
397: */
398: public String formatThrown(final LogRecord record) {
399: String msg;
400: final Throwable t = record.getThrown();
401:• if (t != null) {
402: String site = formatBackTrace(record);
403:• msg = formatMessage(t) + (isNullOrSpaces(site) ? "" : ' ' + site);
404: } else {
405: msg = "";
406: }
407: return msg;
408: }
409:
410: /**
411: * Formats the thrown property of a LogRecord as an error message. The
412: * returned string will not contain a back trace.
413: *
414: * @param record the record.
415: * @return empty string if nothing was thrown or formatted string.
416: * @throws NullPointerException if the given record is null.
417: * @see Throwable#toString()
418: * @see #apply(java.lang.Throwable)
419: * @see #formatMessage(java.lang.Throwable)
420: * @since JavaMail 1.5.4
421: */
422: public String formatError(final LogRecord record) {
423: return formatMessage(record.getThrown());
424: }
425:
426: /**
427: * Formats the back trace for the given log record.
428: *
429: * @param record the log record to format.
430: * @return the formatted back trace.
431: * @throws NullPointerException if the given record is null.
432: * @see #apply(java.lang.Throwable)
433: * @see #formatThrown(java.util.logging.LogRecord)
434: * @see #ignore(java.lang.StackTraceElement)
435: */
436: public String formatBackTrace(final LogRecord record) {
437: String site = "";
438: final Throwable t = record.getThrown();
439:• if (t != null) {
440: final Throwable root = apply(t);
441: StackTraceElement[] trace = root.getStackTrace();
442: site = findAndFormat(trace);
443:• if (isNullOrSpaces(site)) {
444: int limit = 0;
445:• for (Throwable c = t; c != null; c = c.getCause()) {
446: StackTraceElement[] ste = c.getStackTrace();
447: site = findAndFormat(ste);
448:• if (!isNullOrSpaces(site)) {
449: break;
450: } else {
451:• if (trace.length == 0) {
452: trace = ste;
453: }
454: }
455:
456: //Deal with excessive cause chains
457: //and cyclic throwables.
458:• if (++limit == (1 << 16)) {
459: break; //Give up.
460: }
461: }
462:
463: //Punt.
464:• if (isNullOrSpaces(site) && trace.length != 0) {
465: site = formatStackTraceElement(trace[0]);
466: }
467: }
468: }
469: return site;
470: }
471:
472: /**
473: * Finds and formats the first stack frame of interest.
474: *
475: * @param trace the fill stack to examine.
476: * @return a String that best describes the call site.
477: * @throws NullPointerException if stack trace element array is null.
478: */
479: private String findAndFormat(final StackTraceElement[] trace) {
480: String site = "";
481:• for (StackTraceElement s : trace) {
482:• if (!ignore(s)) {
483: site = formatStackTraceElement(s);
484: break;
485: }
486: }
487:
488: //Check if all code was compiled with no debugging info.
489:• if (isNullOrSpaces(site)) {
490:• for (StackTraceElement s : trace) {
491:• if (!defaultIgnore(s)) {
492: site = formatStackTraceElement(s);
493: break;
494: }
495: }
496: }
497: return site;
498: }
499:
500: /**
501: * Formats a stack trace element into a simple call site.
502: *
503: * @param s the stack trace element to format.
504: * @return the formatted stack trace element.
505: * @throws NullPointerException if stack trace element is null.
506: * @see #formatThrown(java.util.logging.LogRecord)
507: */
508: private String formatStackTraceElement(final StackTraceElement s) {
509: String v = simpleClassName(s.getClassName());
510: String result = s.toString().replace(s.getClassName(), v);
511:
512: //If the class name contains the simple file name then remove file name.
513: v = simpleFileName(s.getFileName());
514:• if (v != null && result.startsWith(v)) {
515: result = result.replace(s.getFileName(), "");
516: }
517: return result;
518: }
519:
520: /**
521: * Chooses a single throwable from the cause chain that will be formatted.
522: * This implementation chooses the throwable that best describes the chain.
523: * Subclasses can override this method to choose an alternate throwable for
524: * formatting.
525: *
526: * @param t the throwable from the log record.
527: * @return the chosen throwable or null only if the given argument is null.
528: * @see #formatThrown(java.util.logging.LogRecord)
529: */
530: protected Throwable apply(final Throwable t) {
531: return SeverityComparator.getInstance().apply(t);
532: }
533:
534: /**
535: * Determines if a stack frame should be ignored as the cause of an error.
536: *
537: * @param s the stack trace element.
538: * @return true if this frame should be ignored.
539: * @see #formatThrown(java.util.logging.LogRecord)
540: */
541: protected boolean ignore(final StackTraceElement s) {
542:• return isUnknown(s) || defaultIgnore(s);
543: }
544:
545: /**
546: * Defines the alternate format. This implementation removes all control
547: * characters from the given string.
548: *
549: * @param s any string or null.
550: * @return null if the argument was null otherwise, an alternate string.
551: */
552: protected String toAlternate(final String s) {
553:• return s != null ? s.replaceAll("[\\x00-\\x1F\\x7F]+", "") : null;
554: }
555:
556: /**
557: * Gets the zoned date time from the given log record.
558: *
559: * @param record the current log record.
560: * @return a zoned date time or a legacy date object.
561: * @throws NullPointerException if the given record is null.
562: * @since JavaMail 1.5.6
563: */
564: private Comparable<?> formatZonedDateTime(final LogRecord record) {
565: Comparable<?> zdt = LogManagerProperties.getZonedDateTime(record);
566:• if (zdt == null) {
567: zdt = new java.util.Date(record.getMillis());
568: }
569: return zdt;
570: }
571:
572: /**
573: * Determines if a stack frame should be ignored as the cause of an error.
574: * This does not check for unknown line numbers because code can be compiled
575: * without debugging info.
576: *
577: * @param s the stack trace element.
578: * @return true if this frame should be ignored.
579: */
580: private boolean defaultIgnore(final StackTraceElement s) {
581:• return isSynthetic(s) || isStaticUtility(s) || isReflection(s);
582: }
583:
584: /**
585: * Determines if a stack frame is for a static utility class.
586: *
587: * @param s the stack trace element.
588: * @return true if this frame should be ignored.
589: */
590: private boolean isStaticUtility(final StackTraceElement s) {
591: try {
592: return LogManagerProperties.isStaticUtilityClass(s.getClassName());
593: } catch (Exception | LinkageError ignore) {
594: }
595: final String cn = s.getClassName();
596:• return (cn.endsWith("s") && !cn.endsWith("es"))
597:• || cn.contains("Util") || cn.endsWith("Throwables");
598: }
599:
600: /**
601: * Determines if a stack trace element is for a synthetic method.
602: *
603: * @param s the stack trace element.
604: * @return true if synthetic.
605: * @throws NullPointerException if stack trace element is null.
606: */
607: private boolean isSynthetic(final StackTraceElement s) {
608:• return s.getMethodName().indexOf('$') > -1;
609: }
610:
611: /**
612: * Determines if a stack trace element has an unknown line number or a
613: * native line number.
614: *
615: * @param s the stack trace element.
616: * @return true if the line number is unknown.
617: * @throws NullPointerException if stack trace element is null.
618: */
619: private boolean isUnknown(final StackTraceElement s) {
620:• return s.getLineNumber() < 0;
621: }
622:
623: /**
624: * Determines if a stack trace element represents a reflection frame.
625: *
626: * @param s the stack trace element.
627: * @return true if the line number is unknown.
628: * @throws NullPointerException if stack trace element is null.
629: */
630: private boolean isReflection(final StackTraceElement s) {
631: try {
632: return LogManagerProperties.isReflectionClass(s.getClassName());
633: } catch (Exception | LinkageError ignore) {
634: }
635:• return s.getClassName().startsWith("java.lang.reflect.")
636:• || s.getClassName().startsWith("sun.reflect.");
637: }
638:
639: /**
640: * Gets the format pattern for this formatter.
641: *
642: * @return the format pattern.
643: * @throws SecurityException if a security manager exists and the caller
644: * does not have <code>LoggingPermission("control")</code>.
645: * @since Angus Mail 2.0.3
646: */
647: public String getFormat() {
648: LogManagerProperties.checkLogManagerAccess();
649: return fmt;
650: }
651:
652: /**
653: * Sets the format pattern for this formatter.
654: *
655: * @param format the format pattern. If null, the default pattern is used.
656: * @throws SecurityException if a security manager exists and the caller
657: * does not have <code>LoggingPermission("control")</code>.
658: * @since Angus Mail 2.0.3
659: */
660: public void setFormat(String format) {
661: LogManagerProperties.checkLogManagerAccess();
662: setFormat0(format);
663: }
664:
665: /**
666: * Sets the format pattern but doesn't check for permissions.
667: *
668: * @param format the format pattern. If null, the default pattern is used.
669: * @since Angus Mail 2.0.3
670: */
671: private void setFormat0(String format) {
672:• if (isNullOrSpaces(format)) {
673: format = "%7$#.160s%n"; //160 chars split between message and thrown.
674: }
675: this.fmt = format;
676: }
677:
678: /**
679: * Searches the given message for all instances fully qualified class name
680: * with simple class name based off of the types contained in the given
681: * parameter array.
682: *
683: * @param msg the message.
684: * @param t the throwable cause chain to search or null.
685: * @return the modified message string.
686: */
687: private static String replaceClassName(String msg, Throwable t) {
688:• if (!isNullOrSpaces(msg)) {
689: int limit = 0;
690:• for (Throwable c = t; c != null; c = c.getCause()) {
691: final Class<?> k = c.getClass();
692: msg = msg.replace(k.getName(), simpleClassName(k));
693:
694: //Deal with excessive cause chains and cyclic throwables.
695:• if (++limit == (1 << 16)) {
696: break; //Give up.
697: }
698: }
699: }
700: return msg;
701: }
702:
703: /**
704: * Searches the given message for all instances fully qualified class name
705: * with simple class name based off of the types contained in the given
706: * parameter array.
707: *
708: * @param msg the message or null.
709: * @param p the parameter array or null.
710: * @return the modified message string.
711: */
712: private static String replaceClassName(String msg, Object[] p) {
713:• if (!isNullOrSpaces(msg) && p != null) {
714:• for (Object o : p) {
715:• if (o != null) {
716: final Class<?> k = o.getClass();
717: msg = msg.replace(k.getName(), simpleClassName(k));
718: }
719: }
720: }
721: return msg;
722: }
723:
724: /**
725: * Gets the simple class name from the given class. This is a workaround for
726: * BUG ID JDK-8057919.
727: *
728: * @param k the class object.
729: * @return the simple class name or null.
730: * @since JavaMail 1.5.3
731: */
732: private static String simpleClassName(final Class<?> k) {
733: try {
734: return k.getSimpleName();
735: } catch (final InternalError JDK8057919) {
736: }
737: return simpleClassName(k.getName());
738: }
739:
740: /**
741: * Converts a fully qualified class name to a simple class name. If the
742: * leading part of the given string is not a legal class name then the given
743: * string is returned.
744: *
745: * @param name the fully qualified class name prefix or null.
746: * @return the simple class name or given input.
747: */
748: private static String simpleClassName(String name) {
749:• if (name != null) {
750: int cursor = 0;
751: int sign = -1;
752: int dot = -1;
753:• for (int c, prev = dot; cursor < name.length();
754: cursor += Character.charCount(c)) {
755: c = name.codePointAt(cursor);
756:• if (!Character.isJavaIdentifierPart(c)) {
757:• if (c == ((int) '.')) {
758:• if ((dot + 1) != cursor && (dot + 1) != sign) {
759: prev = dot;
760: dot = cursor;
761: } else {
762: return name;
763: }
764: } else {
765:• if ((dot + 1) == cursor) {
766: dot = prev;
767: }
768: break;
769: }
770: } else {
771:• if (c == ((int) '$')) {
772: sign = cursor;
773: }
774: }
775: }
776:
777:• if (dot > -1 && ++dot < cursor && ++sign < cursor) {
778: name = name.substring(Math.max(sign, dot));
779: }
780: }
781: return name;
782: }
783:
784: /**
785: * Converts a file name with an extension to a file name without an
786: * extension.
787: *
788: * @param name the full file name or null.
789: * @return the simple file name or null.
790: */
791: private static String simpleFileName(String name) {
792:• if (name != null) {
793: final int index = name.lastIndexOf('.');
794:• name = index > -1 ? name.substring(0, index) : name;
795: }
796: return name;
797: }
798:
799: /**
800: * Determines is the given string is null or spaces.
801: *
802: * @param s the string or null.
803: * @return true if null or spaces.
804: */
805: private static boolean isNullOrSpaces(final String s) {
806:• return s == null || s.trim().isEmpty();
807: }
808:
809: /**
810: * Gets the locale of the LogRecord.
811: * @param record a non null record.
812: * @return the locale or null.
813: * @since Angus Mail 2.0.3
814: */
815: private Locale getLocale(final LogRecord record) {
816: ResourceBundle rb = record.getResourceBundle();
817:• return rb == null ? null : rb.getLocale();
818: }
819:
820: /**
821: * Used to format two arguments as fixed length message.
822: */
823: private class Alternate implements java.util.Formattable {
824:
825: /**
826: * The left side of the output.
827: */
828: private final String left;
829: /**
830: * The right side of the output.
831: */
832: private final String right;
833:
834: /**
835: * Creates an alternate output.
836: *
837: * @param left the left side or null.
838: * @param right the right side or null.
839: */
840: Alternate(final String left, final String right) {
841: this.left = String.valueOf(left);
842: this.right = String.valueOf(right);
843: }
844:
845: @SuppressWarnings("override") //JDK-6954234
846: public void formatTo(java.util.Formatter formatter, int flags,
847: int width, int precision) {
848:
849: String l = left;
850: String r = right;
851: if ((flags & java.util.FormattableFlags.UPPERCASE)
852: == java.util.FormattableFlags.UPPERCASE) {
853: l = l.toUpperCase(formatter.locale());
854: r = r.toUpperCase(formatter.locale());
855: }
856:
857: if ((flags & java.util.FormattableFlags.ALTERNATE)
858: == java.util.FormattableFlags.ALTERNATE) {
859: l = toAlternate(l);
860: r = toAlternate(r);
861: }
862:
863: int lc = 0;
864: int rc = 0;
865: if (precision >= 0) {
866: lc = minCodePointCount(l, precision);
867: rc = minCodePointCount(r, precision);
868:
869: if (lc > (precision >> 1)) {
870: lc = Math.max(lc - rc, lc >> 1);
871: }
872: rc = Math.min(precision - lc, rc);
873:
874: l = l.substring(0, l.offsetByCodePoints(0, lc));
875: r = r.substring(0, r.offsetByCodePoints(0, rc));
876: }
877:
878: if (width > 0) {
879: if (precision < 0) {
880: lc = minCodePointCount(l, width);
881: rc = minCodePointCount(r, width);
882: }
883:
884: final int half = width >> 1;
885: if (lc < half) {
886: l = pad(flags, l, half - lc);
887: }
888:
889: if (rc < half) {
890: r = pad(flags, r, half - rc);
891: }
892: }
893:
894: try {
895: formatter.out().append(l);
896: if (!l.isEmpty() && !r.isEmpty()) {
897: formatter.out().append("|");
898: }
899: formatter.out().append(r);
900: } catch (IOException ioe) {
901: throw new UncheckedIOException(ioe);
902: }
903: }
904:
905: /**
906: * Counts the number code points with an upper bound.
907: *
908: * @param s the string to count, never null.
909: * @param limit the max number of code points needed.
910: * @return the number of code points, never greater than the limit.
911: */
912: private int minCodePointCount(String s, final int limit) {
913: //assert limit >= 0 : limit;
914: final int len = s.length();
915: if ((len - limit) >= limit) {
916: return limit;
917: }
918: return Math.min(s.codePointCount(0, len), limit);
919: }
920:
921: /**
922: * Pad the given input string.
923: *
924: * @param flags the formatter flags.
925: * @param s the string to pad.
926: * @param padding the number of spaces to add.
927: * @return the padded string.
928: */
929: private String pad(int flags, String s, int padding) {
930: //assert padding >= 0 : padding;
931: final StringBuilder b = new StringBuilder(
932: Math.max(s.length() + padding, padding));
933: if ((flags & java.util.FormattableFlags.LEFT_JUSTIFY)
934: == java.util.FormattableFlags.LEFT_JUSTIFY) {
935: for (int i = 0; i < padding; ++i) {
936: b.append('\u0020');
937: }
938: b.append(s);
939: } else {
940: b.append(s);
941: for (int i = 0; i < padding; ++i) {
942: b.append('\u0020');
943: }
944: }
945: return b.toString();
946: }
947: }
948: }