---
title: Custom Formatting
description: 自定义日志消息格式、token 集合、颜色和时间戳
---

用 `customLogFormat` 模板字符串控制每条 access log 的渲染。模板里出现哪些 `{token}`,Elogs 就计算哪些 —— 没出现的 token 在 hot path 上完全不跑(`process.hrtime`、颜色包装等)。

## 基本用法

```ts
import { createElogs } from '@eastgold15/elogs'

createElogs({
  config: {
    customLogFormat: '{now} {level} {duration} {method} {pathname} {status}',
  },
})
```

不设置 `customLogFormat` 时,Elogs 用内置默认:

```
🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip} {context}
```

`🦊` 是字面量 emoji,不是 token —— 直接出现在模板里原样输出。

## 可用 token

| Token | 渲染值 |
| --- | --- |
| `{now}` | 时间戳(受 `timestamp` 配置控制) |
| `{epoch}` | Unix 毫秒时间戳(整数) |
| `{level}` | 日志级别;开启颜色时按级别背景色 chip |
| `{method}` | HTTP method;开启颜色时按方法着色(GET 绿、POST 蓝、DELETE 红 等) |
| `{pathname}` | 请求路径;`{path}` 是别名 |
| `{query}` | 原始 query 字符串(如 `?id=123`) |
| `{status}` | 响应状态码;开启颜色时按状态范围着色(2xx 绿、4xx 黄、5xx 红) |
| `{statusText}` | 状态文本(`404 → Not Found`);未识别时为空 |
| `{duration}` | 请求耗时(`12ms` / `1.5s` / `11s`) |
| `{message}` | 自定义消息文本 |
| `{ip}` | 客户端 IP(`x-forwarded-for` / `x-real-ip`);开启 `ip: true` 才渲染 |
| `{context}` | context JSON 字符串(仅当 `showContextTree: false`;默认 tree 模式留空) |
| `{speed}` | 极慢请求标记,`duration >= verySlowThreshold` 时追加 `⚡ <pathname>` |
| `{service}` | 占位符 —— **不会替换为服务名**;`service` 配置在主行前以 `[name]` 前缀渲染 |
| `{requestId}` | 占位符 —— 当前固定为空,留作未来扩展 |

token 解析大小写不敏感(`{RequestId}` 等价 `{requestid}`),但 token 本身的拼写要跟 `replacements` 表对上才能替换,否则保留为字面量。

## 示例

### 极简

```ts
customLogFormat: '{method} {pathname} {status}'
```

```
GET /api/users 200
```

### 带服务名前缀 + emoji

`{service}` token 不会被替换 —— 服务名通过 `service` 配置项以 `[name] ` 前缀加在主行最前面:

```ts
createElogs({
  config: {
    service: 'my-api',
    customLogFormat: '🦊 {now} {level} {duration} {method} {pathname} {status} {message}',
  },
})
```

输出(非 TTY 或 `useColors: false`):

```
[my-api] 🦊 2025-04-13T15:00:19.123Z INFO 12ms GET /api/users 200 User viewed profile
```

颜色终端上,`{level}` 在背景色 chip 里,`{method}` 按方法着色,`{status}` 按状态范围着色。

### 状态文本

```ts
customLogFormat: '{method} {pathname} {status} {statusText}'
```

```
GET /api/users 200 OK
DELETE /api/users/42 404 Not Found
```

`{statusText}` 内部用一张固定的代码→文本表(`200→OK`、`404→Not Found`、`500→Internal Server Error` 等)。未识别的状态码渲染为空字符串。

### 慢请求 + 极慢请求

```ts
createElogs({
  config: {
    customLogFormat: '{now} {level} {duration} {method} {pathname} {status} {message}{speed}',
    slowThreshold: 500,        // 耗时 ≥ 500ms → 末尾追加 ⚡ slow
    verySlowThreshold: 1000,   // 耗时 ≥ 1s   → {speed} 渲染为 ⚡ /api/...
  },
})
```

- `slowThreshold` —— 主行末尾无条件追加 ` ⚡ slow`(不依赖 token)
- `verySlowThreshold` —— `{speed}` token 渲染为 ` ⚡ <pathname>`(可选,只在你模板里写 `{speed}` 才生效)

两者不冲突:`{speed}` 优先;`{speed}` 不出现时,如果 `duration >= slowThreshold` 仍会追加 `⚡ slow`。

### 包含 query string

默认 `pathname` 不带 query。`logQueryParams: true` 后,`{pathname}` 会拼上 query:

```ts
createElogs({
  config: {
    logQueryParams: true,
    customLogFormat: '{method} {pathname}',
  },
})
```

```
GET /api/users?page=2
```

如果只想单独显示 query 不混入 `{pathname}`,直接用 `{query}` token。

### 显示客户端 IP

```ts
createElogs({
  config: {
    ip: true, // 或 showIp: true(legacy 别名)
    customLogFormat: '{method} {pathname} {status} {ip}',
  },
})
```

IP 从 `x-forwarded-for` 第一段或 `x-real-ip` 取;本地测试无这些 header 时为空。

## 时间戳

```ts
import type { ElogsConfig } from '@eastgold15/elogs'

const config: ElogsConfig = {
  customLogFormat: '{now} {level} {method} {pathname}',
  timestamp: 'yyyy-mm-dd HH:MM:ss',
}
```

`timestamp` 字段:

- 字符串 —— 模板,支持 `yyyy` / `mm` / `dd` / `HH` / `MM` / `ss` / `SSS` 占位
- `{ format: string }` 对象 —— 等价,但用对象字面量(方便和别的 config 一起构建)
- 不设置 —— `new Date().toISOString()`(`2025-04-13T15:00:19.123Z`)

```ts
const config: ElogsConfig = {
  timestamp: { format: 'yyyy-mm-dd HH:MM:ss.SSS' },
}
```

`{epoch}` 走 `Date.now()` 拿 Unix 毫秒 —— 不受 `timestamp` 配置影响。

## 颜色

```ts
createElogs({
  config: {
    useColors: true, // 默认 true,且仅 TTY 生效
  },
})
```

- `useColors: true` + TTY —— 开启颜色
- `useColors: false` —— 强制关闭(写到 file / 转发到其它服务时通常关闭)
- `useColors: true` 但非 TTY(pipe / 重定向)—— 自动关闭,避免 ANSI 码污染日志文件

`chalk` 负责颜色:level 背景色 chip、method 按动词着色、status 按范围着色、pathname 白亮、duration 灰。

## 错误日志的格式

`customLogFormat` 同时作用于 access log 和 error log。`logger.handleHttpError(...)` 走的是同一 `emit` 管道,渲染逻辑一致。错误响应里 `data.message` 是错误信息(填到 `{message}`),`data.status` 是 HTTP 状态码(填到 `{status}`)。`{context}` 在错误日志里会渲染 `error.name` / `error.message` 等。

## 性能

模板里**没出现的 token 完全跳过**。比如:

- 模板里没 `{duration}` —— 不会跑 `process.hrtime`
- 模板里没 `{speed}` —— 不会比较 `verySlowThreshold`
- 模板里没 `{ip}` —— 不会读 `x-forwarded-for` / `x-real-ip`
- 模板里没 `{query}` —— 不会解析 URL search

代价是首次创建 logger 时(per logger 实例)解析一次 `TOKEN_PATTERN = /\{([a-zA-Z]+)\}/g`,把出现过的 token 存到 `Set<string>`,后续 emit 只检查 set。热路径上零正则、零字符串扫描。

## 最佳实践

- **生产模板保持简洁** —— 留给下游日志聚合器(Grafana / Datadog)做可视化
- **慢请求标记靠 `slowThreshold` + `{speed}`** —— 比手动在模板里写 `duration > 500` 干净
- **写文件时显式 `useColors: false`** —— 避免 ANSI 码混进文件
- **`logQueryParams: true` 谨慎** —— query 可能含敏感 token(API key / 签名),结合 `autoRedact` 用
- **不要在模板里拼 PII** —— `email: {email}` 这类用 `context` 配合 `autoRedact` 走,别直接进模板字符串
- **`{epoch}` 给机器读,`{now}` 给人读** —— 同一行可同时放两个,自动化解析 `{epoch}`,肉眼读 `{now}`

## API 参考

- [`ElogsConfig.customLogFormat`](https://elogs.vercel.app/api/configuration#elogs-config) — 模板字符串
- [`ElogsConfig.timestamp`](https://elogs.vercel.app/api/configuration#elogs-config) — `string | { format: string }`
- [`ElogsConfig.useColors`](https://elogs.vercel.app/api/configuration#elogs-config) / `ip` / `showIp` / `logQueryParams` / `service` / `slowThreshold` / `verySlowThreshold` / `showContextTree` / `contextDepth`
- [`FormatContext`](https://elogs.vercel.app/api/types#format-context) — per-logger 提升的格式常量(token set、template、useColors、thresholds)
