@dudousxd/nestjs-望远镜
核心项目 @dudousxd/nestjs-telescope ——请求+例外
监视器、记录器、基于ALS的批量关联、SQLite商店、
没有头的API, 和普鲁士。 见根
README.md 为快速开始.
保护路线
望远镜没有它自己的登录: 签入会保留你的应用程序. 一个选择,
authorize,决定谁可以使用控制台。 它在你的应用程序之后运行
认证(全球警卫和 decorators),这样 request.user 已经设定,以及
5种形式之一:
TelescopeModule.forRoot({
authorize: (ctx) => ctx.switchToHttp().getRequest().user?.isAdmin === true,
// authorize: ConsoleAccess, // @Injectable() implements ConsoleAuthorizer
// authorize: () => import('./console-access.js'), // lazy, default export
// authorize: { policy: ConsolePolicy, ability: 'view' }, // @nestjs/authorization
// authorize: 'console.telescope', // gate name (@dudousxd/nestjs-authz)
});
TelescopeUiModule.forRoot({ authorize: /* the same */ });
覆盖API,活流和MCP端点(mcpAuthorize
仅在 MCP 上替换; 将同样值传递给 UI 模块,该模块服务
该页。 返回时 true编号 :false, (中文(简体) ), { allowed: false, reason: 'unauthenticated' }
(401)或(401) { redirectTo: '/login' } (302页,401页 redirectTo
用于API;仪表板随之而来). 拒绝回答
{ "error": "forbidden" } (403)或(403) { "error": "unauthenticated" } (401个);a
放弃否认.
对于基于头的呼叫者(编码代理,Prometheus), authorizeByToken
检查 Authorization: Bearer <token> 恒定时间 :
TelescopeModule.forRoot({
mcp: true,
authorize: { policy: ConsolePolicy, ability: 'view' },
mcpAuthorize: authorizeByToken({ token: process.env.TELESCOPE_MCP_TOKEN }),
});
无 authorize 控制台开放于外部生产
NODE_ENV === 'production' 每个请求都回答 403 和一个警告被记录
在靴子上. decorators (认证,节奏,... ) 仍然运行第一,但
不要算 进入控制自己。 同样的规则覆盖了奥特尔
GET /metrics 刮 (forExporter(exporter, { authorize })) (中文(简体) ).
自己上马. routes: false 不挂载控制器( 抓取,
存储器、监视器和每项服务持续运行),模块输出什么
控制器需要。 扩展导出控制器并用
@UseConsoleAuthorize()同一个合同 没有争论,它运行
模块的 authorize; 列出您自定义的守护者( 应用类)
装饰器从下而上,所以认证警卫先运行然后设定 request.user:
@Controller('ops/telescope/api')
@UseConsoleAuthorize()
@UseGuards(AuthGuard)
class ConsoleApi extends TelescopeController {}
@Controller('ops/telescope')
@UseConsoleAuthorize()
@UseGuards(AuthGuard)
class ConsolePage extends TelescopeUiController {}
@Module({
imports: [
TelescopeModule.forRoot({
path: 'ops/telescope',
routes: false,
authorize: { policy: ConsolePolicy, ability: 'view' },
}),
TelescopeUiModule.forRoot({ path: 'ops/telescope', routes: false }),
],
controllers: [ConsoleApi, ConsolePage],
})
export class OpsModule {}
StreamController, (中文(简体) ), TelescopeMcpController 财务报告和审定财务报表 ClientErrorController 扩展
同样的方式。 保留 path 摆放到您所要求的地方
抓取跳过控制台自己的通话,页面将它的资产重新定位在它上.
每个挂载控制器也被打开 @nestjs/authorization是因为
拒绝默认全局守护Can.Anyone()),这样 authorize 决定;a
Can(...) 内 decorators 现仍在执行.
按设计公开:客户端-错误信标. POST <path>/api/client-errors
(关闭,直到 clientErrors.enabled)接收任何访客浏览器的报告,所以
也不需要钱 decorators 也没有 authorize它保持它的字节上限和每IP
费率限制。 把它放在你自己的认证,使用 routes: false 和扩展
ClientErrorController。 。 。 .
变异 (排队重试/重放/促进/重放/排队、排队、重放、重放),
)是切换到 mutations: true (电话:403) 谁可以
打电话给他们由 authorize 象其他的路线一样
对突变做出规定,自己上路,用
额外 @Can(...) (重新宣布路由装饰器在覆盖上).
内存界限
望远镜在内存中按设计持有有限制的工作装置:
heap ≈ prune.after × ingest rate × bytes per entry
没有无所不包的外泄,但三个因素成倍增加,如此高的摄入量 肥胖的输入率 与宽敞的保留窗口相比 仍然可以吸引很多 堆起来。 库将每个因数绑定在 抓取时间 所以一个宿主不能驱动一个 GC通过捕获大量内容来螺旋死亡.
Redaction 也是一个内存约束
record() 运行 redact() 同步进行. 除了遮住敏感密钥外
步行是 调离它把内容拍成一个简单的无参考的克隆人,
释放任何活物体图(例如水合ORM) req.user 参考文献
其实体经理、身份图和关系。 复制人是 范围 这样
脂肪图不能产生脂肪条目:
| 约束 | 默认 | 超越它 |
|---|---|---|
maxDepth | 8 | 树树 → '[Truncated: depth]' |
maxStringLength | 8_192 | 字符串 → 第一个 N 字符 + '…[truncated]' |
maxArrayLength | 200 | 第一个N项+ '[Truncated: N of M items]' |
maxNodes | 5_000 | 其余树种 '[Truncated: size]' |
默认慷慨: 一个普通请求 / 查询 / 缓存项被克隆
字节一致,从不行走 每个应用都通过 redact数字 :
TelescopeModule.forRoot({
redact: {
keys: ['x-internal-token'],
maxNodes: 2_000, // tighter cap for very fat content
},
});
[尖叫声] 修补必须留下 同步把它拖到被冲掉的捕捉器 成本,但OOM是一个生产主机:
record()然后只藏了RAW的内容,所以 最多bufferSizeoRM实体活体图被保留到冲出. 同步 克隆是负载的——它通过捕获时的分解来约束内存.
向下取出高容量溪流
取样是一个 存储量 杠杆( 请求已经取消 ) 路径。 Cache的命中率特别高 所以要保持 其中一小部分:
TelescopeModule.forRoot({
prune: { after: '1h' },
sampling: { cache: 0.1 }, // keep 10% of cache entries
});
与 prune 设定但 sampling 空出, 库在启动时记录一行 INFO
指着这个食谱.
尾巴抽样:保持重要
每类费率也可以是: 规则对象 来保持一小部分的噪音 总是保留你永远不希望被抽取的条目——错误和 慢点的 平数配置不变;对象形式纯为添加剂:
TelescopeModule.forRoot({
sampling: {
cache: { rate: 0.1, keepErrors: true, keepSlowMs: 500 },
},
});
rate——基础保持率(0-1),适用于该类普通条目.keepErrors总是保留看起来有错误的条目:afailed标签,content.failed === true,或content.statusCode >= 500(便宜,浅) 字段读取——在热道上不得走入深处.keepSlowMs- 总是保留其durationMs >= keepSlowMs。 。 。 .
这么说吧 { rate: 0.1, keepErrors: true, keepSlowMs: 500 } 保持10%的健康速度
缓存点击但 100% 失败或500毫秒.
看着 truncated 计数器
GET /telescope/api/health 报告a truncatedCount: 内容被击中的条目
被剪断了 一个非零,攀登值 意味着监视者是
捕获脂肪含量(通常为ORM实体)——固定为投放更轻
内容(例如:. req.user → { id, email })或收紧 redact / 添加 sampling。 。 。 .
仪表板的 概览 健康卡显示的数字与 截断
统计.
解释慢查询
望远镜是DB-不可知的,所以查询 EXPLAIN 是选入 主机钩 - 你吗
带来连接和方言。 供应 explainQuery还有仪表板
查询细节会长出 解释 按钮 (POST /telescope/api/queries/explain, (中文(简体) ),
{ entryId }),通过你的钩子重放被捕获的 SQL/绑定
调制 { plan }。钩子任意运行 EXPLAIN <sql> 反对你
数据库,因此将其连接范围 只读将表面抛出
干净 { message } 错误,不是崩溃 。 未设置时隐藏( H)meta.explainEnabled) (中文(简体) ).
TelescopeModule.forRoot({
explainQuery: async (sql, bindings) => {
const [rows] = await pool.query(`EXPLAIN FORMAT=JSON ${sql}`, bindings);
return rows;
},
});
点菜
与一个 prune 已配置保留窗口, GET /telescope/api/retention
报告已解决的窗口和 POST /telescope/api/retention/prune 运行 a
随叫随到 普鲁宁是个 变异,所以像队列控制台 它需要
mutations: true (单位:千美元)403 (除此以外) 400 否时 prune 窗口已设置。 概览 保留
卡片显示 普鲁纳 按钮meta.pruneEnabled) (中文(简体) ).
警报
当规则出行时,望远镜向每个已配置的信道发出警报. 规则运行 ; 一个失败的信道被记录 永远 撞倒宿主.
import { slackChannel, webhookChannel } from '@dudousxd/nestjs-telescope';
TelescopeModule.forRoot({
alerts: {
channels: [
slackChannel(process.env.SLACK_WEBHOOK_URL!), // formatted Slack message
webhookChannel('https://relay.example/alerts'), // raw JSON POST
],
every: '1m', // evaluation cadence (default '1m')
cooldown: '15m', // per-rule re-notify suppression (default '15m')
rules: [
{ type: 'exception-rate', window: '5m', threshold: 10 },
{ type: 'slow-request-rate', window: '5m', thresholdMs: 1000, count: 20 },
{ type: 'dropped-entries', threshold: 100 },
],
},
});
exception-rate- 火灾时>= threshold记录了例外情况window。 。 。 .slow-request-rate- 火灾时>= count请求慢于thresholdMs记录在window。 。 。 .dropped-entries- 录音机时开火droppedCount成长>= threshold自上次评价以来(a delta).
一个配置 alerts 没有 channels 或为空 rules 是一个
已关闭的启动错误。 。 。 . webhookChannel POST 此有效载荷:
{ "rule": { "...": "..." }, "value": 12, "threshold": 10, "firedAt": "2026-06-04T00:00:00.000Z", "instanceId": "host-1" }
meta.alerts 报告 { enabled, ruleCount }。 。 。 .
超载保护
望远镜监视进程事件- 启动时滞( U)perf_hooks.monitorEventLoopDelay(中文(简体) )
时间间隔和 暂停记录器 当p99 后缀交叉 a
阈值, 恢复 一旦它恢复了 所以一个望远镜 负载永远不会
扩大一个事件。 暂停时, 新建 record() 电话被取消。 这是 日期
默认值200毫秒; 当 perf_hooks 没有显示器.
TelescopeModule.forRoot({
overloadProtection: true, // default — pause above 200ms p99
// overloadProtection: { maxEventLoopLagMs: 100 }, // tune the threshold
// overloadProtection: false, // disable entirely
});
请求重播
GET /telescope/api/entries/:id/replay 重新发布俘虏 request 条目
对抗本地应用程序( o)127.0.0.1)并返回结果。 实际重放
点击应用程序(它可能写),所以是一个 变异: 如prune/ 队列动作
这需要 mutations: true (单位:千美元)403 (否则) 重播请求包含
x-telescope-replay: 1 标题( 这样主机就可以识别或跳过) 和条形
饼干/授权/主机信头——不得默默地再利用捕获的
届会。 重播的请求被标记 replay。仪表板的要求细节
暴露一个 重播 按钮 mutations: true 已经设定.
MCP 服务器( 编码代理)
与 mcp 启用,望远镜服务 a 无国籍人 JSON-RPC 模型背景
议定书 服务器在可流的 HTTP 位置
POST /telescope/api/mcp,所以一个编码代理(Claude Code, Cursor,...)可以调试
直接取自所获取的数据——由相同的存储/数据/诊断API支持
作为仪表板。 JSON-RPC是手卷式的(不依赖SDK);无国籍,
运输工具 GET 溪流返回 405 财务报告和审定财务报表 DELETE 是一个 200 没有.
TelescopeModule.forRoot({
mcp: true, // omit (or false) and the endpoint answers 404
});
与克洛德密码登记:
claude mcp add --transport http telescope http://localhost:3000/telescope/api/mcp
工具 : list_entries (按类型/搜索/tag/时间窗口过滤), get_entry (一个)
条目+其全部批次), get_batch, (中文(简体) ), get_stats (最后一小时健康快照),以及
diagnose_exception (AI诊断,何时) ai 已配置). 保护它就像
每个控制台路线,通过 authorize,或 mcpAuthorize 仅此路径
(代理客户端发送信头: mcpAuthorize: authorizeByToken({ token })),或
由你自己的控制器.