Skip to content

Standardize language server errors#1429

Merged
JakeDawkins merged 12 commits intomasterfrom
jake/lsp-error-rewrites
Aug 15, 2019
Merged

Standardize language server errors#1429
JakeDawkins merged 12 commits intomasterfrom
jake/lsp-error-rewrites

Conversation

@JakeDawkins
Copy link
Copy Markdown
Contributor

@JakeDawkins JakeDawkins commented Jul 21, 2019

This PR is the result of investigating how other implementations of the language server protocol handle errors. This PR proposes a new method of handling errors across the language server and its clients (CLI and VS Code). The intention is to produce a more friendly, consistent means by which to log and handle errors, as well as set up the basis for what could become usage metrics and error reporting, and verbose/debugging modes.

The state of things now

  • No control over the granularity of errors (full stack traces, all the time)
  • No proper debug logging mechanism to help trace errors.
  • No standard way to log normal messages/warnings from the language server to all clients
  • Most language server implementations don't fail out. If an operation is unsuccessful, they just return an empty response, and the clients are responsible for messaging.

Proposed Solution

At a high level, this method would eliminate the throwing of errors, and rely on errors being passed as notifications from the server to a client. For clients that don't consume the language server from a JSON-RPC transport (like the CLI which calls it directly) the server's error mechanism would be to fallback to console logs and errors.

The key with this proposal is to ONLY have graceful exits from the language server. If something goes wrong, we should explain the error and do nothing. Failing out completely isn't great. Instead, clients should see empty responses from failed operations.

  • Debug classes on the client (vscode) and server.
    • The client Debug class is responsible for printing errors, warnings, and info messages to the console. In CLI projects (or where connection is unavailable), the util will log/warn/error to the console
    • The server Debug class is responsible to notifying the client of errors via the serverDebugMessage notification.
    • There is a serverDebugMessage notification handler on the client which calls the Debug class methods to display errors to the user.
  • How do we support stack traces for these errors?
    • By default, for Errors only (not warnings/info logs) we print the top 4 frames of the stack trace. We do this by creating a stack inside the Debug module and removing the unhelpful frames.
    • The server sends these frames as a string to the client, which can print it.
    • On the client, if a stack trace hasn't been provided (like if the client itself was erroring), we create a stack trace in the Debug module of the client.
    • I think we can support a -v or --verbose mode where errors and warnings have traces. We could also make sure those stacks aren't trimmed.
    • The [INFO] messages could be used for debug logs (maybe)

Notes:

  • Inspiration from the Crane Server implementation.
    • One of the few LSP implementations that explicitly handle errors along with returning empty responses.
  • This will also give us consistent hooks for printing debug info when in a debug mode (not implemented) and telemetry (not implemented).

Future work:

  • the language server's Debug class could allow for a separate error logger to be passed to it to support arbitrary clients that can't support console logs or notifications from the LSP, but since we don't have any clients for that, we shouldn't worry about that right now.

TODO:

  • Update CHANGELOG.md* with your change (include reference to issue & this PR)
  • Make sure all of the significant new logic is covered by tests
  • Rebase your changes on master so that they can be merged easily
  • Make sure all tests and linter rules pass

*Make sure changelog entries note which project(s) has been affected. See older entries for examples on what this looks like.

@JakeDawkins JakeDawkins marked this pull request as ready for review August 9, 2019 02:17
@JakeDawkins JakeDawkins added 🔨 cli related to the CLI itself 🗒️ component - vscode related to editor tooling ❌ errors labels Aug 9, 2019
@JakeDawkins JakeDawkins force-pushed the jake/lsp-error-rewrites branch from 4d58675 to 376ae1b Compare August 9, 2019 18:55
export class Debug {
public static connection?: IConnection;

public static SetConnection(conn: IConnection) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using a static connection in this class, so we don't need to instantiate the class and pass it around anywhere. The user can just import the class itself from this file and call it without worrying like in the workspace file below

...existingProjects,
...projectsForConfig
]);
if (config) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to modify this file to be able to accept null configs. Since the new pattern would be to return empty responses and log errors, we'll need to do this a few places.

Comment thread packages/apollo/src/Command.ts Outdated

const config = await this.createConfig(flags);

if (!config) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Configs can now be null :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use this.error and this.exit here because there aren't many errors that happen in the CLI itself. Most happen in the language server.

Errors in the CLI already have a handler (this.error) and should return with an exit code of 1. We could wrap this up into another function but I don't see the need yet.

// for errors (and other logs in debug mode) we want to print a stack trace showing where they were thrown.
// This uses an Error's stack trace, removes the three frames regarding this file (since they're useless) and
// returns the rest of the trace.
const createAndTrimStackTrace = () => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay that this logic is duplicated in multiple places (language server and here) since it's only one thing, we don't have a great place to put it, and we could change the formatting based on where it is later.

*/
public static error(message: string, stack?: string) {
const stackTrace = stack || createAndTrimStackTrace();
Debug.showConsole();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since errors are urgent, show the output window

* TODO: enable error reporting and telemetry
* Displays and warning message prefixed with [WARN]
*/
// public static sendErrorTelemetry(message: string) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use this to track errors that are thrown in sentry/google analytics/something else.

Copy link
Copy Markdown
Contributor

@trevor-scheer trevor-scheer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, non-blocking stuff! Can't wait to see the future of error handling :)

Comment thread packages/apollo-language-server/src/utilities/debug.ts Outdated
Comment thread packages/apollo/src/Command.ts Outdated
Comment thread packages/vscode-apollo/src/debug.ts
Comment thread packages/vscode-apollo/src/debug.ts
@JakeDawkins JakeDawkins force-pushed the jake/lsp-error-rewrites branch from 87c6c8f to b466e70 Compare August 15, 2019 13:46
@JakeDawkins JakeDawkins merged commit 400bf99 into master Aug 15, 2019
@JakeDawkins JakeDawkins deleted the jake/lsp-error-rewrites branch August 15, 2019 15:08
essaji pushed a commit to essaji/apollo-tooling that referenced this pull request Aug 25, 2019
* Add debug logging utils for language server and vs code
* Add stack traces to debug errors
* moved config loader to use debug util
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 cli related to the CLI itself 🗒️ component - vscode related to editor tooling ❌ errors

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants