npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tpaga-logger

v0.0.9

Published

Structured logging SDK for Tpaga microservices (wide events → CloudWatch)

Readme

tpaga-logger

Structured logging SDK for Tpaga Node.js microservices. Emits wide-event JSON (canonical log line) to stdout for AWS CloudWatch Logs Insights, following loggingsucks.com.

Installation

pnpm add tpaga-logger

Usage

AWS Lambda

Use withLogger to wrap a Lambda handler. It automatically handles timing, correlationId extraction from headers, and emits the terminal log event on success or error.

import { createLogger, withLogger } from 'tpaga-logger';

const logger = createLogger({
  service: 'my-service',
  environment: process.env.STAGE ?? 'dev',
});

export const handler = withLogger(logger, 'createCharge', async (event, log) => {
  log.with({ orderId: event.body.orderId, amount: event.body.amount });
  const result = await chargeService.create(event.body);
  return { statusCode: 200, body: JSON.stringify(result) };
});

What withLogger does automatically:

  • Extracts correlationId from x-correlation-id or x-request-id headers (generates a UUID if absent)
  • Starts a timer and calculates durationMs
  • Emits outcome: "success" with statusCode on completion
  • Emits outcome: "error" with serialized error on failure, then re-throws

Express

Register two middlewares from the library — one before routes, one after. Both are zero-config.

import express from 'express';
import { createLogger, withTpagaExpressLogger, tpagaExpressErrorLogger } from 'tpaga-logger';
import { errorHandler } from './middleware/errorHandler';

const logger = createLogger({ service: 'url-signer-service', environment: process.env.NODE_ENV ?? 'dev' });

const app = express();
app.use(express.json());
app.use(withTpagaExpressLogger(logger)); // before routes: timing, correlationId, req.log

app.use('/api/v1/sign', signRouter);

app.use(tpagaExpressErrorLogger);        // after routes: serialize + log any error
app.use(errorHandler);                   // after routes: map error → HTTP response (your code)

Express error middleware must be registered after routes — this is an Express constraint.

What withTpagaExpressLogger does automatically:

  • Extracts correlationId from x-correlation-id / x-request-id headers (generates UUID if absent)
  • Attaches a typed WideEventBuilder to req.log and res.log
  • Emits outcome: "success" for statusCode < 400, outcome: "error" otherwise
  • Calculates durationMs — always matches the actual response time

What tpagaExpressErrorLogger does automatically:

  • Serializes the error via serializeError — no stack, structured errorDetails for Zod-like errors
  • Adds error to the log context via req.log.with({ error })
  • Calls next(err) to pass the error to your errorHandler

req.log / res.log in controllers:

req.log is available anywhere in the request lifecycle — no import needed:

export const signUrl = async (req: Request, res: Response, next: NextFunction) => {
  try {
    const input = signUrlSchema.parse(req.body);
    req.log.with({ url: input.url, ttlSeconds: input.ttlSeconds });
    res.status(200).json(generateSignedUrl(input));
  } catch (err) {
    req.log.with({ attemptedUrl: req.body?.url, stage: 'signing' }); // extra context on error
    next(err);
  }
};

res.log works the same — useful in error handler middleware where req params are prefixed with _.

statusCode in the log always matches the HTTP response — it's read from res.statusCode after the response is sent.

serializeError output:

| Error type | type | message | errorDetails | |-----------|--------|-----------|----------------| | ZodError | ZodError | Validation failed | array of Zod issues | | AppError / Error | Error | original message | — | | Unknown | UnknownError | String(err) | — |


NestJS + Fastify v0.0.9+

Import from tpaga-logger/fastify. Register the plugin and the global interceptor — both are zero-config.

// app.module.ts
import { Module } from '@nestjs/common';
import { TpagaLoggerModule } from 'tpaga-logger/fastify';

@Module({
  imports: [
    TpagaLoggerModule.forRoot({
      service: process.env.SERVICE_NAME ?? 'my-service',
      environment: process.env.NODE_ENV ?? 'dev',
    }),
  ],
})
export class AppModule {}
// main.ts
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { withTpagaFastifyLogger, TpagaLoggerInterceptor, TPAGA_LOGGER } from 'tpaga-logger/fastify';
import type { Logger } from 'tpaga-logger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());

  const logger = app.get<Logger>(TPAGA_LOGGER);
  await app.register(withTpagaFastifyLogger(logger)); // before routes: timing, correlationId, request.tpagaLog
  app.useGlobalInterceptors(new TpagaLoggerInterceptor()); // catch errors before exception filters

  await app.listen(3000, '0.0.0.0');
}
bootstrap();

What withTpagaFastifyLogger does automatically:

  • Extracts correlationId from x-correlation-id / x-request-id headers (generates UUID if absent)
  • Attaches a typed WideEventBuilder to request.tpagaLog
  • Emits outcome: "success" for statusCode < 400, outcome: "error" otherwise
  • Calculates durationMs — always matches the actual response time

What TpagaLoggerInterceptor does automatically:

  • Wraps every route handler and catches thrown exceptions before NestJS exception filters run
  • Serializes the error via serializeError and adds it to the log context via request.tpagaLog.with({ error })
  • Re-throws so NestJS exception handling continues normally

Use request.tpagaLog (not request.log) — Fastify already attaches its own pino logger to request.log.

request.tpagaLog in controllers:

import { Controller, Get, Param, Req } from '@nestjs/common';
import type { FastifyRequest } from 'fastify';

@Controller('charges')
export class ChargesController {
  @Get(':id')
  async getCharge(@Req() req: FastifyRequest, @Param('id') id: string) {
    req.tpagaLog.with({ chargeId: id });
    const charge = await this.chargesService.findById(id);
    req.tpagaLog.with({ merchantId: charge.merchantId });
    return charge;
  }
}

API

tpaga-logger (Express + Lambda)

| Export | Description | |--------|-------------| | createLogger(config) | Creates a logger instance for a service | | withLogger(logger, name, handler) | Lambda handler wrapper | | withTpagaExpressLogger(logger) | Express middleware (register with app.use) | | tpagaExpressErrorLogger | Express error middleware (register after routes) | | getLog(res) | Gets the WideEventBuilder from an Express response | | serializeError(err) | Serializes an unknown error to a structured object | | LOG_LEVELS | ['info', 'warn', 'error', 'debug'] | | OUTCOMES | ['success', 'error', 'validation_failed', ...] |

tpaga-logger/fastify (NestJS + Fastify)

| Export | Description | |--------|-------------| | withTpagaFastifyLogger(logger) | Fastify plugin — register with app.register() | | TpagaLoggerInterceptor | NestJS interceptor — register with app.useGlobalInterceptors() | | TpagaLoggerModule | NestJS dynamic module — import in AppModule | | TPAGA_LOGGER | Injection token to retrieve the Logger instance via DI |

LoggerConfig

type LoggerConfig = {
  service: string;
  environment?: string;
  redactKeys?: readonly string[]; // paths redacted with '[REDACTED]'
};

WideEventBuilder (log)

type WideEventBuilder = {
  with: (fields: Record<string, unknown>) => void;   // add context fields
  emit: (level: LogLevel, message: string, terminal: TerminalFields) => void; // send the log line
};

emit is called automatically by withLogger and withTpagaExpressLogger. Call it manually only when using startOperation directly.


Local development (pretty logs)

pino-pretty is bundled in the library — no extra install needed in your service. Just start with LOG_PRETTY=true:

LOG_PRETTY=true node dist/index.js
# or for Lambda local testing:
LOG_PRETTY=true npx ts-node src/handler.ts

Output in the console:

[09:11:00.142] INFO: createCharge completed
    service: "cash-in-manager"
    correlationId: "abc-123"
    orderId: "ORD-456"
    amount: 50000
    outcome: "success"
    durationMs: 142

In production, leave LOG_PRETTY unset — raw JSON goes to stdout and CloudWatch Logs Insights queries it natively.


Development

pnpm install
pnpm typecheck
pnpm lint
pnpm test
pnpm build

Docs

Repo

https://bitbucket.org/tpaga/tpaga-logger/src/main/