Skip to content

feat: add structured logger and global error handler (#37) - #45

Open
kodegreen70 wants to merge 1 commit into
arflexx:mainfrom
kodegreen70:feat/issue-37-logger-error-handler
Open

feat: add structured logger and global error handler (#37)#45
kodegreen70 wants to merge 1 commit into
arflexx:mainfrom
kodegreen70:feat/issue-37-logger-error-handler

Conversation

@kodegreen70

Copy link
Copy Markdown
Contributor

PR: Centralized Error Handler & Structured Logger

Summary

Adds a structured pino logger (utils/logger.ts) and a global Express
error-handling middleware (middleware/errorHandler.ts) as foundational
server infrastructure. A shared asyncHandler utility replaces the three
identical per-file copies that existed across the route modules.

Closes #37


Type of Change

  • feat — new feature
  • fix — bug fix
  • refactor — code change with no behaviour change
  • docs — documentation only
  • chore — build, deps, config
  • contract — Soroban smart contract change

What Changed

File Change
server/src/utils/logger.ts New. pino logger — pretty-printed in dev, JSON in production. Redacts Authorization headers and any secret key fields.
server/src/utils/asyncHandler.ts New. Shared asyncHandler wrapper — forwards async route errors to next(err) so every handler is covered without individual try/catch.
server/src/middleware/errorHandler.ts New. Global four-argument Express error handler. Logs method, path, IP, status code, and full error stack. Returns { error: "Internal server error" } in production; full message + stack in development. Respects existing HTTP status codes on the error object.
server/src/index.ts Removed morgan and inline console.error error handler. Imports and registers errorHandler as the last middleware. Startup logging now uses logger.fatal / logger.info.
server/src/routes/auth.ts Removed local asyncHandler copy. Imports shared utility. Replaced three console.error calls with structured logger.error.
server/src/routes/trades.ts Removed local asyncHandler copy. Imports shared utility.
server/src/routes/wallet.ts Removed local asyncHandler copy. Imports shared utility. Replaced console.error with logger.error.
server/package.json Added pino@9.3.2 and pino-pretty@11.2.1 as pinned dependencies.

How It Works

Logger (utils/logger.ts)

import logger from "../utils/logger";

logger.info({ port: 3001 }, "AirFlex API started");
logger.error({ err, userId }, "Wallet provisioning failed");
  • Development (NODE_ENV !== "production"): coloured, human-readable output
    via pino-pretty with timestamps.
  • Production: newline-delimited JSON — structured fields are parseable by
    log aggregators (Datadog, CloudWatch, etc.) without extra configuration.
  • Redacted fields: req.headers.authorization, body.buyerSecretKey,
    body.sellerSecretKey, *.stellar_secret_key, *.encryptedSecretKey.

Error Handler (middleware/errorHandler.ts)

Every unhandled error that reaches next(err) is caught here:

Production response:   { "error": "Internal server error" }          (5xx)
                       { "error": "<actual message>" }               (4xx)

Development response:  { "error": "<message>", "stack": "..." }

Errors with an explicit statusCode or status property (e.g. from
http-errors) retain their original HTTP status rather than defaulting to 500.

asyncHandler (utils/asyncHandler.ts)

import { asyncHandler } from "../utils/asyncHandler";

router.get("/", asyncHandler(async (req, res) => {
  const data = await someAsyncOp(); // any throw → errorHandler
  res.json({ data });
}));

How to Test

cd server
cp .env.example .env   # fill in values
npm install
npm run dev

Verify structured logging (dev):

# You should see coloured pino output in the terminal, not raw console lines
curl http://localhost:3001/health

Verify error handler in development:

# Hit a route that throws — response includes message + stack
curl http://localhost:3001/api/trades/not-a-real-uuid

Verify production mode:

NODE_ENV=production npm run dev
# Error responses must NOT include stack traces

TypeScript check:

npx tsc --noEmit   # must exit 0 with no errors

Checklist

General

  • Code compiles / builds without errors
  • No new TypeScript errors (tsc --noEmit)
  • Follows existing code style and patterns
  • No secrets, keys, or credentials committed
  • .env.example updated if new env vars were added — no new env vars

API changes

  • No request/response shape changes — error format is existing { error: string }
  • No new routes; existing validation unchanged
  • Auth middleware unchanged

Notes for Reviewer

  • morgan has been removed from index.ts. Request-level logging can be
    added back as a pino HTTP logger (pino-http) in a follow-up if needed;
    pino-http integrates the request ID with the child logger automatically.
  • The asyncHandler in auth.ts, trades.ts, and wallet.ts were
    functionally identical — this PR consolidates them with no behaviour change.
  • pino-pretty is a runtime dependency (not devDependency) because the dev
    server runs via ts-node-dev and needs it available at runtime in dev mode.
    It is a no-op in production since the transport is only configured when
    NODE_ENV !== "production".

- Add pino logger (utils/logger.ts): JSON in prod, pretty-printed in dev
- Add global errorHandler middleware (middleware/errorHandler.ts): logs
  method, path, IP and full stack; hides stack traces in production
- Extract shared asyncHandler utility (utils/asyncHandler.ts); remove
  three identical per-file copies from auth, trades and wallet routes
- Replace all console.error calls with structured logger.error
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.

[server] - Add Structured Logging and Error Handling Middleware

1 participant