Add stringifyError that is useful for logging e.g. Something went wrong, ${stringifyError(e)}
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/errors': patch
|
||||
---
|
||||
|
||||
Add `stringifyError` that is useful for logging e.g. `Something went wrong, ${stringifyError(e)}`
|
||||
@@ -18,7 +18,7 @@ export class ConflictError extends CustomErrorBase {}
|
||||
export class CustomErrorBase extends Error {
|
||||
constructor(message?: string, cause?: Error | unknown);
|
||||
// (undocumented)
|
||||
readonly cause?: Error;
|
||||
readonly cause?: Error | undefined;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -98,4 +98,7 @@ export function serializeError(
|
||||
includeStack?: boolean;
|
||||
},
|
||||
): SerializedError;
|
||||
|
||||
// @public
|
||||
export function stringifyError(error: unknown): string;
|
||||
```
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { stringifyError } from '../serialization';
|
||||
import { isError } from './assertion';
|
||||
|
||||
/**
|
||||
@@ -32,25 +33,12 @@ import { isError } from './assertion';
|
||||
* ```
|
||||
*/
|
||||
export class CustomErrorBase extends Error {
|
||||
readonly cause?: Error;
|
||||
readonly cause?: Error | undefined;
|
||||
|
||||
constructor(message?: string, cause?: Error | unknown) {
|
||||
let assignedCause: Error | undefined = undefined;
|
||||
|
||||
let fullMessage = message;
|
||||
if (cause !== undefined) {
|
||||
let causeStr;
|
||||
if (isError(cause)) {
|
||||
assignedCause = cause;
|
||||
causeStr = String(cause);
|
||||
|
||||
// Prefer the cause.toString, but if it's not implemented we use a nicer fallback
|
||||
if (causeStr === '[object Object]') {
|
||||
causeStr = `${cause.name}: ${cause.message}`;
|
||||
}
|
||||
} else {
|
||||
causeStr = `unknown error '${cause}'`;
|
||||
}
|
||||
const causeStr = stringifyError(cause);
|
||||
if (fullMessage) {
|
||||
fullMessage += `; caused by ${causeStr}`;
|
||||
} else {
|
||||
@@ -63,6 +51,6 @@ export class CustomErrorBase extends Error {
|
||||
Error.captureStackTrace?.(this, this.constructor);
|
||||
|
||||
this.name = this.constructor.name;
|
||||
this.cause = assignedCause;
|
||||
this.cause = isError(cause) ? cause : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { deserializeError, serializeError } from './error';
|
||||
import { NotModifiedError } from '../errors';
|
||||
import { deserializeError, serializeError, stringifyError } from './error';
|
||||
|
||||
class CustomError extends Error {
|
||||
readonly customField: any;
|
||||
@@ -61,4 +62,30 @@ describe('serialization', () => {
|
||||
expect(withoutStack1.stack).not.toBeDefined();
|
||||
expect(withoutStack2.stack).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('stringifies all supported forms', () => {
|
||||
expect(stringifyError({})).toEqual("unknown error '[object Object]'");
|
||||
expect(
|
||||
stringifyError({
|
||||
toString() {
|
||||
return 'str';
|
||||
},
|
||||
}),
|
||||
).toEqual("unknown error 'str'");
|
||||
expect(
|
||||
stringifyError({
|
||||
name: 'not used',
|
||||
message: 'not used',
|
||||
toString() {
|
||||
return 'str';
|
||||
},
|
||||
}),
|
||||
).toEqual('str');
|
||||
expect(stringifyError({ name: 'N', message: 'm1' })).toEqual('N: m1');
|
||||
expect(stringifyError(new NotModifiedError('m2'))).toEqual(
|
||||
'NotModifiedError: m2',
|
||||
);
|
||||
expect(stringifyError(new Error('m3'))).toEqual('Error: m3');
|
||||
expect(stringifyError(new TypeError('m4'))).toEqual('TypeError: m4');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
deserializeError as deserializeErrorInternal,
|
||||
serializeError as serializeErrorInternal,
|
||||
} from 'serialize-error';
|
||||
import { isError } from '../errors';
|
||||
|
||||
/**
|
||||
* The serialized form of an Error.
|
||||
@@ -78,3 +79,19 @@ export function deserializeError<T extends Error = Error>(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringifies an error, including its name and message where available.
|
||||
*
|
||||
* @param error - The error.
|
||||
* @public
|
||||
*/
|
||||
export function stringifyError(error: unknown): string {
|
||||
if (isError(error)) {
|
||||
// Prefer error.toString, but if it's not implemented we use a nicer fallback
|
||||
const str = String(error);
|
||||
return str !== '[object Object]' ? str : `${error.name}: ${error.message}`;
|
||||
}
|
||||
|
||||
return `unknown error '${error}'`;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { deserializeError, serializeError } from './error';
|
||||
export { deserializeError, serializeError, stringifyError } from './error';
|
||||
export type { SerializedError } from './error';
|
||||
export { parseErrorResponse } from './response';
|
||||
export type { ErrorResponse } from './response';
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
parseEntityRef,
|
||||
UNSTABLE_EntityStatusItem,
|
||||
} from '@backstage/catalog-model';
|
||||
import { SerializedError } from '@backstage/errors';
|
||||
import { SerializedError, stringifyError } from '@backstage/errors';
|
||||
import { Knex } from 'knex';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@@ -49,7 +49,9 @@ export class Stitcher {
|
||||
try {
|
||||
await this.stitchOne(entityRef);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to stitch ${entityRef}, ${error}`);
|
||||
this.logger.error(
|
||||
`Failed to stitch ${entityRef}, ${stringifyError(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user