UNPKG

@cocalc/backend

Version:

CoCalc backend functionality: functionality used by either the hub, the next.js server or the project.

202 lines (200 loc) 6.75 kB
"use strict"; /* Debug logger for any node.js server. There is used both by the hub(s) and project(s). This is an implementation of basically how winston works for us, but using the vastly simpler super-popular debug module. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.setCounter = exports.getLogger = exports.trimLogFileSize = void 0; // setting env var must come *BEFORE* debug is loaded the first time process.env.DEBUG_HIDE_DATE = "yes"; // since we supply it ourselves // otherwise, maybe stuff like this works: (debug as any).inspectOpts["hideDate"] = true; const debug_1 = __importDefault(require("debug")); const fs_1 = require("fs"); const util_1 = require("util"); const path_1 = require("path"); const data_1 = require("./data"); const MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024; // 20MB const COCALC = (0, debug_1.default)("cocalc"); let _trimLogFileSizePath = ""; function trimLogFileSize() { // THIS JUST DOESN'T REALLY WORK! return; if (!_trimLogFileSizePath) return; let stats; try { stats = (0, fs_1.statSync)(_trimLogFileSizePath); } catch (_) { // this happens if the file doesn't exist, which is fine since "trimming" it would be a no-op return; } if (stats.size > MAX_FILE_SIZE_BYTES) { const fileStream = (0, fs_1.createWriteStream)(_trimLogFileSizePath, { flags: "r+" }); fileStream.on("open", (fd) => { (0, fs_1.ftruncate)(fd, MAX_FILE_SIZE_BYTES, (truncateErr) => { if (truncateErr) { console.error(truncateErr); return; } fileStream.close(); }); }); } } exports.trimLogFileSize = trimLogFileSize; function myFormat(...args) { if (args.length > 1 && typeof args[0] == "string" && !args[0].includes("%")) { // This is something where we didn't use printf formatting. const v = []; for (const x of args) { try { v.push(typeof x == "object" ? JSON.stringify(x) : `${x}`); } catch (_) { // better to not crash everything just for logging v.push(`${x}`); } } return v.join(" "); } // use printf formatting. return (0, util_1.format)(...args); } function defaultTransports() { if (process.env.SMC_TEST) { return {}; } else if (process.env.COCALC_DOCKER) { return { file: "/var/log/hub/log" }; } else if (process.env.NODE_ENV == "production") { return { console: true }; } else { return { file: (0, path_1.join)(data_1.logs, "log") }; } } function initTransports() { if (!process.env.DEBUG) { // console.log("DEBUG is not set, so not setting up debug logging transport"); return; } const transports = defaultTransports(); if (process.env.DEBUG_CONSOLE) { transports.console = process.env.DEBUG_CONSOLE != "no" && process.env.DEBUG_CONSOLE != "false"; } if (process.env.DEBUG_FILE != null) { transports.file = process.env.DEBUG_FILE; } let fileStream; if (transports.file) { const { file } = transports; // ensure directory exists (0, fs_1.mkdirSync)((0, path_1.dirname)(file), { recursive: true }); // create the file stream; using a stream ensures // that everything is written in the right order with // no corruption/collision between different logging. // We use append mode because we mainly watch the file log // when doing dev, and nextjs constantly restarts the process. fileStream = (0, fs_1.createWriteStream)(file, { flags: "a", }); _trimLogFileSizePath = file; trimLogFileSize(); } let firstLog = true; COCALC.log = (...args) => { if (!transports.file && !transports.console) return; if (firstLog && transports.file) { const announce = `***\n\nLogging to "${transports.file}"${transports.console ? " and console.log" : ""} via the debug module\nwith DEBUG='${process.env.DEBUG}'.\nUse DEBUG_FILE='path' and DEBUG_CONSOLE=[yes|no] to override.\nUsing DEBUG='cocalc:*,-cocalc:silly:*' to control log levels.\n\n***`; console.log(announce); if (transports.file) { // the file transport fileStream.write(announce); } firstLog = false; } // Similar as in debug source code, except I stuck a timestamp // at the beginning, which I like... except also aware of // non-printf formatting. const line = `${new Date().toISOString()}: ${myFormat(...args)}\n`; if (transports.console) { // the console transport: console.log(line); } if (transports.file) { // the file transport fileStream.write(line); } }; } initTransports(); const DEBUGGERS = { error: COCALC.extend("error"), warn: COCALC.extend("warn"), info: COCALC.extend("info"), http: COCALC.extend("http"), verbose: COCALC.extend("verbose"), debug: COCALC.extend("debug"), silly: COCALC.extend("silly"), }; const LEVELS = [ "error", "warn", "info", "http", "verbose", "debug", "silly", ]; class Logger { constructor(name) { this.debuggers = {}; this.name = name; for (const level of LEVELS) { this.debuggers[level] = DEBUGGERS[level].extend(name); this[level] = (...args) => { this.counter(level); // @ts-ignore this.debuggers[level](...args); }; } } isEnabled(level) { return this.debuggers[level].enabled; } extend(name) { return new Logger(`${this.name}:${name}`); } counter(level) { if (counter == null) return; counter.labels(this.name, level).inc(1); } } const cache = {}; function getLogger(name) { if (cache[name] != null) { return cache[name]; } // smash it over since we build Logger pretty generically so typescript // doesn't get it. But we care that all *client* code uses the WinstonLogger // interface. return (cache[name] = new Logger(name)); } exports.default = getLogger; exports.getLogger = getLogger; let counter = undefined; function setCounter(f) { counter = f; } exports.setCounter = setCounter; //# sourceMappingURL=logger.js.map