@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
187 lines • 7.46 kB
JavaScript
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogLevelCliContribution = void 0;
const tslib_1 = require("tslib");
const inversify_1 = require("inversify");
const logger_1 = require("../common/logger");
const fs = require("fs-extra");
const watcher_1 = require("@parcel/watcher");
const event_1 = require("../common/event");
const path = require("path");
/**
* Parses command line switches related to log levels, then watches the log
* levels file (if specified) for changes. This is the source of truth for
* what the log level per logger should be.
*/
let LogLevelCliContribution = class LogLevelCliContribution {
constructor() {
this._logLevels = {};
/**
* Log level to use for loggers not specified in `logLevels`.
*/
this._defaultLogLevel = logger_1.LogLevel.INFO;
this.logConfigChangedEvent = new event_1.Emitter();
}
get defaultLogLevel() {
return this._defaultLogLevel;
}
get logLevels() {
return this._logLevels;
}
get logFile() {
return this._logFile;
}
configure(conf) {
conf.option('log-level', {
description: 'Sets the default log level',
choices: Array.from(logger_1.LogLevel.strings.values()),
nargs: 1,
});
conf.option('log-config', {
description: 'Path to the JSON file specifying the configuration of various loggers',
type: 'string',
nargs: 1,
});
conf.option('log-file', {
description: 'Path to the log file',
type: 'string',
nargs: 1
});
}
async setArguments(args) {
if (args['log-level'] !== undefined && args['log-config'] !== undefined) {
throw new Error('--log-level and --log-config are mutually exclusive.');
}
if (args['log-level'] !== undefined) {
this._defaultLogLevel = this.readLogLevelString(args['log-level'], 'Unknown log level passed to --log-level');
}
if (args['log-config'] !== undefined) {
let filename = args['log-config'];
try {
filename = path.resolve(filename);
await this.slurpLogConfigFile(filename);
await this.watchLogConfigFile(filename);
}
catch (e) {
console.error(`Error reading log config file ${filename}: ${e}`);
}
}
if (args['log-file'] !== undefined) {
let filename = args['log-file'];
try {
filename = path.resolve(filename);
try {
const stat = await fs.stat(filename);
if (stat && stat.isFile()) {
// Rename the previous log file to avoid overwriting it
const oldFilename = `${filename}.${stat.ctime.toISOString().replace(/:/g, '-')}.old`;
await fs.rename(filename, oldFilename);
}
}
catch {
// File does not exist, just continue to create it
}
await fs.writeFile(filename, '');
this._logFile = filename;
}
catch (e) {
console.error(`Error creating log file ${filename}: ${e}`);
}
}
// some initial loggers have already been constructed. Fire the event to notify them.
if (args['log-level'] || args['log-config'] || args['log-file']) {
this.logConfigChangedEvent.fire();
}
}
async watchLogConfigFile(filename) {
const dir = path.dirname(filename);
await (0, watcher_1.subscribe)(dir, async (err, events) => {
if (err) {
console.log(`Error during log file watching ${filename}: ${err}`);
return;
}
try {
for (const event of events) {
if (event.path === filename) {
switch (event.type) {
case 'create':
case 'update':
await this.slurpLogConfigFile(filename);
this.logConfigChangedEvent.fire(undefined);
break;
}
}
}
}
catch (e) {
console.error(`Error reading log config file ${filename}: ${e}`);
}
});
}
async slurpLogConfigFile(filename) {
try {
const content = await fs.readFile(filename, 'utf-8');
const data = JSON.parse(content);
let newDefaultLogLevel = logger_1.LogLevel.INFO;
if ('defaultLevel' in data) {
newDefaultLogLevel = this.readLogLevelString(data['defaultLevel'], `Unknown default log level in ${filename}`);
}
const newLogLevels = {};
if ('levels' in data) {
const loggers = data['levels'];
for (const logger of Object.keys(loggers)) {
const levelStr = loggers[logger];
newLogLevels[logger] = this.readLogLevelString(levelStr, `Unknown log level for logger ${logger} in ${filename}`);
}
}
this._defaultLogLevel = newDefaultLogLevel;
this._logLevels = newLogLevels;
console.log(`Successfully read new log config from ${filename}.`);
}
catch (e) {
throw new Error(`Error reading log config file ${filename}: ${e.message}`);
}
}
get onLogConfigChanged() {
return this.logConfigChangedEvent.event;
}
logLevelFor(loggerName) {
const level = this._logLevels[loggerName];
if (level !== undefined) {
return level;
}
else {
return this.defaultLogLevel;
}
}
/**
* Converts the string to a `LogLevel`. Throws an error if invalid.
*/
readLogLevelString(levelStr, errMessagePrefix) {
const level = logger_1.LogLevel.fromString(levelStr);
if (level === undefined) {
throw new Error(`${errMessagePrefix}: "${levelStr}".`);
}
return level;
}
};
exports.LogLevelCliContribution = LogLevelCliContribution;
exports.LogLevelCliContribution = LogLevelCliContribution = tslib_1.__decorate([
(0, inversify_1.injectable)()
], LogLevelCliContribution);
//# sourceMappingURL=logger-cli-contribution.js.map
;