Open zepor opened 1 month ago
To address the issue of differentiating between client and server logs, we need to update the logging mechanisms in both the backend and frontend codebases. By adding identifiers to the log messages, we can easily distinguish between logs generated by the server and those generated by the client.
backend-container/src/utils/logandcatchexceptions.py
We will update the decorator function to include a 'server' identifier in the log messages.
from functools import wraps
from src.utils.log import be_logger
def log_and_catch_exceptions(func):
@wraps(func)
def func_wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
be_logger.error(f"[SERVER] Exception in {func.__name__}: {e}")
raise
return func_wrapper
backend-container/src/utils/log.py
We will modify the logging configuration to include a 'server' identifier in the log messages.
import sys
import os
import logging
from logging.handlers import RotatingFileHandler
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.abspath(os.path.join(current_dir, '..'))
sys.path.append(project_root)
be_logger = logging.getLogger('backend-logger')
be_logger.setLevel(logging.DEBUG)
log_dir = './logs/'
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_file = os.path.join(log_dir, 'app.log')
file_handler = RotatingFileHandler(log_file, maxBytes=10000000, backupCount=5)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - [SERVER] - %(message)s')
file_handler.setFormatter(file_formatter)
be_logger.addHandler(file_handler)
console_handler = logging.StreamHandler(sys.stdout)
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - [SERVER] - %(message)s')
console_handler.setFormatter(console_formatter)
be_logger.addHandler(console_handler)
be_logger.info('FLASK_APP: %s', os.getenv('FLASK_APP'))
be_logger.info('FLASK_DEBUG: %s', os.getenv('FLASK_DEBUG'))
frontend-container/src/utils/logs.js
We will update the logging functions to include a 'client' identifier in the log messages.
import * as diff from 'diff';
import chalk from 'chalk';
let routeCounts = {};
let currentRoute = '';
const loggedMessages = new Set();
const htmlLogLimit = 2000;
const colors = {
server: chalk.green,
client: chalk.blue,
error: chalk.red,
variable: chalk.cyan,
route: chalk.hex('#FFA500'),
filename: chalk.hex('#FFD700'),
};
const isSSR = typeof process !== 'undefined' && process.env.SSR === 'true';
function safeStringify(obj, replacer = null, space = 0) {
const seen = new WeakSet();
return JSON.stringify(
obj,
(key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]';
}
seen.add(value);
}
return replacer ? replacer(key, value) : value;
},
space,
);
}
function truncateMessage(msg, length = 200) {
if (typeof msg === 'string' && msg.length > length) {
return msg.slice(0, length) + '...[truncated]';
}
return msg;
}
function getLogCountForRoute(route) {
if (!routeCounts[route]) {
routeCounts[route] = 0;
}
return routeCounts[route]++;
}
export function log(fileName, functionName, route = '', ...messages) {
if (typeof route !== 'string') {
route = '';
}
if (route.includes('/node_modules')) {
return;
}
if (route !== currentRoute && !route.startsWith('/api')) {
currentRoute = route;
routeCounts[route] = 0;
}
const logCount = getLogCountForRoute(route);
const funcName = functionName || 'NONE';
const formattedMessages = messages
.map((msg) => {
if (typeof msg === 'object') {
return truncateMessage(safeStringify(msg, null, 2), htmlLogLimit);
}
return truncateMessage(msg, htmlLogLimit);
})
.join(' ');
const isError = messages.some((msg) => msg instanceof Error);
const logType = isSSR ? 'server' : 'client';
const color = isError ? colors.error : colors[logType];
const routeColor = route.includes(':') ? colors.filename : colors.route;
let logMessage = `[${logCount}] [R:${route || ''}] [${fileName}:${funcName}]`;
if (formattedMessages) {
logMessage += ` [${formattedMessages}]`;
}
logMessage = truncateMessage(logMessage, 200);
if (loggedMessages.has(logMessage)) {
return;
}
loggedMessages.add(logMessage);
const coloredMessage = logMessage
.replace(/(\[R:[^\]]+\])/g, routeColor('$1'))
.replace(/(\w+::[^ ]+)/g, colors.variable('$1'))
.replace(/^(\[\d+\])/g, color('$1'))
.replace(/\[([^]]+)\]$/, chalk.white('[$1]'));
console.log(color(`[${logType === 'server' ? 'S' : 'C'}] `) + coloredMessage);
}
export function compareHtml(serverHtml, clientHtml) {
const serverHtmlSnippet = truncateMessage(serverHtml, htmlLogLimit);
const clientHtmlSnippet = truncateMessage(clientHtml, htmlLogLimit);
const diffResult = diff.diffLines(serverHtmlSnippet, clientHtmlSnippet);
}
export function logDiff(oldStr, newStr) {
const diffResult = diff.diffWordsWithSpace(oldStr, newStr);
diffResult.forEach((part) => {
const color = part.added
? colors.variable
: part.removed
? colors.error
: chalk.gray;
process.stderr.write(color(part.value));
});
console.log();
}
export function resetLogs() {
routeCounts = {};
currentRoute = '';
loggedMessages.clear();
}
export default {
log,
logDiff,
resetLogs,
};
logType
is 'client'.This update will help in clearly distinguishing between client and server logs in the terminal output.
Click here to create a Pull Request with the proposed solution
Files used for this task:
Logs in terminal when running application do not differentiate vairables between client and server logs.