001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.spi; 015 016import ch.qos.logback.core.CoreConstants; 017 018/** 019 * Convert a throwable into an array of ThrowableDataPoint objects. 020 * 021 * 022 * @author Ceki Gülcü 023 */ 024public class ThrowableProxyUtil { 025 026 public static final int REGULAR_EXCEPTION_INDENT = 1; 027 public static final int SUPPRESSED_EXCEPTION_INDENT = 1; 028 private static final int BUILDER_CAPACITY = 2048; 029 030 public static void build(ThrowableProxy nestedTP, Throwable nestedThrowable, ThrowableProxy parentTP) { 031 032 StackTraceElement[] nestedSTE = nestedThrowable.getStackTrace(); 033 034 int commonFramesCount = -1; 035 if (parentTP != null) { 036 commonFramesCount = findNumberOfCommonFrames(nestedSTE, parentTP.getStackTraceElementProxyArray()); 037 } 038 039 nestedTP.commonFrames = commonFramesCount; 040 nestedTP.stackTraceElementProxyArray = steArrayToStepArray(nestedSTE); 041 } 042 043 static StackTraceElementProxy[] steArrayToStepArray(StackTraceElement[] stea) { 044 if (stea == null) { 045 return new StackTraceElementProxy[0]; 046 } 047 StackTraceElementProxy[] stepa = new StackTraceElementProxy[stea.length]; 048 for (int i = 0; i < stepa.length; i++) { 049 stepa[i] = new StackTraceElementProxy(stea[i]); 050 } 051 return stepa; 052 } 053 054 static int findNumberOfCommonFrames(StackTraceElement[] steArray, StackTraceElementProxy[] parentSTEPArray) { 055 if (parentSTEPArray == null || steArray == null) { 056 return 0; 057 } 058 059 int steIndex = steArray.length - 1; 060 int parentIndex = parentSTEPArray.length - 1; 061 int count = 0; 062 while (steIndex >= 0 && parentIndex >= 0) { 063 StackTraceElement ste = steArray[steIndex]; 064 StackTraceElement otherSte = parentSTEPArray[parentIndex].ste; 065 if (ste.equals(otherSte)) { 066 count++; 067 } else { 068 break; 069 } 070 steIndex--; 071 parentIndex--; 072 } 073 return count; 074 } 075 076 public static String asString(IThrowableProxy tp) { 077 StringBuilder sb = new StringBuilder(BUILDER_CAPACITY); 078 079 recursiveAppend(sb, null, REGULAR_EXCEPTION_INDENT, tp); 080 081 return sb.toString(); 082 } 083 084 private static void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) { 085 if (tp == null) 086 return; 087 subjoinFirstLine(sb, prefix, indent, tp); 088 sb.append(CoreConstants.LINE_SEPARATOR); 089 subjoinSTEPArray(sb, indent, tp); 090 IThrowableProxy[] suppressed = tp.getSuppressed(); 091 if (suppressed != null) { 092 for (IThrowableProxy current : suppressed) { 093 recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + SUPPRESSED_EXCEPTION_INDENT, current); 094 } 095 } 096 recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause()); 097 } 098 099 public static void indent(StringBuilder buf, int indent) { 100 for (int j = 0; j < indent; j++) { 101 buf.append(CoreConstants.TAB); 102 } 103 } 104 105 private static void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThrowableProxy tp) { 106 indent(buf, indent - 1); 107 if (prefix != null) { 108 buf.append(prefix); 109 } 110 subjoinExceptionMessage(buf, tp); 111 } 112 113 public static void subjoinPackagingData(StringBuilder builder, StackTraceElementProxy step) { 114 if (step != null) { 115 ClassPackagingData cpd = step.getClassPackagingData(); 116 if (cpd != null) { 117 if (!cpd.isExact()) { 118 builder.append(" ~["); 119 } else { 120 builder.append(" ["); 121 } 122 123 builder.append(cpd.getCodeLocation()).append(':').append(cpd.getVersion()).append(']'); 124 } 125 } 126 } 127 128 public static void subjoinSTEP(StringBuilder sb, StackTraceElementProxy step) { 129 sb.append(step.toString()); 130 subjoinPackagingData(sb, step); 131 } 132 133 /** 134 * @param sb The StringBuilder the STEPs are appended to. 135 * @param tp the IThrowableProxy containing the STEPs. 136 * @deprecated Use subjoinSTEPArray(StringBuilder sb, int indentLevel, 137 * IThrowableProxy tp) instead. 138 */ 139 public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) { 140 // not called anymore - but it is public 141 subjoinSTEPArray(sb, REGULAR_EXCEPTION_INDENT, tp); 142 } 143 144 /** 145 * @param sb The StringBuilder the STEPs are appended to. 146 * @param indentLevel indentation level used for the STEPs, usually 147 * REGULAR_EXCEPTION_INDENT. 148 * @param tp the IThrowableProxy containing the STEPs. 149 */ 150 public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) { 151 StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); 152 int commonFrames = tp.getCommonFrames(); 153 154 for (int i = 0; i < stepArray.length - commonFrames; i++) { 155 StackTraceElementProxy step = stepArray[i]; 156 indent(sb, indentLevel); 157 subjoinSTEP(sb, step); 158 sb.append(CoreConstants.LINE_SEPARATOR); 159 } 160 161 if (commonFrames > 0) { 162 indent(sb, indentLevel); 163 sb.append("... ").append(commonFrames).append(" common frames omitted") 164 .append(CoreConstants.LINE_SEPARATOR); 165 } 166 167 } 168 169 public static void subjoinFirstLine(StringBuilder buf, IThrowableProxy tp) { 170 int commonFrames = tp.getCommonFrames(); 171 if (commonFrames > 0) { 172 buf.append(CoreConstants.CAUSED_BY); 173 } 174 subjoinExceptionMessage(buf, tp); 175 } 176 177 public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) { 178 if (tp.getCause() != null) { 179 buf.append(CoreConstants.WRAPPED_BY); 180 } 181 subjoinExceptionMessage(buf, tp); 182 } 183 184 private static void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) { 185 if (tp.isCyclic()) { 186 buf.append("[CIRCULAR REFERENCE: ").append(tp.getClassName()).append(": ").append(tp.getMessage()) 187 .append(']'); 188 } else { 189 buf.append(tp.getClassName()).append(": ").append(tp.getMessage()); 190 } 191 } 192}