Skip to content

Commit 910ce2e

Browse files
authored
Merge pull request #2120 from digma-ai/improve-auth-logging
reorder log messages in auth package
2 parents 3df4877 + 3b9e582 commit 910ce2e

File tree

8 files changed

+143
-118
lines changed

8 files changed

+143
-118
lines changed

ide-common/src/main/java/org/digma/intellij/plugin/log/Log.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
import com.intellij.openapi.diagnostic.Logger;
44
import com.intellij.openapi.project.Project;
5-
import org.digma.intellij.plugin.errorreporting.ErrorReporter;
5+
import org.digma.intellij.plugin.errorreporting.*;
66

7+
import java.time.Duration;
78
import java.util.function.Consumer;
89

910
/**
@@ -15,6 +16,11 @@ public class Log {
1516
public static final String DIGMA = "Digma: ";
1617
public static final String DIGMA_PROJECT = "Digma: Project:";
1718

19+
20+
private static final FrequentErrorDetector FREQUENT_ERROR_DETECTOR = new FrequentErrorDetector(Duration.ofMinutes(10));
21+
22+
23+
1824
public static void log(Consumer<String> consumer, Project project, String format, Object... args) {
1925
consumer.accept(DIGMA_PROJECT + project.getName() + ": " + String.format(format.replace("{}", "%s"), args));
2026
}
@@ -24,18 +30,31 @@ public static void log(Consumer<String> consumer, String format, Object... args)
2430
}
2531

2632
public static void debugWithException(Logger logger,Throwable e, String format, Object... args) {
33+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(format, e)) {
34+
return;
35+
}
36+
2737
logger.debug(DIGMA + String.format(format.replace("{}", "%s"), args),e);
2838
}
2939

3040
public static void debugWithException(Logger logger, Project project,Throwable e, String format, Object... args) {
41+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(format, e)) {
42+
return;
43+
}
3144
logger.debug(DIGMA_PROJECT + project.getName() + ": " + String.format(format.replace("{}", "%s"), args),e);
3245
}
3346

3447
public static void warnWithException(Logger logger,Throwable e, String format, Object... args) {
48+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(format, e)) {
49+
return;
50+
}
3551
logger.warn(DIGMA + String.format(format.replace("{}", "%s"), args),e);
3652
}
3753

3854
public static void warnWithException(Logger logger, Project project,Throwable e, String format, Object... args) {
55+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(format, e)) {
56+
return;
57+
}
3958
logger.warn(DIGMA_PROJECT + project.getName() + ": " + String.format(format.replace("{}", "%s"), args),e);
4059
}
4160

@@ -49,15 +68,25 @@ public static void log(Consumer<String> consumer, String msg) {
4968
}
5069

5170
public static void error(Logger logger,Project project, Exception exception, String format, Object... args) {
71+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(format, exception)) {
72+
return;
73+
}
74+
5275
var msg = String.format(format.replace("{}", "%s"), args);
5376
error(logger, exception, DIGMA_PROJECT + project.getName() + ": " + msg);
5477
ErrorReporter.getInstance().reportError(project, "Log.error", exception);
5578
}
5679
public static void error(Logger logger, Exception exception, String format, Object... args) {
80+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(format, exception)) {
81+
return;
82+
}
5783
error(logger, exception, DIGMA + String.format(format.replace("{}", "%s"), args));
5884
}
5985

6086
public static void error(Logger logger, Exception exception, String msg) {
87+
if (FREQUENT_ERROR_DETECTOR.isTooFrequentException(msg, exception)) {
88+
return;
89+
}
6190
logger.error(DIGMA + msg, exception);
6291
}
6392

ide-common/src/main/kotlin/org/digma/intellij/plugin/auth/AbstractLoginHandler.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ abstract class AbstractLoginHandler(protected val analyticsProvider: RestAnalyti
4949

5050
} catch (e: Throwable) {
5151

52+
Log.warnWithException(logger, e, "Exception in login {}, url {}", e, analyticsProvider.apiUrl)
53+
ErrorReporter.getInstance().reportError("AuthManager.login", e)
54+
5255
if (e is AuthenticationException) {
5356
Log.warnWithException(logger, e, "Exception in login, url {}", analyticsProvider.apiUrl)
5457
ErrorReporter.getInstance().reportError("AuthManager.login", e)
@@ -58,11 +61,6 @@ abstract class AbstractLoginHandler(protected val analyticsProvider: RestAnalyti
5861
LoginResult(false, null, e.detailedMessage)
5962
}
6063

61-
//don't report connection errors, there is no point, backend may be down and that happens a lot
62-
if (!ExceptionUtils.isAnyConnectionException(e)) {
63-
Log.warnWithException(logger, e, "Exception in login, url {}", analyticsProvider.apiUrl)
64-
ErrorReporter.getInstance().reportError("AuthManager.login", e)
65-
}
6664

6765
LoginResult(false, null, ExceptionUtils.getNonEmptyMessage(e))
6866

ide-common/src/main/kotlin/org/digma/intellij/plugin/auth/AuthManager.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ class AuthManager : Disposable {
202202

203203
} catch (e: InvocationTargetException) {
204204

205-
Log.debugWithException(logger, e, "Exception in auth proxy {}", ExceptionUtils.getNonEmptyMessage(e))
205+
Log.warnWithException(logger, e, "Exception in auth proxy {}", ExceptionUtils.getNonEmptyMessage(e))
206+
206207
if (isPaused.get()) {
207208
Log.log(logger::trace, "got Exception in auth proxy but proxy is paused, rethrowing")
208209
throw e

ide-common/src/main/kotlin/org/digma/intellij/plugin/auth/CentralizedLoginHandler.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class CentralizedLoginHandler(analyticsProvider: RestAnalyticsProvider) : Abstra
9393
}
9494
} catch (e: Throwable) {
9595

96+
Log.warnWithException(logger, e, "Exception in loginOrRefresh {}, url {}", e, analyticsProvider.apiUrl)
97+
ErrorReporter.getInstance().reportError("AuthManager.loginOrRefresh", e)
98+
9699
//if got AuthenticationException here is may be from refresh or login, in both cases delete the current account,
97100
//and user will be redirected to log in again
98101
if (e is AuthenticationException) {
@@ -101,12 +104,6 @@ class CentralizedLoginHandler(analyticsProvider: RestAnalyticsProvider) : Abstra
101104
logout()
102105
}
103106

104-
//don't report connection errors, there is no point, backend may be down and that happens a lot
105-
if (!ExceptionUtils.isAnyConnectionException(e)) {
106-
Log.warnWithException(logger, e, "Exception in loginOrRefresh, url {}", analyticsProvider.apiUrl)
107-
ErrorReporter.getInstance().reportError("AuthManager.loginOrRefresh", e)
108-
}
109-
110107
false
111108
}
112109

ide-common/src/main/kotlin/org/digma/intellij/plugin/auth/LocalLoginHandler.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ class LocalLoginHandler(analyticsProvider: RestAnalyticsProvider) : AbstractLogi
113113
}
114114
} catch (e: Throwable) {
115115

116+
Log.warnWithException(logger, e, "Exception in loginOrRefresh {}, url {}", e, analyticsProvider.apiUrl)
117+
ErrorReporter.getInstance().reportError("AuthManager.loginOrRefresh", e)
118+
116119
//if got AuthenticationException here is may be from refresh or login, in both cases delete the current account,
117120
//and we'll do silent login on the next loginOrRefresh
118121
if (e is AuthenticationException) {
@@ -121,12 +124,6 @@ class LocalLoginHandler(analyticsProvider: RestAnalyticsProvider) : AbstractLogi
121124
logout()
122125
}
123126

124-
//don't report connection errors, there is no point, backend may be down and that happens a lot
125-
if (!ExceptionUtils.isAnyConnectionException(e)) {
126-
Log.warnWithException(logger, e, "Exception in loginOrRefresh, url {}", analyticsProvider.apiUrl)
127-
ErrorReporter.getInstance().reportError("AuthManager.loginOrRefresh", e)
128-
}
129-
130127
false
131128
}
132129

ide-common/src/main/kotlin/org/digma/intellij/plugin/auth/LoginHandler.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import kotlinx.coroutines.runBlocking
55
import org.digma.intellij.plugin.analytics.RestAnalyticsProvider
66
import org.digma.intellij.plugin.auth.account.DigmaAccountManager
77
import org.digma.intellij.plugin.auth.account.DigmaDefaultAccountHolder
8-
import org.digma.intellij.plugin.common.ExceptionUtils
98
import org.digma.intellij.plugin.errorreporting.ErrorReporter
109
import org.digma.intellij.plugin.log.Log
1110

@@ -30,13 +29,10 @@ interface LoginHandler {
3029

3130
} catch (e: Throwable) {
3231

33-
//don't report connection errors, there is no point, backend may be down and that happens a lot
34-
if (!ExceptionUtils.isAnyConnectionException(e)) {
35-
Log.warnWithException(logger, e, "Exception in createLoginHandler, url {}", analyticsProvider?.apiUrl)
36-
ErrorReporter.getInstance().reportError("AuthManager.createLoginHandler", e)
37-
}
32+
Log.warnWithException(logger, e, "Exception in createLoginHandler {}, url {}", e, analyticsProvider?.apiUrl)
33+
ErrorReporter.getInstance().reportError("AuthManager.createLoginHandler", e)
3834

39-
Log.log(logger::trace, "Exception in createLoginHandler , returning NoOpLoginHandler", analyticsProvider?.apiUrl)
35+
Log.log(logger::trace, "Got exception in createLoginHandler , returning NoOpLoginHandler", analyticsProvider?.apiUrl)
4036
NoOpLoginHandler("error in createLoginHandler $e")
4137
}
4238
}
@@ -75,7 +71,7 @@ interface LoginHandler {
7571
digmaAccount != null
7672

7773
} catch (e: Throwable) {
78-
Log.warnWithException(logger, e, "Exception in logout")
74+
Log.warnWithException(logger, e, "Exception in logout {}", e)
7975
ErrorReporter.getInstance().reportError("AuthManager.logout", e)
8076
false
8177
}

ide-common/src/main/kotlin/org/digma/intellij/plugin/errorreporting/ErrorReporter.kt

Lines changed: 11 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package org.digma.intellij.plugin.errorreporting
22

3-
import com.github.benmanes.caffeine.cache.Caffeine
43
import com.intellij.openapi.components.service
54
import com.intellij.openapi.diagnostic.Logger
6-
import com.intellij.openapi.diagnostic.UntraceableException
75
import com.intellij.openapi.project.Project
86
import net.bytebuddy.ByteBuddy
97
import net.bytebuddy.description.method.MethodDescription
@@ -16,9 +14,8 @@ import org.digma.intellij.plugin.common.ExceptionUtils
1614
import org.digma.intellij.plugin.common.findActiveProject
1715
import org.digma.intellij.plugin.log.Log
1816
import org.digma.intellij.plugin.posthog.ActivityMonitor
19-
import java.lang.reflect.Field
20-
import java.util.concurrent.TimeUnit
21-
import java.util.concurrent.atomic.AtomicInteger
17+
import kotlin.time.Duration.Companion.minutes
18+
import kotlin.time.toJavaDuration
2219

2320

2421
const val SEVERITY_PROP_NAME = "severity"
@@ -31,6 +28,7 @@ open class ErrorReporter {
3128

3229
private val logger: Logger = Logger.getInstance(this::class.java)
3330

31+
private val frequentErrorDetector = FrequentErrorDetector(60.minutes.toJavaDuration())
3432

3533
//must be public class
3634
class MyPauseInterceptor {
@@ -120,7 +118,7 @@ open class ErrorReporter {
120118
open fun reportError(project: Project?, message: String, action: String, details: Map<String, String>) {
121119

122120

123-
if (isTooFrequentError(message, action)) {
121+
if (frequentErrorDetector.isTooFrequentError(message, action)) {
124122
return
125123
}
126124

@@ -147,11 +145,11 @@ open class ErrorReporter {
147145
//many times the exception is no-connection exception, and that may happen too many times.
148146
// when there is no connection all timers will get an AnalyticsService exception every 10 seconds, it's useless
149147
//to report that. AnalyticsService exceptions are reported separately and will include no-connection exceptions.
150-
if (ExceptionUtils.isConnectionException(throwable) || throwable is NoSelectedEnvironmentException) {
148+
if (ExceptionUtils.isAnyConnectionException(throwable) || throwable is NoSelectedEnvironmentException) {
151149
return
152150
}
153151

154-
if (isTooFrequentException(message, throwable)) {
152+
if (frequentErrorDetector.isTooFrequentException(message, throwable)) {
155153
return
156154
}
157155

@@ -196,7 +194,7 @@ open class ErrorReporter {
196194
) {
197195

198196
try {
199-
if (isTooFrequentException(message, exception)) {
197+
if (frequentErrorDetector.isTooFrequentException(message, exception)) {
200198
return
201199
}
202200

@@ -219,7 +217,7 @@ open class ErrorReporter {
219217
}
220218

221219
open fun reportBackendError(project: Project?, message: String, action: String) {
222-
if (isTooFrequentBackendError(message, action)) {
220+
if (frequentErrorDetector.isTooFrequentError(message, action)) {
223221
return
224222
}
225223

@@ -238,97 +236,19 @@ open class ErrorReporter {
238236
reportInternalFatalError(projectToUse, source, exception, details)
239237
}
240238

241-
//this error should be reported only when its a fatal error that we must fix quickly.
239+
//this error should be reported only when it's a fatal error that we must fix quickly.
242240
//don't use it for all errors.
243241
//currently will be reported from EDT.assertNonDispatchThread and ReadActions.assertNotInReadAccess
244242
// which usually should be caught in development but if not, are very urgent to fix.
245-
// if the error is not a result of an exception create a new RuntimeException and send it so we have the stack trace.
243+
// if the error is not a result of an exception create a new RuntimeException and send it, so we have the stack trace.
246244
open fun reportInternalFatalError(project: Project, source: String, exception: Throwable, details: Map<String, String> = mapOf()) {
247245

248-
if (isTooFrequentException(source, exception)) {
246+
if (frequentErrorDetector.isTooFrequentException(source, exception)) {
249247
return
250248
}
251249

252250
ActivityMonitor.getInstance(project).registerInternalFatalError(source, exception, details)
253251

254252
}
255253

256-
257-
258-
private fun isTooFrequentBackendError(message: String, action: String): Boolean {
259-
val counter = MyCache.getOrCreate(message, action)
260-
val occurrences = counter.incrementAndGet()
261-
return occurrences > 1
262-
}
263-
264-
private fun isTooFrequentError(message: String, action: String): Boolean {
265-
val counter = MyCache.getOrCreate(message, action)
266-
val occurrences = counter.incrementAndGet()
267-
return occurrences > 1
268-
}
269-
270-
271-
private fun isTooFrequentException(message: String, t: Throwable): Boolean {
272-
val hash = computeAccurateTraceHashCode(t)
273-
val counter = MyCache.getOrCreate(hash, t, message)
274-
val occurrences = counter.incrementAndGet()
275-
return occurrences > 1
276-
}
277-
278-
279-
private fun computeAccurateTraceHashCode(throwable: Throwable): Int {
280-
val backtrace = getBacktrace(throwable)
281-
if (backtrace == null) {
282-
val trace = if (throwable is UntraceableException) null else throwable.stackTrace
283-
return trace.contentHashCode()
284-
}
285-
return backtrace.contentDeepHashCode()
286-
}
287-
288-
289-
private fun getBacktrace(throwable: Throwable): Array<Any>? {
290-
291-
val backtrace = try {
292-
293-
val backtraceField: Field? = com.intellij.util.ReflectionUtil.getDeclaredField(Throwable::class.java, "backtrace")
294-
if (backtraceField != null) {
295-
backtraceField.isAccessible = true
296-
backtraceField.get(throwable)
297-
} else {
298-
null
299-
}
300-
301-
} catch (e: Throwable) {
302-
null
303-
}
304-
305-
if (backtrace != null && backtrace is Array<*> && backtrace.isArrayOf<Any>()) {
306-
@Suppress("UNCHECKED_CAST")
307-
return backtrace as Array<Any>
308-
}
309-
310-
return null
311-
312-
}
313-
314-
}
315-
316-
317-
private object MyCache {
318-
private val cache = Caffeine.newBuilder()
319-
.maximumSize(1000)
320-
//expireAfterAccess means we don't send the error as long as it keeps occurring until it is quite for this time,
321-
//if it reappears send it again
322-
// .expireAfterAccess(10, TimeUnit.MINUTES)
323-
//expireAfterWrite mean the error will be sent in fixed intervals.
324-
.expireAfterWrite(24, TimeUnit.HOURS)
325-
.build<String, AtomicInteger>()
326-
327-
fun getOrCreate(hash: Int, t: Throwable, message: String): AtomicInteger {
328-
return cache.get("$hash:$t:$message") { AtomicInteger() }
329-
}
330-
331-
fun getOrCreate(message: String, action: String): AtomicInteger {
332-
return cache.get("$message:$action") { AtomicInteger() }
333-
}
334254
}

0 commit comments

Comments
 (0)