diff --git a/changelog.txt b/changelog.txt index 5ff0d920bb..2e33cb6cb6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ vNext ---------- - [MINOR] Launch browser when a switch_browser redirect URL is present (#2553) - [MINOR] Add Broker Webapps operations api. (#2579) +- [PATCH] Add Telemetry for OOM Errors (#2567) Version 20.0.0 ---------- diff --git a/common4j/src/main/com/microsoft/identity/common/java/controllers/ExceptionAdapter.java b/common4j/src/main/com/microsoft/identity/common/java/controllers/ExceptionAdapter.java index fa5e2b4755..283c90db1c 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/controllers/ExceptionAdapter.java +++ b/common4j/src/main/com/microsoft/identity/common/java/controllers/ExceptionAdapter.java @@ -41,6 +41,8 @@ import com.microsoft.identity.common.java.exception.UserCancelException; import com.microsoft.identity.common.java.logging.Logger; import com.microsoft.identity.common.java.net.HttpResponse; +import com.microsoft.identity.common.java.opentelemetry.AttributeName; +import com.microsoft.identity.common.java.opentelemetry.SpanExtension; import com.microsoft.identity.common.java.providers.microsoft.MicrosoftAuthorizationErrorResponse; import com.microsoft.identity.common.java.providers.oauth2.AuthorizationErrorResponse; import com.microsoft.identity.common.java.providers.oauth2.AuthorizationResult; @@ -66,6 +68,7 @@ import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.opentelemetry.api.trace.Span; import lombok.NonNull; public class ExceptionAdapter { @@ -436,6 +439,26 @@ public static ClientException clientExceptionFromException(@NonNull final Throwa if (e instanceof OutOfMemoryError) { + Logger.warn(TAG, + "Received an out of memory error, attempting to attach stacktrace..."); + + // Record exception stacktrace in span + // Put this in a try-catch in case this telemetry emit attempt causes another OOM + try { + final Span currentSpan = SpanExtension.current(); + + // Attach the stack trace, to debug in telemetry later + currentSpan.setAttribute( + AttributeName.out_of_memory_exception_stacktrace.name(), + StringUtil.getStacktraceAsStringFromElementArray(e.getStackTrace()) + ); + + Logger.warn(TAG, + "Received an out of memory error, stacktrace attached to span with id: " + currentSpan.getSpanContext().getSpanId()); + } catch (Throwable throwable) { + Logger.warn(TAG, "Failed to emit telemetry for out of memory exception."); + } + return new ClientException( ClientException.OUT_OF_MEMORY, e.getMessage(), diff --git a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java index 5c5895efb7..ef872ec09f 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java +++ b/common4j/src/main/com/microsoft/identity/common/java/opentelemetry/AttributeName.java @@ -328,5 +328,10 @@ public enum AttributeName { /** * Indicates the exception in generating a keypair. */ - keypair_gen_exception + keypair_gen_exception, + + /** + * Records the stacktrace for an out-of-memory exception. + */ + out_of_memory_exception_stacktrace } diff --git a/common4j/src/main/com/microsoft/identity/common/java/util/StringUtil.java b/common4j/src/main/com/microsoft/identity/common/java/util/StringUtil.java index 3f63ea3871..3aaed6bd97 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/util/StringUtil.java +++ b/common4j/src/main/com/microsoft/identity/common/java/util/StringUtil.java @@ -463,4 +463,21 @@ public static void overwriteWithNull(final char[] chars) { chars[i] = '\0'; } } + + /** + * Converts an array of stack trace elements (one method invocation) into one concatenated string object. + * + * @param elements list of stacktrace elements + * @return concatenated string list + */ + @NonNull + public static String getStacktraceAsStringFromElementArray(@NonNull final StackTraceElement[] elements) { + final StringBuilder builder = new StringBuilder(); + builder.append(elements[0]); + for (int i = 1; i < elements.length; i++) { + builder.append("\n"); + builder.append(elements[i]); + } + return builder.toString(); + } }