mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
update error logging (#9129)
Co-authored-by: Matthew Phillips <matthew@skypack.dev>
This commit is contained in:
parent
4a5f2cde17
commit
8bfc205119
7 changed files with 69 additions and 47 deletions
5
.changeset/three-chairs-sip.md
Normal file
5
.changeset/three-chairs-sip.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Update error log formatting
|
|
@ -20,7 +20,7 @@ export async function throwAndExit(cmd: string, err: unknown) {
|
|||
|
||||
const errorWithMetadata = collectErrorMetadata(createSafeError(err));
|
||||
telemetryPromise = telemetry.record(eventError({ cmd, err: errorWithMetadata, isFatal: true }));
|
||||
errorMessage = formatErrorMessage(errorWithMetadata);
|
||||
errorMessage = formatErrorMessage(errorWithMetadata, true);
|
||||
|
||||
// Timeout the error reporter (very short) because the user is waiting.
|
||||
// NOTE(fks): It is better that we miss some events vs. holding too long.
|
||||
|
|
|
@ -71,7 +71,7 @@ export async function restartContainer(container: Container): Promise<Container
|
|||
const error = createSafeError(_err);
|
||||
// Print all error messages except ZodErrors from AstroConfig as the pre-logged error is sufficient
|
||||
if (!isAstroConfigZodError(_err)) {
|
||||
logger.error('config', formatErrorMessage(collectErrorMetadata(error)) + '\n');
|
||||
logger.error('config', formatErrorMessage(collectErrorMetadata(error), logger.level() === 'debug') + '\n');
|
||||
}
|
||||
// Inform connected clients of the config error
|
||||
container.viteServer.ws.send({
|
||||
|
|
|
@ -490,9 +490,10 @@ export const PageNumberParamNotFound = {
|
|||
*/
|
||||
export const ImageMissingAlt = {
|
||||
name: 'ImageMissingAlt',
|
||||
title: 'Missing alt property.',
|
||||
message: 'The alt property is required.',
|
||||
hint: "The `alt` property is important for the purpose of accessibility, without it users using screen readers or other assistive technologies won't be able to understand what your image is supposed to represent. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-alt for more information.",
|
||||
title: 'Image missing required "alt" property.',
|
||||
message:
|
||||
'Image missing "alt" property. "alt" text is required to describe important images on the page.',
|
||||
hint: 'Use an empty string ("") for decorative images.',
|
||||
} satisfies ErrorData;
|
||||
/**
|
||||
* @docs
|
||||
|
|
|
@ -39,8 +39,8 @@ function debug(type: string, ...messages: Array<any>) {
|
|||
(globalThis as any)._astroGlobalDebug = debug;
|
||||
|
||||
export function enableVerboseLogging() {
|
||||
debugPackage.enable('*,-babel');
|
||||
debug('cli', '--verbose flag enabled! Enabling: DEBUG="*,-babel"');
|
||||
debugPackage.enable('astro:*,vite:*');
|
||||
debug('cli', '--verbose flag enabled! Enabling: DEBUG="astro:*,vite:*"');
|
||||
debug(
|
||||
'cli',
|
||||
'Tip: Set the DEBUG env variable directly for more control. Example: "DEBUG=astro:*,vite:* astro build".'
|
||||
|
|
|
@ -187,58 +187,74 @@ export function formatConfigErrorMessage(err: ZodError) {
|
|||
)}`;
|
||||
}
|
||||
|
||||
export function formatErrorMessage(err: ErrorWithMetadata, args: string[] = []): string {
|
||||
const isOurError = AstroError.is(err) || CompilerError.is(err) || AstroUserError.is(err);
|
||||
// a regex to match the first line of a stack trace
|
||||
const STACK_LINE_REGEXP = /^\s+at /g;
|
||||
const IRRELEVANT_STACK_REGEXP = /(node_modules|astro[\/\\]dist)/g;
|
||||
function formatErrorStackTrace(err: Error | ErrorWithMetadata, showFullStacktrace: boolean): string {
|
||||
const stackLines = (err.stack || '').split('\n').filter((line) => STACK_LINE_REGEXP.test(line));
|
||||
// If full details are required, just return the entire stack trace.
|
||||
if (showFullStacktrace) {
|
||||
return stackLines.join('\n');
|
||||
}
|
||||
// Grab every string from the user's codebase, exit when you hit node_modules or astro/dist
|
||||
const irrelevantStackIndex = stackLines.findIndex((line) => IRRELEVANT_STACK_REGEXP.test(line));
|
||||
if (irrelevantStackIndex <= 0) {
|
||||
const errorId = (err as ErrorWithMetadata).id;
|
||||
const errorLoc = (err as ErrorWithMetadata).loc;
|
||||
if (errorId|| errorLoc?.file) {
|
||||
const prettyLocation = ` at ${errorId?? errorLoc?.file}${
|
||||
errorLoc?.line && errorLoc.column ? `:${errorLoc.line}:${errorLoc.column}` : ''
|
||||
}`;
|
||||
return prettyLocation + '\n [...] See full stack trace in the browser, or rerun with --verbose.';
|
||||
} else {
|
||||
return stackLines.join('\n');
|
||||
}
|
||||
}
|
||||
// If the error occurred inside of a dependency, grab the entire stack.
|
||||
// Otherwise, only grab the part of the stack that is relevant to the user's codebase.
|
||||
return stackLines.splice(0, irrelevantStackIndex).join('\n') + '\n [...] See full stack trace in the browser, or rerun with --verbose.';
|
||||
}
|
||||
|
||||
export function formatErrorMessage(err: ErrorWithMetadata, showFullStacktrace: boolean): string {
|
||||
const isOurError = AstroError.is(err) || CompilerError.is(err) || AstroUserError.is(err);
|
||||
let message = '';
|
||||
if (isOurError) {
|
||||
message += red(`[${err.name}]`) + ' ' + renderErrorMarkdown(err.message, 'cli');
|
||||
} else {
|
||||
message += err.message;
|
||||
}
|
||||
const output = [message];
|
||||
|
||||
args.push(
|
||||
`${bgRed(black(` error `))}${red(
|
||||
padMultilineString(isOurError ? renderErrorMarkdown(err.message, 'cli') : err.message)
|
||||
)}`
|
||||
);
|
||||
if (err.hint) {
|
||||
args.push(` ${bold('Hint:')}`);
|
||||
args.push(
|
||||
yellow(padMultilineString(isOurError ? renderErrorMarkdown(err.hint, 'cli') : err.hint, 4))
|
||||
output.push(` ${bold('Hint:')}`);
|
||||
output.push(
|
||||
yellow(padMultilineString(renderErrorMarkdown(err.hint, 'cli'), 4))
|
||||
);
|
||||
}
|
||||
|
||||
const docsLink = getDocsForError(err);
|
||||
if (docsLink) {
|
||||
args.push(` ${bold('Error reference:')}`);
|
||||
args.push(` ${underline(docsLink)}`);
|
||||
output.push(` ${bold('Error reference:')}`);
|
||||
output.push(` ${cyan(underline(docsLink))}`);
|
||||
}
|
||||
if (err.id || err.loc?.file) {
|
||||
args.push(` ${bold('File:')}`);
|
||||
args.push(
|
||||
red(
|
||||
` ${err.id ?? err.loc?.file}${
|
||||
err.loc?.line && err.loc.column ? `:${err.loc.line}:${err.loc.column}` : ''
|
||||
}`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (err.frame) {
|
||||
args.push(` ${bold('Code:')}`);
|
||||
args.push(red(padMultilineString(err.frame.trim(), 4)));
|
||||
}
|
||||
if (args.length === 1 && err.stack) {
|
||||
args.push(dim(err.stack));
|
||||
} else if (err.stack) {
|
||||
args.push(` ${bold('Stacktrace:')}`);
|
||||
args.push(dim(err.stack));
|
||||
args.push(``);
|
||||
|
||||
if (err.stack) {
|
||||
output.push(` ${bold('Stack trace:')}`);
|
||||
output.push(dim(formatErrorStackTrace(err, showFullStacktrace)));
|
||||
}
|
||||
|
||||
if (err.cause) {
|
||||
args.push(` ${bold('Cause:')}`);
|
||||
output.push(` ${bold('Caused by:')}`);
|
||||
let causeMessage = ' ';
|
||||
if (err.cause instanceof Error) {
|
||||
args.push(dim(err.cause.stack ?? err.cause.toString()));
|
||||
causeMessage += err.cause.message + '\n' + formatErrorStackTrace(err.cause, showFullStacktrace);
|
||||
} else {
|
||||
args.push(JSON.stringify(err.cause));
|
||||
causeMessage += (JSON.stringify(err.cause));
|
||||
}
|
||||
|
||||
args.push(``);
|
||||
output.push(dim(causeMessage));
|
||||
}
|
||||
return args.join('\n');
|
||||
|
||||
return output.join('\n');
|
||||
}
|
||||
|
||||
export function printHelp({
|
||||
|
|
|
@ -2,7 +2,7 @@ import type http from 'node:http';
|
|||
import type { ManifestData, SSRManifest } from '../@types/astro.js';
|
||||
import { collectErrorMetadata } from '../core/errors/dev/index.js';
|
||||
import { createSafeError } from '../core/errors/index.js';
|
||||
import * as msg from '../core/messages.js';
|
||||
import {formatErrorMessage} from '../core/messages.js';
|
||||
import { collapseDuplicateSlashes, removeTrailingForwardSlash } from '../core/path.js';
|
||||
import { eventError, telemetry } from '../events/index.js';
|
||||
import { isServerLikeOutput } from '../prerender/utils.js';
|
||||
|
@ -102,7 +102,7 @@ export async function handleRequest({
|
|||
|
||||
telemetry.record(eventError({ cmd: 'dev', err: errorWithMetadata, isFatal: false }));
|
||||
|
||||
pipeline.logger.error(null, msg.formatErrorMessage(errorWithMetadata));
|
||||
pipeline.logger.error(null, formatErrorMessage(errorWithMetadata, pipeline.logger.level() === 'debug'));
|
||||
handle500Response(moduleLoader, incomingResponse, errorWithMetadata);
|
||||
|
||||
return err;
|
||||
|
|
Loading…
Reference in a new issue