feat: add logger.js and signifigantly improve logging

This commit is contained in:
Rim
2025-04-01 22:02:27 -04:00
parent 2b90e3f567
commit 50f338917c
4 changed files with 508 additions and 389 deletions

168
app.js
View File

@ -2,6 +2,7 @@ const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const API = require("./src/js/index.js");
const { logger } = require('./src/js/logger');
const favicon = require('serve-favicon');
const app = express();
const port = process.env.PORT || 3512;
@ -30,12 +31,12 @@ try {
if (fs.existsSync(replacementsPath)) {
const replacementsContent = fs.readFileSync(replacementsPath, 'utf8');
keyReplacements = JSON.parse(replacementsContent);
// console.log("Replacements loaded successfully");
// logger.debug("Replacements loaded successfully");
} else {
console.log("replacements.json not found, key replacement disabled");
logger.warn("replacements.json not found, key replacement disabled");
}
} catch (error) {
console.error("Error loading replacements file:", error);
logger.error("Error loading replacements file:", { error: error.message });
}
const replaceJsonKeys = (obj) => {
@ -52,14 +53,14 @@ const replaceJsonKeys = (obj) => {
// DEBUG: Log replacements when they happen
// if (newKey !== key) {
// console.log(`Replacing key "${key}" with "${newKey}"`);
// logger.debug(`Replacing key "${key}" with "${newKey}"`);
// }
// Also check if the value should be replaced (if it's a string)
let value = obj[key];
if (typeof value === 'string' && keyReplacements[value]) {
value = keyReplacements[value];
// console.log(`Replacing value "${obj[key]}" with "${value}"`);
// logger.debug(`Replacing value "${obj[key]}" with "${value}"`);
}
// Process value recursively if it's an object or array
@ -140,26 +141,26 @@ const timeoutPromise = (ms) => {
// Helper function to ensure login
const ensureLogin = async (ssoToken) => {
if (!activeSessions.has(ssoToken)) {
console.log(`Attempting to login with SSO token: ${ssoToken.substring(0, 5)}...`);
// console.log(`Attempting to login with SSO token: ${ssoToken}`);
logger.info(`Attempting to login with SSO token: ${ssoToken.substring(0, 5)}...`);
// logger.info(`Attempting to login with SSO token: ${ssoToken}`);
const loginResult = await Promise.race([
API.login(ssoToken),
timeoutPromise(10000), // 10 second timeout
]);
console.log(`Login successful: ${JSON.stringify(loginResult)}`);
console.log(`Session created at: ${new Date().toISOString()}`);
logger.debug(`Login successful: ${JSON.stringify(loginResult)}`);
logger.debug(`Session created at: ${new Date().toISOString()}`);
activeSessions.set(ssoToken, new Date());
} else {
console.log("Using existing session");
logger.debug("Using existing session");
}
};
// Helper function to handle API errors
const handleApiError = (error, res) => {
console.error("API Error:", error);
console.error(`Error Stack: ${error.stack}`);
console.error(`Error Time: ${new Date().toISOString()}`);
logger.error("API Error:", error);
logger.error(`Error Stack: ${error.stack}`);
logger.error(`Error Time: ${new Date().toISOString()}`);
// Try to extract more useful information from the error
let errorMessage = error.message || "Unknown API error";
@ -167,7 +168,7 @@ const handleApiError = (error, res) => {
// Handle the specific JSON parsing error
if (errorName === "SyntaxError" && errorMessage.includes("JSON")) {
console.log("JSON parsing error detected");
logger.debug("JSON parsing error detected");
return res.status(200).json({
status: "error",
message:
@ -188,9 +189,9 @@ const handleApiError = (error, res) => {
// API endpoint to fetch stats
app.post("/api/stats", async (req, res) => {
console.log("Received request for /api/stats");
console.log(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
console.log(`Request JSON: ${JSON.stringify({
logger.debug("Received request for /api/stats");
logger.debug(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
logger.debug(`Request JSON: ${JSON.stringify({
username: req.body.username,
platform: req.body.platform,
game: req.body.game,
@ -203,17 +204,17 @@ app.post("/api/stats", async (req, res) => {
const { username, ssoToken, platform, game, apiCall, sanitize, replaceKeys } = req.body;
/*
console.log(
logger.debug(
`Request details - Username: ${username}, Platform: ${platform}, Game: ${game}, API Call: ${apiCall}`
);
console.log("=== STATS REQUEST ===");
console.log(`Username: ${username || 'Not provided'}`);
console.log(`Platform: ${platform}`);
console.log(`Game: ${game}`);
console.log(`API Call: ${apiCall}`);
console.log(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
console.log("====================="); */
logger.debug("=== STATS REQUEST ===");
logger.debug(`Username: ${username || 'Not provided'}`);
logger.debug(`Platform: ${platform}`);
logger.debug(`Game: ${game}`);
logger.debug(`API Call: ${apiCall}`);
logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
logger.debug("====================="); */
if (!ssoToken) {
return res.status(400).json({ error: "SSO Token is required" });
@ -226,7 +227,7 @@ app.post("/api/stats", async (req, res) => {
// Clear previous session if it exists
if (activeSessions.has(ssoToken)) {
console.log("Clearing previous session");
logger.debug("Clearing previous session");
activeSessions.delete(ssoToken);
}
@ -255,7 +256,7 @@ app.post("/api/stats", async (req, res) => {
// Check if the platform is valid for the game
const requiresUno = ["mw2", "wz2", "mw3", "wzm"].includes(game);
if (requiresUno && platform !== "uno" && apiCall !== "mapList") {
console.log(`${game} requires Uno ID`);
logger.warn(`${game} requires Uno ID`);
return res.status(200).json({
status: "error",
message: `${game} requires Uno ID (numerical ID)`,
@ -264,7 +265,7 @@ app.post("/api/stats", async (req, res) => {
}
try {
console.log(
logger.debug(
`Attempting to fetch ${game} data for ${username} on ${platform}`
);
let data;
@ -384,13 +385,13 @@ app.post("/api/stats", async (req, res) => {
}
}
console.log("Data fetched successfully");
console.log(`Response Size: ~${JSON.stringify(data).length / 1024} KB`);
console.log(`Response Time: ${new Date().toISOString()}`);
logger.debug("Data fetched successfully");
logger.debug(`Response Size: ~${JSON.stringify(data).length / 1024} KB`);
logger.debug(`Response Time: ${new Date().toISOString()}`);
// Safely handle the response data
if (!data) {
console.log("No data returned from API");
logger.warn("No data returned from API");
return res.json({
status: "partial_success",
message: "No data returned from API, but no error thrown",
@ -399,7 +400,7 @@ app.post("/api/stats", async (req, res) => {
});
}
// console.log("Returning data to client");
// logger.debug("Returning data to client");
const { sanitize, replaceKeys } = req.body;
@ -426,9 +427,9 @@ app.post("/api/stats", async (req, res) => {
// API endpoint to fetch recent matches
app.post("/api/matches", async (req, res) => {
console.log("Received request for /api/matches");
console.log(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
console.log(`Request JSON: ${JSON.stringify({
logger.debug("Received request for /api/matches");
logger.debug(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
logger.debug(`Request JSON: ${JSON.stringify({
username: req.body.username,
platform: req.body.platform,
game: req.body.game,
@ -440,16 +441,16 @@ app.post("/api/matches", async (req, res) => {
const { username, ssoToken, platform, game, sanitize, replaceKeys } = req.body;
/*
console.log(
logger.debug(
`Request details - Username: ${username}, Platform: ${platform}, Game: ${game}`
);
console.log("=== MATCHES REQUEST ===");
console.log(`Username: ${username}`);
console.log(`Platform: ${platform}`);
console.log(`Game: ${game}`);
console.log(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
console.log("========================"); */
logger.debug("=== MATCHES REQUEST ===");
logger.debug(`Username: ${username}`);
logger.debug(`Platform: ${platform}`);
logger.debug(`Game: ${game}`);
logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
logger.debug("========================"); */
if (!username || !ssoToken) {
return res
@ -478,7 +479,7 @@ app.post("/api/matches", async (req, res) => {
};
try {
console.log(
logger.debug(
`Attempting to fetch combat history for ${username} on ${platform}`
);
let data;
@ -565,9 +566,9 @@ app.post("/api/matches", async (req, res) => {
// API endpoint to fetch match info
app.post("/api/matchInfo", async (req, res) => {
console.log("Received request for /api/matchInfo");
console.log(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
console.log(`Request JSON: ${JSON.stringify({
logger.debug("Received request for /api/matchInfo");
logger.debug(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
logger.debug(`Request JSON: ${JSON.stringify({
matchId: req.body.matchId,
platform: req.body.platform,
game: req.body.game,
@ -580,16 +581,16 @@ app.post("/api/matchInfo", async (req, res) => {
const mode = "mp";
/*
console.log(
logger.debug(
`Request details - Match ID: ${matchId}, Platform: ${platform}, Game: ${game}`
);
console.log("=== MATCH INFO REQUEST ===");
console.log(`Match ID: ${matchId}`);
console.log(`Platform: ${platform}`);
console.log(`Game: ${game}`);
console.log(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
console.log("=========================="); */
logger.debug("=== MATCH INFO REQUEST ===");
logger.debug(`Match ID: ${matchId}`);
logger.debug(`Platform: ${platform}`);
logger.debug(`Game: ${game}`);
logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
logger.debug("=========================="); */
if (!matchId || !ssoToken) {
return res
@ -618,7 +619,7 @@ app.post("/api/matchInfo", async (req, res) => {
};
try {
console.log(`Attempting to fetch match info for match ID: ${matchId}`);
logger.debug(`Attempting to fetch match info for match ID: ${matchId}`);
let data;
// Fetch match info based on game
@ -691,9 +692,9 @@ app.post("/api/matchInfo", async (req, res) => {
// API endpoint for user-related API calls
app.post("/api/user", async (req, res) => {
console.log("Received request for /api/user");
console.log(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
console.log(`Request JSON: ${JSON.stringify({
logger.debug("Received request for /api/user");
logger.debug(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
logger.debug(`Request JSON: ${JSON.stringify({
username: req.body.username,
platform: req.body.platform,
userCall: req.body.userCall,
@ -705,16 +706,16 @@ app.post("/api/user", async (req, res) => {
const { username, ssoToken, platform, userCall, sanitize, replaceKeys } = req.body;
/*
console.log(
logger.debug(
`Request details - Username: ${username}, Platform: ${platform}, User Call: ${userCall}`
);
console.log("=== USER DATA REQUEST ===");
console.log(`Username: ${username || 'Not provided'}`);
console.log(`Platform: ${platform}`);
console.log(`User Call: ${userCall}`);
console.log(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
console.log("========================="); */
logger.debug("=== USER DATA REQUEST ===");
logger.debug(`Username: ${username || 'Not provided'}`);
logger.debug(`Platform: ${platform}`);
logger.debug(`User Call: ${userCall}`);
logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
logger.debug("========================="); */
if (!ssoToken) {
return res.status(400).json({ error: "SSO Token is required" });
@ -753,7 +754,7 @@ app.post("/api/user", async (req, res) => {
};
try {
console.log(`Attempting to fetch user data for ${userCall}`);
logger.debug(`Attempting to fetch user data for ${userCall}`);
let data;
// Fetch user data based on userCall
@ -817,9 +818,9 @@ app.post("/api/user", async (req, res) => {
// API endpoint for fuzzy search
app.post("/api/search", async (req, res) => {
console.log("Received request for /api/search");
console.log(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
console.log(`Request JSON: ${JSON.stringify({
logger.debug("Received request for /api/search");
logger.debug(`Request IP: ${req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress}`);
logger.debug(`Request JSON: ${JSON.stringify({
username: req.body.username,
platform: req.body.platform,
sanitize: req.body.sanitize,
@ -830,15 +831,15 @@ app.post("/api/search", async (req, res) => {
const { username, ssoToken, platform, sanitize, replaceKeys } = req.body;
/*
console.log(
logger.debug(
`Request details - Username to search: ${username}, Platform: ${platform}`
);
console.log("=== SEARCH REQUEST ===");
console.log(`Search Term: ${username}`);
console.log(`Platform: ${platform}`);
console.log(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
console.log("======================"); */
logger.debug("=== SEARCH REQUEST ===");
logger.debug(`Search Term: ${username}`);
logger.debug(`Platform: ${platform}`);
logger.debug(`Processing Options - Sanitize: ${sanitize}, Replace Keys: ${replaceKeys}`);
logger.debug("======================"); */
if (!username || !ssoToken) {
return res
@ -867,7 +868,7 @@ app.post("/api/search", async (req, res) => {
};
try {
console.log(
logger.debug(
`Attempting fuzzy search for ${username} on platform ${platform}`
);
const data = await fetchWithTimeout(() =>
@ -929,15 +930,14 @@ app.post('/api/log', (req, res) => {
}
};
// For structured logging in production, consider using a logging service
console.log(`[USER_ACTIVITY] ${JSON.stringify(enrichedLog)}`);
// Optional: Store logs in database for advanced analytics
// storeLogInDatabase(enrichedLog);
// Use the dedicated user activity logger
logger.userActivity(enrichedLog.eventType || 'unknown', enrichedLog);
} catch (error) {
console.error('Error processing log data:', error);
console.log('Raw request body:', req.body);
logger.error('Error processing log data', {
error: error.message,
rawBody: typeof req.body === 'object' ? '[Object]' : req.body
});
}
// Always return 200 to avoid client-side errors
@ -980,5 +980,5 @@ app.get("/", (req, res) => {
// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
logger.info(`Server running on http://localhost:${port}`);
});