Skip to content

Package: CompactFormatter

CompactFormatter

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