Logging
Zintrust features a robust, file-based logging system that helps you monitor your application and debug issues.
Basic Usage
Use the Logger namespace to record information:
import { Logger } from '@config/logger';
Logger.info('User logged in', { userId: 1 });
Logger.error('Database connection failed', { error: err.message });
Logger.debug('Query executed', { sql: query });Log Levels
Zintrust supports standard log levels:
debug: Detailed information for debugging.info: General application events.warn: Exceptional events that are not errors.error: Runtime errors that require attention.
Environment Configuration
Control logging behavior using environment variables:
LOG_LEVEL
Controls the minimum log level that will be recorded. The logging system uses priority-based filtering:
# Default: debug (captures all logs)
LOG_LEVEL=debug # Captures: debug, info, warn, error
LOG_LEVEL=info # Captures: info, warn, error
LOG_LEVEL=warn # Captures: warn, error
LOG_LEVEL=error # Captures: error onlyThe logger is initialized in the Application constructor with the environment setting:
// In src/boot/Application.ts
if (!Env.DISABLE_LOGGING) {
Logger.initialize(undefined, undefined, undefined, Env.LOG_LEVEL);
}Development Example:
# Development: Capture all logs including debug
export LOG_LEVEL=debug
npm run devProduction Example:
# Production: Only capture warnings and errors
export LOG_LEVEL=warn
NODE_ENV=production npm startDISABLE_LOGGING
Completely disables the logging system. Use with caution:
# Default: false (logging enabled)
DISABLE_LOGGING=true # Disables all logging
DISABLE_LOGGING=false # Enables loggingWarning: Disabling logging in production removes the ability to debug production issues. Only disable logging if you have alternative observability systems in place.
Log Files
Logs are stored in the logs/ directory:
logs/app/: General application logs.logs/errors/: Error-specific logs.logs/cli/: CLI command execution logs.logs/migrations/: Database migration logs.
Log Rotation
Zintrust automatically rotates log files daily or when they reach a certain size (default 10MB), keeping your disk space usage under control.
Viewing Logs
You can view and tail logs using the CLI:
# View recent logs
zin logs
# Tail logs in real-time
zin logs --follow
# Filter by level
zin logs --level errorError Handling
Zintrust enforces a "Zero-Swallow" safety guarantee: all errors must be logged before being handled or re-thrown.
Required Logger.error() in Catch Blocks
The ESLint rule no-restricted-syntax enforces that every catch block includes a Logger.error() call:
// ❌ INVALID - ESLint Error
try {
const { User } = await import('@app/Models/User');
await User.query().get();
} catch (error) {
// Missing Logger.error() call!
return null;
}
// ✅ VALID - Compliant with safety rule
try {
const { User } = await import('@app/Models/User');
await User.query().get();
} catch (error) {
Logger.error('Database query failed', error);
return null;
}How Log Level Filtering Works
Important: Logger.error() calls are always required in catch blocks, regardless of LOG_LEVEL setting. The filtering happens inside the Logger, not at the call site:
// This error WILL BE LOGGED even if LOG_LEVEL=warn
try {
await connectDatabase();
} catch (error) {
Logger.error('Database connection failed', error); // Always executes
}
// At runtime:
// - If LOG_LEVEL=error → This error message is recorded
// - If LOG_LEVEL=warn → This error message is recorded (warn < error priority)
// - If LOG_LEVEL=info → This error message is recorded
// - If LOG_LEVEL=debug → This error message is recordedWhy This Matters
The safety guarantee ensures that:
- No Silent Failures: Every error path includes logging
- Production Debugging: Error logs are always available when needed
- Consistency: All catch blocks follow the same pattern
You control which errors appear in production logs using LOG_LEVEL, but you cannot prevent an error from being logged through code—only through environment configuration.
Example: Handling and Filtering Errors
// Source code - always has Logger.error()
async function processPayment(userId: number) {
try {
return await paymentGateway.charge(userId);
} catch (error) {
Logger.error('Payment processing failed', error); // Always executed
throw error; // Re-throw after logging
}
}
// Runtime behavior:
// $ LOG_LEVEL=error → Only payment errors appear in logs
// $ LOG_LEVEL=info → Payment errors + info messages appear
// $ LOG_LEVEL=debug → All details including debug logs appear