diff --git a/extensions/git/package.json b/extensions/git/package.json index 0c7829b795d..3517a2803d9 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2287,6 +2287,30 @@ "default": [], "scope": "resource", "markdownDescription": "%config.commandsToLog%" + }, + "git.logLevel": { + "type": "string", + "default": "Info", + "enum": [ + "Trace", + "Debug", + "Info", + "Warning", + "Error", + "Critical", + "Off" + ], + "enumDescriptions": [ + "%config.logLevel.trace%", + "%config.logLevel.debug%", + "%config.logLevel.info%", + "%config.logLevel.warn%", + "%config.logLevel.error%", + "%config.logLevel.critical%", + "%config.logLevel.off%" + ], + "markdownDescription": "%config.logLevel%", + "scope": "window" } } }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 7988fd9dd9e..523b9945928 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -209,6 +209,21 @@ "config.repositoryScanIgnoredFolders": "List of folders that are ignored while scanning for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`.", "config.repositoryScanMaxDepth": "Controls the depth used when scanning workspace folders for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`. Can be set to `-1` for no limit.", "config.useIntegratedAskPass": "Controls whether GIT_ASKPASS should be overwritten to use the integrated version.", + "config.logLevel": { + "message": "Specifies how much information (if any) to log to the [git output](command:git.showOutput).", + "comment": [ + "{Locked='](command:git.showOutput'}", + "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", + "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" + ] + }, + "config.logLevel.trace": "Log all information", + "config.logLevel.debug": "Log only debug, information, warning, error, and critical information", + "config.logLevel.info": "Log only information, warning, error, and critical information", + "config.logLevel.warn": "Log only warning, error, and critical information", + "config.logLevel.error": "Log only error, and critical information", + "config.logLevel.critical": "Log only critical information", + "config.logLevel.off": "Log nothing", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index 5409f512963..81895a0e0d6 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, InputBoxOptions, Uri, OutputChannel, Disposable, workspace } from 'vscode'; -import { IDisposable, EmptyDisposable, toDisposable, logTimestamp } from './util'; +import { window, InputBoxOptions, Uri, Disposable, workspace } from 'vscode'; +import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; +import { OutputChannelLogger } from './log'; export class Askpass implements IIPCHandler { @@ -15,11 +16,11 @@ export class Askpass implements IIPCHandler { private cache = new Map(); private credentialsProviders = new Set(); - static async create(outputChannel: OutputChannel, context?: string): Promise { + static async create(outputChannelLogger: OutputChannelLogger, context?: string): Promise { try { return new Askpass(await createIPCServer(context)); } catch (err) { - outputChannel.appendLine(`${logTimestamp()} [error] Failed to create git askpass IPC: ${err}`); + outputChannelLogger.logError(`Failed to create git askpass IPC: ${err}`); return new Askpass(); } } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 03910fc8217..a3479a0b3b5 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,7 +5,7 @@ import * as os from 'os'; import * as path from 'path'; -import { Command, commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; +import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git'; @@ -14,8 +14,8 @@ import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; import { fromGitUri, toGitUri, isGitUri } from './uri'; -import { grep, isDescendant, logTimestamp, pathEquals, relativePath } from './util'; -import { Log, LogLevel } from './log'; +import { grep, isDescendant, pathEquals, relativePath } from './util'; +import { LogLevel, OutputChannelLogger } from './log'; import { GitTimelineItem } from './timelineProvider'; import { ApiRepository } from './api/api1'; import { pickRemoteSource } from './remoteSource'; @@ -310,7 +310,7 @@ export class CommandCenter { constructor( private git: Git, private model: Model, - private outputChannel: OutputChannel, + private outputChannelLogger: OutputChannelLogger, private telemetryReporter: TelemetryReporter ) { this.disposables = Commands.map(({ commandId, key, method, options }) => { @@ -328,11 +328,25 @@ export class CommandCenter { @command('git.setLogLevel') async setLogLevel(): Promise { - const createItem = (logLevel: LogLevel) => ({ - label: LogLevel[logLevel], - logLevel, - description: Log.logLevel === logLevel ? localize('current', "Current") : undefined - }); + const createItem = (logLevel: LogLevel) => { + let description: string | undefined; + const defaultDescription = localize('default', "Default"); + const currentDescription = localize('current', "Current"); + + if (logLevel === this.outputChannelLogger.defaultLogLevel && logLevel === this.outputChannelLogger.currentLogLevel) { + description = `${defaultDescription} & ${currentDescription} `; + } else if (logLevel === this.outputChannelLogger.defaultLogLevel) { + description = defaultDescription; + } else if (logLevel === this.outputChannelLogger.currentLogLevel) { + description = currentDescription; + } + + return { + label: LogLevel[logLevel], + logLevel, + description + }; + }; const items = [ createItem(LogLevel.Trace), @@ -352,8 +366,7 @@ export class CommandCenter { return; } - Log.logLevel = choice.logLevel; - this.outputChannel.appendLine(localize('changed', "{0} Log level changed to: {1}", logTimestamp(), LogLevel[Log.logLevel])); + this.outputChannelLogger.setLogLevel(choice.logLevel); } @command('git.refresh', { repository: true }) @@ -820,14 +833,14 @@ export class CommandCenter { @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { - this.outputChannel.appendLine(`${logTimestamp()} git.stage ${resourceStates.length}`); + this.outputChannelLogger.logDebug(`git.stage ${resourceStates.length} `); resourceStates = resourceStates.filter(s => !!s); if (resourceStates.length === 0 || (resourceStates[0] && !(resourceStates[0].resourceUri instanceof Uri))) { const resource = this.getSCMResource(); - this.outputChannel.appendLine(`${logTimestamp()} git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null}`); + this.outputChannelLogger.logDebug(`git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null} `); if (!resource) { return; @@ -870,7 +883,7 @@ export class CommandCenter { const untracked = selection.filter(s => s.resourceGroupType === ResourceGroupType.Untracked); const scmResources = [...workingTree, ...untracked, ...resolved, ...unresolved]; - this.outputChannel.appendLine(`${logTimestamp()} git.stage.scmResources ${scmResources.length}`); + this.outputChannelLogger.logDebug(`git.stage.scmResources ${scmResources.length} `); if (!scmResources.length) { return; } @@ -2809,8 +2822,8 @@ export class CommandCenter { const choices = new Map void>(); const openOutputChannelChoice = localize('open git log', "Open Git Log"); - const outputChannel = this.outputChannel as OutputChannel; - choices.set(openOutputChannelChoice, () => outputChannel.show()); + const outputChannelLogger = this.outputChannelLogger; + choices.set(openOutputChannelChoice, () => outputChannelLogger.showOutputChannel()); const showCommandOutputChoice = localize('show command output', "Show Command Output"); if (err.stderr) { @@ -2913,10 +2926,10 @@ export class CommandCenter { private getSCMResource(uri?: Uri): Resource | undefined { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); - this.outputChannel.appendLine(`${logTimestamp()} git.getSCMResource.uri ${uri && uri.toString()}`); + this.outputChannelLogger.logDebug(`git.getSCMResource.uri ${uri && uri.toString()}`); for (const r of this.model.repositories.map(r => r.root)) { - this.outputChannel.appendLine(`${logTimestamp()} repo root ${r}`); + this.outputChannelLogger.logDebug(`repo root ${r}`); } if (!uri) { diff --git a/extensions/git/src/log.ts b/extensions/git/src/log.ts index 487228e57a7..570e90c5d85 100644 --- a/extensions/git/src/log.ts +++ b/extensions/git/src/log.ts @@ -3,7 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, EventEmitter } from 'vscode'; +import * as nls from 'vscode-nls'; +const localize = nls.loadMessageBundle(); + +import { commands, Disposable, Event, EventEmitter, OutputChannel, window, workspace } from 'vscode'; +import { dispose } from './util'; /** * The severity level of a log message @@ -18,33 +22,93 @@ export enum LogLevel { Off = 7 } -let _logLevel: LogLevel = LogLevel.Info; -const _onDidChangeLogLevel = new EventEmitter(); +/** + * Output channel logger + */ +export class OutputChannelLogger { -export const Log = { - /** - * Current logging level. - */ - get logLevel(): LogLevel { - return _logLevel; - }, + private _onDidChangeLogLevel = new EventEmitter(); + readonly onDidChangeLogLevel: Event = this._onDidChangeLogLevel.event; - /** - * Current logging level. - */ - set logLevel(logLevel: LogLevel) { - if (_logLevel === logLevel) { + private _currentLogLevel: LogLevel; + get currentLogLevel(): LogLevel { + return this._currentLogLevel; + } + + private _defaultLogLevel: LogLevel; + get defaultLogLevel(): LogLevel { + return this._defaultLogLevel; + } + + private _outputChannel: OutputChannel; + private _disposables: Disposable[] = []; + + constructor() { + // Output channel + this._outputChannel = window.createOutputChannel('Git'); + commands.registerCommand('git.showOutput', () => this.showOutputChannel()); + this._disposables.push(this._outputChannel); + + // Initialize log level + const config = workspace.getConfiguration('git'); + const logLevel: keyof typeof LogLevel = config.get('logLevel', 'Info'); + this._currentLogLevel = this._defaultLogLevel = LogLevel[logLevel] ?? LogLevel.Info; + + this.logInfo(localize('gitLogLevel', "Log level: {0}", LogLevel[this._currentLogLevel])); + } + + private log(message: string, logLevel: LogLevel): void { + if (logLevel < this._currentLogLevel) { return; } - _logLevel = logLevel; - _onDidChangeLogLevel.fire(logLevel); - }, - - /** - * An [event](#Event) that fires when the log level has changed. - */ - get onDidChangeLogLevel(): Event { - return _onDidChangeLogLevel.event; + this._outputChannel.appendLine(`[${new Date().toISOString()}] [${LogLevel[logLevel].toLowerCase()}] ${message}`); } -}; + + logCritical(message: string): void { + this.log(message, LogLevel.Critical); + } + + logDebug(message: string): void { + this.log(message, LogLevel.Debug); + } + + logError(message: string): void { + this.log(message, LogLevel.Error); + } + + logInfo(message: string): void { + this.log(message, LogLevel.Info); + } + + logTrace(message: string): void { + this.log(message, LogLevel.Trace); + } + + logWarning(message: string): void { + this.log(message, LogLevel.Warning); + } + + logGitCommand(command: string): void { + this._outputChannel.appendLine(`[${new Date().toISOString()}] ${command}`); + } + + setLogLevel(logLevel: LogLevel): void { + if (this._currentLogLevel === logLevel) { + return; + } + + this._currentLogLevel = logLevel; + this._onDidChangeLogLevel.fire(logLevel); + + this.logInfo(localize('changed', "Log level changed to: {0}", LogLevel[logLevel])); + } + + showOutputChannel(): void { + this._outputChannel.show(); + } + + dispose(): void { + this._disposables = dispose(this._disposables); + } +} diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 97ac84b27cb..24768c1dba4 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -6,14 +6,14 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { env, ExtensionContext, workspace, window, Disposable, commands, Uri, OutputChannel, version as vscodeVersion, WorkspaceFolder } from 'vscode'; +import { env, ExtensionContext, workspace, window, Disposable, commands, Uri, version as vscodeVersion, WorkspaceFolder } from 'vscode'; import { findGit, Git, IGit } from './git'; import { Model } from './model'; import { CommandCenter } from './commands'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; -import { toDisposable, filterEvent, eventToPromise, logTimestamp } from './util'; +import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from '@vscode/extension-telemetry'; import { GitExtension } from './api/git'; import { GitProtocolHandler } from './protocolHandler'; @@ -24,6 +24,7 @@ import * as os from 'os'; import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; +import { OutputChannelLogger } from './log'; const deactivateTasks: { (): Promise }[] = []; @@ -33,7 +34,7 @@ export async function deactivate(): Promise { } } -async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { +async function createModel(context: ExtensionContext, outputChannelLogger: OutputChannelLogger, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise { const pathValue = workspace.getConfiguration('git').get('path'); let pathHints = Array.isArray(pathValue) ? pathValue : pathValue ? [pathValue] : []; @@ -46,7 +47,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann } const info = await findGit(pathHints, gitPath => { - outputChannel.appendLine(localize('validating', "{0} Validating found git in: {1}", logTimestamp(), gitPath)); + outputChannelLogger.logInfo(localize('validating', "Validating found git in: {0}", gitPath)); if (excludes.length === 0) { return true; } @@ -54,12 +55,12 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann const normalized = path.normalize(gitPath).replace(/[\r\n]+$/, ''); const skip = excludes.some(e => normalized.startsWith(e)); if (skip) { - outputChannel.appendLine(localize('skipped', "{0} Skipped found git in: {1}", logTimestamp(), gitPath)); + outputChannelLogger.logInfo(localize('skipped', "Skipped found git in: {0}", gitPath)); } return !skip; }); - const askpass = await Askpass.create(outputChannel, context.storagePath); + const askpass = await Askpass.create(outputChannelLogger, context.storagePath); disposables.push(askpass); const environment = askpass.getEnv(); @@ -73,7 +74,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann version: info.version, env: environment, }); - const model = new Model(git, askpass, context.globalState, outputChannel, telemetryReporter); + const model = new Model(git, askpass, context.globalState, outputChannelLogger, telemetryReporter); disposables.push(model); const onRepository = () => commands.executeCommand('setContext', 'gitOpenRepositoryCount', `${model.repositories.length}`); @@ -81,7 +82,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann model.onDidCloseRepository(onRepository, null, disposables); onRepository(); - outputChannel.appendLine(localize('using git', "{0} Using git {1} from {2}", logTimestamp(), info.version, info.path)); + outputChannelLogger.logInfo(localize('using git', "Using git {0} from {1}", info.version, info.path)); const onOutput = (str: string) => { const lines = str.split(/\r?\n/mg); @@ -90,12 +91,12 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann lines.pop(); } - outputChannel.appendLine(`${logTimestamp()} ${lines.join('\n')}`); + outputChannelLogger.logGitCommand(lines.join('\n')); }; git.onOutput.addListener('log', onOutput); disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput))); - const cc = new CommandCenter(git, model, outputChannel, telemetryReporter); + const cc = new CommandCenter(git, model, outputChannelLogger, telemetryReporter); disposables.push( cc, new GitFileSystemProvider(model), @@ -161,9 +162,8 @@ export async function _activate(context: ExtensionContext): Promise Disposable.from(...disposables).dispose())); - const outputChannel = window.createOutputChannel('Git'); - commands.registerCommand('git.showOutput', () => outputChannel.show()); - disposables.push(outputChannel); + const outputChannelLogger = new OutputChannelLogger(); + disposables.push(outputChannelLogger); const { name, version, aiKey } = require('../package.json') as { name: string; version: string; aiKey: string }; const telemetryReporter = new TelemetryReporter(name, version, aiKey); @@ -177,12 +177,12 @@ export async function _activate(context: ExtensionContext): Promise workspace.getConfiguration('git', null).get('enabled') === true); const result = new GitExtensionImpl(); - eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannel, telemetryReporter, disposables)); + eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannelLogger, telemetryReporter, disposables)); return result; } try { - const model = await createModel(context, outputChannel, telemetryReporter, disposables); + const model = await createModel(context, outputChannelLogger, telemetryReporter, disposables); return new GitExtensionImpl(model); } catch (err) { if (!/Git installation not found/.test(err.message || '')) { @@ -190,7 +190,7 @@ export async function _activate(context: ExtensionContext): Promise('autoRepositoryDetection'); // Log repository scan settings - if (Log.logLevel <= LogLevel.Trace) { - this.outputChannel.appendLine(`${logTimestamp()} Trace: autoRepositoryDetection="${autoRepositoryDetection}"`); - } + this.outputChannelLogger.logTrace(`autoRepositoryDetection="${autoRepositoryDetection}"`); if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { return; @@ -352,15 +350,13 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannelLogger, this.telemetryReporter); this.open(repository); repository.status(); // do not await this, we want SCM to know about the repo asap } catch (ex) { // noop - if (Log.logLevel <= LogLevel.Trace) { - this.outputChannel.appendLine(`${logTimestamp()} Trace: Opening repository for path='${repoPath}' failed; ex=${ex}`); - } + this.outputChannelLogger.logTrace(`Opening repository for path='${repoPath}' failed; ex=${ex}`); } } @@ -386,7 +382,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } private open(repository: Repository): void { - this.outputChannel.appendLine(`${logTimestamp()} Open repository: ${repository.root}`); + this.outputChannelLogger.logInfo(`Open repository: ${repository.root}`); const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); const disappearListener = onDidDisappearRepository(() => dispose()); @@ -443,7 +439,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return; } - this.outputChannel.appendLine(`${logTimestamp()} Close repository: ${repository.root}`); + this.outputChannelLogger.logInfo(`Close repository: ${repository.root}`); openRepository.dispose(); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 76c30e5c1d4..c7962fb1a62 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git'; @@ -14,9 +14,9 @@ import { debounce, memoize, throttle } from './decorators'; import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, logTimestamp, onceEvent, pathEquals, relativePath } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; -import { Log, LogLevel } from './log'; +import { LogLevel, OutputChannelLogger } from './log'; import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; @@ -504,10 +504,10 @@ class FileEventLogger { constructor( private onWorkspaceWorkingTreeFileChange: Event, private onDotGitFileChange: Event, - private outputChannel: OutputChannel + private outputChannelLogger: OutputChannelLogger ) { - this.logLevelDisposable = Log.onDidChangeLogLevel(this.onDidChangeLogLevel, this); - this.onDidChangeLogLevel(Log.logLevel); + this.logLevelDisposable = outputChannelLogger.onDidChangeLogLevel(this.onDidChangeLogLevel, this); + this.onDidChangeLogLevel(outputChannelLogger.currentLogLevel); } private onDidChangeLogLevel(level: LogLevel): void { @@ -518,8 +518,8 @@ class FileEventLogger { } this.eventDisposable = combinedDisposable([ - this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [wt] Change: ${uri.fsPath}`)), - this.onDotGitFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [.git] Change: ${uri.fsPath}`)) + this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannelLogger.logDebug(`[wt] Change: ${uri.fsPath}`)), + this.onDotGitFileChange(uri => this.outputChannelLogger.logDebug(`[.git] Change: ${uri.fsPath}`)) ]); } @@ -539,7 +539,7 @@ class DotGitWatcher implements IFileWatcher { constructor( private repository: Repository, - private outputChannel: OutputChannel + private outputChannelLogger: OutputChannelLogger ) { const rootWatcher = watch(repository.dotGit.path); this.disposables.push(rootWatcher); @@ -570,9 +570,7 @@ class DotGitWatcher implements IFileWatcher { this.transientDisposables.push(upstreamWatcher); upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); } catch (err) { - if (Log.logLevel <= LogLevel.Error) { - this.outputChannel.appendLine(`${logTimestamp()} Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`); - } + this.outputChannelLogger.logWarning(`Failed to watch ref '${upstreamPath}', is most likely packed.`); } } @@ -857,7 +855,7 @@ export class Repository implements Disposable { private pushErrorHandlerRegistry: IPushErrorHandlerRegistry, remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry, globalState: Memento, - outputChannel: OutputChannel, + outputChannelLogger: OutputChannelLogger, private telemetryReporter: TelemetryReporter ) { const repositoryWatcher = workspace.createFileSystemWatcher(new RelativePattern(Uri.file(repository.root), '**')); @@ -869,13 +867,11 @@ export class Repository implements Disposable { let onRepositoryDotGitFileChange: Event; try { - const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); + const dotGitFileWatcher = new DotGitWatcher(this, outputChannelLogger); onRepositoryDotGitFileChange = dotGitFileWatcher.event; this.disposables.push(dotGitFileWatcher); } catch (err) { - if (Log.logLevel <= LogLevel.Error) { - outputChannel.appendLine(`${logTimestamp()} Failed to watch path:'${this.dotGit.path}' or commonPath:'${this.dotGit.commonPath}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); - } + outputChannelLogger.logError(`Failed to watch path:'${this.dotGit.path}' or commonPath:'${this.dotGit.commonPath}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); onRepositoryDotGitFileChange = filterEvent(onRepositoryFileChange, uri => /\.git($|\/)/.test(uri.path)); } @@ -889,7 +885,7 @@ export class Repository implements Disposable { // Relevate repository changes should trigger virtual document change events onRepositoryDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); - this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannel)); + this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannelLogger)); const root = Uri.file(repository.root); this._sourceControl = scm.createSourceControl('git', 'Git', root); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index bb61552c1da..b568080bf48 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -16,10 +16,6 @@ export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); } -export function logTimestamp(): string { - return `[${new Date().toISOString()}]`; -} - export interface IDisposable { dispose(): void; }