@dudousxd/nestjs-telescope
Version:
Laravel Telescope-style observability console for NestJS — core: watchers, recorder, correlation, SQLite store, headless API.
173 lines (138 loc) • 6.35 kB
Markdown
name: telescope-alerts-ai
description: >-
Alerting and AI exception diagnosis in nestjs-telescope. Configure alerts with
channel helpers (slackChannel, webhookChannel, customChannel) and rules
(new-exception, exception-rate, slow-request-rate, dropped-entries,
metric-threshold); understand the default that 4xx HttpExceptions are NOT
recorded as exception entries (exceptions.captureHttp4xx restores it); wire AI
diagnosis with ai.{diagnoser,mode} + createAiSdkDiagnoser({ model }) from
@dudousxd/nestjs-telescope-ai over any Vercel AI SDK model (Bedrock/OpenAI/
Anthropic); enable public client-error ingestion. Use for "Slack alert on new
errors", "AI diagnose exceptions", "why is my 403 not in the exceptions tab".
license: MIT
metadata:
type: core
library: "@dudousxd/nestjs-telescope"
library_version: "1.12.0"
framework: nestjs
# Alerting & AI diagnosis
Telescope can page you when a genuinely new exception family appears (or a rate
threshold trips) and, optionally, attach an AI probable-cause report to the
exception. Both are `forRoot` options.
## Setup
Fan alerts to one or more channels; `rules` selects what fires. A configured
`alerts` with no destination or empty `rules` is a fail-closed boot error.
```ts
import { TelescopeModule, slackChannel } from '@dudousxd/nestjs-telescope';
TelescopeModule.forRoot({
alerts: {
channels: [slackChannel(process.env.SLACK_WEBHOOK_URL!)],
dashboardUrl: 'https://telescope.example.com/telescope/', // enables Slack deep links
rules: [
{ type: 'new-exception', window: '1h' },
{ type: 'exception-rate', window: '5m', threshold: 10 },
],
},
});
```
Source: `packages/core/src/alerts/alert-rule.ts` (`AlertsOptions`, `AlertRule`),
`packages/core/src/alerts/alert-channel.ts` (`slackChannel` / `webhookChannel` /
`customChannel`).
## Core patterns
### Pattern 1 — the alert rule types
`rules` is a non-empty array of discriminated unions:
```ts
rules: [
{ type: 'new-exception', window: '1h' }, // a brand-new family
{ type: 'exception-rate', window: '5m', threshold: 10 }, // > N exceptions / window
{ type: 'slow-request-rate', window: '5m', thresholdMs: 1000, count: 5 }, // N slow reqs / window
{ type: 'dropped-entries', threshold: 100 }, // Telescope itself dropped entries
];
```
Source: `packages/core/src/alerts/alert-rule.ts` (`AlertRule`).
### Pattern 2 — AI exception diagnosis
Add the AI package and supply a `diagnoser`. `mode: 'on-demand'` (default) only
runs on the dashboard button; `mode: 'auto'` also diagnoses a NEW family on the
flush path and enriches its `new-exception` alert.
```ts
import { TelescopeModule } from '@dudousxd/nestjs-telescope';
import { createAiSdkDiagnoser } from '@dudousxd/nestjs-telescope-ai';
import { openai } from '@ai-sdk/openai';
TelescopeModule.forRoot({
ai: {
diagnoser: createAiSdkDiagnoser({ model: openai('gpt-4o-mini'), maxOutputTokens: 1024 }),
mode: 'on-demand',
},
});
```
The diagnoser is provider-agnostic via the Vercel AI SDK (swap `openai(...)` for
`bedrock(...)` / `anthropic(...)`). It runs on already-redacted exception data.
Source: `packages/ai/src/ai-sdk-diagnoser.ts` (`createAiSdkDiagnoser`),
`packages/core/src/ai/diagnoser.ts` (`TelescopeAiOptions`),
`website/content/docs/recipes/ai-exception-diagnosis.mdx`.
### Pattern 3 — public client-error ingestion
Let browsers POST errors to `<telescope>/api/client-errors`; they become
`client_exception` entries through the same pipeline. Disabled by default.
```ts
TelescopeModule.forRoot({
clientErrors: {
enabled: true,
maxBodyBytes: 32_768,
rateLimit: { perMinute: 60 },
authorize: (req) => hasValidSession(req), // optional; throw/false => 403
},
});
```
Source: `packages/core/src/nest/telescope.options.ts` (`ClientErrorsOptions`).
## Common mistakes
### Mistake 1 — expecting every 4xx in the exceptions tab
```ts
// Wrong — assuming a 403/404/validation-400 shows up as an exception entry. By
// default it does NOT (it's control flow), so no family, no alert, no diagnosis.
TelescopeModule.forRoot({ alerts: { channels: [slackChannel(url)], rules: [{ type: 'new-exception', window: '1h' }] } });
```
```ts
// Correct — opt in only if you truly treat 4xx as incidents worth grouping/paging.
TelescopeModule.forRoot({
exceptions: { captureHttp4xx: true },
alerts: { channels: [slackChannel(url)], rules: [{ type: 'new-exception', window: '1h' }] },
});
```
Mechanism: a 4xx `HttpException` is expected control flow; recording it would open
a new family and page Slack for every permission denial. The 4xx is still on the
`request` entry's `statusCode`. Source:
`packages/core/src/nest/telescope.options.ts` (`ExceptionsOptions`).
### Mistake 2 — `alerts` with rules but no channel
```ts
// Wrong — no channels and no webhookUrl is a fail-closed BOOT error.
TelescopeModule.forRoot({ alerts: { rules: [{ type: 'new-exception', window: '1h' }] } });
```
```ts
// Correct — supply at least one destination.
TelescopeModule.forRoot({
alerts: { channels: [slackChannel(url)], rules: [{ type: 'new-exception', window: '1h' }] },
});
```
Mechanism: a configured `alerts` with no destination (neither `channels` nor the
legacy `webhookUrl`) or empty `rules` throws at boot rather than silently never
alerting. Source: `packages/core/src/alerts/alert-rule.ts` (`AlertsOptions`).
### Mistake 3 — hand-rolling a Slack POST instead of the channel helper
```ts
// Wrong — a custom function that posts plain text loses the Block Kit formatting,
// route/user context, and the "Open in Telescope" deep link.
alerts: { channels: [customChannel(async (a) => fetch(url, { method: 'POST', body: a.title }))], rules };
```
```ts
// Correct — use slackChannel; pass dashboardUrl for deep links.
alerts: {
channels: [slackChannel(process.env.SLACK_WEBHOOK_URL!)],
dashboardUrl: 'https://telescope.example.com/telescope/',
rules,
};
```
Mechanism: `slackChannel` renders Block Kit with route/user and builds an
`${dashboardUrl}#/entries/exception/${id}` deep link when `dashboardUrl` is set;
`customChannel` is the escape hatch for non-Slack sinks. Source:
`packages/core/src/alerts/alert-channel.ts`, `packages/core/src/alerts/alert-rule.ts`.