|
8 | 8 | import net.bytebuddy.dynamic.DynamicType; |
9 | 9 | import net.bytebuddy.utility.JavaModule; |
10 | 10 |
|
| 11 | +import javax.annotation.concurrent.ThreadSafe; |
11 | 12 | import java.time.Duration; |
| 13 | +import java.util.ArrayList; |
| 14 | +import java.util.List; |
12 | 15 | import java.util.concurrent.Executors; |
13 | 16 | import java.util.concurrent.ScheduledExecutorService; |
14 | 17 | import java.util.concurrent.TimeUnit; |
| 18 | +import java.util.concurrent.atomic.AtomicReference; |
15 | 19 |
|
16 | | -import static com.ulyp.core.util.LoggingSettings.LOG_LEVEL_PROPERTY; |
17 | | - |
| 20 | +/** |
| 21 | + * A listener which is called by byte-buddy library |
| 22 | + */ |
18 | 23 | @Slf4j |
19 | 24 | public class InstrumentationListener implements AgentBuilder.Listener { |
20 | 25 |
|
21 | 26 | private static final Duration ERROR_DUMP_INTERVAL = Duration.ofSeconds(10); |
22 | 27 |
|
23 | | - private final InstrumentationErrors errors = new InstrumentationErrors(); |
| 28 | + private final AtomicReference<Errors> errors = new AtomicReference<>(new Errors()); |
24 | 29 |
|
25 | 30 | public InstrumentationListener() { |
26 | 31 | ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor( |
@@ -62,37 +67,54 @@ public void onError(String typeName, ClassLoader classLoader, JavaModule module, |
62 | 67 | if (LoggingSettings.DEBUG_ENABLED) { |
63 | 68 | log.debug("Failed to instrument class " + typeName, throwable); |
64 | 69 | } |
65 | | - errors.onError(); |
| 70 | + errors.get().onError(typeName, throwable); |
| 71 | + } |
| 72 | + |
| 73 | + @Override |
| 74 | + public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) { |
| 75 | + |
66 | 76 | } |
67 | 77 |
|
68 | 78 | private void dumpErrors() { |
69 | | - long errorCount = errors.getErrorCountAndReset(); |
70 | | - if (errorCount > 0) { |
71 | | - log.info("There were {} instrumentation errors. You may want to enable " + |
72 | | - "debug log level using {} system prop to check actual errors", |
73 | | - errorCount, |
74 | | - LOG_LEVEL_PROPERTY |
75 | | - ); |
| 79 | + Errors errors = this.errors.getAndSet(new Errors()); |
| 80 | + if (!errors.isEmpty()) { |
| 81 | + log.info("There were {} instrumentation errors. Some types: {}", errors.errorsCount, errors.errors); |
76 | 82 | } |
77 | 83 | } |
78 | 84 |
|
79 | | - @Override |
80 | | - public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) { |
| 85 | + private static class Error { |
| 86 | + |
| 87 | + private final String typeName; |
| 88 | + private final Throwable throwable; |
| 89 | + |
| 90 | + private Error(String typeName, Throwable throwable) { |
| 91 | + this.typeName = typeName; |
| 92 | + this.throwable = throwable; |
| 93 | + } |
81 | 94 |
|
| 95 | + @Override |
| 96 | + public String toString() { |
| 97 | + return "Error{typeName='" + typeName + '\'' + ", throwable=" + throwable + '}'; |
| 98 | + } |
82 | 99 | } |
83 | 100 |
|
84 | | - private static class InstrumentationErrors { |
| 101 | + @ThreadSafe |
| 102 | + private static class Errors { |
| 103 | + |
| 104 | + private static final int MAX_ERRORS_STORED = 10; |
85 | 105 |
|
86 | | - private long errorsCount = 0L; |
| 106 | + private int errorsCount; |
| 107 | + private final List<Error> errors = new ArrayList<>(); |
87 | 108 |
|
88 | | - public synchronized void onError() { |
| 109 | + public synchronized void onError(String typeName, Throwable throwable) { |
89 | 110 | errorsCount++; |
| 111 | + if (errors.size() < MAX_ERRORS_STORED) { |
| 112 | + errors.add(new Error(typeName, throwable)); |
| 113 | + } |
90 | 114 | } |
91 | 115 |
|
92 | | - public synchronized long getErrorCountAndReset() { |
93 | | - long value = errorsCount; |
94 | | - errorsCount = 0L; |
95 | | - return value; |
| 116 | + public boolean isEmpty() { |
| 117 | + return errorsCount == 0; |
96 | 118 | } |
97 | 119 | } |
98 | 120 | } |
0 commit comments