1717import java .util .function .BiConsumer ;
1818
1919final class SimpleErrorTracker implements ErrorTracker {
20+ private final int messageLength = Math .min (1000 , Integer .getInteger ("faststats.message-length" , 500 ));
21+ private final int stackTraceLength = Math .min (500 , Integer .getInteger ("faststats.stack-trace-length" , 300 ));
2022 private final int stackTraceLimit = Math .min (50 , Integer .getInteger ("faststats.stack-trace-limit" , 15 ));
23+
2124 private final Map <String , Integer > collected = new ConcurrentHashMap <>();
2225 private final Map <String , JsonObject > reports = new ConcurrentHashMap <>();
2326
@@ -48,8 +51,10 @@ private String hash(JsonObject report) {
4851 return Long .toHexString (hash [0 ]) + Long .toHexString (hash [1 ]);
4952 }
5053
51- private JsonObject compile (Throwable error , @ Nullable List <StackTraceElement > suppress ) {
52- var stack = Arrays .asList (error .getStackTrace ());
54+ // todo: cleanup this absolute mess
55+ private JsonObject compile (Throwable error , @ Nullable List <String > suppress ) {
56+ var elements = error .getStackTrace ();
57+ var stack = collapseStackTrace (elements );
5358 var list = new ArrayList <>(stack );
5459 if (suppress != null ) list .removeAll (suppress );
5560
@@ -59,18 +64,22 @@ private JsonObject compile(Throwable error, @Nullable List<StackTraceElement> su
5964 var stacktrace = new JsonArray (traces );
6065
6166 for (var i = 0 ; i < traces ; i ++) {
62- stacktrace .add (list .get (i ).toString ());
67+ var string = list .get (i );
68+ if (string .length () <= stackTraceLength ) stacktrace .add (string );
69+ else stacktrace .add (string .substring (0 , stackTraceLength ) + "..." );
6370 }
6471 if (traces > 0 && traces < list .size ()) {
6572 stacktrace .add ("and " + (list .size () - traces ) + " more..." );
6673 } else {
67- var i = stack . size () - list .size ();
74+ var i = elements . length - list .size ();
6875 if (i > 0 ) stacktrace .add ("Omitted " + i + " duplicate stack frame" + (i == 1 ? "" : "s" ));
6976 }
7077
7178 report .addProperty ("error" , error .getClass ().getName ());
72- if (error .getMessage () != null ) {
73- report .addProperty ("message" , anonymize (error .getMessage ()));
79+ var message = error .getMessage ();
80+ if (message != null ) {
81+ if (message .length () > messageLength ) message = message .substring (0 , messageLength ) + "..." ;
82+ report .addProperty ("message" , anonymize (message ));
7483 }
7584 if (!stacktrace .isEmpty ()) {
7685 report .add ("stack" , stacktrace );
@@ -84,6 +93,58 @@ private JsonObject compile(Throwable error, @Nullable List<StackTraceElement> su
8493 return report ;
8594 }
8695
96+ public static List <String > collapseStackTrace (StackTraceElement [] trace ) {
97+ var lines = Arrays .stream (trace )
98+ .map (StackTraceElement ::toString )
99+ .toList ();
100+
101+ return collapseRepeatingPattern (lines );
102+ }
103+
104+ public static List <String > collapseRepeatingPattern (List <String > lines ) {
105+ // First, collapse consecutive duplicate lines
106+ var deduplicated = collapseConsecutiveDuplicates (lines );
107+
108+ var n = deduplicated .size ();
109+
110+ for (var cycleLen = 1 ; cycleLen <= n / 2 ; cycleLen ++) {
111+ var isPattern = true ;
112+ var repetitions = 0 ;
113+
114+ for (var i = 0 ; i < n ; i ++) {
115+ if (!deduplicated .get (i ).equals (deduplicated .get (i % cycleLen ))) {
116+ isPattern = false ;
117+ break ;
118+ }
119+ if (i > 0 && i % cycleLen == 0 ) {
120+ repetitions ++;
121+ }
122+ }
123+
124+ if (isPattern && repetitions >= 2 ) {
125+ return deduplicated .subList (0 , cycleLen );
126+ }
127+ }
128+
129+ return deduplicated ;
130+ }
131+
132+ private static List <String > collapseConsecutiveDuplicates (List <String > lines ) {
133+ if (lines .isEmpty ()) return lines ;
134+
135+ var result = new ArrayList <String >();
136+ String previous = null ;
137+
138+ for (var line : lines ) {
139+ if (!line .equals (previous )) {
140+ result .add (line );
141+ previous = line ;
142+ }
143+ }
144+
145+ return result ;
146+ }
147+
87148 private static final String IPV4_PATTERN =
88149 "\\ b(\\ d{1,3})\\ .(\\ d{1,3})\\ .(\\ d{1,3})\\ .(\\ d{1,3})\\ b" ;
89150 private static final String IPV6_PATTERN =
0 commit comments