Skip to content

Commit a82223f

Browse files
committed
Consolidate error tracker into core module
1 parent 7a25f29 commit a82223f

23 files changed

+205
-255
lines changed

bukkit/example-plugin/src/main/java/com/example/ExamplePlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import dev.faststats.bukkit.BukkitMetrics;
44
import dev.faststats.core.Metrics;
55
import dev.faststats.core.chart.Chart;
6-
import dev.faststats.errors.ErrorTracker;
6+
import dev.faststats.core.ErrorTracker;
77
import org.bukkit.plugin.java.JavaPlugin;
88

99
import java.net.URI;

core/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
dependencies {
2-
api(project(":error-tracker"))
2+
compileOnlyApi("com.google.code.gson:gson:2.13.2")
3+
compileOnlyApi("org.jetbrains:annotations:26.0.2-1")
4+
compileOnlyApi("org.jspecify:jspecify:1.0.0")
35

46
testImplementation("com.google.code.gson:gson:2.13.2")
57
testImplementation("org.junit.jupiter:junit-jupiter")

error-tracker/src/main/java/dev/faststats/errors/ErrorTracker.java renamed to core/src/main/java/dev/faststats/core/ErrorTracker.java

Lines changed: 8 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
1-
package dev.faststats.errors;
1+
package dev.faststats.core;
22

3-
import com.google.gson.JsonArray;
4-
import dev.faststats.errors.concurrent.TrackingExecutors;
5-
import dev.faststats.errors.concurrent.TrackingThreadFactory;
6-
import dev.faststats.errors.concurrent.TrackingThreadPoolExecutor;
7-
import dev.faststats.errors.impl.SimpleErrorTracker;
3+
import dev.faststats.core.concurrent.TrackingExecutors;
4+
import dev.faststats.core.concurrent.TrackingBase;
5+
import dev.faststats.core.concurrent.TrackingThreadFactory;
6+
import dev.faststats.core.concurrent.TrackingThreadPoolExecutor;
87
import org.jetbrains.annotations.Contract;
98
import org.jspecify.annotations.Nullable;
109

11-
import java.security.PrivilegedAction;
12-
import java.security.PrivilegedExceptionAction;
13-
import java.util.Optional;
14-
1510
/**
1611
* An error tracker.
1712
*
1813
* @since 0.10.0
1914
*/
20-
// todo: cleanup
2115
public sealed interface ErrorTracker permits SimpleErrorTracker {
2216
/**
2317
* Create and attach a new context-aware error tracker.
@@ -76,23 +70,6 @@ static ErrorTracker contextUnaware() {
7670
@Contract(mutates = "this")
7771
void trackError(Throwable error);
7872

79-
/**
80-
* Gets the error data.
81-
*
82-
* @return the error data
83-
* @since 0.10.0
84-
*/
85-
@Contract(pure = true)
86-
Optional<JsonArray> getData(); // todo: keep public?
87-
88-
/**
89-
* Clears the error data.
90-
*
91-
* @since 0.10.0
92-
*/
93-
@Contract(mutates = "this")
94-
void clear(); // todo: keep public?
95-
9673
/**
9774
* Attaches an error context to the tracker.
9875
*
@@ -102,45 +79,13 @@ static ErrorTracker contextUnaware() {
10279
void attachErrorContext(@Nullable ClassLoader loader);
10380

10481
/**
105-
* Checks if the given error is from the given class loader.
82+
* Returns the tracking base.
10683
*
107-
* @param loader the class loader
108-
* @param error the error
109-
* @return {@code true} if the error is from the given class loader.
84+
* @return the tracking base
11085
* @since 0.10.0
11186
*/
11287
@Contract(pure = true)
113-
boolean isSameLoader(ClassLoader loader, Throwable error); // todo: keep public?
114-
115-
/**
116-
* Creates a tracked runnable.
117-
*
118-
* @param runnable the runnable
119-
* @return the tracked runnable
120-
* @since 0.10.0
121-
*/
122-
@Contract(value = "_ -> new", pure = true)
123-
Runnable tracked(Runnable runnable); // todo: move to extra interface?
124-
125-
/**
126-
* Creates a tracked action.
127-
*
128-
* @param action the action
129-
* @return the tracked action
130-
* @since 0.10.0
131-
*/
132-
@Contract(value = "_ -> new", pure = true)
133-
<T> PrivilegedAction<T> tracked(PrivilegedAction<T> action); // todo: move to extra interface?
134-
135-
/**
136-
* Creates a tracked exception action.
137-
*
138-
* @param action the exception action
139-
* @return the tracked exception action
140-
* @since 0.10.0
141-
*/
142-
@Contract(value = "_ -> new", pure = true)
143-
<T> PrivilegedExceptionAction<T> tracked(PrivilegedExceptionAction<T> action); // todo: move to extra interface?
88+
TrackingBase base();
14489

14590
/**
14691
* Returns the tracking equivalent to {@link java.util.concurrent.Executors}.

core/src/main/java/dev/faststats/core/Metrics.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dev.faststats.core;
22

33
import dev.faststats.core.chart.Chart;
4-
import dev.faststats.errors.ErrorTracker;
54
import org.jetbrains.annotations.Async;
65
import org.jetbrains.annotations.Contract;
76

error-tracker/src/main/java/dev/faststats/errors/impl/MurmurHash3.java renamed to core/src/main/java/dev/faststats/core/MurmurHash3.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.faststats.errors.impl;
1+
package dev.faststats.core;
22

33
import org.jetbrains.annotations.Contract;
44

error-tracker/src/main/java/dev/faststats/errors/impl/SimpleErrorTracker.java renamed to core/src/main/java/dev/faststats/core/SimpleErrorTracker.java

Lines changed: 56 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
1-
package dev.faststats.errors.impl;
1+
package dev.faststats.core;
22

33
import com.google.gson.JsonArray;
44
import com.google.gson.JsonObject;
5-
import dev.faststats.errors.ErrorTracker;
6-
import dev.faststats.errors.concurrent.TrackingExecutors;
7-
import dev.faststats.errors.concurrent.TrackingThreadFactory;
8-
import dev.faststats.errors.concurrent.TrackingThreadPoolExecutor;
5+
import dev.faststats.core.concurrent.TrackingBase;
6+
import dev.faststats.core.concurrent.TrackingExecutors;
7+
import dev.faststats.core.concurrent.TrackingThreadFactory;
8+
import dev.faststats.core.concurrent.TrackingThreadPoolExecutor;
99
import org.jspecify.annotations.Nullable;
1010

11-
import java.security.PrivilegedAction;
12-
import java.security.PrivilegedExceptionAction;
1311
import java.util.ArrayList;
1412
import java.util.Arrays;
1513
import java.util.List;
1614
import java.util.Map;
17-
import java.util.Optional;
1815
import java.util.concurrent.ConcurrentHashMap;
1916

20-
public final class SimpleErrorTracker implements ErrorTracker {
17+
final class SimpleErrorTracker implements ErrorTracker {
2118
private final int stackTraceLimit = Integer.getInteger("faststats.stack-trace-limit", 15);
2219
private final Map<String, Integer> collected = new ConcurrentHashMap<>();
2320
private final Map<String, JsonObject> reports = new ConcurrentHashMap<>();
2421

22+
private final TrackingBase base = new SimpleTrackingBase(this);
2523
private final TrackingExecutors executors = new SimpleTrackingExecutors(this);
2624
private final TrackingThreadFactory threadFactory = new SimpleTrackingThreadFactory(this);
2725
private final TrackingThreadPoolExecutor threadPoolExecutor = new SimpleTrackingThreadPoolExecutor(this);
@@ -86,20 +84,20 @@ private JsonObject compile(Throwable error, @Nullable List<StackTraceElement> su
8684
"\\b(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\b";
8785
private static final String IPV6_PATTERN =
8886
"(?i)\\b([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}\\b|" + // Full form
89-
"(?i)\\b([0-9a-f]{1,4}:){1,7}:\\b|" + // Trailing ::
90-
"(?i)\\b([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}\\b|" + // :: in middle (1 group after)
91-
"(?i)\\b([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}\\b|" + // :: in middle (2 groups after)
92-
"(?i)\\b([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}\\b|" + // :: in middle (3 groups after)
93-
"(?i)\\b([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}\\b|" + // :: in middle (4 groups after)
94-
"(?i)\\b([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}\\b|" + // :: in middle (5 groups after)
95-
"(?i)\\b[0-9a-f]{1,4}:(:[0-9a-f]{1,4}){1,6}\\b|" + // :: in middle (6 groups after)
96-
"(?i)\\b:(:[0-9a-f]{1,4}){1,7}\\b|" + // Leading ::
97-
"(?i)\\b::([0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4}\\b|" + // :: at start
98-
"(?i)\\b::\\b"; // Just ::
87+
"(?i)\\b([0-9a-f]{1,4}:){1,7}:\\b|" + // Trailing ::
88+
"(?i)\\b([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}\\b|" + // :: in middle (1 group after)
89+
"(?i)\\b([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}\\b|" + // :: in middle (2 groups after)
90+
"(?i)\\b([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}\\b|" + // :: in middle (3 groups after)
91+
"(?i)\\b([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}\\b|" + // :: in middle (4 groups after)
92+
"(?i)\\b([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}\\b|" + // :: in middle (5 groups after)
93+
"(?i)\\b[0-9a-f]{1,4}:(:[0-9a-f]{1,4}){1,6}\\b|" + // :: in middle (6 groups after)
94+
"(?i)\\b:(:[0-9a-f]{1,4}){1,7}\\b|" + // Leading ::
95+
"(?i)\\b::([0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4}\\b|" + // :: at start
96+
"(?i)\\b::\\b"; // Just ::
9997
private static final String USER_HOME_PATH_PATTERN =
10098
"(/home/)[^/\\s]+" + // Linux: /home/username
101-
"|(/Users/)[^/\\s]+" + // macOS: /Users/username
102-
"|((?i)[A-Z]:\\\\Users\\\\)[^\\\\\\s]+"; // Windows: A-Z:\\Users\\username
99+
"|(/Users/)[^/\\s]+" + // macOS: /Users/username
100+
"|((?i)[A-Z]:\\\\Users\\\\)[^\\\\\\s]+"; // Windows: A-Z:\\Users\\username
103101

104102
private String anonymize(String message) {
105103
message = message.replaceAll(IPV4_PATTERN, "[IP hidden]");
@@ -110,8 +108,7 @@ private String anonymize(String message) {
110108
return message;
111109
}
112110

113-
@Override
114-
public Optional<JsonArray> getData() {
111+
public JsonArray getData() {
115112
var report = new JsonArray(reports.size());
116113

117114
reports.forEach((hash, object) -> {
@@ -132,10 +129,9 @@ public Optional<JsonArray> getData() {
132129
report.add(entry);
133130
});
134131

135-
return Optional.of(report);
132+
return report;
136133
}
137134

138-
@Override
139135
public void clear() {
140136
collected.replaceAll((k, v) -> 0);
141137
reports.clear();
@@ -152,21 +148,40 @@ public void attachErrorContext(@Nullable ClassLoader loader) {
152148
}
153149

154150
@Override
155-
public boolean isSameLoader(ClassLoader loader, Throwable error) {
156-
StackTraceElement[] stackTrace = error.getStackTrace();
151+
public TrackingBase base() {
152+
return base;
153+
}
154+
155+
@Override
156+
public TrackingExecutors executors() {
157+
return executors;
158+
}
159+
160+
@Override
161+
public TrackingThreadFactory threadFactory() {
162+
return threadFactory;
163+
}
164+
165+
@Override
166+
public TrackingThreadPoolExecutor threadPoolExecutor() {
167+
return threadPoolExecutor;
168+
}
169+
170+
private boolean isSameLoader(final ClassLoader loader, final Throwable error) {
171+
var stackTrace = error.getStackTrace();
157172
if (stackTrace == null || stackTrace.length == 0) {
158173
return false;
159174
}
160175

161-
int firstNonLibraryIndex = findFirstNonLibraryFrameIndex(stackTrace);
176+
var firstNonLibraryIndex = findFirstNonLibraryFrameIndex(stackTrace);
162177
if (firstNonLibraryIndex == -1) {
163178
return false;
164179
}
165180

166-
int framesToCheck = Math.min(5, stackTrace.length - firstNonLibraryIndex);
181+
var framesToCheck = Math.min(5, stackTrace.length - firstNonLibraryIndex);
167182

168-
for (int i = 0; i < framesToCheck; i++) {
169-
StackTraceElement frame = stackTrace[firstNonLibraryIndex + i];
183+
for (var i = 0; i < framesToCheck; i++) {
184+
var frame = stackTrace[firstNonLibraryIndex + i];
170185
if (isLibraryClass(frame.getClassName())) {
171186
continue;
172187
}
@@ -178,75 +193,24 @@ public boolean isSameLoader(ClassLoader loader, Throwable error) {
178193
return true;
179194
}
180195

181-
@Override
182-
public Runnable tracked(Runnable runnable) {
183-
return () -> {
184-
try {
185-
runnable.run();
186-
} catch (Throwable error) {
187-
trackError(error);
188-
throw error;
189-
}
190-
};
191-
}
192-
193-
@Override
194-
public <T> PrivilegedAction<T> tracked(PrivilegedAction<T> action) {
195-
return () -> {
196-
try {
197-
return action.run();
198-
} catch (Throwable error) {
199-
trackError(error);
200-
throw error;
201-
}
202-
};
203-
}
204-
205-
@Override
206-
public <T> PrivilegedExceptionAction<T> tracked(PrivilegedExceptionAction<T> action) {
207-
return () -> {
208-
try {
209-
return action.run();
210-
} catch (Throwable error) {
211-
trackError(error);
212-
throw error;
213-
}
214-
};
215-
}
216-
217-
@Override
218-
public TrackingExecutors executors() {
219-
return executors;
220-
}
221-
222-
@Override
223-
public TrackingThreadFactory threadFactory() {
224-
return threadFactory;
225-
}
226-
227-
@Override
228-
public TrackingThreadPoolExecutor threadPoolExecutor() {
229-
return threadPoolExecutor;
230-
}
231-
232-
private int findFirstNonLibraryFrameIndex(StackTraceElement[] stackTrace) {
233-
for (int i = 0; i < stackTrace.length; i++) {
196+
private int findFirstNonLibraryFrameIndex(final StackTraceElement[] stackTrace) {
197+
for (var i = 0; i < stackTrace.length; i++) {
234198
if (!isLibraryClass(stackTrace[i].getClassName())) {
235199
return i;
236200
}
237201
}
238202
return -1;
239203
}
240204

241-
private boolean isLibraryClass(String className) {
205+
private boolean isLibraryClass(final String className) {
242206
return className.startsWith("java.")
243207
|| className.startsWith("javax.")
244208
|| className.startsWith("sun.")
245209
|| className.startsWith("com.sun.")
246210
|| className.startsWith("jdk.");
247211
}
248212

249-
private boolean isFromLoader(StackTraceElement frame, ClassLoader loader) {
213+
private boolean isFromLoader(final StackTraceElement frame, final ClassLoader loader) {
250214
try {
251215
var clazz = Class.forName(frame.getClassName(), false, loader);
252216
return isSameClassLoader(clazz.getClassLoader(), loader);
@@ -255,18 +219,13 @@ private boolean isFromLoader(StackTraceElement frame, ClassLoader loader) {
255219
}
256220
}
257221

258-
private boolean isSameClassLoader(ClassLoader classLoader, ClassLoader loader) {
259-
if (classLoader == loader) {
260-
return true;
261-
}
262-
// Walk up the class loader hierarchy
263-
ClassLoader current = classLoader;
264-
while (current != null) {
265-
if (current == loader) {
266-
return true;
267-
}
222+
private boolean isSameClassLoader(final ClassLoader classLoader, final ClassLoader loader) {
223+
if (classLoader == loader) return true;
224+
225+
var current = classLoader;
226+
while (current != null && current != loader) {
268227
current = current.getParent();
269228
}
270-
return false;
229+
return loader == current;
271230
}
272231
}

0 commit comments

Comments
 (0)