/* * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import chalk from 'chalk'; import {stringify} from 'flatted'; import {isNodeEnvironment, isProductiveNodeEnvironment} from './common'; import {Transport} from './Transport'; /** * Logger with colors, loglevel and transport * * Log level can be defined by setting the environment variable STAPPS_LOG_LEVEL to a valid log level. Log levels are * set in a binary way. For example STAPPS_LOG_LEVEL=12 does result in logs only for `Logger.warn` and `Logger.error`. * * Log levels in that order are: * ``` * INFO: 1 * LOG: 2 * WARN: 4 * ERROR: 8 * OK: 16 * ``` */ export class Logger { /** * Log levels */ private static logLevels = [ 'INFO', 'LOG', 'WARN', 'ERROR', 'OK', ]; /** * Transport for errors */ private static transport?: Transport; /** * Check if intended log level is allowed in environment log level * * @param logLevel */ private static checkLogLevel(logLevel: string): boolean { const logLevelNumber = Math.pow(2, Logger.logLevels.indexOf(logLevel) + 1) - 1; /* tslint:disable-next-line:no-bitwise */ return !!(Logger.getLogLevel() & logLevelNumber); } /** * Return log level from environment */ private static getLogLevel(): number { if (isNodeEnvironment() && typeof process.env.STAPPS_LOG_LEVEL !== 'undefined') { // Node.js environment exists return parseInt(process.env.STAPPS_LOG_LEVEL, 10); } else if (typeof window !== 'undefined' && typeof (window as any).STAPPS_LOG_LEVEL === 'number') { // browser environment exists return (window as any).STAPPS_LOG_LEVEL; } // Log everything return 31; } /** * Log an error * * @param args Arguments to log */ public static async error(...args: any[]): Promise { if (!Logger.checkLogLevel('ERROR')) { return; } /* tslint:disable-next-line:no-console */ console.error(chalk.bold.red(`[ERROR] ${Logger.stringifyArguments(...args)}`)); if (isProductiveNodeEnvironment()) { if (typeof Logger.transport !== 'undefined') { return Logger.transport.send('Error', Logger.stringifyArguments(...args)); } else if (!process.env.ALLOW_NO_TRANSPORT) { throw new Error(`Error couldn't be transported! Please set a transport or set ALLOW_NO_TRANSPORT='true'.`); } } } /** * Log an information * * @param args Arguments to log */ public static info(...args: any[]): void { if (!Logger.checkLogLevel('INFO')) { return; } /* tslint:disable-next-line:no-console */ console.info(chalk.cyan(`[INFO] ${Logger.stringifyArguments(args)}`)); } /** * Check if the logger is initialized correctly */ public static initialized(): void { if (isProductiveNodeEnvironment() && typeof Logger.transport === 'undefined') { if (!process.env.ALLOW_NO_TRANSPORT) { throw new Error(`Productive environment doesn't set a transport for error notifications.`); } else { /* tslint:disable-next-line:no-console */ console.warn(chalk.yellow(`Productive environment doesn't set a transport for error notifications.`)); } } } /** * Log something * * @param args Arguments to log */ public static log(...args: any[]): void { if (!this.checkLogLevel('LOG')) { return; } /* tslint:disable-next-line:no-console */ console.log(chalk.white(`[LOG] ${Logger.stringifyArguments(args)}`)); } /** * Log something successful * * @param args Arguments to log */ public static ok(...args: any[]): void { if (!this.checkLogLevel('OK')) { return; } /* tslint:disable-next-line:no-console */ console.log(chalk.bold.green(`[OK] ${Logger.stringifyArguments(args)}`)); } /** * Set a transport * * @param transport Transport to set */ public static setTransport(transport?: Transport) { Logger.transport = transport; } /** * Stringify a list of arguments * * @param args Arguments to stringify */ public static stringifyArguments(...args: any[]): string { const result: string[] = []; args.forEach((argument) => { const type = typeof argument; if (['string', 'number'].indexOf(type) !== -1) { result.push(argument); } else if (argument instanceof Error) { result.push(argument.message); if (typeof argument.stack !== 'undefined') { result.push(argument.stack); } } else { result.push(stringify(argument, null, 2)); } }); return result.join(', '); } /** * Log a warning * * @param args Arguments to log */ public static warn(...args: any[]): void { if (!this.checkLogLevel('WARN')) { return; } /* tslint:disable-next-line:no-console */ console.warn(chalk.yellow(`[WARN] ${Logger.stringifyArguments(args)}`)); } }