Log Application
The Log application allows viewing, filtering, and analyzing system logs. It currently supports displaying error logs; activity logs are under development.
Overview
Section titled “Overview”The Log application consists of two main parts:
- Error Logs — System errors, warnings, and debug messages
- Activity Logs — User action logging (under development)
Main features
Section titled “Main features”- Display error logs in tabular form
- Filter by log level (debug, info, warn, error, fatal)
- Filter by source
- Sort by column
- Pagination for large datasets
- Color-coded log levels
- Permission-based access
File structure
Section titled “File structure”apps/log/├── index.svelte # Main layout (AppLayout + menu)├── menu.json # Menu definition (Error, Activity)├── error-logs.remote.ts # Server action for fetching error logs└── components/ ├── ErrorLog.svelte # Error logs table with filters ├── ActivityLog.svelte # Activity logs (placeholder) └── errorLogColumns.ts # Table column definitionsMenu structure
Section titled “Menu structure”The menu.json defines the application menu items:
[ { "labelKey": "menu.error", "href": "#error", "icon": "ShieldAlert", "component": "ErrorLog", "requiredPermission": "log.error.view" }, { "labelKey": "menu.activity", "href": "#activity", "icon": "ShieldAlert", "component": "ActivityLog" }]Permissions:
log.error.view— View error logs (admin only)- Activity Log is currently not permission-restricted
Server Actions
Section titled “Server Actions”error-logs.remote.ts
Section titled “error-logs.remote.ts”fetchErrorLogs (command)
Section titled “fetchErrorLogs (command)”Fetch error logs with filtering and pagination parameters.
const result = await fetchErrorLogs({ page: 1, pageSize: 20, level: ['error', 'fatal'], // optional, string or string[] source: 'server', // optional search: 'database', // optional (currently unused) sortBy: 'timestamp', // optional, default: 'timestamp' sortOrder: 'desc' // optional, 'asc' or 'desc'});Validation:
page: minimum 1, default: 1pageSize: between 1-100, default: 20level: ‘debug’ | ‘info’ | ‘warn’ | ‘error’ | ‘fatal’ (single or array)source: stringsortBy: stringsortOrder: ‘asc’ | ‘desc’
Return value:
{ success: true, data: LogEntry[], pagination: { page: number, pageSize: number, totalCount: number, totalPages: number }}Components
Section titled “Components”ErrorLog.svelte
Section titled “ErrorLog.svelte”Display error logs using the DataTable component with filters and pagination.
State:
let data = $state<LogEntry[]>([]);let loading = $state(false);let paginationInfo = $state<PaginationInfo>({...});let levelFilter = $state<string[]>([]);let sourceFilter = $state('');let tableState = $state<DataTableState>({ page: 1, pageSize: 20, sortBy: 'timestamp', sortOrder: 'desc'});Filters:
- Level filter — Faceted filter component (multi-select: debug, info, warn, error, fatal)
- Source filter — Input field with 300ms debounce, partial match (ILIKE)
Reactivity:
$effect(() => { tableState; levelFilter; sourceFilter; untrack(() => loadData());});errorLogColumns.ts
Section titled “errorLogColumns.ts”Table column definitions for TanStack Table.
Columns:
- Level — Log level with color coding (debug: gray, info: blue, warn: yellow, error: red, fatal: dark red bold)
- Message — Error message (max width 500px, truncated, tooltip with full message)
- Source — Source (e.g., ‘server’, ‘client’, ‘database’) — monospace font, badge style
- Timestamp — Timestamp with localized format (
toLocaleString())
Logging system
Section titled “Logging system”Logger class
Section titled “Logger class”The logger.ts file contains the central Logger class.
Initialization:
const config = createLogConfig( env.LOG_TARGETS, // 'console' | 'file' | 'database' env.LOG_LEVEL, // 'debug' | 'info' | 'warn' | 'error' | 'fatal' env.LOG_DIR // File log directory);export const logger = new Logger(config);Usage:
import { logger } from '$lib/server/logging/logger';
await logger.debug('Debug message', { source: 'myModule' });await logger.info('User logged in', { source: 'auth', userId: '123' });await logger.warn('Deprecated API used', { source: 'api' });await logger.error('Database connection failed', { source: 'database', stack: error.stack });await logger.fatal('System crash', { source: 'server', stack: error.stack });Log level priority:
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4};Transports
Section titled “Transports”The Logger supports three transports:
- ConsoleTransport — Write to console (for development)
- FileTransport — Write to file (for production)
- DatabaseTransport — Write to database (for UI display)
Configuration:
LOG_TARGETS=console,database # Comma-separatedLOG_LEVEL=info # Minimum log levelLOG_DIR=./logs # File log directoryLogEntry
Section titled “LogEntry”interface LogEntry { id: string; level: LogLevel; message: string; source: string; timestamp: string; stack?: string; context?: Record<string, unknown>; userId?: string; url?: string; method?: string; routeId?: string; userAgent?: string;}LogLevel
Section titled “LogLevel”type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';Database schema
Section titled “Database schema”error_logs table
Section titled “error_logs table”{ id: serial('id').primaryKey(), level: varchar('level', { length: 10 }).notNull(), message: text('message').notNull(), source: varchar('source', { length: 100 }).notNull(), stack: text('stack'), context: jsonb('context'), userId: varchar('user_id', { length: 255 }), url: text('url'), method: varchar('method', { length: 10 }), routeId: varchar('route_id', { length: 255 }), userAgent: text('user_agent'), createdAt: timestamp('created_at').defaultNow().notNull()}Indexes: level, source, createdAt, userId
Usage examples
Section titled “Usage examples”Logging an error
Section titled “Logging an error”import { logger } from '$lib/server/logging/logger';
try { await riskyOperation();} catch (error) { await logger.error('Operation failed', { source: 'myModule', stack: error.stack, context: { operation: 'riskyOperation' } }); throw error;}HTTP request logging
Section titled “HTTP request logging”export async function handle({ event, resolve }) { try { const response = await resolve(event); await logger.info('HTTP request', { source: 'http', url: event.url.pathname, method: event.request.method, userId: event.locals.user?.id }); return response; } catch (error) { await logger.error('HTTP request failed', { source: 'http', url: event.url.pathname, stack: error.stack }); throw error; }}Best practices
Section titled “Best practices”- Use appropriate log levels — debug for development, info for normal operation, warn for warnings, error for errors, fatal for critical failures
- Add context — Always add relevant context (userId, url, method, etc.)
- Stack trace — Always include the stack trace for errors
- Sensitive data — Never log passwords, API keys, or other sensitive data
- Database size — Regularly clean up old log entries (e.g., delete entries older than 30 days)
- Source — Always specify the source for easier filtering and debugging
Troubleshooting
Section titled “Troubleshooting”Logs not appearing
Section titled “Logs not appearing”Problem: No log entries visible in the Log application.
Solution:
- Check the
LOG_TARGETSenvironment variable — does it includedatabase? - Check the
LOG_LEVELsetting — it may be too high - Check the
error_logstable directly in the database - Check permissions (
log.error.view)
Filtering not working
Section titled “Filtering not working”Problem: Filters don’t filter the data.
Solution:
- Check the browser console for errors
- Check the network tab — is
fetchErrorLogsbeing called? - Check
$effectreactivity — are all filter variables included?