<commit_before>import {Console} from "console"; // eslint-disable-line node/prefer-global/console
import electron from "electron";
import fs from "fs";
import os from "os";

import {initSetUp} from "./default-util";
import {sentryInit, captureException} from "./sentry-util";

const {app} = process.type === "renderer" ? electron.remote : electron;

interface LoggerOptions {
  timestamp?: true | (() => string);
  file?: string;
  level?: boolean;
  logInDevMode?: boolean;
}

initSetUp();

let reportErrors = true;
if (process.type === "renderer") {
  // Report Errors to Sentry only if it is enabled in settings
  // Gets the value of reportErrors from config-util for renderer process
  // For main process, sentryInit() is handled in index.js
  const {ipcRenderer} = electron;
  ipcRenderer.send("error-reporting");
  ipcRenderer.on(
    "error-reporting-val",
    (_event: Event, errorReporting: boolean) => {
      reportErrors = errorReporting;
      if (reportErrors) {
        sentryInit();
      }
    },
  );
}

const logDir = `${app.getPath("userData")}/Logs`;

type Level = "log" | "debug" | "info" | "warn" | "error";
const levels: Level[] = ["log", "debug", "info", "warn", "error"];
type LogMethod = (...args: unknown[]) => void;

export default class Logger {
  log: LogMethod;
  debug: LogMethod;
  info: LogMethod;
  warn: LogMethod;
  error: LogMethod;

  nodeConsole: Console;
  timestamp?: () => string;
  level: boolean;
  logInDevMode: boolean;

  constructor(options: LoggerOptions = {}) {
    let {
      timestamp = true,
      file = "console.log",
      level = true,
      logInDevMode = false,
    } = options;

    file = `${logDir}/${file}`;
    if (timestamp === true) {
      timestamp = this.getTimestamp;
    }

    // Trim log according to type of process
    if (process.type === "renderer") {
      requestIdleCallback(async () => this.trimLog(file));
    } else {
      process.nextTick(async () => this.trimLog(file));
    }

    const fileStream = fs.createWriteStream(file, {flags: "a"});
    const nodeConsole = new Console(fileStream);

    this.nodeConsole = nodeConsole;
    this.timestamp = timestamp;
    this.level = level;
    this.logInDevMode = logInDevMode;
    this.setUpConsole();
  }

  _log(type: Level, ...args: unknown[]): void {
    const {nodeConsole, timestamp, level, logInDevMode} = this;

    switch (true) {
      case typeof timestamp === "function":
        args.unshift(timestamp() + " |\t");
      // Fall through

      case level:
        args.unshift(type.toUpperCase() + " |");
      // Fall through

      case !app.isPackaged || logInDevMode:
        nodeConsole[type](...args);
        break;

      default:
    }

    console[type](...args);
  }

  setUpConsole(): void {
    for (const type of levels) {
      this.setupConsoleMethod(type);
    }
  }

  setupConsoleMethod(type: Level): void {
    this[type] = (...args: unknown[]) => {
      this._log(type, ...args);
    };
  }

  getTimestamp(): string {
    const date = new Date();
    const timestamp =
      `${date.getMonth()}/${date.getDate()} ` +
      `${date.getMinutes()}:${date.getSeconds()}`;
    return timestamp;
  }

  reportSentry(error: unknown): void {
    if (reportErrors) {
      captureException(error);
    }
  }

  async trimLog(file: string): Promise<void> {
    const data = await fs.promises.readFile(file, "utf8");

    const MAX_LOG_FILE_LINES = 500;
    const logs = data.split(os.EOL);
    const logLength = logs.length - 1;
<commit_msg>Logger: Remove dynamic method assignment.<commit_after>import {Console} from "console"; // eslint-disable-line node/prefer-global/console
import electron from "electron";
import fs from "fs";
import os from "os";

import {initSetUp} from "./default-util";
import {sentryInit, captureException} from "./sentry-util";

const {app} = process.type === "renderer" ? electron.remote : electron;

interface LoggerOptions {
  timestamp?: true | (() => string);
  file?: string;
  level?: boolean;
  logInDevMode?: boolean;
}

initSetUp();

let reportErrors = true;
if (process.type === "renderer") {
  // Report Errors to Sentry only if it is enabled in settings
  // Gets the value of reportErrors from config-util for renderer process
  // For main process, sentryInit() is handled in index.js
  const {ipcRenderer} = electron;
  ipcRenderer.send("error-reporting");
  ipcRenderer.on(
    "error-reporting-val",
    (_event: Event, errorReporting: boolean) => {
      reportErrors = errorReporting;
      if (reportErrors) {
        sentryInit();
      }
    },
  );
}

const logDir = `${app.getPath("userData")}/Logs`;

type Level = "log" | "debug" | "info" | "warn" | "error";

export default class Logger {
  nodeConsole: Console;
  timestamp?: () => string;
  level: boolean;
  logInDevMode: boolean;

  constructor(options: LoggerOptions = {}) {
    let {
      timestamp = true,
      file = "console.log",
      level = true,
      logInDevMode = false,
    } = options;

    file = `${logDir}/${file}`;
    if (timestamp === true) {
      timestamp = this.getTimestamp;
    }

    // Trim log according to type of process
    if (process.type === "renderer") {
      requestIdleCallback(async () => this.trimLog(file));
    } else {
      process.nextTick(async () => this.trimLog(file));
    }

    const fileStream = fs.createWriteStream(file, {flags: "a"});
    const nodeConsole = new Console(fileStream);

    this.nodeConsole = nodeConsole;
    this.timestamp = timestamp;
    this.level = level;
    this.logInDevMode = logInDevMode;
  }

  _log(type: Level, ...args: unknown[]): void {
    const {nodeConsole, timestamp, level, logInDevMode} = this;

    switch (true) {
      case typeof timestamp === "function":
        args.unshift(timestamp() + " |\t");
      // Fall through

      case level:
        args.unshift(type.toUpperCase() + " |");
      // Fall through

      case !app.isPackaged || logInDevMode:
        nodeConsole[type](...args);
        break;

      default:
    }

    console[type](...args);
  }

  log(...args: unknown[]): void {
    this._log("log", ...args);
  }

  debug(...args: unknown[]): void {
    this._log("debug", ...args);
  }

  info(...args: unknown[]): void {
    this._log("info", ...args);
  }

  warn(...args: unknown[]): void {
    this._log("warn", ...args);
  }

  error(...args: unknown[]): void {
    this._log("error", ...args);
  }

  getTimestamp(): string {
    const date = new Date();
    const timestamp =
      `${date.getMonth()}/${date.getDate()} ` +
      `${date.getMinutes()}:${date.getSeconds()}`;
    return timestamp;
  }

  reportSentry(error: unknown): void {
    if (reportErrors) {
      captureException(error);
    }
  }

  async trimLog(file: string): Promise<void> {
    const data = await fs.promises.readFile(file, "utf8");

    const MAX_LOG_FILE_LINES = 500;
    const logs = data.split(os.EOL);
    const logLength = logs.length - 1;

{'fraction_non_alphanumeric': 0.15923199076079111, 'fraction_numerical': 0.0014436263894904, 'mean_word_length': 2.9520821448944665, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1041804', 'n_tokens_mistral': 2209, 'n_tokens_neox': 2176, 'n_words': 545}