// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
/**
* Formatter for the Output channel.
*/
import * as vscode from "vscode";
import { ILogger, LogLevel, LogHelper, getLoggingDirectory } from "./LogHelper";
import * as fs from "fs";
import * as path from "path";
import { getFormattedDatetimeString } from "../../common/utils";
const channels: { [channelName: string]: OutputChannelLogger } = {};
export class OutputChannelLogger implements ILogger {
public static MAIN_CHANNEL_NAME: string = "React Native";
private readonly channelLogFilePath: string | undefined;
private channelLogFileStream: fs.WriteStream;
private outputChannel: vscode.OutputChannel;
private logTimestamps: boolean;
private static forbiddenFileNameSymbols: RegExp = /\W/gi;
public static disposeChannel(channelName: string): void {
if (channels[channelName]) {
channels[channelName].getOutputChannel().dispose();
if (channels[channelName].channelLogFileStream) {
channels[channelName].channelLogFileStream.end();
}
delete channels[channelName];
}
}
public static getMainChannel(): OutputChannelLogger {
return this.getChannel(this.MAIN_CHANNEL_NAME, true);
}
public static getChannel(
channelName: string,
lazy?: boolean,
preserveFocus?: boolean,
logTimestamps?: boolean,
): OutputChannelLogger {
if (!channels[channelName]) {
channels[channelName] = new OutputChannelLogger(
channelName,
lazy,
preserveFocus,
logTimestamps,
);
}
return channels[channelName];
}
constructor(
public readonly channelName: string,
lazy: boolean = false,
private preserveFocus: boolean = false,
logTimestamps: boolean = false,
) {
this.logTimestamps = logTimestamps;
const channelLogFolder = getLoggingDirectory();
if (channelLogFolder) {
const filename = channelName.replace(OutputChannelLogger.forbiddenFileNameSymbols, "");
this.channelLogFilePath = path.join(channelLogFolder, `${filename}.txt`);
this.channelLogFileStream = fs.createWriteStream(this.channelLogFilePath);
this.channelLogFileStream.on("error", err => {
this.error(
`Error writing to log file at path: ${
this.channelLogFilePath
}. Error: ${err.toString()}\n`,
);
});
}
if (!lazy) {
this.channel = vscode.window.createOutputChannel(this.channelName);
this.channel.show(this.preserveFocus);
}
}
public log(message: string, level: LogLevel): void {
if (LogHelper.LOG_LEVEL === LogLevel.None) {
return;
}
if (level >= LogHelper.LOG_LEVEL) {
message = OutputChannelLogger.getFormattedMessage(
message,
LogLevel[level],
this.logTimestamps,
);
this.channel.appendLine(message);
if (this.channelLogFileStream) {
this.channelLogFileStream.write(message);
}
}
}
public logWithCustomTag(tag: string, message: string, level: LogLevel): void {
if (LogHelper.LOG_LEVEL === LogLevel.None) {
return;
}
if (level === LogLevel.Custom) {
message = OutputChannelLogger.getFormattedMessage(message, tag, this.logTimestamps);
this.channel.appendLine(message);
if (this.channelLogFileStream) {
this.channelLogFileStream.write(message);
}
}
}
public info(message: string): void {
this.log(message, LogLevel.Info);
}
public warning(message: string | Error): void {
this.log(message.toString(), LogLevel.Warning);
}
public error(errorMessage: string, error?: Error, logStack: boolean = true): void {
const message = OutputChannelLogger.getFormattedMessage(
errorMessage,
LogLevel[LogLevel.Error],
this.logTimestamps,
);
this.channel.appendLine(message);
if (this.channelLogFileStream) {
this.channelLogFileStream.write(message);
}
// Print the error stack if necessary
if (logStack && error && (<Error>error).stack) {
this.channel.appendLine(`Stack: ${(<Error>error).stack}`);
if (this.channelLogFileStream) {
this.channelLogFileStream.write(`Stack: ${(<Error>error).stack}`);
}
}
}
public debug(message: string): void {
this.log(message, LogLevel.Debug);
}
public logStream(data: Buffer | string): void {
this.channel.append(data.toString());
if (this.channelLogFileStream) {
this.channelLogFileStream.write(data);
}
}
public setFocusOnLogChannel(): void {
this.channel.show(false);
}
protected static getFormattedMessage(
message: string,
tag: string,
prependTimestamp: boolean = false,
): string {
let formattedMessage = `[${tag}] ${message}\n`;
if (prependTimestamp) {
formattedMessage = `[${getFormattedDatetimeString(new Date())}] ${formattedMessage}`;
}
return formattedMessage;
}
public getOutputChannel(): vscode.OutputChannel {
return this.channel;
}
public clear(): void {
this.channel.clear();
}
private get channel(): vscode.OutputChannel {
if (this.outputChannel) {
return this.outputChannel;
} else {
this.outputChannel = vscode.window.createOutputChannel(this.channelName);
this.outputChannel.show(this.preserveFocus);
return this.outputChannel;
}
}
private set channel(channel: vscode.OutputChannel) {
this.outputChannel = channel;
}
}microsoft/vscode-react-native
Publicmirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable
src/extension/log/OutputChannelLogger.ts
191lines · modepreview