astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
226 lines (225 loc) • 5.69 kB
JavaScript
import colors from "piccolore";
/**
* Defined logger labels. Add more as needed, but keep them high-level & reusable,
* rather than specific to a single command, function, use, etc. The label will be
* shown in the log message to the user, so it should be relevant.
*/
const AstroLoggerLabels = [
"add",
"build",
"check",
"config",
"content",
"crypto",
"deprecated",
"markdown",
"router",
"types",
"vite",
"watch",
"middleware",
"preferences",
"redirects",
"sync",
"session",
"toolbar",
"assets",
"env",
"update",
"adapter",
"islands",
"cache",
"csp",
"SKIP_FORMAT"
];
// Hey, locales are pretty complicated! Be careful modifying this logic...
// If we throw at the top-level, international users can't use Astro.
//
// Using `[]` sets the default locale properly from the system!
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#parameters
//
// Here be the dragons we've slain:
// https://github.com/withastro/astro/issues/2625
// https://github.com/withastro/astro/issues/3309
/** @lintignore */
export const dateTimeFormat = new Intl.DateTimeFormat([], {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
});
export const levels = {
debug: 20,
info: 30,
warn: 40,
error: 50,
silent: 90
};
/** Full logging API */
function log(opts, level, label, message, newLine = true) {
const logLevel = opts.level;
const dest = opts.destination;
const event = {
label,
level,
message,
newLine
};
// test if this level is enabled or not
if (!isLogLevelEnabled(logLevel, level)) {
return;
}
dest.write(event);
}
export function isLogLevelEnabled(configuredLogLevel, level) {
return levels[configuredLogLevel] <= levels[level];
}
/** Emit a user-facing message. Useful for UI and other console messages. */
function info(opts, label, message, newLine = true) {
return log(opts, "info", label, message, newLine);
}
/** Emit a warning message. Useful for high-priority messages that aren't necessarily errors. */
function warn(opts, label, message, newLine = true) {
return log(opts, "warn", label, message, newLine);
}
/** Emit an error message, Useful when Astro can't recover from some error. */
function error(opts, label, message, newLine = true) {
return log(opts, "error", label, message, newLine);
}
export function debug(...args) {
if ("_astroGlobalDebug" in globalThis) {
globalThis._astroGlobalDebug(...args);
}
}
/**
* Get the prefix for a log message.
* This includes the timestamp, log level, and label all properly formatted
* with colors. This is shared across different loggers, so it's defined here.
*/
export function getEventPrefix({ level, label }) {
const timestamp = `${dateTimeFormat.format(new Date())}`;
const prefix = [];
if (level === "error" || level === "warn") {
prefix.push(colors.bold(timestamp));
prefix.push(`[${level.toUpperCase()}]`);
} else {
prefix.push(timestamp);
}
if (label) {
prefix.push(`[${label}]`);
}
if (level === "error") {
return colors.red(prefix.join(" "));
}
if (level === "warn") {
return colors.yellow(prefix.join(" "));
}
if (prefix.length === 1) {
return colors.dim(prefix[0]);
}
return colors.dim(prefix[0]) + " " + colors.blue(prefix.splice(1).join(" "));
}
/** Print out a timer message for debug() */
export function timerMessage(message, startTime = Date.now()) {
let timeDiff = Date.now() - startTime;
let timeDisplay = timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1e3).toFixed(1)}s`;
return `${message} ${colors.dim(timeDisplay)}`;
}
export class AstroLogger {
options;
constructor(options) {
this.options = options;
}
info(label, message, newLine = true) {
info(this.options, label, message, newLine);
}
warn(label, message, newLine = true) {
warn(this.options, label, message, newLine);
}
error(label, message, newLine = true) {
error(this.options, label, message, newLine);
}
debug(label, ...messages) {
debug(label, ...messages);
}
level() {
return this.options.level;
}
forkIntegrationLogger(label) {
return new AstroIntegrationLogger(this.options, label);
}
setDestination(destination) {
this.options.destination = destination;
}
/**
* It calls the `close` function of the provided destination, if it exists.
*/
close() {
if (this.options.destination.close) {
this.options.destination.close();
}
}
/**
* It calls the `flush` function of the provided destination, if it exists.
*/
flush() {
if (this.options.destination.flush) {
this.options.destination.flush();
}
}
}
export class AstroIntegrationLogger {
options;
label;
constructor(logging, label) {
this.options = logging;
this.label = label;
}
/**
* Creates a new logger instance with a new label, but the same log options.
*/
fork(label) {
return new AstroIntegrationLogger(this.options, label);
}
info(message) {
info(this.options, this.label, message);
}
warn(message) {
warn(this.options, this.label, message);
}
error(message) {
error(this.options, this.label, message);
}
debug(message) {
debug(this.label, message);
}
/**
* It calls the `flush` function of the provided destination, if it exists.
*/
flush() {
if (this.options.destination.flush) {
this.options.destination.flush();
}
}
/**
* It calls the `close` function of the provided destination, if it exists.
*/
close() {
if (this.options.destination.close) {
this.options.destination.close();
}
}
}
export function astroToRuntimeLogger(logger) {
return {
info(msg) {
logger.info(null, msg);
},
warn(msg) {
logger.warn(null, msg);
},
error(msg) {
logger.error(null, msg);
}
};
}