Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify Error Throwing Methods #696

Closed
chacha912 opened this issue Nov 27, 2023 · 4 comments · Fixed by #878
Closed

Unify Error Throwing Methods #696

chacha912 opened this issue Nov 27, 2023 · 4 comments · Fixed by #878
Assignees
Labels
cleanup 🧹 Paying off technical debt good first issue 🐤 Good for newcomers

Comments

@chacha912
Copy link
Contributor

chacha912 commented Nov 27, 2023

Description:

yorkie-js-sdk uses three different methods for throwing errors. Let's pick one way to throw errors and apply it consistently across yorkie-js-sdk. If there's a better approach, let's talk about it and see if we should switch.

  1. throw new Error:

Generates a general error using the throw statement. This is a straightforward and common method.

throw new Error('This is a general error.');
  1. throw new YorkieError:

Creates and throws a custom error, allowing additional information to be added to the error. This approach helps identify errors originating from Yorkie, provides a way to categorize errors, but requires handling of error codes for each case.

// src/util/error.ts
export class YorkieError extends Error {
  name = 'YorkieError';
  stack?: string;

  constructor(readonly code: Code, readonly message: string) {
    super(message);
    this.toString = (): string =>
      `${this.name}: [code=${this.code}]: ${this.message}`;
  }
}

throw new YorkieError(Code.Unsupported, `unsupported type: ${valueType}`);
  1. logger.fatal():

Records errors using the logger with the fatal level. Different processing can be applied based on log levels.

// src/util/logger.ts
export const logger = {
  fatal: (message: string, ...messages: Array<unknown>): void => {
    if (typeof console !== 'undefined') {
      if (typeof console.error !== 'undefined') {
        console.error('YORKIE F:', ...messages);
      } else {
        console.log('YORKIE F:', ...messages);
      }
    }

    throw new Error(`YORKIE F: ${message}`);
  },
  // Other log levels like debug, info, warn, error, etc.
};

logger.fatal('This is a fatal error.');

However, note that an error is being thrown within logger.fatal. This can lead to TypeScript errors when using the method, as showed in the example below:

// operation.ts
public getExecutedAt(): TimeTicket {
  if (!this.executedAt) {
    logger.fatal(`executedAt has not been set yet`);
  }
  return this.executedAt; 
// ^^^^^^  Type 'TimeTicket | undefined' is not assignable to type 'TimeTicket'.
}

Why:

Standardize the error-throwing approach for consistency and clarity throughout the yorkie-js-sdk codebase.

@gwbaik9717
Copy link
Contributor

Could I give it a try?

@krapie
Copy link
Member

krapie commented Jul 19, 2024

@gwbaik9717 Sure! Happy to see you doing error related issues again.

@devleejb devleejb moved this from Backlog to In progress in Yorkie Project - 2024 Jul 20, 2024
@gwbaik9717
Copy link
Contributor

gwbaik9717 commented Jul 20, 2024

I'm considering solving this issue as follows. Please review and let me know if it is alright.

I believe the logger should focus solely on logging and not be responsible for throwing errors.

I suggest logging messages with the logger and throwing errors separately using YorkieError.

After the change, there will be only two scenarios in logging and throwing errors:

  1. Case when only throwing Error
throw new YorkieError(Code.ErrInvalidArgument, ERROR_MESSAGE);
  1. Case when logging and throwing Error
// src/util/logger.ts
export const logger = {
  fatal: (message: string, ...messages: Array<unknown>): void => {
    if (typeof console !== 'undefined') {
      if (typeof console.error !== 'undefined') {
        console.error('YORKIE F:', ...messages);
      } else {
        console.log('YORKIE F:', ...messages);
      }
    }
     // throw error removed
  },
  // Other log levels like debug, info, warn, error, etc.
};

const ERROR_MESSASGE = "This is a fatal error."

logger.fatal(ERROR_MESSAGE);
throw new YorkieError(Code.ErrInvalidArgument, ERROR_MESSAGE);

In this way, I believe we can solve the type problem of the logger function, and users will be explicitly aware when an error is being thrown.

@chacha912

@chacha912
Copy link
Contributor Author

@gwbaik9717 Thank you for sharing your approach. As you suggested, it would be good to standardize the error handling by throwing custom YorkieError, and use the logger for all logging purposes.

cc. @hackerwins

@github-project-automation github-project-automation bot moved this from In progress to Done in Yorkie Project - 2024 Aug 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cleanup 🧹 Paying off technical debt good first issue 🐤 Good for newcomers
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants