Add API logging functionality and enhance scheduler service

Introduced ApiLog model and integrated logging for scheduled tasks in the SchedulerService. Updated server.js to include request logging middleware and new API log routes. Enhanced frontend navigation by adding a link to system logs for admin users. Adjusted session check interval in App.vue for improved performance. This update improves monitoring and debugging capabilities across the application.
This commit is contained in:
Torsten Schulz (local)
2025-10-29 13:35:25 +01:00
parent 7a35a0a1d3
commit 0b1e745f03
12 changed files with 1307 additions and 6 deletions

View File

@@ -0,0 +1,161 @@
import ApiLog from '../models/ApiLog.js';
import { Op } from 'sequelize';
class ApiLogService {
/**
* Log an API request/response
*/
async logRequest(options) {
try {
const {
userId = null,
method,
path,
statusCode = null,
requestBody = null,
responseBody = null,
executionTime = null,
errorMessage = null,
ipAddress = null,
userAgent = null,
logType = 'api_request',
schedulerJobType = null
} = options;
// Truncate long fields
const truncate = (str, maxLen = 10000) => {
if (!str) return null;
const strVal = typeof str === 'string' ? str : JSON.stringify(str);
return strVal.length > maxLen ? strVal.substring(0, maxLen) + '... (truncated)' : strVal;
};
await ApiLog.create({
userId,
method,
path,
statusCode,
requestBody: truncate(requestBody),
responseBody: truncate(responseBody),
executionTime,
errorMessage: truncate(errorMessage, 5000),
ipAddress,
userAgent,
logType,
schedulerJobType
});
} catch (error) {
console.error('Error logging API request:', error);
// Don't throw - logging failures shouldn't break the main operation
}
}
/**
* Log scheduler execution
*/
async logSchedulerExecution(jobType, success, message, executionTime = null, errorMessage = null) {
try {
await ApiLog.create({
userId: null,
method: 'SCHEDULER',
path: `/scheduler/${jobType}`,
statusCode: success ? 200 : 500,
responseBody: message,
executionTime,
errorMessage,
logType: 'scheduler',
schedulerJobType: jobType
});
} catch (error) {
console.error('Error logging scheduler execution:', error);
}
}
/**
* Get logs with filters
*/
async getLogs(options = {}) {
try {
const {
userId = null,
logType = null,
method = null,
path = null,
statusCode = null,
startDate = null,
endDate = null,
limit = 100,
offset = 0
} = options;
const where = {};
if (userId) {
where.userId = userId;
}
if (logType) {
where.logType = logType;
}
if (method) {
where.method = method;
}
if (path) {
where.path = { [Op.like]: `%${path}%` };
}
if (statusCode !== null) {
where.statusCode = statusCode;
}
if (startDate || endDate) {
where.createdAt = {};
if (startDate) {
where.createdAt[Op.gte] = new Date(startDate);
}
if (endDate) {
where.createdAt[Op.lte] = new Date(endDate);
}
}
const logs = await ApiLog.findAndCountAll({
where,
order: [['createdAt', 'DESC']],
limit: parseInt(limit),
offset: parseInt(offset),
attributes: [
'id', 'userId', 'method', 'path', 'statusCode',
'executionTime', 'errorMessage', 'ipAddress', 'logType',
'schedulerJobType', 'createdAt'
]
});
return {
logs: logs.rows,
total: logs.count,
limit: parseInt(limit),
offset: parseInt(offset)
};
} catch (error) {
console.error('Error getting logs:', error);
throw error;
}
}
/**
* Get a single log by ID
*/
async getLogById(logId) {
try {
const log = await ApiLog.findByPk(logId);
return log;
} catch (error) {
console.error('Error getting log by ID:', error);
throw error;
}
}
}
export default new ApiLogService();

View File

@@ -1,6 +1,7 @@
import cron from 'node-cron';
import autoUpdateRatingsService from './autoUpdateRatingsService.js';
import autoFetchMatchResultsService from './autoFetchMatchResultsService.js';
import apiLogService from './apiLogService.js';
import { devLog } from '../utils/logger.js';
class SchedulerService {
@@ -22,11 +23,33 @@ class SchedulerService {
// Schedule automatic rating updates at 6:00 AM daily
const ratingUpdateJob = cron.schedule('0 6 * * *', async () => {
const startTime = Date.now();
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] CRON: Executing scheduled rating updates...`);
devLog('Executing scheduled rating updates...');
let success = false;
let message = '';
let errorMessage = null;
try {
await autoUpdateRatingsService.executeAutomaticUpdates();
const executionTime = Date.now() - startTime;
success = true;
message = 'Rating updates completed successfully';
console.log(`[${new Date().toISOString()}] CRON: Rating updates completed successfully`);
// Log to ApiLog
await apiLogService.logSchedulerExecution('rating_updates', true, message, executionTime, null);
} catch (error) {
console.error('Error in scheduled rating updates:', error);
const executionTime = Date.now() - startTime;
success = false;
errorMessage = error.message;
console.error(`[${new Date().toISOString()}] CRON ERROR in scheduled rating updates:`, error);
console.error('Stack trace:', error.stack);
// Log to ApiLog
await apiLogService.logSchedulerExecution('rating_updates', false, 'Rating updates failed', executionTime, errorMessage);
}
}, {
scheduled: false, // Don't start automatically
@@ -35,14 +58,37 @@ class SchedulerService {
this.jobs.set('ratingUpdates', ratingUpdateJob);
ratingUpdateJob.start();
console.log('[Scheduler] Rating update job scheduled and started');
// Schedule automatic match results fetching at 6:30 AM daily
const matchResultsJob = cron.schedule('30 6 * * *', async () => {
const startTime = Date.now();
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] CRON: Executing scheduled match results fetch...`);
devLog('Executing scheduled match results fetch...');
let success = false;
let message = '';
let errorMessage = null;
try {
await autoFetchMatchResultsService.executeAutomaticFetch();
const executionTime = Date.now() - startTime;
success = true;
message = 'Match results fetch completed successfully';
console.log(`[${new Date().toISOString()}] CRON: Match results fetch completed successfully`);
// Log to ApiLog
await apiLogService.logSchedulerExecution('match_results', true, message, executionTime, null);
} catch (error) {
console.error('Error in scheduled match results fetch:', error);
const executionTime = Date.now() - startTime;
success = false;
errorMessage = error.message;
console.error(`[${new Date().toISOString()}] CRON ERROR in scheduled match results fetch:`, error);
console.error('Stack trace:', error.stack);
// Log to ApiLog
await apiLogService.logSchedulerExecution('match_results', false, 'Match results fetch failed', executionTime, errorMessage);
}
}, {
scheduled: false, // Don't start automatically
@@ -51,8 +97,25 @@ class SchedulerService {
this.jobs.set('matchResults', matchResultsJob);
matchResultsJob.start();
console.log('[Scheduler] Match results fetch job scheduled and started');
this.isRunning = true;
const now = new Date();
const tomorrow6AM = new Date(now);
tomorrow6AM.setDate(tomorrow6AM.getDate() + 1);
tomorrow6AM.setHours(6, 0, 0, 0);
const tomorrow630AM = new Date(now);
tomorrow630AM.setDate(tomorrow630AM.getDate() + 1);
tomorrow630AM.setHours(6, 30, 0, 0);
console.log('[Scheduler] ===== SCHEDULER SERVICE STARTED =====');
console.log(`[Scheduler] Server time: ${now.toISOString()}`);
console.log(`[Scheduler] Timezone: Europe/Berlin`);
console.log(`[Scheduler] Rating updates: Next execution at ${tomorrow6AM.toISOString()} (6:00 AM Berlin time)`);
console.log(`[Scheduler] Match results fetch: Next execution at ${tomorrow630AM.toISOString()} (6:30 AM Berlin time)`);
console.log('[Scheduler] =====================================');
devLog('Scheduler service started successfully');
devLog('Rating updates scheduled for 6:00 AM daily (Europe/Berlin timezone)');
devLog('Match results fetch scheduled for 6:30 AM daily (Europe/Berlin timezone)');