hazo_logs
v1.0.13
Published
Logger for hazo packages - Winston wrapper with singleton pattern
Maintainers
Readme
hazo_logs
A Winston-based logging library for Node.js/Next.js applications with a built-in log viewer UI.
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_logsFor the UI components, also install peer dependencies:
npm install hazo_logs next react react-dom hazo_uiBasic 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.iniOr 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 = 14dThat'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 tabThe 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=1743303600000Returns 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.iniConfiguration 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 filteringIf 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 classesembedded?: boolean- Embedded mode (default:false)showHeader?: boolean- Show header (default:true)
Individual Components
LogTable- Table view of logsLogTimeline- Timeline view with groupingLogPagination- Pagination controlsLogLevelBadge- 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 timestamplevel- Log level (error, warn, info, debug)package- Package name from createLoggermessage- Log messagefilename- Source file nameline- Line number in source fileexecutionId- Unique ID per server startsessionId- Optional session ID from contextreference- Optional reference from contextdepth- Optional call depth from contextdata- 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.iniLogs not appearing in UI
- Check that you're looking at the correct date (UI defaults to today)
- Verify logs are being written to the
log_directoryconfigured - Check browser console for API errors
Styles broken in log viewer
- Ensure
tailwindContentPathis in your Tailwindcontentarray - Ensure
tailwindSafelistis in your Tailwindsafelistarray - 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:
- Upgrade hazo_logs: Run
npm update hazo_logsto get v1.0.10 or later - Clear build cache: Run
rm -rf .nextand rebuild - For logging in client components: The base
hazo_logsimport now works automatically (returns console logger on client) - For client logging that posts to server: Use
createClientLoggerfromhazo_logs/ui - For full server capabilities: Import from
hazo_logs/serverfor file logging, sessions, and context
hazo_ui missing errors
The log viewer UI requires hazo_ui package. Install it:
npm install hazo_uiPOST /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:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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-appLicense
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
