feat: add comprehensive logging system with file output
- Add backend logging to logs/backend.log with file rotation support - Add frontend logging to logs/frontend.log via /api/logs endpoint - Add frontend logger utility with batching and user context - Update .gitignore to exclude log files but preserve logs directory - Update CLAUDE.md with logging documentation and usage examples Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
import Database from 'bun:sqlite';
|
||||
import { drizzle } from 'drizzle-orm/bun-sqlite';
|
||||
import * as schema from './db/schema/index.js';
|
||||
import { join } from 'path';
|
||||
import { join, dirname } from 'path';
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// ===================================================================
|
||||
// Configuration
|
||||
// ===================================================================
|
||||
|
||||
// ES module equivalent of __dirname
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
|
||||
export const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||
@@ -19,16 +25,46 @@ export const LOG_LEVEL = process.env.LOG_LEVEL || (isDevelopment ? 'debug' : 'in
|
||||
const logLevels = { debug: 0, info: 1, warn: 2, error: 3 };
|
||||
const currentLogLevel = logLevels[LOG_LEVEL] ?? 1;
|
||||
|
||||
// Log file paths
|
||||
const logsDir = join(__dirname, '../../logs');
|
||||
const backendLogFile = join(logsDir, 'backend.log');
|
||||
|
||||
// Ensure log directory exists
|
||||
if (!existsSync(logsDir)) {
|
||||
mkdirSync(logsDir, { recursive: true });
|
||||
}
|
||||
|
||||
function formatLogMessage(level, message, data) {
|
||||
const timestamp = new Date().toISOString();
|
||||
let logMessage = `[${timestamp}] ${level.toUpperCase()}: ${message}`;
|
||||
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
logMessage += ' ' + JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
return logMessage + '\n';
|
||||
}
|
||||
|
||||
function log(level, message, data) {
|
||||
if (logLevels[level] < currentLogLevel) return;
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
const logMessage = `[${timestamp}] ${level.toUpperCase()}: ${message}`;
|
||||
const logMessage = formatLogMessage(level, message, data);
|
||||
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
console.log(logMessage, JSON.stringify(data, null, 2));
|
||||
} else {
|
||||
console.log(logMessage);
|
||||
// Write to file asynchronously (fire and forget for performance)
|
||||
Bun.write(backendLogFile, logMessage, { createPath: true }).catch(err => {
|
||||
console.error('Failed to write to log file:', err);
|
||||
});
|
||||
|
||||
// Also log to console in development
|
||||
if (isDevelopment) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const consoleMessage = `[${timestamp}] ${level.toUpperCase()}: ${message}`;
|
||||
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
console.log(consoleMessage, data);
|
||||
} else {
|
||||
console.log(consoleMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +75,27 @@ export const logger = {
|
||||
error: (message, data) => log('error', message, data),
|
||||
};
|
||||
|
||||
// Frontend logger - writes to separate log file
|
||||
const frontendLogFile = join(logsDir, 'frontend.log');
|
||||
|
||||
export function logToFrontend(level, message, data = null, context = {}) {
|
||||
if (logLevels[level] < currentLogLevel) return;
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
let logMessage = `[${timestamp}] [${context.userAgent || 'unknown'}] [${context.userId || 'anonymous'}] ${level.toUpperCase()}: ${message}`;
|
||||
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
logMessage += ' ' + JSON.stringify(data, null, 2);
|
||||
}
|
||||
|
||||
logMessage += '\n';
|
||||
|
||||
// Write to frontend log file
|
||||
Bun.write(frontendLogFile, logMessage, { createPath: true }).catch(err => {
|
||||
console.error('Failed to write to frontend log file:', err);
|
||||
});
|
||||
}
|
||||
|
||||
export default logger;
|
||||
|
||||
// ===================================================================
|
||||
@@ -46,7 +103,6 @@ export default logger;
|
||||
// ===================================================================
|
||||
|
||||
// Get the directory containing this config file, then go to parent for db location
|
||||
const __dirname = new URL('.', import.meta.url).pathname;
|
||||
const dbPath = join(__dirname, 'award.db');
|
||||
|
||||
const sqlite = new Database(dbPath);
|
||||
|
||||
Reference in New Issue
Block a user