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

express-std-logger

v1.1.0

Published

Winston logger and Express response helpers with the same console and file format for every Node backend.

Readme

express-std-logger

A small logger and Express response helper you can drop into any Node backend.

The console output, log files, and JSON response shapes match the logger and ResponseHandler used across existing services: a colorized single-line message in development, JSON in production, and logs/error.log plus logs/combined.log on disk.

Works in JavaScript and TypeScript. The package is ESM only ("type": "module") and ships its own .d.ts files. You do not need @types/express-std-logger. Use it from a project that has "type": "module", or load it with await import() from CommonJS.

Install

npm install express-std-logger

Requires Node.js 18 or newer.

Quick start

JavaScript

import express from "express";
import morgan from "morgan";
import { createLogger } from "express-std-logger";
import ResponseHandler from "express-std-logger/responsehandler";

const logger = createLogger({ service: "service_name" });

const app = express();

app.use(
  morgan("tiny", {
    stream: { write: (message) => logger.info(message.trim()) },
  }),
);
app.use(ResponseHandler.apiRequestLogger);
app.use(express.json());

app.get("/health", (req, res) => {
  return ResponseHandler.respondWithSuccess(
    req,
    res,
    { uptime: process.uptime() },
    { message: "Service is running" },
  );
});

app.use((err, req, res, next) => {
  if (res.headersSent) return next(err);
  logger.error("Unhandled request error", {
    message: err?.message,
    stack: err?.stack,
    path: req.originalUrl,
  });
  return ResponseHandler.respondBadRequest(req, res, null, {
    message: err?.message || "Internal server error",
    status: err?.status || 500,
  });
});

TypeScript

import express, { type ErrorRequestHandler } from "express";
import morgan from "morgan";
import { createLogger, type StdLogger } from "express-std-logger";
import ResponseHandler from "express-std-logger/responsehandler";

const logger: StdLogger = createLogger({ service: "obligation-api" });

const app = express();

app.use(
  morgan("tiny", {
    stream: { write: (message: string) => logger.info(message.trim()) },
  }),
);
app.use(ResponseHandler.apiRequestLogger);
app.use(express.json());

app.get("/health", (req, res) => {
  return ResponseHandler.respondWithSuccess(
    req,
    res,
    { uptime: process.uptime() },
    { message: "Service is running" },
  );
});

const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  if (res.headersSent) return next(err);
  logger.error("Unhandled request error", {
    message: err?.message,
    stack: err?.stack,
    path: req.originalUrl,
  });
  return ResponseHandler.respondBadRequest(req, res, null, {
    message: err?.message || "Internal server error",
    status: err?.status || 500,
  });
};

app.use(errorHandler);

Call createLogger once at startup, before the server handles traffic. ResponseHandler always writes through the active logger, including one you create with createLogger. Express req / res values are valid arguments; you do not need to cast them.

If you do not need a custom service name, import the default logger and skip createLogger:

import logger from "express-std-logger";

logger.info("Server started");
import logger, { type StdLogger } from "express-std-logger";

const log: StdLogger = logger;
log.info("Server started");

Imports

JavaScript:

import logger, { createLogger } from "express-std-logger";
import ResponseHandler from "express-std-logger/responsehandler";

TypeScript (same runtime imports, plus types):

import logger, {
  createLogger,
  formatMetaForConsole,
  type StdLogger,
  type CreateLoggerOptions,
  type LogLevel,
} from "express-std-logger";
import ResponseHandler, {
  type RequestLike,
  type ResponseLike,
  type SuccessOptions,
  type ErrorOptions,
} from "express-std-logger/responsehandler";

| Import | What it is | | --- | --- | | logger | Default Winston logger (StdLogger). Safe to use immediately. | | createLogger(options) | Builds a new logger and makes it the active one. | | formatMetaForConsole(meta) | Formats metadata the same way the dev console does. | | StdLogger | Type of the logger, including object and array. | | CreateLoggerOptions | Type of createLogger({ ... }). | | LogLevel | "error" \| "warn" \| "info" \| "http" \| "debug". | | express-std-logger/responsehandler | ResponseHandler class (static methods). |

There is no @types/express-std-logger package. If the editor says a declaration file is missing, you are on an older published version — install [email protected] or later.

createLogger options:

| Option | Default | Purpose | | --- | --- | --- | | service | process.env.SERVICE_NAME or "app" | Value stored as service on every log record. It is written to log files and omitted from the dev console line. | | level | process.env.LOG_LEVEL, or debug when NODE_ENV=development, otherwise info | Minimum level that is printed. | | logsDir | <process.cwd()>/logs | Folder for error.log and combined.log. Created if it does not exist. |

const logger = createLogger({
  service: "billing-api",
  level: "debug",
  logsDir: "/var/log/billing-api",
});
const options: CreateLoggerOptions = {
  service: "billing-api",
  level: "debug",
  logsDir: "/var/log/billing-api",
};
const logger = createLogger(options);

Logger

Levels, from most to least severe: error, warn, info, http, debug.

logger.error("Readiness check failed");
logger.warn("Redis reconnecting");
logger.info("Listening on port 3000");
logger.http("inbound request");
logger.debug("cache miss");

Pass a metadata object as the second argument. Nested values are printed with util.inspect (full depth, sorted keys):

logger.error("Readiness check failed", {
  message: error.message,
  stack: error.stack,
  requestId: req.requestId,
});

logger.object and logger.array

Use these when you want a value pretty-printed on its own, without a key: label. The default level is debug.

logger.object("user record", { id: 1, name: "Ada" });
logger.array("ids", [1, 2, 3], "info");

If you call logger.array with a non-array, or logger.object with an array, the logger prints a warning and still logs the value.

Development console format

When NODE_ENV is not production, each line looks like this (the level is colorized in a terminal):

2026-10-25 02:21:00 | info | Listening on port 3000
2026-10-25 02:21:01 | info | API Hit [GET] /health:200 - 4ms
2026-10-25 02:21:02 | error | Readiness check failed connect ECONNREFUSED
requestId: '8f1c...'

Shape: YYYY-MM-DD HH:mm:ss | <level> | <message>. The level is colorized in a terminal. Metadata is printed under the line. A metadata field named message is appended to the log line, and a metadata field named stack is printed after the other fields. That is Winston's behavior, same as the in-repo logger.

Production console format

When NODE_ENV=production, the console is one JSON object per line:

{"level":"info","message":"Listening on port 3000","service":"billing-api","timestamp":"2026-10-25T02:21:00.000Z"}

Log files

Both environments also append JSON lines to:

  • logs/error.log — error only
  • logs/combined.log — every level that passes the current level filter

The folder is created on import. It defaults to ./logs relative to the process working directory (the app you run), not the folder where this package is installed.

Environment variables

| Variable | Effect | | --- | --- | | NODE_ENV=development | Default level becomes debug if LOG_LEVEL is unset. | | NODE_ENV=production | Console switches from the colorized text format to JSON. | | LOG_LEVEL | One of error, warn, info, http, debug. Overrides the NODE_ENV default. createLogger({ level }) overrides this. | | SERVICE_NAME | Default service metadata when createLogger({ service }) is not passed. |

ResponseHandler

All methods are static. req is accepted so call sites stay the same; the success and error helpers do not read it.

In TypeScript, req and res are typed as RequestLike and ResponseLike. Express Request and Response satisfy those types.

ResponseHandler.apiRequestLogger

Express middleware. On res "finish" it logs:

API Hit [GET] /health:200 - 4ms
app.use(ResponseHandler.apiRequestLogger);

ResponseHandler.respondWithSuccess(req, res, result, options)

| Option | Default | Meaning | | --- | --- | --- | | status | 200 | HTTP status | | message | omitted | Included only when you pass it | | key | "result" | JSON key that holds result |

ResponseHandler.respondWithSuccess(req, res, { id: 1 }, { message: "Created", status: 201 });
const options: SuccessOptions = { message: "Created", status: 201 };
ResponseHandler.respondWithSuccess(req, res, { id: 1 }, options);
{
  "success": true,
  "result": { "id": 1 },
  "message": "Created"
}

ResponseHandler.respondBadRequest(req, res, result, options)

Logs options.message with logger.error, then sends JSON.

| Option | Default | Meaning | | --- | --- | --- | | message | "Bad request" | Logged and returned as error | | status | 400 | HTTP status. Pass 500 (or err.status) for unexpected failures. |

result is included only when it is not null or undefined.

ResponseHandler.respondBadRequest(req, res, null, {
  message: "Name is required",
});
const options: ErrorOptions = { message: "Name is required" };
ResponseHandler.respondBadRequest(req, res, null, options);
{
  "success": false,
  "error": "Name is required"
}

Error middleware

JavaScript:

app.use((err, req, res, next) => {
  if (res.headersSent) return next(err);
  logger.error("Unhandled request error", {
    message: err?.message,
    stack: err?.stack,
    path: req.originalUrl,
  });
  return ResponseHandler.respondBadRequest(req, res, null, {
    message: err?.message || "Internal server error",
    status: err?.status || 500,
  });
});

TypeScript:

const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  if (res.headersSent) return next(err);
  logger.error("Unhandled request error", {
    message: err?.message,
    stack: err?.stack,
    path: req.originalUrl,
  });
  return ResponseHandler.respondBadRequest(req, res, null, {
    message: err?.message || "Internal server error",
    status: err?.status || 500,
  });
};

app.use(errorHandler);

CommonJS

This package cannot be loaded with require(). From a CommonJS file:

const { createLogger } = await import("express-std-logger");
const { default: ResponseHandler } = await import("express-std-logger/responsehandler");

The file that contains that await must be an ES module or a CommonJS file run by a tool that supports top-level await. Otherwise wrap it in an async function.

License

MIT