Examples
Real-world examples and usage patterns for node-syslog.
Web Application Examples
Express.js Server
import express from 'express'
import { Syslog, SyslogFacility, SyslogOption } from 'node-syslog'
const app = express()
const logger = new Syslog({
ident: 'webapp',
facility: SyslogFacility.LOCAL0,
options: [SyslogOption.PID, SyslogOption.NDELAY]
})
// Request logging middleware
app.use((req, res, next) => {
const start = Date.now()
res.on('finish', () => {
const duration = Date.now() - start
logger.info('HTTP Request', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration: `${duration}ms`,
userAgent: req.get('User-Agent'),
ip: req.ip,
contentLength: res.get('Content-Length')
})
})
next()
})
// Error handling middleware
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.error('Unhandled error', {
error: err.message,
stack: err.stack,
url: req.url,
method: req.method,
userAgent: req.get('User-Agent'),
ip: req.ip
})
res.status(500).json({
error: 'Internal server error',
requestId: req.headers['x-request-id']
})
})
// Routes
app.get('/health', (req, res) => {
logger.debug('Health check requested')
res.json({ status: 'ok', timestamp: new Date().toISOString() })
})
app.post('/api/users', (req, res) => {
logger.info('Creating user', {
ip: req.ip,
userAgent: req.get('User-Agent')
})
try {
// User creation logic here
const userId = Math.floor(Math.random() * 10000)
logger.info('User created successfully', {
userId,
ip: req.ip
})
res.status(201).json({ userId, message: 'User created' })
} catch (error) {
logger.error('User creation failed', {
error: error instanceof Error ? error.message : String(error),
ip: req.ip
})
res.status(400).json({ error: 'Failed to create user' })
}
})
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
logger.info('Server started', {
port: PORT,
nodeVersion: process.version,
environment: process.env.NODE_ENV || 'development'
})
})
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM received, shutting down gracefully')
logger.close()
process.exit(0)
})
process.on('SIGINT', () => {
logger.info('SIGINT received, shutting down gracefully')
logger.close()
process.exit(0)
})
Fastify Server
import fastify from 'fastify'
import { Syslog, SyslogFacility } from 'node-syslog'
const app = fastify({
logger: false // We'll use our own logger
})
const logger = new Syslog({
ident: 'fastify-app',
facility: SyslogFacility.LOCAL1
})
// Request logging hook
app.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now()
})
app.addHook('onResponse', async (request, reply) => {
const duration = Date.now() - (request.startTime as number)
logger.info('HTTP Request', {
method: request.method,
url: request.url,
statusCode: reply.statusCode,
duration: `${duration}ms`,
userAgent: request.headers['user-agent'],
ip: request.ip,
contentLength: reply.getHeader('content-length')
})
})
// Error handling hook
app.addHook('onError', async (request, reply, error) => {
logger.error('Request error', {
error: error.message,
stack: error.stack,
url: request.url,
method: request.method,
statusCode: reply.statusCode
})
})
// Routes
app.get('/health', async (request, reply) => {
logger.debug('Health check requested')
return {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime()
}
})
app.post('/api/data', async (request, reply) => {
logger.info('Processing data', {
ip: request.ip,
contentType: request.headers['content-type']
})
try {
const data = request.body as any
// Validate data
if (!data || typeof data !== 'object') {
logger.warning('Invalid data received', { ip: request.ip })
return reply.status(400).send({ error: 'Invalid data' })
}
// Process data
const result = {
id: Math.floor(Math.random() * 1000),
processed: true,
timestamp: new Date().toISOString()
}
logger.info('Data processed successfully', {
resultId: result.id,
ip: request.ip
})
return reply.status(201).send(result)
} catch (error) {
logger.error('Data processing failed', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
ip: request.ip
})
return reply.status(500).send({ error: 'Processing failed' })
}
})
const start = async () => {
try {
const PORT = process.env.PORT || 3000
await app.listen({ port: PORT, host: '0.0.0.0' })
logger.info('Fastify server started', {
port: PORT,
nodeVersion: process.version,
environment: process.env.NODE_ENV || 'development'
})
} catch (err) {
logger.error('Failed to start server', {
error: err instanceof Error ? err.message : String(err),
stack: err instanceof Error ? err.stack : undefined
})
process.exit(1)
}
}
start()
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM received, shutting down gracefully')
logger.close()
process.exit(0)
})
Database Examples
PostgreSQL with pg
import { Pool, PoolClient } from 'pg'
import { Syslog, SyslogFacility } from 'node-syslog'
const logger = new Syslog({
ident: 'database-service',
facility: SyslogFacility.LOCAL2
})
// Database connection pool
const pool = new Pool({
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD,
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
})
// Log pool events
pool.on('connect', (client) => {
logger.debug('New database client connected', {
totalCount: pool.totalCount,
idleCount: pool.idleCount,
waitingCount: pool.waitingCount
})
})
pool.on('error', (err, client) => {
logger.error('Database pool error', {
error: err.message,
stack: err.stack,
totalCount: pool.totalCount
})
})
interface User {
id: number
name: string
email: string
created_at: Date
}
class UserService {
async createUser(name: string, email: string): Promise<User> {
const client = await pool.connect()
try {
logger.info('Creating user', { name, email })
const result = await client.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
[name, email]
)
const user = result.rows[0]
logger.info('User created successfully', {
userId: user.id,
name: user.name,
email: user.email
})
return user
} catch (error) {
logger.error('Failed to create user', {
error: error instanceof Error ? error.message : String(error),
name,
email,
stack: error instanceof Error ? error.stack : undefined
})
throw error
} finally {
client.release()
logger.debug('Database client released', {
totalCount: pool.totalCount,
idleCount: pool.idleCount,
waitingCount: pool.waitingCount
})
}
}
async getUserById(id: number): Promise<User | null> {
const client = await pool.connect()
try {
logger.debug('Fetching user by ID', { userId: id })
const result = await client.query(
'SELECT * FROM users WHERE id = $1',
[id]
)
const user = result.rows[0] || null
if (user) {
logger.debug('User found', { userId: id, name: user.name })
} else {
logger.warning('User not found', { userId: id })
}
return user
} catch (error) {
logger.error('Failed to fetch user', {
error: error instanceof Error ? error.message : String(error),
userId: id,
stack: error instanceof Error ? error.stack : undefined
})
throw error
} finally {
client.release()
}
}
async updateUser(id: number, updates: Partial<User>): Promise<User> {
const client = await pool.connect()
try {
logger.info('Updating user', { userId: id, updates })
const fields = Object.keys(updates).filter(key => updates[key as keyof User] !== undefined)
const values = fields.map(field => updates[field as keyof User])
const setClause = fields.map((field, index) => `${field} = $${index + 2}`).join(', ')
const query = `UPDATE users SET ${setClause}, updated_at = NOW() WHERE id = $1 RETURNING *`
const result = await client.query(query, [id, ...values])
const user = result.rows[0]
if (user) {
logger.info('User updated successfully', {
userId: id,
updatedFields: fields
})
} else {
logger.warning('User not found for update', { userId: id })
}
return user
} catch (error) {
logger.error('Failed to update user', {
error: error instanceof Error ? error.message : String(error),
userId: id,
updates,
stack: error instanceof Error ? error.stack : undefined
})
throw error
} finally {
client.release()
}
}
}
// Usage example
async function main() {
const userService = new UserService()
try {
// Create a user
const user = await userService.createUser('John Doe', 'john@example.com')
console.log('Created user:', user)
// Fetch the user
const fetchedUser = await userService.getUserById(user.id)
console.log('Fetched user:', fetchedUser)
// Update the user
const updatedUser = await userService.updateUser(user.id, { name: 'John Smith' })
console.log('Updated user:', updatedUser)
} catch (error) {
logger.error('Application error', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined
})
} finally {
await pool.end()
logger.close()
}
}
main().catch(error => {
logger.error('Failed to start application', {
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined
})
process.exit(1)
})
Background Service Examples
Worker Thread Service
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads'
import { Syslog, SyslogFacility, SyslogLevel } from 'node-syslog'
interface Job {
id: string
type: string
data: any
priority: 'low' | 'medium' | 'high'
createdAt: Date
}
interface JobResult {
jobId: string
success: boolean
result?: any
error?: string
processedAt: Date
duration: number
}
class WorkerService {
private logger: Syslog
private workers: Worker[] = []
private jobQueue: Job[] = []
private isProcessing = false
constructor(private workerCount: number = 4) {
this.logger = new Syslog({
ident: 'worker-service',
facility: SyslogFacility.DAEMON,
options: ['pid', 'ndelay'],
logLevel: SyslogLevel.INFO
})
}
async start(): Promise<void> {
this.logger.info('Starting worker service', { workerCount: this.workerCount })
// Create worker threads
for (let i = 0; i < this.workerCount; i++) {
const worker = new Worker(__filename, {
workerData: { workerId: i }
})
worker.on('message', (result: JobResult) => {
this.handleJobResult(result)
})
worker.on('error', (error) => {
this.logger.error('Worker error', {
workerId: i,
error: error.message,
stack: error.stack
})
})
worker.on('exit', (code) => {
if (code !== 0) {
this.logger.error('Worker stopped unexpectedly', {
workerId: i,
exitCode: code
})
}
})
this.workers.push(worker)
this.logger.info('Worker created', { workerId: i })
}
// Start processing jobs
this.processJobs()
this.logger.info('Worker service started successfully', {
workerCount: this.workers.length
})
}
addJob(job: Omit<Job, 'createdAt'>): void {
const fullJob: Job = {
...job,
createdAt: new Date()
}
this.jobQueue.push(fullJob)
this.logger.info('Job added to queue', {
jobId: fullJob.id,
type: fullJob.type,
priority: fullJob.priority,
queueSize: this.jobQueue.length
})
}
private async processJobs(): Promise<void> {
this.isProcessing = true
while (this.isProcessing) {
if (this.jobQueue.length > 0 && this.workers.length > 0) {
const job = this.jobQueue.shift()!
const worker = this.workers[Math.floor(Math.random() * this.workers.length)]
this.logger.debug('Dispatching job to worker', {
jobId: job.id,
type: job.type,
queueSize: this.jobQueue.length
})
worker.postMessage(job)
} else {
// Wait a bit before checking again
await new Promise(resolve => setTimeout(resolve, 100))
}
}
}
private handleJobResult(result: JobResult): void {
if (result.success) {
this.logger.info('Job completed successfully', {
jobId: result.jobId,
duration: result.duration,
processedAt: result.processedAt
})
} else {
this.logger.error('Job failed', {
jobId: result.jobId,
error: result.error,
duration: result.duration,
processedAt: result.processedAt
})
}
}
async stop(): Promise<void> {
this.logger.info('Stopping worker service')
this.isProcessing = false
// Wait for current jobs to finish (with timeout)
const timeout = setTimeout(() => {
this.logger.warning('Timeout waiting for jobs to finish')
}, 30000)
while (this.jobQueue.length > 0) {
await new Promise(resolve => setTimeout(resolve, 1000))
}
clearTimeout(timeout)
// Terminate workers
await Promise.all(this.workers.map(worker => worker.terminate()))
this.logger.info('Worker service stopped')
this.logger.close()
}
getStats() {
return {
workerCount: this.workers.length,
queueSize: this.jobQueue.length,
isProcessing: this.isProcessing
}
}
}
// Worker thread code
if (!isMainThread) {
const { workerId } = workerData as { workerId: number }
const logger = new Syslog({
ident: `worker-${workerId}`,
facility: SyslogFacility.LOCAL3
})
parentPort?.on('message', async (job: Job) => {
const startTime = Date.now()
logger.debug('Processing job', {
workerId,
jobId: job.id,
type: job.type
})
try {
// Simulate different job types
let result: any
switch (job.type) {
case 'email':
result = await processEmail(job.data)
break
case 'image':
result = await processImage(job.data)
break
case 'report':
result = await processReport(job.data)
break
default:
throw new Error(`Unknown job type: ${job.type}`)
}
const jobResult: JobResult = {
jobId: job.id,
success: true,
result,
processedAt: new Date(),
duration: Date.now() - startTime
}
logger.info('Job processed successfully', {
workerId,
jobId: job.id,
type: job.type,
duration: jobResult.duration
})
parentPort?.postMessage(jobResult)
} catch (error) {
const jobResult: JobResult = {
jobId: job.id,
success: false,
error: error instanceof Error ? error.message : String(error),
processedAt: new Date(),
duration: Date.now() - startTime
}
logger.error('Job processing failed', {
workerId,
jobId: job.id,
type: job.type,
error: jobResult.error,
duration: jobResult.duration
})
parentPort?.postMessage(jobResult)
}
})
async function processEmail(data: any): Promise<any> {
// Simulate email processing
await new Promise(resolve => setTimeout(resolve, Math.random() * 1000 + 500))
return { sent: true, recipient: data.to }
}
async function processImage(data: any): Promise<any> {
// Simulate image processing
await new Promise(resolve => setTimeout(resolve, Math.random() * 2000 + 1000))
return { processed: true, size: data.size, format: 'jpeg' }
}
async function processReport(data: any): Promise<any> {
// Simulate report generation
await new Promise(resolve => setTimeout(resolve, Math.random() * 3000 + 2000))
return { generated: true, pages: Math.floor(Math.random() * 50) + 10 }
}
}
// Main application
if (isMainThread) {
async function main() {
const service = new WorkerService(4)
await service.start()
// Add some sample jobs
for (let i = 1; i <= 20; i++) {
const types = ['email', 'image', 'report']
const priorities = ['low', 'medium', 'high']
service.addJob({
id: `job-${i}`,
type: types[Math.floor(Math.random() * types.length)],
data: { id: i, content: `Sample data ${i}` },
priority: priorities[Math.floor(Math.random() * priorities.length)] as any
})
}
// Monitor progress
const monitor = setInterval(() => {
const stats = service.getStats()
console.log('Stats:', stats)
if (stats.queueSize === 0) {
clearInterval(monitor)
setTimeout(() => service.stop(), 2000)
}
}, 2000)
}
main().catch(error => {
console.error('Failed to start application:', error)
process.exit(1)
})
}
CLI Application Examples
Command Line Tool
#!/usr/bin/env node
import { Command } from 'commander'
import { readFileSync, writeFileSync } from 'fs'
import { Syslog, SyslogFacility, SyslogLevel } from 'node-syslog'
const program = new Command()
const logger = new Syslog({
ident: 'mycli',
facility: SyslogFacility.LOCAL1,
options: ['pid', 'ndelay']
})
program
.name('mycli')
.description('My CLI application with logging')
.version('1.0.0')
program
.command('process')
.description('Process a file')
.argument('<file>', 'file to process')
.option('-o, --output <file>', 'output file')
.option('-v, --verbose', 'verbose output')
.option('-l, --level <level>', 'log level', 'info')
.action((file, options) => {
logger.info('Processing file started', {
file,
output: options.output,
verbose: options.verbose
})
try {
// Set log level if specified
if (options.level && options.level !== 'info') {
logger.info(`Log level set to ${options.level}`)
}
// Read file
if (options.verbose) {
logger.debug('Reading file', { file })
}
const content = readFileSync(file, 'utf8')
const lines = content.split('\n').filter(line => line.trim())
logger.info('File read successfully', {
file,
lines: lines.length,
size: content.length
})
// Process content
const processed = lines.map((line, index) => {
if (options.verbose) {
logger.debug('Processing line', {
lineNumber: index + 1,
content: line.substring(0, 50) + '...'
})
}
// Example processing: convert to uppercase and add line number
return `${index + 1}: ${line.toUpperCase()}`
})
const result = processed.join('\n')
// Write output
if (options.output) {
writeFileSync(options.output, result)
logger.info('Output written', {
output: options.output,
size: result.length
})
} else {
console.log(result)
logger.info('Output printed to console')
}
logger.info('File processing completed successfully', {
file,
linesProcessed: lines.length,
outputSize: result.length
})
} catch (error) {
logger.error('File processing failed', {
file,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined
})
process.exit(1)
}
})
program
.command('monitor')
.description('Monitor system resources')
.option('-i, --interval <seconds>', 'monitoring interval', '5')
.option('-c, --count <number>', 'number of iterations', '10')
.action((options) => {
const interval = parseInt(options.interval) * 1000
const count = parseInt(options.count)
logger.info('Starting system monitoring', {
interval: interval / 1000,
count
})
let iteration = 0
const monitor = setInterval(() => {
iteration++
const memUsage = process.memoryUsage()
const cpuUsage = process.cpuUsage()
logger.info('System metrics', {
iteration,
memory: {
rss: `${Math.round(memUsage.rss / 1024 / 1024)}MB`,
heapTotal: `${Math.round(memUsage.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`,
external: `${Math.round(memUsage.external / 1024 / 1024)}MB`
},
cpu: {
user: cpuUsage.user,
system: cpuUsage.system
},
uptime: `${Math.round(process.uptime())}s`
})
if (iteration >= count) {
clearInterval(monitor)
logger.info('System monitoring completed', {
totalIterations: iteration
})
}
}, interval)
})
// Global error handling
process.on('uncaughtException', (error) => {
logger.critical('Uncaught exception', {
error: error.message,
stack: error.stack
})
logger.close()
process.exit(1)
})
process.on('unhandledRejection', (reason, promise) => {
logger.error('Unhandled rejection', {
reason: reason instanceof Error ? reason.message : String(reason),
stack: reason instanceof Error ? reason.stack : undefined
})
})
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM received, shutting down')
logger.close()
process.exit(0)
})
process.on('SIGINT', () => {
logger.info('SIGINT received, shutting down')
logger.close()
process.exit(0)
})
program.parse()
Testing Examples
Unit Testing with Vitest
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { Syslog, SyslogFacility, SyslogLevel } from '../src/index'
// Mock the native module
vi.mock('../binding.node', () => ({
init: vi.fn(),
log: vi.fn(),
close: vi.fn(),
setLogLevel: vi.fn()
}))
describe('Syslog', () => {
let logger: Syslog
beforeEach(() => {
logger = new Syslog({
ident: 'test-app',
facility: SyslogFacility.LOCAL0,
logLevel: SyslogLevel.DEBUG
})
})
afterEach(() => {
logger.close()
})
describe('constructor', () => {
it('should create logger with valid options', () => {
expect(logger).toBeDefined()
})
it('should throw error with invalid facility', () => {
expect(() => new Syslog({
ident: 'test',
facility: 'invalid' as any
})).toThrow('Invalid facility: invalid')
})
it('should throw error with invalid log level', () => {
expect(() => new Syslog({
ident: 'test',
logLevel: 'invalid' as any
})).toThrow('Invalid log level: invalid')
})
it('should require ident parameter', () => {
expect(() => new Syslog({} as any)).toThrow('ident is required')
})
})
describe('logging methods', () => {
it('should log emergency messages', () => {
const { log } = require('../binding.node')
logger.emerg('Emergency message', { context: 'test' })
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_EMERG
expect.stringContaining('Emergency message')
)
})
it('should log alert messages', () => {
const { log } = require('../binding.node')
logger.alert('Alert message')
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_ALERT
expect.stringContaining('Alert message')
)
})
it('should log critical messages', () => {
const { log } = require('../binding.node')
logger.crit('Critical message')
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_CRIT
expect.stringContaining('Critical message')
)
})
it('should log error messages', () => {
const { log } = require('../binding.node')
logger.error('Error message', { code: 'TEST_ERROR' })
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_ERR
expect.stringContaining('Error message')
)
})
it('should log warning messages', () => {
const { log } = require('../binding.node')
logger.warning('Warning message')
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_WARNING
expect.stringContaining('Warning message')
)
})
it('should log notice messages', () => {
const { log } = require('../binding.node')
logger.notice('Notice message')
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_NOTICE
expect.stringContaining('Notice message')
)
})
it('should log info messages', () => {
const { log } = require('../binding.node')
logger.info('Info message')
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_INFO
expect.stringContaining('Info message')
)
})
it('should log debug messages', () => {
const { log } = require('../binding.node')
logger.debug('Debug message')
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_DEBUG
expect.stringContaining('Debug message')
)
})
})
describe('log level filtering', () => {
it('should filter debug messages when log level is info', () => {
const { log } = require('../binding.node')
const infoLogger = new Syslog({
ident: 'test',
logLevel: SyslogLevel.INFO
})
infoLogger.debug('This should not appear')
infoLogger.info('This should appear')
expect(log).toHaveBeenCalledTimes(1)
expect(log).toHaveBeenCalledWith(
expect.any(Number), // LOG_INFO
expect.stringContaining('This should appear')
)
})
it('should check if debug is enabled', () => {
const infoLogger = new Syslog({
ident: 'test',
logLevel: SyslogLevel.INFO
})
expect(infoLogger.isDebugEnabled()).toBe(false)
const debugLogger = new Syslog({
ident: 'test',
logLevel: SyslogLevel.DEBUG
})
expect(debugLogger.isDebugEnabled()).toBe(true)
})
})
describe('context handling', () => {
it('should handle context objects', () => {
const { log } = require('../binding.node')
logger.info('Test message', {
userId: 123,
action: 'login',
ip: '192.168.1.1'
})
expect(log).toHaveBeenCalledWith(
expect.any(Number),
expect.stringContaining('Test message'),
expect.stringContaining('userId=123'),
expect.stringContaining('action=login'),
expect.stringContaining('ip=192.168.1.1')
)
})
it('should handle circular references in context', () => {
const { log } = require('../binding.node')
const context: any = { userId: 123 }
context.circular = context
expect(() => {
logger.info('Test message', context)
}).not.toThrow()
})
})
describe('cleanup', () => {
it('should close syslog connection', () => {
const { close } = require('../binding.node')
logger.close()
expect(close).toHaveBeenCalled()
})
})
})
These examples demonstrate various real-world usage patterns for node-syslog in different application types and scenarios.