Skip to content

feat: error alerting + configurable log level (#93 & #94) - #168

Open
gabrielujelistic-collab wants to merge 1 commit into
stellar-vortex-protocol:mainfrom
gabrielujelistic-collab:feat/error-alerting-and-configurable-log-level
Open

feat: error alerting + configurable log level (#93 & #94)#168
gabrielujelistic-collab wants to merge 1 commit into
stellar-vortex-protocol:mainfrom
gabrielujelistic-collab:feat/error-alerting-and-configurable-log-level

Conversation

@gabrielujelistic-collab

Copy link
Copy Markdown

feat: error alerting + configurable log level

Summary

This PR addresses two observability gaps in the vortex-backend service:

  1. Issue Add error alerting wired through HttpExceptionFilter's 500 branch #93 — Unhandled exceptions were silently swallowed after being logged; no human was ever alerted.
  2. Issue Make log level configurable via environment variable #94 — The Winston log level was hardcoded, making it impossible to increase verbosity for a production debug session or suppress noise during tests.

Changes

Issue #93 — Error alerting via Sentry (HttpExceptionFilter 500 branch)

Problem: HttpExceptionFilter.catch() logged unexpected errors with logger.error() but nothing actually paged anyone. A recurring 500 could go unnoticed indefinitely.

Solution: Wire @sentry/node into the generic-Error catch branch — and only that branch — so alert fatigue on routine 404/400 HttpExceptions is avoided.

New file: src/common/sentry.ts

import * as Sentry from "@sentry/node";

export function initSentry(): void {
  const dsn = process.env.SENTRY_DSN;
  if (!dsn) return;          // no-op when DSN not set (local dev / CI)
  Sentry.init({ dsn, environment: process.env.NODE_ENV ?? "development", ... });
}

export function captureException(err: unknown): void {
  Sentry.captureException(err);   // safe even when not initialised
}
  • initSentry() is a no-op when SENTRY_DSN is absent — no environment changes are required for local development or CI.
  • captureException() delegates to the Sentry SDK which internally guards against uninitialised state.

Modified: src/common/http-exception.filter.ts

// Only fires for generic (unhandled) errors, never for expected HttpExceptions
const err = exception instanceof Error ? exception : new Error("Unknown error");
logger.error(err.stack ?? err.message);
captureException(err);   // ← NEW
response.status(500).json({ error: err.message || "Internal server error" });

Modified: src/main.ts

initSentry() is called before NestFactory.create() so that any startup crash is also captured.

New env var: SENTRY_DSN

Variable Default Description
SENTRY_DSN "" (disabled) Sentry project DSN. Leave blank to disable alerting.

Issue #94 — Configurable log level via LOG_LEVEL

Problem: src/common/logger.ts created a Winston logger with a hardcoded level. There was no way to:

  • Turn up verbosity (debug/verbose) against a production issue.
  • Silence noisy logs in test runs.

Solution: Export a resolveLogLevel() helper that applies the following priority:

  1. LOG_LEVEL env var (explicit override, works in any environment)
  2. NODE_ENV heuristic: "info" in production, "debug" everywhere else

Modified: src/common/logger.ts

export function resolveLogLevel(): string {
  const explicit = process.env.LOG_LEVEL;
  if (explicit) return explicit;
  return process.env.NODE_ENV === "production" ? "info" : "debug";
}

export const logger = createLogger({
  level: resolveLogLevel(),   // ← was hardcoded
  ...
});

Modified: src/config/env.validation.ts

LOG_LEVEL: Joi.string()
  .valid("error", "warn", "info", "http", "verbose", "debug", "silly")
  .default("debug"),

Follows the existing Joi validation pattern. The schema default acts as a documentation hint and validation guard; resolveLogLevel() handles the runtime NODE_ENV heuristic.

New env var: LOG_LEVEL

Variable Default (dev/test) Default (production) Valid values
LOG_LEVEL debug info error | warn | info | http | verbose | debug | silly

Test output

Unit tests (32 passed)

PASS src/common/http-exception.filter.spec.ts
PASS src/common/logger.spec.ts
PASS src/intents/intents.service.spec.ts
PASS src/solvers/solvers.service.spec.ts
PASS src/tokens/tokens.service.spec.ts

Test Suites: 5 passed, 5 total
Tests:       32 passed, 32 total
Time:        5.118 s

New test cases added for each issue:

http-exception.filter.spec.ts (issue #93)

  • does NOT call captureException for expected HttpExceptions (no alert fatigue)
  • calls captureException with the Error instance (Sentry alerting)
  • wraps non-Error throws and still calls captureException
  • returns 500 with fallback message for unknown throws

logger.spec.ts (issue #94)

  • returns LOG_LEVEL when explicitly set, regardless of NODE_ENV
  • LOG_LEVEL override works in development too
  • defaults to 'info' in production when LOG_LEVEL is unset
  • defaults to 'debug' in development when LOG_LEVEL is unset
  • defaults to 'debug' in test environment when LOG_LEVEL is unset
  • accepts 'verbose' as a valid LOG_LEVEL
  • accepts 'silly' as a valid LOG_LEVEL

e2e tests (23 passed)

PASS test/intents.e2e-spec.ts
PASS test/stats.e2e-spec.ts
PASS test/tokens.e2e-spec.ts
PASS test/solvers.e2e-spec.ts
PASS test/health.e2e-spec.ts

Test Suites: 5 passed, 5 total
Tests:       23 passed, 23 total
Time:        12.529 s

Lint & typecheck

npm run lint       → 0 warnings, 0 errors
npm run typecheck  → 0 errors

Files changed

File Change
src/common/sentry.ts New — Sentry init + captureException helpers
src/common/http-exception.filter.ts Call captureException() in the 500 branch
src/common/logger.ts Export resolveLogLevel(); use it for the Winston level
src/config/env.validation.ts Add LOG_LEVEL and SENTRY_DSN Joi entries
src/main.ts Call initSentry() before app bootstrap
.env.example Document LOG_LEVEL and SENTRY_DSN
src/common/http-exception.filter.spec.ts New — unit tests for alerting behaviour
src/common/logger.spec.ts New — unit tests for resolveLogLevel()
package.json Add @sentry/node@8.54.0 dependency
package-lock.json Updated lockfile

How to configure

Local development (Sentry disabled)

No changes needed. SENTRY_DSN defaults to empty and is a no-op.

Enabling Sentry

# .env
SENTRY_DSN=https://your-key@o0.ingest.sentry.io/your-project-id

Changing log verbosity

# Verbose output for debugging a production issue
LOG_LEVEL=debug

# Quiet during automated tests
LOG_LEVEL=warn

Closes #93
Closes #94

- Wire Sentry into HttpExceptionFilter's generic-Error branch (issue stellar-vortex-protocol#93)
  Only unhandled exceptions trigger an alert; expected HttpExceptions
  (404, 400, etc.) are silently ignored to prevent alert fatigue.
  src/common/sentry.ts  — initSentry() + captureException() helpers
  src/main.ts           — call initSentry() at application boot
  src/common/http-exception.filter.ts — call captureException in 500 branch

- Make log level configurable via LOG_LEVEL env var (issue stellar-vortex-protocol#94)
  src/common/logger.ts         — resolveLogLevel() reads LOG_LEVEL,
                                  falls back to 'info' in prod / 'debug' elsewhere
  src/config/env.validation.ts — LOG_LEVEL + SENTRY_DSN Joi entries

- Tests
  src/common/http-exception.filter.spec.ts — verify captureException called
    only on generic errors, not on HttpExceptions (7 cases)
  src/common/logger.spec.ts                — verify resolveLogLevel() priority
    and NODE_ENV heuristic (7 cases)

- Dependencies
  @sentry/node@8.54.0 added to production dependencies

Closes stellar-vortex-protocol#93
Closes stellar-vortex-protocol#94
@drips-wave

drips-wave Bot commented Jul 28, 2026

Copy link
Copy Markdown

@gabrielujelistic-collab Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make log level configurable via environment variable Add error alerting wired through HttpExceptionFilter's 500 branch

1 participant