This boilerplate includes a structured logging utility that provides context-aware logging with multiple severity levels, making it easy to track application behavior and debug issues.
The logging system provides:
The logger is located in shared/utils/logger.ts and provides structured logging with context support:
import { logger } from '@@/shared/utils/logger'
// Basic logging
logger.info('User signed up', { userId, email })
logger.warn('Rate limit approaching', { ip, count })
logger.error('Database query failed', error)
// With additional context
logger.error('Failed to process payment', error, {
userId,
amount,
currency,
paymentId,
})
| Level | When to use | Example |
|---|---|---|
error | Unexpected failures, exceptions | Database errors, API failures |
warn | Concerning but handled situations | Rate limits, deprecation warnings |
info | Important state changes, successful operations | User signups, successful payments |
debug | Detailed flow information (development only) | Request/response details, function calls |
// Information logging
logger.info('Product created', { productId, userId })
// Warning logging
logger.warn('Cache miss', { key, timestamp })
// Error logging
logger.error('Failed to send email', error)
Add relevant metadata to help with debugging:
logger.info('Payment processed', {
userId,
amount,
currency,
paymentId,
processor: 'stripe',
})
logger.error('Payment failed', error, {
userId,
amount,
attemptCount,
lastError: error.message,
})
export async function createProduct(data: CreateProductData) {
logger.info('Creating product', { name: data.name, price: data.price })
const product = await prisma.product.create({ data })
logger.info('Product created', { productId: product.id })
return product
}
export default defineEventHandler(async event => {
const userId = await requireAuth(event)
logger.info('Fetching user products', { userId })
const products = await getProducts(userId)
logger.info('Retrieved products', { userId, count: products.length })
return products
})
export async function processPayment(paymentData: PaymentData) {
try {
const charge = await stripe.charges.create(paymentData)
logger.info('Payment processed', { chargeId: charge.id, amount: charge.amount })
return charge
} catch (error) {
logger.error('Payment processing failed', error, {
amount: paymentData.amount,
currency: paymentData.currency,
customerId: paymentData.customer,
})
throw error
}
}
Use objects for logging instead of string concatenation:
// Good - structured data
logger.info('Payment processed', {
userId,
amount,
currency,
paymentId
})
// Avoid - string concatenation
logger.info(`User ${userId} paid ${amount} ${currency}`)
Add context that helps with debugging:
logger.error('Failed to update user', error, {
userId,
attemptedChanges: changes,
timestamp: new Date().toISOString(),
})
Never log sensitive information:
// Never do this
logger.info('User login', { email, password })
logger.info('Payment details', { creditCard, cvv })
// Do this instead
logger.info('User login', { email })
logger.info('Payment processed', { last4, brand })
// Error - something went wrong
logger.error('Database connection failed', error)
// Warn - concerning but not critical
logger.warn('Slow query detected', { query, duration })
// Info - important events
logger.info('User registered', { userId, email })
// Debug - detailed information (dev only)
logger.debug('Cache lookup', { key, hit: true })
logger.error(message: string, error?: unknown, context?: Record<string, unknown>)
logger.warn(message: string, context?: Record<string, unknown>)
logger.info(message: string, context?: Record<string, unknown>)
logger.debug(message: string, context?: Record<string, unknown>)
logger.error)In production, consider:
// In your logger utility or server middleware
import { Logger } from '@datadog/browser-logs'
export function logToDatadog(level: string, message: string, context?: object) {
if (process.env.NODE_ENV === 'production') {
Logger.log(message, context, level)
}
}
shared/utils/logger.ts - Logger utility implementationserver/middleware/99-error-handler.ts - Uses logger for error logging