{"id":48429085,"url":"https://github.com/iqaicom/alert-logger","last_synced_at":"2026-04-12T16:01:27.283Z","repository":{"id":349387021,"uuid":"1202114285","full_name":"IQAIcom/alert-logger","owner":"IQAIcom","description":"Smart alert aggregation for any destination. Exponential suppression, auto-resolution detection, and adapters for Discord, Slack, Sentry, and more.","archived":false,"fork":false,"pushed_at":"2026-04-06T11:29:46.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-07T11:02:27.878Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IQAIcom.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-05T16:06:03.000Z","updated_at":"2026-04-06T11:29:23.000Z","dependencies_parsed_at":null,"dependency_job_id":"e4f6403b-ef2b-4cf5-acc1-f7f37de3ae6c","html_url":"https://github.com/IQAIcom/alert-logger","commit_stats":null,"previous_names":["iqaicom/alert-logger"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/IQAIcom/alert-logger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IQAIcom%2Falert-logger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IQAIcom%2Falert-logger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IQAIcom%2Falert-logger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IQAIcom%2Falert-logger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IQAIcom","download_url":"https://codeload.github.com/IQAIcom/alert-logger/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IQAIcom%2Falert-logger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31554109,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-06T10:00:28.772Z","updated_at":"2026-04-12T16:01:27.255Z","avatar_url":"https://github.com/IQAIcom.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @iqai/alert-logger\n\nSmart alert aggregation for any destination. One call to log everywhere — Discord, Sentry, Slack, console, or your own adapter.\n\nStop drowning in alert storms. `@iqai/alert-logger` groups repeated errors using exponential suppression, sends periodic digests during sustained incidents, and notifies you when issues resolve — automatically.\n\n## ✨ Features\n\n- **Unified API** — `logger.error('msg', error, { fields })` routes to every configured adapter\n- **Exponential suppression** — alerts fire at 1, 2, 4, 8, 16, 32, 64... then switch to periodic digests\n- **Resolution detection** — get a \"resolved\" message when an error stops occurring\n- **Error fingerprinting** — same bug from different requests groups automatically (strips IDs, timestamps, UUIDs)\n- **Multi-channel routing** — route by severity level or custom tags to different channels\n- **Adapter architecture** — Discord, Slack, Telegram, and Console built-in; or build your own\n- **NestJS integration** — drop-in `@Global()` module with automatic exception filter\n- **NextJS integration** — `instrumentation.ts` hook with automatic `onRequestError` handler\n- **Per-environment config** — different suppression thresholds, levels, and ping rules for prod/staging/dev\n- **Environment badges** — `[PROD]`, `[STG]`, `[DEV]` prefix on every alert so you never confuse environments\n- **Request context (NestJS)** — auto-attaches request ID, method, path via `AsyncLocalStorage`\n- **Rate-limit aware** — respects per-adapter limits, queues on failure, drains on recovery\n- **Zero framework deps in core** — just `node:crypto` and `fetch`\n\n## 📦 Install\n\n```bash\nnpm install @iqai/alert-logger\n# or\npnpm add @iqai/alert-logger\n# or\nyarn add @iqai/alert-logger\n```\n\n## 🚀 Quick Start\n\n### Standalone (any Node.js project)\n\n```ts\nimport { AlertLogger, DiscordAdapter, SlackAdapter, TelegramAdapter } from '@iqai/alert-logger'\n\nconst logger = AlertLogger.init({\n  adapters: [\n    new DiscordAdapter({\n      webhookUrl: process.env.DISCORD_WEBHOOK_URL,\n      channels: { critical: process.env.DISCORD_ONCALL_WEBHOOK },\n      mentions: { critical: ['\u003c@\u0026oncall-role\u003e'] },\n    }),\n    new SlackAdapter({\n      webhookUrl: process.env.SLACK_WEBHOOK_URL,\n      mentions: { critical: ['\u003c@U0123ONCALL\u003e'] },\n    }),\n    new TelegramAdapter({\n      botToken: process.env.TELEGRAM_BOT_TOKEN,\n      chatId: process.env.TELEGRAM_CHAT_ID,\n      topics: { critical: 42, warning: 43, info: 44 },\n    }),\n  ],\n  serviceName: 'my-service',\n})\n\n// Simple error — goes to all adapters with full context\nlogger.error('Payment failed', error)\n\n// With metadata\nlogger.error('Payment failed', error, {\n  fields: { orderId: 'abc', amount: '$50' },\n  tags: ['billing'],\n})\n\n// Warning\nlogger.warn('Queue depth high', { fields: { depth: 150 } })\n\n// Info\nlogger.info('Deployment complete', { fields: { version: '1.2.3' } })\n```\n\n### NestJS\n\n```bash\nnpm install @iqai/alert-logger @nestjs/common @nestjs/config\n```\n\n```ts\n// app.module.ts\nimport { AlertLoggerModule } from '@iqai/alert-logger/nestjs'\nimport { DiscordAdapter } from '@iqai/alert-logger'\n\n@Module({\n  imports: [\n    AlertLoggerModule.forRoot({\n      adapters: [\n        new DiscordAdapter({ webhookUrl: process.env.DISCORD_WEBHOOK_URL }),\n      ],\n      serviceName: 'backend',\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n```ts\n// any.service.ts — AlertLoggerService is globally available\nimport { AlertLoggerService } from '@iqai/alert-logger/nestjs'\n\n@Injectable()\nexport class PaymentService {\n  constructor(private readonly alert: AlertLoggerService) {}\n\n  async charge(order: Order) {\n    try {\n      await this.process(order)\n    } catch (error) {\n      this.alert.error('Payment failed', error, {\n        fields: { orderId: order.id, amount: order.total },\n      })\n      throw error\n    }\n  }\n}\n```\n\nUnhandled 5xx errors are caught automatically by the built-in global exception filter — no extra code needed.\n\n### NextJS\n\n```bash\nnpm install @iqai/alert-logger next\n```\n\n```ts\n// instrumentation.ts\nimport { createAlertLoggerHandler, captureRequestError } from '@iqai/alert-logger/nextjs'\nimport { DiscordAdapter } from '@iqai/alert-logger'\n\nexport function register() {\n  createAlertLoggerHandler({\n    adapters: [\n      new DiscordAdapter({ webhookUrl: process.env.DISCORD_WEBHOOK_URL }),\n    ],\n    serviceName: 'frontend',\n  })\n}\n\nexport { captureRequestError as onRequestError }\n```\n\nThat's it. All server-side errors (API routes, server components, server actions) are captured automatically.\n\n## 🧠 How Aggregation Works\n\nWhen the same error fires repeatedly, the library doesn't spam your channel:\n\n| Phase | Trigger | What gets sent |\n|-------|---------|----------------|\n| **Onset** | 1st occurrence | Full alert with stack trace, fields, tags |\n| **Ramp** | 2nd, 4th, 8th, 16th, 32nd, 64th | Compact: `\"Payment failed (x8 — 4 suppressed)\"` |\n| **Sustained** | \u003e64 in window | Digest every 5min: `\"x4,812 in last 5m\"` |\n| **Resolution** | 0 hits for 2min | `\"Resolved: Payment failed — 12,847 total over 23m\"` |\n\nErrors are grouped by **fingerprint** — the library strips variable parts (IDs, timestamps, UUIDs, hex addresses) from the error message and hashes it with the top stack frames. Same bug, different request = same group.\n\n## 🌍 Per-Environment Config\n\nSame codebase, different behavior per environment. Dev won't bug you as much as prod:\n\n```ts\nAlertLogger.init({\n  adapters: [new DiscordAdapter({ webhookUrl: '...' })],\n  environment: process.env.NODE_ENV,\n  environments: {\n    production: {\n      levels: ['warning', 'critical'],\n      aggregation: { digestIntervalMs: 5 * 60_000 },\n    },\n    staging: {\n      levels: ['critical'],           // only errors, no warnings\n      aggregation: { digestIntervalMs: 15 * 60_000 },\n    },\n    development: {\n      levels: ['critical'],\n      aggregation: { rampThreshold: 8, digestIntervalMs: 30 * 60_000 },\n    },\n  },\n})\n```\n\nEvery alert is prefixed with an environment badge (`[PROD]`, `[STG]`, `[DEV]`) so you never mistake staging for production.\n\n## 📡 Multi-Channel Routing\n\nEach adapter owns its routing. Route alerts to different channels/topics by severity or tags:\n\n```ts\nAlertLogger.init({\n  adapters: [\n    // Discord: route by level to different webhook URLs\n    new DiscordAdapter({\n      webhookUrl: process.env.DISCORD_DEFAULT_WEBHOOK,\n      channels: {\n        critical: process.env.DISCORD_ONCALL_WEBHOOK,\n        warning: process.env.DISCORD_WARNINGS_WEBHOOK,\n      },\n      tags: {\n        indexer: process.env.DISCORD_INDEXER_WEBHOOK,\n      },\n      mentions: {\n        critical: ['\u003c@\u0026oncall-role\u003e'],\n      },\n    }),\n\n    // Slack: same pattern with Incoming Webhook URLs\n    new SlackAdapter({\n      webhookUrl: process.env.SLACK_DEFAULT_WEBHOOK,\n      channels: {\n        critical: process.env.SLACK_ONCALL_WEBHOOK,\n      },\n      mentions: {\n        critical: ['\u003c@U0123ONCALL\u003e'],\n      },\n    }),\n\n    // Telegram: route by level to forum topics\n    new TelegramAdapter({\n      botToken: process.env.TELEGRAM_BOT_TOKEN,\n      chatId: process.env.TELEGRAM_CHAT_ID,\n      topics: {\n        critical: 42,\n        warning: 43,\n        info: 44,\n      },\n      tags: {\n        indexer: 99,\n      },\n      mentions: {\n        critical: ['@oncall_dev'],\n      },\n    }),\n  ],\n})\n```\n\n## 🔌 Custom Adapters\n\nImplement the `AlertAdapter` interface to send alerts anywhere:\n\n```ts\nimport { AlertAdapter, FormattedAlert, AlertLevel } from '@iqai/alert-logger'\n\nclass PagerDutyAdapter implements AlertAdapter {\n  readonly name = 'pagerduty'\n  levels: AlertLevel[] = ['critical']\n\n  rateLimits() {\n    return { maxPerWindow: 60, windowMs: 60_000 }\n  }\n\n  async send(alert: FormattedAlert): Promise\u003cvoid\u003e {\n    // POST to PagerDuty Events API\n  }\n}\n```\n\n## ⚙️ Full Configuration\n\n```ts\nAlertLogger.init({\n  // Required — each adapter configures its own routing\n  adapters: [\n    new DiscordAdapter({\n      webhookUrl: '...',\n      channels: {},                // level → webhook URL\n      tags: {},                    // tag → webhook URL\n      mentions: {},                // level → mention strings\n    }),\n  ],\n\n  // Identity\n  serviceName: 'backend',         // defaults to hostname\n  environment: 'production',      // attached to every alert\n\n  // Aggregation tuning\n  aggregation: {\n    rampThreshold: 64,             // switch from ramp to digest phase\n    digestIntervalMs: 5 * 60_000,  // how often to send digests\n    resolutionCooldownMs: 2 * 60_000, // silence before \"resolved\"\n  },\n\n  // Per-environment overrides\n  environments: {\n    production: { levels: ['warning', 'critical'] },\n    staging: { levels: ['critical'] },\n    development: { levels: ['critical'], aggregation: { rampThreshold: 8 } },\n  },\n\n  // Reliability\n  queue: {\n    maxSize: 500,                  // retry buffer size\n    persistPath: null,             // optional disk persistence\n  },\n\n  // Fingerprinting\n  fingerprint: {\n    stackDepth: 3,                 // stack frames to hash\n    normalizers: [],               // custom regex replacements\n  },\n})\n```\n\n## 🧩 Adapters Ecosystem\n\n| Adapter | Package | Status |\n|---------|---------|--------|\n| Discord | `@iqai/alert-logger` (built-in) | Available |\n| Slack | `@iqai/alert-logger` (built-in) | Available |\n| Telegram | `@iqai/alert-logger` (built-in) | Available |\n| Console | `@iqai/alert-logger` (built-in) | Available |\n| Sentry | `@iqai/alert-logger-sentry` | Planned |\n\n## 🤝 Contributing\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## 📄 License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiqaicom%2Falert-logger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiqaicom%2Falert-logger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiqaicom%2Falert-logger/lists"}