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

hazo_logs

v1.0.13

Published

Logger for hazo packages - Winston wrapper with singleton pattern

Readme

hazo_logs

A Winston-based logging library for Node.js/Next.js applications with a built-in log viewer UI.

npm version License: MIT

Features

  • Structured Logging: Winston wrapper with singleton pattern for consistent logging across your app
  • Package Tagging: Automatically tag logs by package/module name
  • Daily Rotation: Automatic log file rotation with configurable retention
  • Session Tracking: Track logs across async operations with sessionId and reference
  • Built-in UI: React-based log viewer with filtering, sorting, and pagination
  • Zero Config: Works out of the box with sensible defaults
  • Minimal Integration: Add log viewer to your app with just 2 files (~6 lines of code)

Quick Start

Installation

npm install hazo_logs

For the UI components, also install peer dependencies:

npm install hazo_logs next react react-dom hazo_ui

Basic Usage (Logging Only)

1. Create a logger in your code:

import { createLogger } from 'hazo_logs';

const logger = createLogger('my-package');

logger.info('Application started');
logger.warn('This is a warning', { userId: 123 });
logger.error('Something went wrong', { error: 'details' });
logger.debug('Debug information');

2. (Optional) Configure logging:

Create a config/ directory and add hazo_logs_config.ini:

mkdir -p config
cp node_modules/hazo_logs/config/hazo_logs_config.example.ini config/hazo_logs_config.ini

Or create config/hazo_logs_config.ini manually:

[hazo_logs]
log_directory = ./logs
log_level = info
enable_console = true
enable_file = true
max_file_size = 20m
max_files = 14d

That's it! Logs will be written to ./logs/hazo-YYYY-MM-DD.log files.

Add Log Viewer UI (Optional)

Add a log viewer to your Next.js app with just 2 files:

1. Create API route (app/api/logs/route.ts):

import { createLogApiHandler } from 'hazo_logs/ui/server';

const handler = createLogApiHandler();

// GET for log viewer, POST for client-side logging
export const { GET, POST } = handler;

2. Create UI page (app/logs/page.tsx):

'use client';

import { LogViewerPage } from 'hazo_logs/ui';

export default function LogsPage() {
  return <LogViewerPage apiBasePath="/api/logs" title="System Logs" />;
}

3. Configure Tailwind CSS (tailwind.config.ts):

import type { Config } from 'tailwindcss';
import { tailwindSafelist, tailwindContentPath } from 'hazo_logs/tailwind';

const config: Config = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    tailwindContentPath, // Add hazo_logs components
  ],
  safelist: tailwindSafelist, // Prevent purging of dynamic classes
  // ... rest of your config
};

export default config;

Visit /logs in your app to view logs!

Client-Side Logging (Browser)

For logging from client components (browser), use the client logger.

1. Configure global defaults (recommended):

Set up global configuration once at app initialization. This ensures all client loggers (including those from dependency packages) use the correct API endpoint:

// app/providers.tsx or lib/hazo-init.ts
'use client';
import { configureClientLogger } from 'hazo_logs/ui';

// Configure once at app startup
configureClientLogger({
  apiBasePath: '/api/logs',  // Required: your log API endpoint
  minLevel: 'info',          // Optional: minimum log level
});

2. Create loggers in your components:

'use client';
import { createClientLogger } from 'hazo_logs/ui';

// No need to specify apiBasePath - inherits from global config
const logger = createClientLogger({
  packageName: 'my-app-client',
});

logger.info('User clicked button', { buttonId: 'submit' });
logger.error('Failed to load data', { error: err.message });

Why use global configuration?

  • Dependency packages (like hazo_collab_forms) automatically use your configured endpoint
  • Avoids 404 errors from loggers using the wrong default path
  • Single place to configure all client logging settings

Important Notes

Server/Client Import Paths

hazo_logs provides different import paths for different environments:

| Import Path | Environment | Use Case | |-------------|-------------|----------| | hazo_logs | Universal | Basic logging (console on client, file on server) | | hazo_logs/server | Server-only | Full logging with context, sessions, log reader | | hazo_logs/ui | Client | React components, createClientLogger() | | hazo_logs/ui/server | Server-only | API handlers for log viewer |

// Universal - works everywhere (recommended for libraries)
import { createLogger } from 'hazo_logs';

// Server-only - full capabilities (file logging, context, sessions)
import { createLogger, runWithLogContext } from 'hazo_logs/server';

// Client components - sends logs to server API
import { createClientLogger } from 'hazo_logs/ui';

// API routes - create log viewer endpoints
import { createLogApiHandler } from 'hazo_logs/ui/server';

Note: The hazo_logs/server and hazo_logs/ui/server imports use the server-only package and will throw an error if accidentally imported in client bundles. This prevents Next.js build errors from Node.js APIs (fs, async_hooks) being bundled for the browser.

Next.js 16+ Turbopack Compatibility: As of v1.0.10, hazo_logs uses conditional exports in package.json to automatically serve a client-safe bundle to browser environments. When bundlers (Next.js Turbopack, Webpack, etc.) resolve hazo_logs, they automatically receive:

  • Browser/Client: A lightweight console-only logger with zero Node.js dependencies
  • Node.js/Server: The full winston-based logger with file transports

This eliminates "Can't resolve 'fs'" errors completely without requiring any special import paths for basic logging.

Tailwind CSS Setup Required

The log viewer UI uses dynamic Tailwind classes that would be purged during build.

Tailwind v4 Setup

Add the @source directive to your globals.css to enable Tailwind to scan hazo_logs classes:

@import "tailwindcss";

/* Required: Enable Tailwind to scan hazo_logs package classes */
@source "../node_modules/hazo_logs/dist";

Why this is needed: Tailwind v4's JIT compiler doesn't scan node_modules/ by default. Without this directive, hover states and interactive styles will be invisible.

Tailwind v3 Setup

Configure both the content path and safelist:

import { tailwindSafelist, tailwindContentPath } from 'hazo_logs/tailwind';

export default {
  content: [
    // your paths...
    tailwindContentPath,
  ],
  safelist: tailwindSafelist,
};

Alternatively, use the preset:

import { hazoLogsPreset } from 'hazo_logs/tailwind';

export default {
  presets: [hazoLogsPreset],
  content: [
    // your paths...
    './node_modules/hazo_logs/dist/**/*.{js,jsx}',
  ],
};

Advanced Usage

Session and Reference Tracking

Track logs across async operations (server-only feature):

import { createLogger, runWithLogContext } from 'hazo_logs/server';

const logger = createLogger('auth');

async function handleRequest(req) {
  await runWithLogContext(
    {
      sessionId: req.sessionId,
      reference: `user-${req.userId}`,
      depth: 0,
    },
    async () => {
      logger.info('Request started'); // Automatically includes sessionId and reference
      await processRequest(req);
      logger.info('Request completed');
    }
  );
}

Embedded Log Viewer

Use the log viewer as a sidebar component:

import { LogViewerPage } from 'hazo_logs/ui';

function AdminPanel() {
  return (
    <div className="flex">
      <Sidebar />
      <div className="flex-1">
        <LogViewerPage
          apiBasePath="/api/logs"
          title="Recent Logs"
          embedded={true}
          showHeader={true}
          className="h-screen"
        />
      </div>
    </div>
  );
}

hazo_debug Integration

Pipe all log entries to hazo_debug's Debug tab using the on_log callback:

'use client';
import { createClientLogger } from 'hazo_logs/ui';
import { use_debug_log } from 'hazo_debug/client';

// In a React component:
const { log_debug } = use_debug_log('MyApp');
const logger = createClientLogger({
  packageName: 'hazo_files',
  on_log: log_debug,  // Pipes all logs to hazo_debug's Debug tab
});

logger.info('Upload started', { file: 'report.pdf' });
// → Appears in both server logs AND hazo_debug's Debug tab

The on_log callback also works with the basic ConsoleLogger:

import { ConsoleLogger } from 'hazo_logs';

const logger = new ConsoleLogger('my-package', 'debug', log_debug);

Child loggers inherit on_log from their parent unless overridden.

hazo_debug Server Dual-Write Endpoint

hazo_debug has a dual-write feature that posts debug console entries to a server endpoint for persistence. Set up the endpoint:

1. Create API route (app/api/hazo_logs/client-log/route.ts):

import { createClientLogHandler } from 'hazo_logs/ui/server';

const handler = createClientLogHandler();
export const { GET, POST } = handler;

2. POST entries (done automatically by hazo_debug when hazo_logs_endpoint is configured):

{
  "level": "debug",
  "source": "ComponentName",
  "message": "Something happened",
  "data": { "key": "value" },
  "timestamp": 1743303600000,
  "origin": "hazo_debug_console"
}

3. Query entries (for Claude/LLMs to read recent debug logs):

GET /api/hazo_logs/client-log?limit=50&level=error&since=1743303600000

Returns entries with origin: 'hazo_debug_console' in reverse chronological order.

Custom Authentication

Protect your log viewer with authentication:

import { createLogApiHandler, withLogAuth } from 'hazo_logs/ui/server';

const handler = createLogApiHandler();

const authHandler = withLogAuth(handler, async (request) => {
  const session = await getSession(request);
  return session?.user?.role === 'admin';
});

export const { GET } = authHandler;

Individual Components

Import components separately for custom layouts:

import {
  LogTable,
  LogTimeline,
  LogPagination,
  LogLevelBadge,
} from 'hazo_logs/ui';

// Build your own custom log viewer
function CustomLogViewer() {
  const [logs, setLogs] = useState([]);

  return (
    <div>
      <LogTable logs={logs} isLoading={false} />
      <LogPagination
        currentPage={1}
        totalPages={10}
        pageSize={50}
        total={500}
        onPageChange={setPage}
        onPageSizeChange={setPageSize}
      />
    </div>
  );
}

Configuration Reference

Create a config/ directory in your project root and add hazo_logs_config.ini:

mkdir -p config
cp node_modules/hazo_logs/config/hazo_logs_config.example.ini config/hazo_logs_config.ini

Configuration options:

[hazo_logs]
# Core Logging Settings
log_directory = ./logs          # Where to write log files
log_level = info                # Minimum level: error, warn, info, debug
enable_console = true           # Log to console
enable_file = true              # Log to files with rotation
max_file_size = 20m             # Max size per file (supports k, m, g)
max_files = 14d                 # Retention period (e.g., 14d = 14 days)
date_pattern = YYYY-MM-DD       # Date format for log filenames

# Log Viewer UI Settings
enable_log_viewer = true        # Enable the API endpoints
log_viewer_page_size = 50       # Results per page
log_viewer_max_results = 1000   # Max entries to load for filtering

If no config file is found, sensible defaults are used.

API Reference

Core Exports (hazo_logs)

createLogger(packageName: string): Logger

Create a package-specific logger.

const logger = createLogger('my-package');
logger.info('Hello world');

Logger Interface

interface Logger {
  error(message: string, data?: Record<string, unknown>): void;
  warn(message: string, data?: Record<string, unknown>): void;
  info(message: string, data?: Record<string, unknown>): void;
  debug(message: string, data?: Record<string, unknown>): void;
}

runWithLogContext(context: LogContext, fn: () => Promise<T>): Promise<T>

Run code with log context (sessionId, reference, depth).

readLogs(options): Promise<LogQueryResult>

Read logs from files with filtering and pagination (server-side only).

UI Exports (hazo_logs/ui)

configureClientLogger(config: ClientLoggerGlobalConfig): void

Configure global defaults for all client loggers. Call once at app initialization.

configureClientLogger({
  apiBasePath: '/api/logs',  // Required
  minLevel: 'info',          // Optional
  consoleOutput: true,       // Optional
  batchMode: false,          // Optional
  batchInterval: 5000,       // Optional (ms)
});

getClientLoggerConfig(): ClientLoggerGlobalConfig | undefined

Get current global configuration (returns undefined if not configured).

isClientLoggerConfigured(): boolean

Check if global configuration has been set.

createClientLogger(config?: ClientLoggerConfig): ClientLogger

Create a client-side logger. Inherits from global config if set.

const logger = createClientLogger({
  packageName: 'my-component',  // Tag for this logger
  sessionId: 'sess_123',        // Optional session tracking
  reference: 'user_456',        // Optional reference tracking
});

LogViewerPage

Main log viewer component.

Props:

  • apiBasePath?: string - API endpoint path (default: /api/logs)
  • title?: string - Page title (default: Log Viewer)
  • className?: string - Additional CSS classes
  • embedded?: boolean - Embedded mode (default: false)
  • showHeader?: boolean - Show header (default: true)

Individual Components

  • LogTable - Table view of logs
  • LogTimeline - Timeline view with grouping
  • LogPagination - Pagination controls
  • LogLevelBadge - Badge for log levels

Server Exports (hazo_logs/ui/server)

createLogApiHandler(config?): LogApiHandler

Create Next.js API route handler for the log viewer.

Options:

  • logDirectory?: string - Override default log directory

Returns:

{
  GET: (request: Request) => Promise<Response>,  // Log viewer queries
  POST: (request: Request) => Promise<Response>, // Client-side log ingestion
}

createClientLogHandler(config?): LogApiHandler

Create Next.js API route handler for hazo_debug_console dual-write entries.

Options:

  • logDirectory?: string - Override default log directory

Returns:

{
  GET: (request: Request) => Promise<Response>,  // Query debug console entries
  POST: (request: Request) => Promise<Response>, // Receive debug console entries
}

withLogAuth(handler, authCheck): LogApiHandler

Wrap handler with authentication.

const authHandler = withLogAuth(handler, async (request) => {
  // Return true to allow access, false to deny
  return await isAdmin(request);
});

Log File Format

Logs are stored as newline-delimited JSON:

{"timestamp":"2025-12-18T10:30:45.123Z","level":"info","package":"auth","message":"User logged in","filename":"auth.ts","line":42,"executionId":"2025-12-18-10:30:45-1234","sessionId":"sess_abc123","reference":"user-456","data":{"userId":456}}

Fields:

  • timestamp - ISO 8601 timestamp
  • level - Log level (error, warn, info, debug)
  • package - Package name from createLogger
  • message - Log message
  • filename - Source file name
  • line - Line number in source file
  • executionId - Unique ID per server start
  • sessionId - Optional session ID from context
  • reference - Optional reference from context
  • depth - Optional call depth from context
  • data - Optional additional data

UI Screenshots

Table View

Filter and sort logs by level, package, session, execution ID, or search text.

Timeline View

Visualize logs grouped by package or session with hierarchical depth display.

Examples

See the test-app/ directory for a complete working example.

Troubleshooting

Config file not found

If you see [HazoLog] Config file 'config/hazo_logs_config.ini' not found, the library will still work with defaults. The warning shows the paths that were searched. To configure custom settings:

mkdir -p config
cp node_modules/hazo_logs/config/hazo_logs_config.example.ini config/hazo_logs_config.ini

Logs not appearing in UI

  1. Check that you're looking at the correct date (UI defaults to today)
  2. Verify logs are being written to the log_directory configured
  3. Check browser console for API errors

Styles broken in log viewer

  1. Ensure tailwindContentPath is in your Tailwind content array
  2. Ensure tailwindSafelist is in your Tailwind safelist array
  3. Rebuild your project after config changes (npm run build)

Import errors in client components

Error: Module not found: Can't resolve 'async_hooks'
Error: Module not found: Can't resolve 'fs'

This error occurred in earlier versions when server-only code was imported in client components. As of v1.0.10, this is fully fixed - the library now uses conditional exports in package.json, so bundlers automatically receive a client-safe bundle with zero Node.js dependencies.

If you still see this error:

  1. Upgrade hazo_logs: Run npm update hazo_logs to get v1.0.10 or later
  2. Clear build cache: Run rm -rf .next and rebuild
  3. For logging in client components: The base hazo_logs import now works automatically (returns console logger on client)
  4. For client logging that posts to server: Use createClientLogger from hazo_logs/ui
  5. For full server capabilities: Import from hazo_logs/server for file logging, sessions, and context

hazo_ui missing errors

The log viewer UI requires hazo_ui package. Install it:

npm install hazo_ui

POST /api/logs 404 errors

If you see repeated POST /api/logs 404 errors, client loggers are using the default endpoint which doesn't match your API route location.

Solution: Configure the global client logger at app startup:

// app/providers.tsx or lib/hazo-init.ts
'use client';
import { configureClientLogger } from 'hazo_logs/ui';

configureClientLogger({
  apiBasePath: '/api/hazo_logs/logs',  // Match your actual route
});

This ensures all client loggers (including those from dependency packages) use the correct endpoint.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# Clone the repo
git clone https://github.com/pub12/hazo_logs.git
cd hazo_logs

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

# Test with example app
npm run dev:test-app

License

MIT - See LICENSE file for details

Author

Pubs Abayasiri

Links

Related Packages

  • hazo_ui - UI component library (required for log viewer)

Support

For issues and questions:

  • Open an issue on GitHub
  • Check existing issues for solutions