| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.lib.uihandler; |
| |
| import java.awt.Component; |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.GregorianCalendar; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ResourceBundle; |
| import java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import java.util.logging.Logger; |
| import java.util.logging.XMLFormatter; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import javax.swing.AbstractButton; |
| import javax.swing.Action; |
| import javax.swing.JMenuItem; |
| import org.openide.util.Lookup; |
| import static java.util.Calendar.*; |
| |
| /**this class uses most of XML Formater code, but it adds cause of throwable |
| * |
| * @author Jindrich Sedek |
| */ |
| class LogFormatter extends XMLFormatter{ |
| |
| private static final int MAX_NUM_CAUSE = 20; // Do not recurse more than this number of causes during exception printing |
| |
| private final Pattern javaHome; |
| private final Pattern userHome; |
| private final Pattern netbeansUserDir; |
| private final Pattern netbeansHome; |
| private final List<Pattern> installDirs; |
| private final Pattern filePrefix = Pattern.compile("file:", Pattern.LITERAL); |
| private final Pattern nbjclPrefix = Pattern.compile("nbjcl:", Pattern.LITERAL); |
| private final Pattern jarPrefix = Pattern.compile("jar:", Pattern.LITERAL); |
| private final Pattern hexPattern = Pattern.compile("@[0-9a-fA-F]*"); |
| |
| /** Creates a new instance of LogFormatter */ |
| public LogFormatter() { |
| javaHome = convert(System.getProperty("java.home", ""));// NOI18N |
| userHome = convert(System.getProperty("user.home", ""));// NOI18N |
| netbeansUserDir = convert(System.getProperty("netbeans.user", ""));// NOI18N |
| netbeansHome = convert(System.getProperty("netbeans.home", ""));// NOI18N |
| String nbdirsStr = System.getProperty("netbeans.dirs");// NOI18N |
| if (nbdirsStr != null){ |
| String [] fields = nbdirsStr.split(File.pathSeparator); |
| Pattern [] resultFields = new Pattern[fields.length]; |
| for (int i = 0; i < fields.length; i++) { |
| resultFields[i] = convert(fields[i]); |
| } |
| installDirs = Arrays.asList(resultFields); |
| }else{ |
| installDirs = Collections.emptyList(); |
| } |
| } |
| |
| private Pattern convert(String str){ |
| try{ |
| String name = new File(str).toURI().toURL().toString(); |
| return Pattern.compile(name, Pattern.LITERAL); |
| }catch(MalformedURLException exc){ |
| Logger.getLogger(LogFormatter.class.getName()).log(Level.INFO, "unaccessible file", exc);// NOI18N |
| } |
| return null; |
| } |
| |
| private void a2(StringBuffer sb, int x) { |
| if (x < 10) { |
| sb.append('0'); |
| } |
| sb.append(x); |
| } |
| |
| private void escape(StringBuffer sb, String text) { |
| if (text == null) { |
| text = "<null>";// NOI18N |
| } |
| for (int i = 0; i < text.length(); i++) { |
| char ch = text.charAt(i); |
| if (ch == '<') { |
| sb.append("<");// NOI18N |
| } else if (ch == '>') { |
| sb.append(">");// NOI18N |
| } else if (ch == '&') { |
| sb.append("&");// NOI18N |
| } else if (ch < 0x20 && ch != '\t' && ch != '\r' && ch != '\n') { // #119820 |
| sb.append('^').append((char) (ch + 0x40)); |
| } else { |
| sb.append(ch); |
| } |
| } |
| } |
| private String doReplace(String where, Pattern pattern, String replacement){ |
| return pattern.matcher(where).replaceAll(Matcher.quoteReplacement(replacement)); |
| } |
| |
| private void printFrame(StackTraceElement frame, StringBuffer sb){ |
| if (frame == null){ // might be caused by OOM bugs see #145298 |
| return; |
| } |
| sb.append(" <frame>\n");// NOI18N |
| sb.append(" <class>");// NOI18N |
| escape(sb, frame.getClassName()); |
| sb.append("</class>\n");// NOI18N |
| sb.append(" <method>");// NOI18N |
| escape(sb, frame.getMethodName()); |
| sb.append("</method>\n");// NOI18N |
| // Check for a line number. |
| if (frame.getLineNumber() >= 0) { |
| sb.append(" <line>");// NOI18N |
| sb.append(frame.getLineNumber()); |
| sb.append("</line>\n");// NOI18N |
| } |
| sb.append(" <file>");// NOI18N |
| ClassLoader loader = Lookup.getDefault().lookup(ClassLoader.class); |
| Class clazz=null; |
| URL jarName=null; |
| String fileName=null; |
| try{ |
| clazz = loader.loadClass(frame.getClassName()); |
| }catch(Throwable exc){ |
| Logger.getLogger(LogFormatter.class.getName()).log(Level.FINE, "Class loading error", exc);// NOI18N |
| } |
| if (clazz != null){ |
| String[] fields = clazz.getName().split("\\.");// NOI18N |
| if (fields.length> 0){ |
| jarName = clazz.getResource(fields[fields.length-1]+".class");// NOI18N |
| } |
| if (jarName!= null){ |
| fileName = jarName.toString(); |
| int index = fileName.indexOf("!");// NOI18N |
| if (index!= -1){ |
| fileName = fileName.substring(0, index); |
| } |
| fileName = doReplace(fileName, jarPrefix, "");// NOI18N |
| if (javaHome != null){ |
| fileName = doReplace(fileName, javaHome, "${java.home}");// NOI18N |
| } |
| if (netbeansHome != null){ |
| fileName = doReplace(fileName, netbeansHome, "${netbeans.home}");// NOI18N |
| } |
| if (netbeansUserDir != null){ |
| fileName = doReplace(fileName, netbeansUserDir, "${user.dir}");// NOI18N |
| } |
| for (Iterator<Pattern> it = installDirs.iterator(); it.hasNext();) { |
| fileName = doReplace(fileName, it.next(), "${netBeansDir}");// NOI18N |
| } |
| if (userHome != null){ |
| fileName = doReplace(fileName, userHome, "${user.home}");// NOI18N |
| } |
| fileName = doReplace(fileName, filePrefix, "");// NOI18N |
| fileName = doReplace(fileName, nbjclPrefix, "");// NOI18N |
| escape(sb, fileName); |
| } |
| } |
| sb.append("</file>\n");// NOI18N |
| sb.append(" </frame>\n");// NOI18N |
| } |
| |
| |
| private void printCause(Throwable th, StringBuffer sb, StackTraceElement[] causedTrace){ |
| printCause(th, sb, causedTrace, 0); |
| } |
| |
| private void printCause(Throwable th, StringBuffer sb, StackTraceElement[] causedTrace, int depth){ |
| sb.append(" <exception>\n");// NOI18N |
| sb.append(" <message>");// NOI18N |
| escape(sb, th.toString()); |
| sb.append("</message>\n");// NOI18N |
| StackTraceElement[] trace = th.getStackTrace(); |
| int m = trace.length-1; |
| int n = causedTrace.length-1; |
| while (m >= 0 && n >=0 && trace[m].equals(causedTrace[n])) { |
| m--; n--; |
| } |
| int framesInCommon = trace.length - 1 - m; |
| |
| for (int i=0; i <= m; i++) { |
| printFrame(trace[i], sb); |
| } |
| sb.append(" <more>");// NOI18N |
| sb.append(framesInCommon); |
| sb.append("</more>\n");// NOI18N |
| sb.append(" </exception>\n");// NOI18N |
| if (th.getCause() != null && depth < MAX_NUM_CAUSE){ |
| printCause(th.getCause(), sb, trace, depth + 1); |
| } |
| } |
| |
| // Report on the state of the throwable. |
| private void printThrown(Throwable th, StringBuffer sb){ |
| sb.append(" <exception>\n");// NOI18N |
| sb.append(" <message>");// NOI18N |
| escape(sb, th.toString()); |
| sb.append("</message>\n");// NOI18N |
| StackTraceElement trace[] = th.getStackTrace(); |
| for (int i = 0; i < trace.length; i++) { |
| printFrame(trace[i], sb); |
| } |
| sb.append(" </exception>\n");// NOI18N |
| if (th.getCause() != null){ |
| printCause(th.getCause(), sb, trace); |
| } |
| } |
| |
| // Append the time and date in ISO 8601 format |
| private void appendISO8601(StringBuffer sb, long millis) { |
| Date date = new Date(millis); |
| Calendar calendar = new GregorianCalendar(); |
| calendar.setTime(date); |
| sb.append(calendar.get(YEAR)); |
| sb.append('-'); |
| a2(sb, calendar.get(MONTH) + 1); |
| sb.append('-'); |
| a2(sb, calendar.get(DAY_OF_MONTH)); |
| sb.append('T'); |
| a2(sb, calendar.get(HOUR_OF_DAY)); |
| sb.append(':'); |
| a2(sb, calendar.get(MINUTE)); |
| sb.append(':'); |
| a2(sb, calendar.get(SECOND)); |
| } |
| |
| /** |
| * Format the given message to XML. |
| * @param record the log record to be formatted. |
| * @return a formatted log record |
| */ |
| public @Override String format(LogRecord record) { |
| StringBuffer sb = new StringBuffer(1000); |
| sb.append("<record>\n");// NOI18N |
| |
| sb.append(" <date>");// NOI18N |
| appendISO8601(sb, record.getMillis()); |
| sb.append("</date>\n");// NOI18N |
| |
| sb.append(" <millis>");// NOI18N |
| sb.append(record.getMillis()); |
| sb.append("</millis>\n");// NOI18N |
| |
| sb.append(" <sequence>");// NOI18N |
| sb.append(record.getSequenceNumber()); |
| sb.append("</sequence>\n");// NOI18N |
| |
| String name = record.getLoggerName(); |
| if (name != null) { |
| sb.append(" <logger>");// NOI18N |
| escape(sb, name); |
| sb.append("</logger>\n");// NOI18N |
| } |
| |
| sb.append(" <level>");// NOI18N |
| String level = Integer.toString(record.getLevel().intValue()); |
| escape(sb, level); |
| sb.append("</level>\n");// NOI18N |
| |
| if (record.getSourceClassName() != null) { |
| sb.append(" <class>");// NOI18N |
| escape(sb, record.getSourceClassName()); |
| sb.append("</class>\n");// NOI18N |
| } |
| |
| if (record.getSourceMethodName() != null) { |
| sb.append(" <method>");// NOI18N |
| escape(sb, record.getSourceMethodName()); |
| sb.append("</method>\n");// NOI18N |
| } |
| |
| sb.append(" <thread>");// NOI18N |
| sb.append(record.getThreadID()); |
| sb.append("</thread>\n");// NOI18N |
| |
| String message = record.getMessage(); |
| if (message != null) { |
| sb.append(" <message>");// NOI18N |
| escape(sb, message); |
| sb.append("</message>\n");// NOI18N |
| } |
| |
| // If the message is being localized, output the key, resource |
| // bundle name, and params. |
| ResourceBundle bundle = record.getResourceBundle(); |
| try { |
| if (bundle != null && bundle.getString(message) != null) { |
| sb.append(" <key>");// NOI18N |
| escape(sb, message); |
| sb.append("</key>\n");// NOI18N |
| sb.append(" <catalog>");// NOI18N |
| escape(sb, record.getResourceBundleName()); |
| sb.append("</catalog>\n");// NOI18N |
| } |
| } catch (Exception exc) { |
| // The message is not in the catalog. Drop through. |
| Logger.getLogger(LogFormatter.class.getName()).log(Level.FINE, "Catalog loading error", exc);// NOI18N |
| } |
| |
| Object parameters[] = record.getParameters(); |
| // Check to see if the parameter was not a messagetext format |
| // or was not null or empty |
| if ( parameters != null && parameters.length != 0 |
| && (message == null || message.indexOf("{") == -1) ) { |
| for (int i = 0; i < parameters.length; i++) { |
| sb.append(" <param>");// NOI18N |
| try { |
| escape(sb, paramToString(parameters[i])); |
| } catch (Exception ex) { |
| sb.append("???");// NOI18N |
| } |
| sb.append("</param>\n");// NOI18N |
| } |
| } |
| |
| if (record.getThrown() != null) { |
| printThrown(record.getThrown(), sb); |
| } |
| |
| sb.append("</record>\n");// NOI18N |
| return sb.toString(); |
| } |
| |
| private String paramToString(Object obj) { |
| if (obj == null) { |
| return "null"; // NOI18N |
| } |
| |
| if (obj instanceof JMenuItem) { |
| JMenuItem ab = (JMenuItem)obj; |
| Action a = ab.getAction(); |
| if (a == null) { |
| // fall thru to AbstractButton |
| } else { |
| return ab.getClass().getName() + '[' + paramToString(a) + ']'; |
| } |
| } |
| if (obj instanceof AbstractButton) { |
| AbstractButton ab = (AbstractButton)obj; |
| return ab.getClass().getName() + '[' + ab.getText() + ']'; |
| } |
| if (obj instanceof Action) { |
| Action a = (Action)obj; |
| if ( |
| a.getClass().getName().endsWith("$DelegateAction") && // NOI18N |
| a.getClass().getName().startsWith("org.openide") // NOI18N |
| ) { |
| return hexPattern.matcher(a.toString()).replaceAll("," + a.getValue(Action.NAME)); |
| } |
| return a.getClass().getName() + '[' + a.getValue(Action.NAME) + ']'; |
| } |
| if (obj instanceof Component) { |
| Component c = (Component)obj; |
| return c.getClass().getName() + '[' + c.getName() + ']'; // NOI18N |
| } |
| |
| return obj.toString(); |
| } |
| } |