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

@ecom-micro/common

v2.4.3

Published

`app.use(errorHandler)` function is to catch global level error. It's alternative to express error controller. You need to use it in your `app.js` or where you write your express app logic.

Readme

ecom-micro/common helps developer to catch production level nodejs application error easy way.

app.use(errorHandler) function is to catch global level error. It's alternative to express error controller. You need to use it in your app.js or where you write your express app logic.

Example to use any error class

import { BadRequestError } from '@ecom-micro/common';

return next(new BadRequestError('Invalid email or password'));
// OR
throw new BadRequestError('Invalid email or password');
List of method
import { BadRequestError, NotFoundError, errorHandler } from '@ecom-micro/common';

Logging

import { createLogger } from '@ecom-micro/common';

const logger = createLogger({ service: 'auth-service' });
logger.info('User logged in', { userId: user.id, email: user.email });

Each call writes one JSON line to stdout (timestamp, level, service, version, environment, message, error, plus your fields). In the cluster, Alloy ships these lines to Loki, where Grafana can query them (ADR 0001).

| Env var | Effect | | --- | --- | | LOG_LEVEL | Lowest level written (default info) | | LOG_FORMAT | json or pretty; overrides the default | | NODE_ENV=development | Pretty output unless LOG_FORMAT says otherwise | | SERVICE_VERSION, DEPLOYMENT_ENVIRONMENT | Fill version / environment |

winstonLogger(esNode, name, level) still works but is deprecated. It ignores the Elasticsearch argument.

Redaction policy

Every log line is redacted before it's written, so a forgotten field can't leak a secret.

Fields with secret names are logged as [REDACTED], whatever their value. This covers passwords, tokens, secrets, credentials, auth headers, cookies, sessions, signatures, API/private keys and card fields. The exact word lists live in src/observability/sensitiveKeys.ts. That file is the single source, so this README doesn't repeat them.

Inside every string:

  • Emails become j***@example.com. The first character and the domain are kept, and addresses in any alphabet are covered.
  • Stripe keys (sk_live_…, sk_test_…, rk_…, whsec_…) become [REDACTED].

Objects:

  • An object's toJSON() result is what gets redacted, because that's what JSON prints (e.g. Mongoose documents).
  • Errors, Maps and Sets are logged with their contents, redacted.
  • A loop (node.self = node) becomes [Circular].
  • Past 10 levels deep or 2,000 objects, the rest becomes [Truncated].
  • A field whose getter throws becomes [Unreadable]. The rest of the line is still written.

To protect a new kind of field, add a word to sensitiveKeys.ts and a case to src/observability/__test__/redaction.test.ts. The collector will add a second redaction layer (ticket 15), but don't rely on it.

Inside every string, query parameters with secret names are masked too, so /reset-password?token=abc is logged as ?token=[REDACTED], and a %40-encoded email is decoded and then masked.

Not covered: secrets written into the message text itself (e.g. logger.info('pwd is ' + pwd)). Always pass sensitive values as named fields.

Tracing

Start the SDK in a file of its own, and import that file first in the service's entry file. Auto-instrumentation only patches modules loaded after the SDK starts, so anything imported earlier (Express, Mongoose, amqplib…) is never traced.

// src/tracing.ts
import { startTelemetry } from '@ecom-micro/common/telemetry';
export const telemetry = startTelemetry({ serviceName: 'auth-service' });

// src/index.ts
import './tracing';
import app from './app';

Import the bootstrap from the @ecom-micro/common/telemetry subpath, never from the main entry: the main entry loads Express.

Mount traceIdHeader before your routes so every response carries x-trace-id:

import { traceIdHeader } from '@ecom-micro/common';
app.use(traceIdHeader);

What you get with no manual spans:

  • HTTP, Express, MongoDB/Mongoose, pg, ioredis/redis and amqplib are traced.
  • An incoming W3C traceparent is continued.
  • Log lines written during a request carry trace_id and span_id.
  • Span attributes go through the same redaction policy as logs.

| Env var | Effect | | --- | --- | | OTEL_EXPORTER_OTLP_ENDPOINT | OTLP/HTTP collector, e.g. http://alloy-srv:4318. Unset means nothing is exported | | NODE_ENV=test | Nothing is exported, even with an endpoint set | | OTEL_TRACES_SAMPLER_ARG | Share of new traces kept, 0–1 (default 1); a child always follows its parent | | SERVICE_VERSION, DEPLOYMENT_ENVIRONMENT | service.version / deployment.environment | | K8S_POD_NAME, K8S_NAMESPACE | k8s.pod.name / k8s.namespace.name (set via the downward API) | | OTEL_LOG_LEVEL | Prints the SDK's own diagnostics, e.g. debug when spans don't arrive |

When there's no endpoint, spans are still created locally, so logs keep their trace_id. If the collector is down, spans are dropped and requests are unaffected.

Metrics and custom spans

Mount mountObservability(app, { service: 'auth-service' }) before service routes. It installs x-trace-id, one redacted access log per request, the route-template HTTP duration histogram and /metrics on the app's existing port. Prometheus scrapes /metrics; the latency histogram includes sampled trace exemplars for Grafana's Tempo link. The bundle also exposes event-loop lag, heap use and GC pause duration.

Use getServiceMeter('auth-service') to create service-owned counters or histograms, and getServiceTracer('auth-service') to create custom spans. Keep metric attributes bounded (result or route template, never IDs or user input):

const logins = getServiceMeter('auth-service').createCounter('auth.logins');
logins.add(1, { result: 'success' });

const tracer = getServiceTracer('auth-service');
await tracer.startActiveSpan('send-reset-email', async (span) => {
  try { await sendResetEmail(); } finally { span.end(); }
});

Tests inject an in-memory exporter with startTelemetry({ serviceName, testSpanExporter }). Under Jest, two extra steps are needed. Run Jest with node --experimental-vm-modules, because the OTLP exporter uses a dynamic import(). And map Jest's mocked module built-in back to the real one, so http gets patched (see src/observability/__test__/telemetry.test.ts).

Health endpoints

Mount createHealthRoutes(checks, { timeoutMs: 1000 }) before authentication and application routes:

import { createHealthRoutes } from '@ecom-micro/common';
app.use(createHealthRoutes({
  mongodb: async () => { await db.command({ ping: 1 }); },
}));

Checks return normally when healthy and throw or reject when unavailable. /healthz always returns HTTP 200 with { "status": "ok" } without invoking checks. /readyz runs all named checks concurrently with an independent deadline (default 1000 ms). It returns HTTP 200 when every check passes, otherwise HTTP 503, for example { "status": "error", "checks": { "mongodb": "timeout", "rabbitmq": "ok" } }. Each check is ok, error or timeout; exception messages are never exposed.

The deadline bounds the HTTP response; it cannot cancel the underlying dependency operation. Set driver timeouts as well. Configure Kubernetes liveness on /healthz and readiness on /readyz, with probe timeouts longer than the check deadline. A dependency outage should remove a pod from service without restarting its live process.