LoveofSportsLLC / NFL

NFL + AI
https://loveoffootball.io/
MIT License
0 stars 0 forks source link

Updating Logs to Differentiate Between Client and Server. #54

Open zepor opened 1 month ago

zepor commented 1 month ago

Logs in terminal when running application do not differentiate vairables between client and server logs.

codeautopilot[bot] commented 1 month ago

Potential solution

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.

How to implement

Backend Changes

File: 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

File: 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 Changes

File: 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,
};

Summary of Changes

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:

Changes on backend-container/src/utils/logandcatchexceptions.py To address the issue of differentiating between client and server logs, we need to update the decorator function in this file to include a 'server' identifier in the log messages. This will help in clearly identifying the source of the logs. Here's the updated code for `backend-container/src/utils/logandcatchexceptions.py`: ```python 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 ``` ### Explanation: 1. **Import Statements**: No changes needed here. 2. **Decorator Function**: - **`func_wrapper`**: This is the wrapper function that will catch exceptions. - **Logging**: The log message has been updated to include `[SERVER]` as a prefix to clearly indicate that this log is coming from the server side. This change ensures that any log messages generated by this decorator will be easily identifiable as server logs.
Changes on backend-container/src/utils/log.py To address the issue of differentiating between client and server logs, we need to update the logging configuration in the `backend-container/src/utils/log.py` file to include a 'server' identifier in the log messages. This can be achieved by modifying the log format to include a custom identifier. Here's a step-by-step plan to update the logging configuration: 1. **Modify the Log Format**: Update the log format to include a 'server' identifier. 2. **Apply the New Format**: Ensure that both the file handler and console handler use the updated format. ### Updated Code ```python 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) # Updated log format to include 'server' identifier file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - [SERVER] - %(message)s') file_handler.setFormatter(file_formatter) be_logger.addHandler(file_handler) # Set up logging to console console_handler = logging.StreamHandler(sys.stdout) # Updated log format to include 'server' identifier console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - [SERVER] - %(message)s') console_handler.setFormatter(console_formatter) be_logger.addHandler(console_handler) # Example of logging environment variables or other info be_logger.info('FLASK_APP: %s', os.getenv('FLASK_APP')) be_logger.info('FLASK_DEBUG: %s', os.getenv('FLASK_DEBUG')) ``` ### Explanation 1. **Log Format Update**: The log format strings for both `file_formatter` and `console_formatter` have been updated to include `[SERVER]`. This will prepend the 'server' identifier to every log message. - Old format: `'%(asctime)s - %(levelname)s - %(message)s'` - New format: `'%(asctime)s - %(levelname)s - [SERVER] - %(message)s'` 2. **Handlers**: Both the file handler and console handler are updated to use the new format. By following these steps, the log messages will now include a 'server' identifier, making it easier to differentiate between client and server logs.
Changes on frontend-container/src/utils/logs.js To address the issue of differentiating between client and server logs in the `frontend-container/src/utils/logs.js` file, we need to ensure that each log message includes a 'client' identifier. This will help in distinguishing the logs generated by the client-side code from those generated by the server-side code. Here's a concrete plan to update the logging functions: 1. **Identify the Log Type**: Determine whether the log is from the client or the server. 2. **Include the Identifier**: Modify the log message to include a 'client' identifier. The current implementation already includes a `logType` variable that distinguishes between 'server' and 'client' based on the `isSSR` flag. We will leverage this to prepend a 'C' (for client) to the log messages. ### Updated Code Here's the updated code with the necessary changes: ```javascript import * as diff from 'diff'; import chalk from 'chalk'; let routeCounts = {}; let currentRoute = ''; const loggedMessages = new Set(); const htmlLogLimit = 2000; // Limit the length of HTML logs const colors = { server: chalk.green, client: chalk.blue, error: chalk.red, variable: chalk.cyan, route: chalk.hex('#FFA500'), // Orange color filename: chalk.hex('#FFD700'), // Light orange color }; 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 = ''; } // Skip logging for routes from /node_modules 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); // Uncomment and adjust the following code as needed for your application // diffResult.forEach((part) => { // if (part.added) { // log('HTML Diff', 'added', '', part.value); // } else if (part.removed) { // log('HTML Diff', 'removed', '', part.value); // } // }); } 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, }; ``` ### Summary of Changes - Added a 'C' (for client) identifier to the log messages when `logType` is 'client'. - Ensured that the log messages are appropriately colored and formatted to include the client identifier. This update will help in clearly distinguishing between client and server logs in the terminal output.