diff --git a/astro.sidebar.ts b/astro.sidebar.ts
index f2bcf97c84029..9cc4c15349db2 100644
--- a/astro.sidebar.ts
+++ b/astro.sidebar.ts
@@ -158,6 +158,7 @@ export const sidebar = [
'reference/experimental-flags/svg-optimization',
'reference/experimental-flags/queued-rendering',
'reference/experimental-flags/rust-compiler',
+ 'reference/experimental-flags/logger',
],
}),
'reference/legacy-flags',
diff --git a/src/content/docs/en/reference/experimental-flags/logger.mdx b/src/content/docs/en/reference/experimental-flags/logger.mdx
new file mode 100644
index 0000000000000..527f0703fc107
--- /dev/null
+++ b/src/content/docs/en/reference/experimental-flags/logger.mdx
@@ -0,0 +1,296 @@
+---
+title: Experimental logger
+sidebar:
+ label: Logger
+i18nReady: true
+---
+
+import Since from '~/components/Since.astro';
+
+
+
+**Type:** `object`
+**Default:** `undefined`
+
+
+
+Enables an experimental, custom logger that can be controlled by the user.
+
+When provided, the user has total control over the logs emitted by Astro. Additionally, the feature comes with some built-in loggers that can be used via the new `logHandlers`.
+
+The following example enables the built-in JSON logger, with a pretty output:
+
+```js title="astro.config.mjs" {5} ins="logHandlers"
+import { defineConfig, logHandlers } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: logHandlers.json({ pretty: true })
+ }
+});
+```
+
+Alternatively, you can enable JSON logging the `dev`, `build` and `sync` commands using the `--experimentalJson` flag:
+
+```shell
+astro dev --experimentalJson
+astro sync --experimentalJson
+astro build --experimentalJson
+```
+
+
+## Built-in loggers
+
+The feature comes with three built-in loggers.
+
+### `logHandlers.json`
+
+A logger that outputs messages in a JSON format. A log would look like this:
+```json
+{ "message": "", "label": "router", "level": "info", "time": "" }
+```
+
+#### JSON logger options
+
+
+**Type:** `{ pretty: boolean; level: AstroLoggerLevel; }`
+**Default:** `{ pretty: false, level: 'info' }`
+
+
+
+The `json` logger accepts the following options:
+
+- `pretty`: when `true`, the JSON log is printed on multiple lines. Defaults to `false`
+- `level`: the [level](#log-level) of logs that should be printed.
+
+```js title="astro.config.mjs" {5} ins="json"
+import { defineConfig, logHandlers } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: logHandlers.json({ pretty: true })
+ }
+});
+```
+
+### `logHandlers.console`
+
+A logger that prints messages using the `console` as its destination. Based on the level of the message, it uses different channels:
+- `error` messages are printed using `console.error`
+- `warn` messages are printed using `console.warn`
+- `info` messages are printed using `console.info`
+
+#### Console logger options
+
+
+**Type:** `{ level: AstroLoggerLevel }`
+**Default:** `{ level: 'info' }`
+
+
+
+The `console` logger accepts the following options:
+
+- `level`: the [level](#log-level) of logs that should be printed.
+
+```js title="astro.config.mjs" {5} ins="console"
+import { defineConfig, logHandlers } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: logHandlers.console({ level: 'warn' })
+ }
+});
+```
+
+### `logHandlers.node`
+
+A logger that prints messages to [`process.stdout`](https://nodejs.org/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/api/process.html#processstderr). Level `error` messages are printed to `stderr`, while the others are printed to `stdout`.
+
+This is Astro's default logger.
+
+#### Node logger options
+
+
+**Type:** `{ level: AstroLoggerLevel }`
+**Default:** `{ level: 'info' }`
+
+
+
+The `node` logger accepts the following options:
+
+- `level`: the [level](#log-level) of logs that should be printed.
+
+```js title="astro.config.mjs" {5} ins="node"
+import { defineConfig, logHandlers } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: logHandlers.node({ level: 'warn' })
+ }
+});
+```
+
+### `logHandlers.compose`
+
+A particular function that allows using multiple loggers. The same message is broadcasted to all loggers. The function accepts an arbitrary number of logger configurations.logHandlers
+
+The following example composes the console logger and JSON logger using the default log level:
+
+```js title="astro.config.mjs"
+import { defineConfig, logHandlers } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: logHandlers.compose(
+ logHandlers.console(),
+ logHandlers.json()
+ )
+ }
+});
+```
+
+## Custom loggers
+
+You can create a custom logger by providing the correct configuration to the `logger` setting. It accepts an object with a mandatory `entrypoint`, the module where the logger is exported, and an optional configuration to pass to the logger. The configuration must be serializable.
+
+The logger function must be exported as `default`.
+
+When you define a custom logger, you are in charge of all logs, even the ones emitted by Astro.
+
+The following example defines a custom logger exported by the `@org/custom-logger` package and accepting only one parameter to configure the logging `level`:
+
+```js title="astro.config.mjs"
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: {
+ entrypoint: "@org/custom-logger",
+ config: {
+ level: "warn"
+ }
+ }
+ }
+});
+```
+
+The following example implements a minimal logger returning an `AstroLoggerDestination` object with the required `write()` function:
+
+```ts title="@org/custom-logger/index.ts"
+import type {
+ AstroLoggerLevel,
+ AstroLoggerDestination,
+ AstroLoggerMessage
+} from "astro";
+import { matchesLevel } from "astro/logger";
+
+type LoggerOptions = {
+ level: AstroLoggerLevel
+}
+
+function orgLogger(options: LoggerOptions = {}): AstroLoggerDestination {
+ const { level = 'info' } = options;
+ return {
+ write(message: AstroLoggerMessage) {
+ // Use utility to understand if the message should be printed
+ if (matchesLevel(message.level, level)) {
+ // log message somewhere and take level into consideration
+ }
+ }
+ }
+}
+
+export default orgLogger;
+```
+
+## Runtime
+
+The [context object](/en/reference/api-reference/#the-context-object) now exposes a `logger` object containing the following functions:
+
+- `error()`, which emits an message with `error` level.
+- `warn()`, which emits an message with `warn` level.
+- `info()`, which emits an message with `info` level.
+
+```astro
+---
+Astro.logger.error("This is an error");
+Astro.logger.warn("This is a warning");
+Astro.logger.info("This is an info");
+---
+```
+
+## Log level
+
+A level is an internal, arbitrary score, assigned to each message. When a logger is configured with a certain level, only the messages with equals level is equal or higher are printed.
+
+There are three levels, from the highest to the lowest score:
+1. `error`
+2. `warn`
+3. `info`
+
+The following example configures the JSON logger to print only messages that have the `warn` level or higher:
+
+```js title="astro.config.mjs" {5} ins='level: "warn"'
+import { defineConfig, logHandlers } from 'astro/config';
+
+export default defineConfig({
+ experimental: {
+ logger: logHandlers.json({ level: "warn" })
+ }
+});
+```
+
+We will expose an utility form the `astro/logger` package to check the log level.
+
+```js
+import { matchesLevel } from "astro/logger"
+
+matchesLevel("error", "info");
+```
+
+
+## Types reference
+
+The following types can be imported from the `astro` specifier.
+
+### `AstroLoggerDestination`
+
+This is the interface that custom loggers must implement. The following can be implemented:
+- `write(message: AstroLoggerMessage) => void`: a mandatory method that accepts a `AstroLoggerMessage`. It's called for every log.
+- `flush() => Promise | void`: an optional function that is called at end of each request. Useful for advanced loggers for flushing logger messages while keeping the connection to the destination alive.
+- `close() => Promise | void`: an optional function that is called before a server is shut down. This is a function usually called by adapters such as `@astrojs/node`.
+
+### `AstroLoggerLevel`
+
+The level of the message. Available variants:
+- `'debug'`
+- `'info'`
+- `'warn'`
+- `'error'`
+- `'silent'`
+
+### `AstroLoggerMessage`
+
+The incoming object from the `AstroLoggerDestination.write` function:
+- `message: string`: the message being logged.
+- `level`: the level of the message.
+- `label`: an arbitrary label assigned to the log message.
+- `newLine`: whether this message should add a trailing newline.
+
+## APIs reference
+
+The following APIs can me imported from the `astro/logger` specifier.
+
+### `matchesLevel`
+
+`matchesLevel(messageLevel: AstroLoggerLevel, configuredLevel: AstroLoggerLevel): boolean`
+
+Given two [logger level](#log-level), it returns whether the first level matches the second level.
+
+```js
+import { matchesLevel } from "astro/logger"
+
+matchesLevel("error", "info"); // true
+matchesLevel("info", "error"); // false
+
+```