summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/odd-books-live.md23
-rw-r--r--packages/astro/src/@types/astro.ts25
-rw-r--r--packages/astro/src/core/logger/console.ts2
-rw-r--r--packages/astro/src/core/logger/core.ts69
-rw-r--r--packages/astro/src/core/logger/node.ts14
-rw-r--r--packages/astro/src/integrations/index.ts49
-rw-r--r--packages/astro/test/static-build.test.js2
7 files changed, 154 insertions, 30 deletions
diff --git a/.changeset/odd-books-live.md b/.changeset/odd-books-live.md
new file mode 100644
index 000000000..40f0d7c17
--- /dev/null
+++ b/.changeset/odd-books-live.md
@@ -0,0 +1,23 @@
+---
+'astro': minor
+---
+
+Integrations can now log messages using Astro’s built-in logger.
+
+The logger is available to all hooks as an additional parameter:
+
+```ts
+import {AstroIntegration} from "./astro";
+
+// integration.js
+export function myIntegration(): AstroIntegration {
+ return {
+ name: "my-integration",
+ hooks: {
+ "astro:config:done": ({ logger }) => {
+ logger.info("Configure integration...");
+ }
+ }
+ }
+}
+```
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index b6f45d037..8803cb575 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -22,6 +22,7 @@ import type { AstroCookies } from '../core/cookies';
import type { LogOptions, LoggerLevel } from '../core/logger/core';
import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server';
import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js';
+import { AstroIntegrationLogger } from '../core/logger/core';
export type {
MarkdownHeading,
MarkdownMetadata,
@@ -1879,6 +1880,7 @@ export interface AstroIntegration {
injectScript: (stage: InjectedScriptStage, content: string) => void;
injectRoute: (injectRoute: InjectedRoute) => void;
addClientDirective: (directive: ClientDirectiveConfig) => void;
+ logger: AstroIntegrationLogger;
// TODO: Add support for `injectElement()` for full HTML element injection, not just scripts.
// This may require some refactoring of `scripts`, `styles`, and `links` into something
// more generalized. Consider the SSR use-case as well.
@@ -1887,10 +1889,17 @@ export interface AstroIntegration {
'astro:config:done'?: (options: {
config: AstroConfig;
setAdapter: (adapter: AstroAdapter) => void;
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
- 'astro:server:setup'?: (options: { server: vite.ViteDevServer }) => void | Promise<void>;
- 'astro:server:start'?: (options: { address: AddressInfo }) => void | Promise<void>;
- 'astro:server:done'?: () => void | Promise<void>;
+ 'astro:server:setup'?: (options: {
+ server: vite.ViteDevServer;
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
+ 'astro:server:start'?: (options: {
+ address: AddressInfo;
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
+ 'astro:server:done'?: (options: { logger: AstroIntegrationLogger }) => void | Promise<void>;
'astro:build:ssr'?: (options: {
manifest: SerializedSSRManifest;
/**
@@ -1902,19 +1911,25 @@ export interface AstroIntegration {
* File path of the emitted middleware
*/
middlewareEntryPoint: URL | undefined;
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
- 'astro:build:start'?: () => void | Promise<void>;
+ 'astro:build:start'?: (options: { logger: AstroIntegrationLogger }) => void | Promise<void>;
'astro:build:setup'?: (options: {
vite: vite.InlineConfig;
pages: Map<string, PageBuildData>;
target: 'client' | 'server';
updateConfig: (newConfig: vite.InlineConfig) => void;
+ logger: AstroIntegrationLogger;
+ }) => void | Promise<void>;
+ 'astro:build:generated'?: (options: {
+ dir: URL;
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
- 'astro:build:generated'?: (options: { dir: URL }) => void | Promise<void>;
'astro:build:done'?: (options: {
pages: { pathname: string }[];
dir: URL;
routes: RouteData[];
+ logger: AstroIntegrationLogger;
}) => void | Promise<void>;
};
}
diff --git a/packages/astro/src/core/logger/console.ts b/packages/astro/src/core/logger/console.ts
index dfe732bd7..f39f6b74d 100644
--- a/packages/astro/src/core/logger/console.ts
+++ b/packages/astro/src/core/logger/console.ts
@@ -15,7 +15,7 @@ export const consoleLogDestination = {
function getPrefix() {
let prefix = '';
- let type = event.type;
+ let type = event.label;
if (type) {
// hide timestamp when type is undefined
prefix += dim(dateTimeFormat.format(new Date()) + ' ');
diff --git a/packages/astro/src/core/logger/core.ts b/packages/astro/src/core/logger/core.ts
index 4f0c281e0..e5d0aee1f 100644
--- a/packages/astro/src/core/logger/core.ts
+++ b/packages/astro/src/core/logger/core.ts
@@ -6,7 +6,6 @@ interface LogWritable<T> {
}
export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'; // same as Pino
-export type LoggerEvent = 'info' | 'warn' | 'error';
export interface LogOptions {
dest: LogWritable<LogMessage>;
@@ -29,7 +28,7 @@ export const dateTimeFormat = new Intl.DateTimeFormat([], {
});
export interface LogMessage {
- type: string | null;
+ label: string | null;
level: LoggerLevel;
message: string;
}
@@ -43,11 +42,11 @@ export const levels: Record<LoggerLevel, number> = {
};
/** Full logging API */
-export function log(opts: LogOptions, level: LoggerLevel, type: string | null, message: string) {
+export function log(opts: LogOptions, level: LoggerLevel, label: string | null, message: string) {
const logLevel = opts.level;
const dest = opts.dest;
const event: LogMessage = {
- type,
+ label,
level,
message,
};
@@ -61,18 +60,18 @@ export function log(opts: LogOptions, level: LoggerLevel, type: string | null, m
}
/** Emit a user-facing message. Useful for UI and other console messages. */
-export function info(opts: LogOptions, type: string | null, message: string) {
- return log(opts, 'info', type, message);
+export function info(opts: LogOptions, label: string | null, message: string) {
+ return log(opts, 'info', label, message);
}
/** Emit a warning message. Useful for high-priority messages that aren't necessarily errors. */
-export function warn(opts: LogOptions, type: string | null, message: string) {
- return log(opts, 'warn', type, message);
+export function warn(opts: LogOptions, label: string | null, message: string) {
+ return log(opts, 'warn', label, message);
}
/** Emit a error message, Useful when Astro can't recover from some error. */
-export function error(opts: LogOptions, type: string | null, message: string) {
- return log(opts, 'error', type, message);
+export function error(opts: LogOptions, label: string | null, message: string) {
+ return log(opts, 'error', label, message);
}
type LogFn = typeof info | typeof warn | typeof error;
@@ -127,3 +126,53 @@ export function timerMessage(message: string, startTime: number = Date.now()) {
timeDiff < 750 ? `${Math.round(timeDiff)}ms` : `${(timeDiff / 1000).toFixed(1)}s`;
return `${message} ${dim(timeDisplay)}`;
}
+
+export class Logger {
+ options: LogOptions;
+ constructor(options: LogOptions) {
+ this.options = options;
+ }
+
+ info(label: string, message: string) {
+ info(this.options, label, message);
+ }
+ warn(label: string, message: string) {
+ warn(this.options, label, message);
+ }
+ error(label: string, message: string) {
+ error(this.options, label, message);
+ }
+ debug(label: string, message: string) {
+ debug(this.options, label, message);
+ }
+}
+
+export class AstroIntegrationLogger {
+ options: LogOptions;
+ label: string;
+
+ constructor(logging: LogOptions, label: string) {
+ this.options = logging;
+ this.label = label;
+ }
+
+ /**
+ * Creates a new logger instance with a new label, but the same log options.
+ */
+ fork(label: string): AstroIntegrationLogger {
+ return new AstroIntegrationLogger(this.options, label);
+ }
+
+ info(message: string) {
+ info(this.options, this.label, message);
+ }
+ warn(message: string) {
+ warn(this.options, this.label, message);
+ }
+ error(message: string) {
+ error(this.options, this.label, message);
+ }
+ debug(message: string) {
+ debug(this.options, this.label, message);
+ }
+}
diff --git a/packages/astro/src/core/logger/node.ts b/packages/astro/src/core/logger/node.ts
index 513ba257e..aeef4bd84 100644
--- a/packages/astro/src/core/logger/node.ts
+++ b/packages/astro/src/core/logger/node.ts
@@ -21,19 +21,19 @@ export const nodeLogDestination = new Writable({
function getPrefix() {
let prefix = '';
- let type = event.type;
- if (type) {
+ let label = event.label;
+ if (label) {
// hide timestamp when type is undefined
prefix += dim(dateTimeFormat.format(new Date()) + ' ');
if (event.level === 'info') {
- type = bold(cyan(`[${type}]`));
+ label = bold(cyan(`[${label}]`));
} else if (event.level === 'warn') {
- type = bold(yellow(`[${type}]`));
+ label = bold(yellow(`[${label}]`));
} else if (event.level === 'error') {
- type = bold(red(`[${type}]`));
+ label = bold(red(`[${label}]`));
}
- prefix += `${type} `;
+ prefix += `${label} `;
}
return reset(prefix);
}
@@ -87,7 +87,7 @@ export const nodeLogOptions: Required<LogOptions> = {
};
export interface LogMessage {
- type: string | null;
+ label: string | null;
level: LoggerLevel;
message: string;
}
diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts
index cf50df0e1..d54ac3803 100644
--- a/packages/astro/src/integrations/index.ts
+++ b/packages/astro/src/integrations/index.ts
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
import type { InlineConfig, ViteDevServer } from 'vite';
import type {
AstroConfig,
+ AstroIntegration,
AstroRenderer,
AstroSettings,
ContentEntryType,
@@ -16,7 +17,7 @@ import type { SerializedSSRManifest } from '../core/app/types';
import type { PageBuildData } from '../core/build/types';
import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js';
import { mergeConfig } from '../core/config/index.js';
-import { info, type LogOptions } from '../core/logger/core.js';
+import { info, type LogOptions, AstroIntegrationLogger } from '../core/logger/core.js';
import { isServerLikeOutput } from '../prerender/utils.js';
async function withTakingALongTimeMsg<T>({
@@ -38,6 +39,19 @@ async function withTakingALongTimeMsg<T>({
return result;
}
+// Used internally to store instances of loggers.
+const Loggers = new WeakMap<AstroIntegration, AstroIntegrationLogger>();
+
+function getLogger(integration: AstroIntegration, logging: LogOptions) {
+ if (Loggers.has(integration)) {
+ // SAFETY: we check the existence in the if block
+ return Loggers.get(integration)!;
+ }
+ const logger = new AstroIntegrationLogger(logging, integration.name);
+ Loggers.set(integration, logger);
+ return logger;
+}
+
export async function runHookConfigSetup({
settings,
command,
@@ -72,6 +86,8 @@ export async function runHookConfigSetup({
* ```
*/
if (integration.hooks?.['astro:config:setup']) {
+ const logger = getLogger(integration, logging);
+
const hooks: HookParameters<'astro:config:setup'> = {
config: updatedConfig,
command,
@@ -107,6 +123,7 @@ export async function runHookConfigSetup({
}
addedClientDirectives.set(name, buildClientDirectiveEntrypoint(name, entrypoint));
},
+ logger,
};
// ---
@@ -167,6 +184,7 @@ export async function runHookConfigDone({
logging: LogOptions;
}) {
for (const integration of settings.config.integrations) {
+ const logger = getLogger(integration, logging);
if (integration?.hooks?.['astro:config:done']) {
await withTakingALongTimeMsg({
name: integration.name,
@@ -180,6 +198,7 @@ export async function runHookConfigDone({
}
settings.adapter = adapter;
},
+ logger,
}),
logging,
});
@@ -198,9 +217,10 @@ export async function runHookServerSetup({
}) {
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:server:setup']) {
+ const logger = getLogger(integration, logging);
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:server:setup']({ server }),
+ hookResult: integration.hooks['astro:server:setup']({ server, logger }),
logging,
});
}
@@ -217,10 +237,12 @@ export async function runHookServerStart({
logging: LogOptions;
}) {
for (const integration of config.integrations) {
+ const logger = getLogger(integration, logging);
+
if (integration?.hooks?.['astro:server:start']) {
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:server:start']({ address }),
+ hookResult: integration.hooks['astro:server:start']({ address, logger }),
logging,
});
}
@@ -235,10 +257,12 @@ export async function runHookServerDone({
logging: LogOptions;
}) {
for (const integration of config.integrations) {
+ const logger = getLogger(integration, logging);
+
if (integration?.hooks?.['astro:server:done']) {
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:server:done'](),
+ hookResult: integration.hooks['astro:server:done']({ logger }),
logging,
});
}
@@ -254,9 +278,11 @@ export async function runHookBuildStart({
}) {
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:start']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:build:start'](),
+ hookResult: integration.hooks['astro:build:start']({ logger }),
logging,
});
}
@@ -280,6 +306,8 @@ export async function runHookBuildSetup({
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:setup']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
hookResult: integration.hooks['astro:build:setup']({
@@ -289,6 +317,7 @@ export async function runHookBuildSetup({
updateConfig: (newConfig) => {
updatedConfig = mergeConfig(updatedConfig, newConfig);
},
+ logger,
}),
logging,
});
@@ -315,12 +344,15 @@ export async function runHookBuildSsr({
}: RunHookBuildSsr) {
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:ssr']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
hookResult: integration.hooks['astro:build:ssr']({
manifest,
entryPoints,
middlewareEntryPoint,
+ logger,
}),
logging,
});
@@ -338,10 +370,12 @@ export async function runHookBuildGenerated({
const dir = isServerLikeOutput(config) ? config.build.client : config.outDir;
for (const integration of config.integrations) {
+ const logger = getLogger(integration, logging);
+
if (integration?.hooks?.['astro:build:generated']) {
await withTakingALongTimeMsg({
name: integration.name,
- hookResult: integration.hooks['astro:build:generated']({ dir }),
+ hookResult: integration.hooks['astro:build:generated']({ dir, logger }),
logging,
});
}
@@ -361,12 +395,15 @@ export async function runHookBuildDone({ config, pages, routes, logging }: RunHo
for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:done']) {
+ const logger = getLogger(integration, logging);
+
await withTakingALongTimeMsg({
name: integration.name,
hookResult: integration.hooks['astro:build:done']({
pages: pages.map((p) => ({ pathname: p })),
dir,
routes,
+ logger,
}),
logging,
});
diff --git a/packages/astro/test/static-build.test.js b/packages/astro/test/static-build.test.js
index d4a687a5d..0552c353f 100644
--- a/packages/astro/test/static-build.test.js
+++ b/packages/astro/test/static-build.test.js
@@ -175,7 +175,7 @@ describe('Static build', () => {
let found = false;
for (const log of logs) {
if (
- log.type === 'ssg' &&
+ log.label === 'ssg' &&
/[hH]eaders are not exposed in static \(SSG\) output mode/.test(log.message)
) {
found = true;