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

@heady/logger

v1.0.2

Published

![Language](https://img.shields.io/badge/language-TypeScript-blue) ![License](https://img.shields.io/badge/license-ISC-green) ![Version](https://img.shields.io/badge/version-1.0.1-orange)

Readme

Structured CloudWatch Logger

Language License Version

A production-ready TypeScript logger wrapper around Winston, designed specifically for AWS CloudWatch Logs and CloudWatch Insights.

It enforces structured JSON logging while maintaining human-readable messages, ensuring your logs are easy to debug locally and easy to query in production.

Features

  • Structured JSON: automatically formats logs for CloudWatch (no manual JSON.stringify).
  • Context Awareness: automates [Class:Method] prefixes in messages.
  • Metadata Support: persistent metadata (like requestId, userId) attached to every log.
  • Error Optimization: extracts error messages to the top level for easy sorting in CloudWatch Insights.
  • Type Safety: built with TypeScript, ships with .d.ts declarations.

Installation

npm install @heady/logger
# or
yarn add @heady/logger

Usage

1. Basic Usage

Initialize the logger with a context name (usually the class name).

import { LogService } from '@heady/logger';

class UserService {
  private logger = new LogService('UserService');

  getUser(id: string) {
    this.logger.info(
      'getUser',                // Method name
      'fetching user data',     // Short message
      'checking cache first',   // Detailed description
      { id, cache: true }       // Data object (no JSON.stringify needed)
    );
  }
}

Output:

{
  "timestamp": "2024-01-15T10:00:00.000Z",
  "level": "info",
  "message": "[UserService:getUser] fetching user data",
  "context": "UserService",
  "method": "getUser",
  "description": "checking cache first",
  "data": { "id": "123", "cache": true },
  "service": "my-app"
}

2. Using Metadata (Request Tracing)

Pass metadata (like requestId or userId) during initialization. It will be attached to every log line generated by that instance.

// Typically done in your controller or middleware
const logger = new LogService('OrderController', {
  requestId: 'req-12345',
  userId: 'user-987'
});

logger.info('createOrder', 'order received');
// Every log will automatically include "requestId" and "userId"

3. Error Handling

Pass the raw Error object as the last argument. The logger extracts the stack trace and lifts the error message to the top level for better visibility in CloudWatch Insights.

try {
  await db.connect();
} catch (err) {
  logger.error('connect', 'db connection failed', 'retrying in 5s', err);
}

Output:

{
  "timestamp": "2024-01-15T10:00:00.000Z",
  "level": "error",
  "message": "[OrderController:connect] db connection failed",
  "method": "connect",
  "description": "retrying in 5s",
  "errorMessage": "ECONNREFUSED 127.0.0.1:5432",
  "error": {
    "message": "ECONNREFUSED 127.0.0.1:5432",
    "name": "Error",
    "stack": "Error: ECONNREFUSED ...\n    at ..."
  },
  "service": "my-app"
}

Note: Non-Error values (plain strings or objects) can also be passed as the last argument. They will be serialized into errorMessage without a stack trace.

4. Debug Logging

Debug logs are hidden by default. They only appear when the LOG_LEVEL environment variable is set to debug.

logger.debug('calc', 'loop iteration', 'checking boundaries', { x: 10, y: 20 });

5. Dynamic Metadata (setMetadata)

Use setMetadata to add or update metadata on an existing instance — useful when context (like userId) is only available after an async operation such as authentication.

const logger = new LogService('AuthService', { requestId: 'req-abc' });

// Before auth: userId is unknown
logger.info('authenticate', 'verifying token');

const user = await verifyToken(token);

// After auth: attach userId to all subsequent logs
logger.setMetadata('userId', user.id);

logger.info('authenticate', 'token verified', 'user authenticated');
// This log will include both "requestId" and "userId"

Configuration

Control logger behavior using environment variables.

| Variable | Default | Description | |----------|---------|-------------| | LOG_LEVEL | info | Set to debug for verbose logs, or error to reduce noise. | | SERVICE_NAME | my-app | Tags all logs with a service name for filtering in aggregated log streams. |

Example:

LOG_LEVEL=debug SERVICE_NAME=user-api npm run start

API Reference

LogContext interface

interface LogContext {
  requestId?: string;
  userId?: string;
  [key: string]: any; // any additional fields
}

LogService class

Constructor

new LogService(context: string, defaultMeta?: LogContext)

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | context | string | Yes | Identifier for the class/module (appears in every log message). | | defaultMeta | LogContext | No | Key-value metadata attached to all logs from this instance. |

Methods

| Method | Signature | Description | |--------|-----------|-------------| | info | (method, message, desc?, data?) => void | Standard informational log. | | warn | (method, message, desc?, data?) => void | Warning — something unexpected but recoverable. | | error | (method, message, desc?, error?) => void | Error — last param is an Error object or any value. | | debug | (method, message, desc?, data?) => void | Verbose dev log, hidden unless LOG_LEVEL=debug. | | setMetadata | (key: string, value: any) => void | Adds or updates a metadata field on this instance. |

Shared parameters for info / warn / error / debug:

| Parameter | Type | Description | |-----------|------|-------------| | method | string | The method/function name. Included in the log message prefix. | | message | string | Short, human-readable summary (used in the message field). | | desc | string? | Optional longer description stored in the description field. | | data / error | any | Optional payload. For error(), pass the raw Error object here. |

Log output fields

{
  "timestamp": "ISO 8601",
  "level": "info | warn | error | debug",
  "message": "[Context:method] short message",
  "context": "context name from constructor",
  "method": "method name",
  "description": "optional longer description",
  "data": {},
  "service": "SERVICE_NAME env var",
  "requestId": "if set in metadata",
  "userId": "if set in metadata",
  "errorMessage": "error.message (error logs only)",
  "error": {
    "message": "...",
    "name": "...",
    "stack": "..."
  }
}

Framework Integration

AWS Lambda

import { LogService } from '@heady/logger';

export const handler = async (event: any) => {
  const logger = new LogService('LambdaHandler', {
    requestId: event.requestContext?.requestId
  });

  logger.info('handler', 'processing event', null, { source: event.source });

  try {
    // ... business logic
  } catch (err) {
    logger.error('handler', 'unhandled error', 'event processing failed', err);
    throw err;
  }
};

NestJS Service

// app-logger.service.ts
import { Injectable, Scope } from '@nestjs/common';
import { LogService } from '@heady/logger';

@Injectable({ scope: Scope.TRANSIENT })
export class AppLogger extends LogService {
  constructor() {
    super('AppLogger');
  }
}

// user.service.ts
@Injectable()
export class UserService {
  constructor(private readonly logger: AppLogger) {
    this.logger.setMetadata('service', 'UserService');
  }

  async getUser(id: string) {
    this.logger.info('getUser', 'fetching user', undefined, { id });
    // ...
  }
}

CloudWatch Insights Queries

Since logs are structured JSON, you can run powerful queries in AWS CloudWatch Insights.

Find all errors in a specific class:

fields @timestamp, message, errorMessage, error.stack
| filter level = "error"
| filter context = "UserService"
| sort @timestamp desc

Trace a specific request end-to-end:

fields @timestamp, level, message, method
| filter requestId = "req-12345"
| sort @timestamp asc

Filter by service name across multiple log groups:

fields @timestamp, message, level
| filter service = "user-api"
| filter level in ["warn", "error"]
| sort @timestamp desc

License

ISC