Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion astro.sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export const sidebar = [
'reference/dev-toolbar-app-reference',
'reference/session-driver-reference',
'reference/font-provider-reference',
'reference/logger-reference',
'reference/container-reference',
'reference/programmatic-reference',
],
Expand All @@ -162,7 +163,6 @@ export const sidebar = [
'reference/experimental-flags/queued-rendering',
'reference/experimental-flags/rust-compiler',
'reference/experimental-flags/advanced-routing',
'reference/experimental-flags/logger',
],
}),
'reference/legacy-flags',
Expand Down
51 changes: 51 additions & 0 deletions src/content/docs/en/reference/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1229,3 +1229,54 @@ content="
"
>
```
### `logger`

<p>

**Type**: `object | undefined`
<Since v="6.4.0" />
</p>

Comment thread
ematipico marked this conversation as resolved.
#### `error`
Comment thread
ematipico marked this conversation as resolved.
Outdated
<p>

**Type**: `(msg: string) => void`
<Since v="6.4.0" />
</p>

Emits a message with `error` level.

```astro title="src/pages/index.astro"
---
Astro.logger.error("Can't find the checkout ID.");
---
```

#### `warn`
Comment thread
ematipico marked this conversation as resolved.
Outdated
<p>

**Type**: `(msg: string) => void`
<Since v="6.4.0" />
</p>
Emits a message with `warn` level.

```astro title="src/pages/index.astro"
---
Astro.logger.warn("Can't find the checkout ID.");
---
```

#### `info`
Comment thread
ematipico marked this conversation as resolved.
Outdated
<p>

**Type**: `(msg: string) => void`
<Since v="6.4.0" />
</p>

Emits a message with `info` level.

```astro title="src/pages/index.astro"
---
Astro.logger.info("Can't find the checkout ID.");
---
```
Original file line number Diff line number Diff line change
@@ -1,47 +1,105 @@
---
title: Experimental logger
title: Astro Logger API
sidebar:
label: Logger
i18nReady: true
label: Logger API
i18nReady: true
Comment thread
ematipico marked this conversation as resolved.
Outdated
---

import Since from '~/components/Since.astro';

<p>

**Type:** `object`<br />
**Default:** `undefined`<br />
<Since v="6.2.0" />
<Since v="6.4.0" />
</p>

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`.
Astro allows to customize, change and compose loggers.
Comment thread
ematipico marked this conversation as resolved.
Outdated
## Custom loggers

The following example enables the built-in JSON logger, with a pretty output:
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.

```js title="astro.config.mjs" {5} ins="logHandlers"
import { defineConfig, logHandlers } from 'astro/config';
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: logHandlers.json({ pretty: true })
logger: {
entrypoint: "@org/custom-logger",
config: {
level: "warn"
}
}
}
});
```

Alternatively, you can enable JSON logging for the `dev`, `build`, and `sync` commands using the `--experimentalJson` flag:
The following example implements a minimal logger returning an [`AstroLoggerDestination` object](#astrologgerdestination) 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 this utility to understand if the message should be printed
if (matchesLevel(message.level, level)) {
// log message somewhere and take the level into consideration
}
}
}
}

export default orgLogger;
```



## 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 a level equal to or higher are printed.

There are three levels, from the highest to the lowest score:
1. `error`
2. `warn`
3. `info`
Comment on lines +78 to +81
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to say I'm a bit confused by this because in AstroLoggerLevel we also have debug and silent. So, 5 and not 3 levels. But, I just saw the unresolved question in the RFC:

debug info level has been kept out of the picture because we use obug for it, which is noisy and completely different from the rest of the logs. Should we keep it out? Docs should address the differences from normal logging.

It seems in the end we'll keep it? I guess this does not contribute to the score and this is a special case like silent, as this is not available in the runtime API. And, these two levels match the verbose and silent flags. Am I right?

In any case, we should probably have something explaining they are also available and how they differ.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were kept out on purpose because:

  • debug logging isn't astro logging. It involves obug, which also enables vite logging. It's a mess. I wanted to keep users out of it. Perhaps we could mention how our/users logging interact with debug logging
  • silent logging isn't really a level that you want to configure and/or use, right? Specifically Astro.logger.silent. Perhaps we could expand the docs of --silent to explain that users logs are hidden too

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I was referring to levels because this is a value allowed by AstroLoggerLevel. Naming... 😄 But, yeah, I agree those are not levels. I just feel there is something missing and that could be confusing for the users too.

Maybe this section is fine as it is because this is about the real levels. And, maybe, the AstroLoggerLevel description should be reworked a little instead? I mean to link the possible values ​​and what they refer to. This way we can link back to the Log Level section for info, warn, and error, and to the CLI flags for silent and debug.

I'm not yet sure about the wording to make a suggestion (too late to have a clear head 😴 ) but it might make more sense than updating the Log level section...

But, yes, updating --silent sounds like a good idea!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into it, we shouldn't provide silent and debug levels to the user


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';

```shell
astro dev --experimentalJson
astro sync --experimentalJson
astro build --experimentalJson
export default defineConfig({
experimental: {
logger: logHandlers.json({ level: "warn" })
}
});
```

The `astro/logger` package exposes a [`matchesLevel()`](#matcheslevel) helper to check the log level. This can be useful when [building a custom logger](#custom-loggers).

```js
import { matchesLevel } from "astro/logger";

matchesLevel("error", "info");
```

## Built-in loggers

The feature comes with three built-in loggers.
Astro offers built-in loggers that applications can use.

### `logHandlers.json`
Comment thread
ematipico marked this conversation as resolved.
Outdated

Expand All @@ -53,9 +111,9 @@ A logger that outputs messages in a JSON format. A log would look like this:
#### JSON logger options

<p>
**Type:** `{ pretty: boolean; level: AstroLoggerLevel; }`<br />
**Default:** `{ pretty: false, level: 'info' }`
<Since v="6.2.0" />
**Type:** `{ pretty: boolean; level: AstroLoggerLevel; }`<br />
**Default:** `{ pretty: false, level: 'info' }`
<Since v="6.4.0" />
</p>

The `json` logger accepts the following options:
Expand Down Expand Up @@ -84,9 +142,9 @@ A logger that prints messages using the `console` as its destination. Based on t
#### Console logger options

<p>
**Type:** `{ level: AstroLoggerLevel }`<br />
**Default:** `{ level: 'info' }`
<Since v="6.2.0" />
**Type:** `{ level: AstroLoggerLevel }`<br />
**Default:** `{ level: 'info' }`
<Since v="6.4.0" />
</p>

The `console` logger accepts the following options:
Expand All @@ -112,9 +170,9 @@ This is Astro's default logger.
#### Node logger options

<p>
**Type:** `{ level: AstroLoggerLevel }`<br />
**Default:** `{ level: 'info' }`
<Since v="6.2.0" />
**Type:** `{ level: AstroLoggerLevel }`<br />
**Default:** `{ level: 'info' }`
<Since v="6.4.0" />
</p>

The `node` logger accepts the following options:
Expand Down Expand Up @@ -150,104 +208,7 @@ export default defineConfig({
});
```

## 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](#astrologgerdestination) 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 this utility to understand if the message should be printed
if (matchesLevel(message.level, level)) {
// log message somewhere and take the 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 a message with `error` level.
- `warn()`, which emits a message with `warn` level.
- `info()`, which emits a 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 a level equal to 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" })
}
});
```

The `astro/logger` package exposes a [`matchesLevel()`](#matcheslevel) helper to check the log level. This can be useful when [building a custom logger](#custom-loggers).

```js
import { matchesLevel } from "astro/logger";

matchesLevel("error", "info");
```


## Types reference
Expand All @@ -262,7 +223,7 @@ This is the interface that custom loggers must implement.

<p>

**Type:** `(message: AstroLoggerMessage) => void`
**Type:** `(message: AstroLoggerMessage) => void`
</p>

A mandatory method called for each log and accepting an [`AstroLoggerMessage`](#astrologgermessage).
Expand All @@ -271,7 +232,7 @@ A mandatory method called for each log and accepting an [`AstroLoggerMessage`](#

<p>

**Type:** `() => Promise<void> | void`
**Type:** `() => Promise<void> | void`
</p>

An optional function called at the end of each request. This is useful for advanced loggers that need to flush log messages while keeping the connection to the destination alive.
Expand All @@ -280,7 +241,7 @@ An optional function called at the end of each request. This is useful for advan

<p>

**Type:** `() => Promise<void> | void`
**Type:** `() => Promise<void> | void`
</p>

An optional function called before a server is shut down. This function is usually called by adapters such as `@astrojs/node`.
Expand All @@ -298,26 +259,26 @@ The level of the message. Available variants:

<p>

**Type:** `{ label: string | null; level: AstroLoggerLevel; message: string; newLine: boolean; }`
**Type:** `{ label: string | null; level: AstroLoggerLevel; message: string; newLine: boolean; }`
</p>
The incoming object from the [`AstroLoggerDestination.write()`](#astrologgerdestinationwrite) function:
- `message`: 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
## APIs reference

The following APIs can be imported from the `astro/logger` specifier.

### `matchesLevel`
Comment thread
ematipico marked this conversation as resolved.
Outdated

<p>

**Type:** `matchesLevel(messageLevel: AstroLoggerLevel, configuredLevel: AstroLoggerLevel) => boolean`
**Type:** `matchesLevel(messageLevel: AstroLoggerLevel, configuredLevel: AstroLoggerLevel) => boolean`
</p>

Given two [log levels](#log-level), it returns whether the first level matches the second level.
Given two [log levels](#log-level), it returns whether the first level matches the second level.

```js
import { matchesLevel } from "astro/logger";
Expand Down
Loading