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

@ezstart/logger

v0.2.1

Published

Universal structured logger for the @ezstart monorepo. Auto-resolves to Pino server-side, console wrappers browser-side — single import works everywhere.

Readme

@ezstart/logger

Universal structured logger for the @ezstart monorepo. Auto-resolves to Pino on the server, console wrappers in the browser. Single import works everywhere.

Install

pnpm add @ezstart/logger

pino and pino-pretty are bundled as direct dependencies — no extra peer install required.

Quickstart — Universal (recommended)

One import, runtime-appropriate impl auto-selected by the bundler. Works in server components, client components, API routes, scripts, and edge runtimes.

import { logger } from '@ezstart/logger'

// Both call signatures are accepted on every method:
logger.info({ userId: '123' }, 'User logged in') // Pino native
logger.info('User logged in', { userId: '123' }) // Legacy
logger.error({ err }, 'Payment failed')

The package's . entry point uses conditional browser / node exports so:

| Bundle context | Resolves to | Backend | | -------------------------- | ----------- | ---------------- | | Next.js server components | ./server | Pino (JSON) | | API routes (Node, Express) | ./server | Pino (JSON) | | 'use client' (browser) | ./index | console wrappers | | Edge runtimes (no pino) | ./index | console wrappers |

Pino never ships to the client bundle — the bundler tree-shakes the unreachable branch at build time.

Quickstart — Server-only (force Pino)

Use the explicit /server sub-path when you must guarantee Pino regardless of bundler context (e.g. a Node CLI script, an unusual SSR setup).

import { logger } from '@ezstart/logger/server'

logger.info({ port: 6100 }, 'Server started')
logger.error({ err, paymentId }, 'Payment processing failed')

Quickstart — Client-only (force console)

Use the explicit /client sub-path when you must guarantee the console wrapper (e.g. a shared utility imported from both server and client code that intentionally avoids Pino on the server).

import { logger } from '@ezstart/logger/client'

logger.info('User clicked', { buttonId: '123' })

API

logger.debug(msgOrObj, dataOrMsg?)

Verbose diagnostic information. Filtered out in production on the browser variant; controlled by LOG_LEVEL on the server variant.

logger.info(msgOrObj, dataOrMsg?)

Routine operational events. Filtered out in production on the browser variant; default on the server in development.

logger.warn(msgOrObj, dataOrMsg?)

Recoverable problems that warrant attention. Always logged.

logger.error(msgOrObj, dataOrMsg?)

Error conditions. Always logged.

type Logger

The shared interface implemented by both browser and server variants. SDK packages that accept a logger?: Logger prop should re-export this type rather than declare their own LoggerLike shape.

import { logger, type Logger } from '@ezstart/logger'

function makeFoo(opts: { logger?: Logger } = {}) {
  const log = opts.logger ?? logger
  log.info({ ready: true }, 'foo initialized')
}

type LogLevel

Union of supported levels: 'debug' | 'info' | 'warn' | 'error'.

createLogger(pinoInstance) (server only)

Wrap an arbitrary Pino instance behind the universal {@link Logger} interface. Useful for custom sinks, redaction, child loggers, or tests that want to assert on log output without spying on process.stdout.

import pino from 'pino'
import { createLogger } from '@ezstart/logger/server'

const sink = pino({ level: 'debug' }, pino.destination('/var/log/app.log'))
const logger = createLogger(sink)
logger.info({ ready: true }, 'Booted')

pinoLogger (server only)

The default Pino instance backing the package-level logger singleton. Exposed so advanced consumers can derive child loggers or read the resolved level for diagnostics.

import { pinoLogger } from '@ezstart/logger/server'

const child = pinoLogger.child({ requestId: 'abc' })

Configuration

Server variant (@ezstart/logger/server)

| Env var | Default (dev) | Default (prod) | Description | | ----------- | ------------- | -------------- | -------------------------------------------- | | LOG_LEVEL | info | warn | Pino level — overrides the env-based default | | NODE_ENV | — | production | Selects default level + transport |

In development (NODE_ENV !== 'production'), the server variant uses pino-pretty for a colorized, human-readable transport. In production it emits structured JSON suitable for log aggregation (Railway, Datadog, CloudWatch, etc.).

Browser variant (@ezstart/logger, @ezstart/logger/client)

| Env var | Behavior | | ---------- | ------------------------------------------------------- | | NODE_ENV | When production, suppresses debug and info output |

warn and error are always emitted. Output is forwarded to console.log / console.warn / console.error / console.debug, with a fallback to console.log when console.debug is unavailable.

How conditional resolution works

The package.json exports field declares three runtime conditions on the . entry point:

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "browser": "./dist/index.js",
      "node": "./dist/server.js",
      "default": "./dist/index.js"
    }
  }
}

| Bundler / runtime | Picks | | ----------------------------- | ---------------------------------------- | | Webpack / Next.js (server) | node -> ./dist/server.js (Pino) | | Webpack / Next.js (browser) | browser -> ./dist/index.js (console) | | Vite, esbuild (server build) | node -> ./dist/server.js | | Vite, esbuild (browser build) | browser -> ./dist/index.js | | Node REPL / scripts | node -> ./dist/server.js | | Edge runtimes | default -> ./dist/index.js |

If your tooling does not understand the node condition, it falls back to default (the browser variant) — safe because the server variant is the only one that pulls in Pino.

Migration from console.log

- console.log('User logged in', { userId })
+ logger.info({ userId }, 'User logged in')

- console.warn('Slow query:', ms)
+ logger.warn({ ms }, 'Slow query')

- console.error('Crash:', err)
+ logger.error({ err }, 'Crash')

- if (process.env.NODE_ENV !== 'production') {
-   console.debug('trace', data)
- }
+ logger.debug(data, 'trace')

The debug / info calls are already production-gated on the browser variant, so dropping the NODE_ENV guard is safe.

Related