Logger for hazo packages - Winston wrapper with singleton pattern
npm install hazo_logsA Winston-based logging library for Node.js/Next.js applications with a built-in log viewer UI.


- 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)
``bash`
npm install hazo_logs
For the UI components, also install peer dependencies:
`bash`
npm install hazo_logs next react hazo_ui
1. Create a logger in your code:
`typescript
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:
`bash`
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:
`ini`
[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 a log viewer to your Next.js app with just 2 files:
1. Create API route (app/api/logs/route.ts):
`typescript
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):
`typescript
'use client';
import { LogViewerPage } from 'hazo_logs/ui';
export default function LogsPage() {
return
}
`
3. Configure Tailwind CSS (tailwind.config.ts):
`typescript
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!
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:
`typescript
// 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:
`typescript
'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
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 |
`typescript
// 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.
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:
`css
@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:
`typescript
import { tailwindSafelist, tailwindContentPath } from 'hazo_logs/tailwind';
export default {
content: [
// your paths...
tailwindContentPath,
],
safelist: tailwindSafelist,
};
`
Alternatively, use the preset:
`typescript
import { hazoLogsPreset } from 'hazo_logs/tailwind';
export default {
presets: [hazoLogsPreset],
content: [
// your paths...
'./node_modules/hazo_logs/dist/*/.{js,jsx}',
],
};
`
Track logs across async operations (server-only feature):
`typescript
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');
}
);
}
Use the log viewer as a sidebar component:
`typescript
import { LogViewerPage } from 'hazo_logs/ui';
function AdminPanel() {
return (
$3
Protect your log viewer with authentication:
`typescript
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;
`$3
Import components separately for custom layouts:
`typescript
import {
LogTable,
LogTimeline,
LogPagination,
LogLevelBadge,
} from 'hazo_logs/ui';// Build your own custom log viewer
function CustomLogViewer() {
const [logs, setLogs] = useState([]);
return (
currentPage={1}
totalPages={10}
pageSize={50}
total={500}
onPageChange={setPage}
onPageSizeChange={setPageSize}
/>
);
}
`Configuration Reference
Create a
config/ directory in your project root and add hazo_logs_config.ini:`bash
mkdir -p config
cp node_modules/hazo_logs/config/hazo_logs_config.example.ini config/hazo_logs_config.ini
`Configuration options:
`ini
[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 filenamesLog 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
$3
####
createLogger(packageName: string): LoggerCreate a package-specific logger.
`typescript
const logger = createLogger('my-package');
logger.info('Hello world');
`####
Logger Interface`typescript
interface Logger {
error(message: string, data?: Record): void;
warn(message: string, data?: Record): void;
info(message: string, data?: Record): void;
debug(message: string, data?: Record): void;
}
`####
runWithLogContext(context: LogContext, fn: () => PromiseRun code with log context (sessionId, reference, depth).
####
readLogs(options): PromiseRead logs from files with filtering and pagination (server-side only).
$3
####
configureClientLogger(config: ClientLoggerGlobalConfig): voidConfigure global defaults for all client loggers. Call once at app initialization.
`typescript
configureClientLogger({
apiBasePath: '/api/logs', // Required
minLevel: 'info', // Optional
consoleOutput: true, // Optional
batchMode: false, // Optional
batchInterval: 5000, // Optional (ms)
});
`####
getClientLoggerConfig(): ClientLoggerGlobalConfig | undefinedGet current global configuration (returns undefined if not configured).
####
isClientLoggerConfigured(): booleanCheck if global configuration has been set.
####
createClientLogger(config?: ClientLoggerConfig): ClientLoggerCreate a client-side logger. Inherits from global config if set.
`typescript
const logger = createClientLogger({
packageName: 'my-component', // Tag for this logger
sessionId: 'sess_123', // Optional session tracking
reference: 'user_456', // Optional reference tracking
});
`####
LogViewerPageMain 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$3
####
createLogApiHandler(config?): LogApiHandlerCreate Next.js API route handler.
Options:
-
logDirectory?: string - Override default log directoryReturns:
`typescript
{
GET: (request: Request) => Promise, // Log viewer queries
POST: (request: Request) => Promise, // Client-side log ingestion
}
`####
withLogAuth(handler, authCheck): LogApiHandlerWrap handler with authentication.
`typescript
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:
`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 dataUI Screenshots
$3
Filter and sort logs by level, package, session, execution ID, or search text.$3
Visualize logs grouped by package or session with hierarchical depth display.Examples
See the
test-app/ directory for a complete working example.Troubleshooting
$3
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:`bash
mkdir -p config
cp node_modules/hazo_logs/config/hazo_logs_config.example.ini config/hazo_logs_config.ini
`$3
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$3
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)$3
`
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$3
The log viewer UI requires
hazo_ui package. Install it:`bash
npm install hazo_ui
`$3
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:
`typescript
// 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$3
`bash
Clone the repo
git clone https://github.com/pub12/hazo_logs.git
cd hazo_logsInstall dependencies
npm installRun tests
npm testBuild
npm run buildTest with example app
npm run dev:test-app
``MIT - See LICENSE file for details
Pubs Abayasiri
- GitHub Repository
- Issue Tracker
- NPM Package
- hazo_ui - UI component library (required for log viewer)
For issues and questions:
- Open an issue on GitHub
- Check existing issues for solutions