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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@heyatlas/logger

v1.4.5

Published

A structured logger with Slack integration and Lambda support

Readme

@heyatlas/logger

A structured logger with Slack integration and AWS Lambda support.

Changelog

See CHANGELOG.md for a detailed list of changes.

Features

  • Structured logging with Bunyan
  • Slack integration
  • AWS Lambda context support
  • TypeScript support
  • Log level management
  • Async context tracking
  • Context management with AsyncLocalStorage

Installation

npm install @heyatlas/logger

Usage

Basic Usage

import { Logger } from "@heyatlas/logger";

// Only name is required at root level
const logger = new Logger({
  name: "my-app",
});

// Environment is determined by process.env.NODE_ENV
// Defaults to "local" if not set
logger.info("Hello world", { userId: "123" });

Usage Scenarios

1. AWS Lambda Functions

import { AsyncLocalStorage } from "async_hooks";
import { Logger, withLoggerLambda } from "@heyatlas/logger";

// Create execution context
const executionContext = new AsyncLocalStorage();

// Create logger configuration
const loggerConfig = {
  name: "my-lambda-service",
  slackApiToken: process.env.SLACK_TOKEN,
  production: {
    streams: [{ level: "info", type: "stdout" }],
    slack: {
      defaultChannel: "#prod-alerts",
      level: "error",
    },
  },
};

// Create logger instance
const logger = new Logger(loggerConfig);

// Lambda handler
export const handler = withLoggerLambda(
  executionContext,
  logger, // Pass the logger instance
  async (event, context) => {
    // The logger is automatically available in the execution context
    const contextLogger = executionContext.getStore()?.logger;

    contextLogger?.info("Processing event", { event });

    try {
      // Your handler logic here
      const result = await processEvent(event);
      contextLogger?.info("Event processed successfully", { result });
      return result;
    } catch (error) {
      contextLogger?.error("Failed to process event", { error });
      throw error;
    }
  }
);

// Alternative: Pass config directly to withLoggerLambda
export const alternativeHandler = withLoggerLambda(
  executionContext,
  loggerConfig, // Pass the config directly
  async (event, context) => {
    // Logger is created internally and available in context
    const contextLogger = executionContext.getStore()?.logger;
    contextLogger?.info("Processing with config-based logger");
    // ... handler logic
  }
);

2. EC2/ECS API Service (Express)

import express from "express";
import { AsyncLocalStorage } from "async_hooks";
import { createLogger } from "@heyatlas/logger";

const app = express();
const executionContext = new AsyncLocalStorage();

// Create logger factory
const { logger, getLogger } = createLogger(executionContext, {
  name: "my-api-service",
  slackApiToken: process.env.SLACK_TOKEN,
  production: {
    streams: [{ level: "info", type: "stdout" }],
    slack: {
      defaultChannel: "#prod-alerts",
      level: "error",
    },
  },
});

// Export getLogger for use in other modules
export { getLogger };

// Middleware to create context for each request
app.use((req, res, next) => {
  executionContext.run({ logger }, () => next());
});

// Example route
app.get("/users/:id", async (req, res) => {
  const logger = getLogger();

  try {
    logger.info("Fetching user", { userId: req.params.id });
    const user = await userService.getUser(req.params.id);
    logger.info("User fetched successfully", { user });
    res.json(user);
  } catch (error) {
    logger.error("Failed to fetch user", { error });
    res.status(500).json({ error: "Internal server error" });
  }
});

3. GraphQL Service

import { ApolloServer } from "apollo-server";
import { AsyncLocalStorage } from "async_hooks";
import { createLogger } from "@heyatlas/logger";

const executionContext = new AsyncLocalStorage();

// Create logger factory
const { logger, getLogger } = createLogger(executionContext, {
  name: "my-graphql-service",
  slackApiToken: process.env.SLACK_TOKEN,
  production: {
    streams: [{ level: "info", type: "stdout" }],
    slack: {
      defaultChannel: "#prod-alerts",
      level: "error",
    },
  },
});

// Export getLogger for use in other modules
export { getLogger };

// Apollo Server context
const context = async ({ req }) => {
  return executionContext.run({ logger }, () => ({ req }));
};

// Example resolver
const resolvers = {
  Query: {
    user: async (_, { id }, context) => {
      const logger = getLogger();

      try {
        logger.info("Fetching user", { userId: id });
        const user = await userService.getUser(id);
        logger.info("User fetched successfully", { user });
        return user;
      } catch (error) {
        logger.error("Failed to fetch user", { error });
        throw error;
      }
    },
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context,
});

Slack Integration

const logger = new Logger({
  name: "my-app",
  slackApiToken: "your-slack-token",
  staging: {
    streams: [{ type: "stdout", level: "info" }],
    slack: {
      defaultChannel: "#staging-logs",
      level: "warn",
    },
  },
  production: {
    streams: [{ type: "stdout", level: "info" }],
    slack: {
      defaultChannel: "#production-logs",
      level: "error",
    },
  },
});

// Regular logging with level validation
logger.error("Something went wrong", {
  slack: { channel: "#alerts" },
  error: new Error("Details here"),
});

// Direct Slack messaging without level validation
// Useful for sending notifications that don't need to go through the logger
logger.slackLogger?.sendDirect("#notifications", "Important notification");

The sendDirect method allows you to send messages directly to Slack without any formatting or level validation. This is useful for:

  • Sending simple notifications that don't need to be logged
  • Reusing the SlackLogger instance in other parts of your application
  • Sending plain text messages to specific channels

Environment-based Configuration

The logger uses NODE_ENV to determine which environment configuration to use:

import { Logger } from "@heyatlas/logger";

const logger = new Logger({
  name: "my-service",
  slackApiToken: process.env.SLACK_TOKEN, // Optional: enable Slack integration

  // Environment-specific configurations
  local: {
    streams: [{ level: "debug", type: "stdout" }],
  },
  test: {
    streams: [{ level: "fatal", type: "stdout" }],
  },
  staging: {
    streams: [{ level: "info", type: "stdout" }],
    slack: {
      defaultChannel: "#staging-logs",
      level: "warn",
    },
  },
  production: {
    streams: [{ level: "info", type: "stdout" }],
    slack: {
      defaultChannel: "#prod-alerts",
      level: "error",
    },
  },
});

Configuration Structure

The logger uses two levels of configuration:

  1. Root Configuration:

    • name (required): Logger name
    • slackApiToken (optional): Slack API token for integration
  2. Environment Configuration:

    • streams (required): Array of stream configurations
    • slack (optional): Slack settings per environment
      • defaultChannel: Default Slack channel
      • level: Minimum level for Slack notifications

Child Loggers

The logger supports creating child loggers that inherit context from their parent while allowing for additional bound fields. This is particularly useful for tracking request-specific information across different parts of your application.

// Create a parent logger
const logger = new Logger(config);

// Create a child logger with request-specific fields
const requestLogger = logger.child({ reqId: "123" });

// The child logger inherits the parent's context
logger.setContext("userId", "456");
requestLogger.info("Processing request"); // Includes both reqId and userId

// Child loggers can have their own context
requestLogger.setContext("component", "auth");
requestLogger.info("Authenticating user"); // Includes reqId, userId, and component

// Child loggers maintain their context across async boundaries
const executionContext = new AsyncLocalStorage<LoggerContext>();
executionContext.run({ logger: requestLogger }, async () => {
  // The logger maintains its context here
  requestLogger.info("Inside async context");
});

Context Inheritance

Child loggers inherit their parent's context and can override it with their own bound fields. The inheritance works as follows:

  1. Parent context is copied to the child
  2. Bound fields are merged with the parent's context
  3. Child-specific context can be added using setContext
  4. Context is maintained across async boundaries

API Documentation

Log Levels

The logger supports the following log levels in order of priority:

  • fatal: System is unusable
  • error: Error conditions
  • warn: Warning conditions
  • info: Informational messages
  • debug: Debug-level messages
  • trace: Trace-level messages

License

MIT