001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, 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 v2.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 void appendNominalFirstLine(StringBuilder buf, String classname, String message) { 077 buf.append(classname).append(": ").append(message); 078 } 079 080 public static String asString(IThrowableProxy tp) { 081 StringBuilder sb = new StringBuilder(BUILDER_CAPACITY); 082 083 recursiveAppend(sb, null, REGULAR_EXCEPTION_INDENT, tp); 084 085 return sb.toString(); 086 } 087 088 private static void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) { 089 if (tp == null) 090 return; 091 subjoinFirstLine(sb, prefix, indent, tp); 092 sb.append(CoreConstants.LINE_SEPARATOR); 093 subjoinSTEPArray(sb, indent, tp); 094 IThrowableProxy[] suppressed = tp.getSuppressed(); 095 if (suppressed != null) { 096 for (IThrowableProxy current : suppressed) { 097 recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + SUPPRESSED_EXCEPTION_INDENT, current); 098 } 099 } 100 recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause()); 101 } 102 103 public static void indent(StringBuilder buf, int indent) { 104 for (int j = 0; j < indent; j++) { 105 buf.append(CoreConstants.TAB); 106 } 107 } 108 109 private static void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThrowableProxy tp) { 110 indent(buf, indent - 1); 111 if (prefix != null) { 112 buf.append(prefix); 113 } 114 subjoinExceptionMessage(buf, tp); 115 } 116 117 public static void subjoinPackagingData(StringBuilder builder, StackTraceElementProxy step) { 118 if (step != null) { 119 ClassPackagingData cpd = step.getClassPackagingData(); 120 if (cpd != null) { 121 if (!cpd.isExact()) { 122 builder.append(" ~["); 123 } else { 124 builder.append(" ["); 125 } 126 127 builder.append(cpd.getCodeLocation()).append(':').append(cpd.getVersion()).append(']'); 128 } 129 } 130 } 131 132 public static void subjoinSTEP(StringBuilder sb, StackTraceElementProxy step) { 133 sb.append(step.toString()); 134 subjoinPackagingData(sb, step); 135 } 136 137 /** 138 * @param sb The StringBuilder the STEPs are appended to. 139 * @param tp the IThrowableProxy containing the STEPs. 140 * @deprecated Use subjoinSTEPArray(StringBuilder sb, int indentLevel, 141 * IThrowableProxy tp) instead. 142 */ 143 public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) { 144 // not called anymore - but it is public 145 subjoinSTEPArray(sb, REGULAR_EXCEPTION_INDENT, tp); 146 } 147 148 /** 149 * @param sb The StringBuilder the STEPs are appended to. 150 * @param indentLevel indentation level used for the STEPs, usually 151 * REGULAR_EXCEPTION_INDENT. 152 * @param tp the IThrowableProxy containing the STEPs. 153 */ 154 public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) { 155 StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); 156 int commonFrames = tp.getCommonFrames(); 157 158 for (int i = 0; i < stepArray.length - commonFrames; i++) { 159 StackTraceElementProxy step = stepArray[i]; 160 indent(sb, indentLevel); 161 subjoinSTEP(sb, step); 162 sb.append(CoreConstants.LINE_SEPARATOR); 163 } 164 165 if (commonFrames > 0) { 166 indent(sb, indentLevel); 167 sb.append("... ").append(commonFrames).append(" common frames omitted") 168 .append(CoreConstants.LINE_SEPARATOR); 169 } 170 171 } 172 173 public static void subjoinFirstLine(StringBuilder buf, IThrowableProxy tp) { 174 int commonFrames = tp.getCommonFrames(); 175 if (commonFrames > 0) { 176 buf.append(CoreConstants.CAUSED_BY); 177 } 178 subjoinExceptionMessage(buf, tp); 179 } 180 181 public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableProxy tp) { 182 if (tp.getCause() != null) { 183 buf.append(CoreConstants.WRAPPED_BY); 184 } 185 subjoinExceptionMessage(buf, tp); 186 } 187 188 public static void subjoinExceptionMessage(StringBuilder stringBuilder, IThrowableProxy tp) { 189 if (tp.isCyclic()) { 190 stringBuilder.append("[CIRCULAR REFERENCE: "); 191 appendNominalOrOverridingMessage(stringBuilder, tp); 192 stringBuilder.append(']'); 193 } else { 194 appendNominalOrOverridingMessage(stringBuilder, tp); 195 } 196 } 197 198 private static void appendNominalOrOverridingMessage(StringBuilder stringBuilder, IThrowableProxy tp) { 199 if(tp.getOverridingMessage() == null) { 200 appendNominalFirstLine(stringBuilder, tp.getClassName(), tp.getMessage()); 201 } else { 202 stringBuilder.append(tp.getOverridingMessage()); 203 } 204 } 205}