Ensure all unhandled exceptions are recorded on the server

Change-Id: Ib88236d1fed67a9c482be639d15fd7586375482c
Reviewed-on: https://dart-review.googlesource.com/c/89510
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Danny Tuppeny <dantup@google.com>
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index 925e5a7..9ae9716 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -299,16 +299,14 @@
                 errorMessage,
                 null,
               ));
-          logError(error.toString());
-          if (stackTrace != null) {
-            logError(stackTrace.toString());
-          }
+          logException(errorMessage, error, stackTrace);
         }
       });
     }, onError: error);
   }
 
-  void logError(String message) {
+  /// Logs the error on the client using window/logMessage.
+  void logErrorToClient(String message) {
     channel.sendNotification(new NotificationMessage(
       Method.window_logMessage,
       new LogMessageParams(MessageType.Error, message),
@@ -341,7 +339,7 @@
           new ResponseMessage(message.id, null, error, jsonRpcVersion));
       // Since the LSP client might not show the failed requests to the user,
       // also ensure the error is logged to the client.
-      logError(error.message);
+      logErrorToClient(error.message);
     } else if (message is ResponseMessage) {
       // For bad response messages where we can't respond with an error, send it as
       // show instead of log.
@@ -359,7 +357,7 @@
       messageHandler = new FailureStateMessageHandler(this);
 
       final message = 'An unrecoverable error occurred.';
-      logError(
+      logErrorToClient(
           '$message\n\n${error.message}\n\n${error.code}\n\n${error.data}');
 
       shutdown();
@@ -399,24 +397,28 @@
   }
 
   void sendServerErrorNotification(String message, exception, stackTrace) {
-    final fullError = new StringBuffer();
-
-    fullError.writeln(exception == null ? message : '$message: $exception');
+    message = exception == null ? message : '$message: $exception';
 
     // Show message (without stack) to the user.
-    showError(fullError.toString());
+    showError(message);
 
-    if (stackTrace != null) {
-      fullError.writeln(stackTrace.toString());
-    }
-    // Log the full message since showMessage above may be truncated or formatted
-    // badly (eg. VS Code takes the newlines out).
-    logError(fullError.toString());
+    logException(message, exception, stackTrace);
+  }
 
-    // remember the last few exceptions
+  /// Logs an exception by sending it to the client (window/logMessage) and
+  /// recording it in a buffer on the server for diagnostics.
+  void logException(String message, exception, stackTrace) {
     if (exception is CaughtException) {
       stackTrace ??= exception.stackTrace;
     }
+
+    final fullError = stackTrace == null ? message : '$message\n$stackTrace';
+
+    // Log the full message since showMessage above may be truncated or formatted
+    // badly (eg. VS Code takes the newlines out).
+    logErrorToClient(fullError);
+
+    // remember the last few exceptions
     exceptions.add(new ServerException(
       message,
       exception,