diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index e8b505142b0..e8dc62329e3 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -632,3 +632,35 @@ export class LRUCache extends LinkedMap { } } } + +export class CounterSet { + + private map = new Map(); + + add(value: T): CounterSet { + this.map.set(value, (this.map.get(value) || 0) + 1); + return this; + } + + delete(value: T): boolean { + let counter = this.map.get(value) || 0; + + if (counter === 0) { + return false; + } + + counter--; + + if (counter === 0) { + this.map.delete(value); + } else { + this.map.set(value, counter); + } + + return true; + } + + has(value: T): boolean { + return this.map.has(value); + } +} diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 67affd19f69..7e3207ae595 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -16,7 +16,7 @@ import { IPathWithLineAndColumn, isValidBasename, parseLineAndColumnAware, sanit import { once } from 'vs/base/common/functional'; import { getPathLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; -import { basename, join, resolve } from 'vs/base/common/path'; +import { basename, resolve } from 'vs/base/common/path'; import { mark } from 'vs/base/common/performance'; import { IProcessEnvironment, isMacintosh, isWindows, OS } from 'vs/base/common/platform'; import { cwd } from 'vs/base/common/process'; @@ -131,7 +131,7 @@ class CodeMain { }); // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) - bufferLogService.logger = loggerService.createLogger(URI.file(join(environmentMainService.logsPath, 'main.log')), { id: 'mainLog', name: localize('mainLog', "Main") }); + bufferLogService.logger = loggerService.createLogger('main', { name: localize('mainLog', "Main") }); // Lifecycle once(lifecycleMainService.onWillShutdown)(evt => { @@ -162,7 +162,7 @@ class CodeMain { services.set(IEnvironmentMainService, environmentMainService); // Logger - const loggerService = new LoggerMainService(getLogLevel(environmentMainService)); + const loggerService = new LoggerMainService(getLogLevel(environmentMainService), environmentMainService.logsHome); services.set(ILoggerMainService, loggerService); // Log: We need to buffer the spdlog logs until we are sure @@ -246,7 +246,7 @@ class CodeMain { Promise.all([ environmentMainService.extensionsPath, environmentMainService.codeCachePath, - environmentMainService.logsPath, + environmentMainService.logsHome.fsPath, userDataProfilesMainService.defaultProfile.globalStorageHome.fsPath, environmentMainService.workspaceStorageHome.fsPath, environmentMainService.localHistoryHome.fsPath, diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index bf01f4d1811..0320f835502 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -39,8 +39,7 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks'; -import { ConsoleLogger, getLogLevel, ILogger, ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import { ConsoleLogger, getLogLevel, ILogger, ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; @@ -62,6 +61,8 @@ import { UserDataProfilesReadonlyService } from 'vs/platform/userDataProfile/nod import { resolveMachineId } from 'vs/platform/telemetry/node/telemetryUtils'; import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/node/extensionsProfileScannerService'; import { LogService } from 'vs/platform/log/common/logService'; +import { LoggerService } from 'vs/platform/log/node/loggerService'; +import { localize } from 'vs/nls'; class CliMain extends Disposable { @@ -124,15 +125,18 @@ class CliMain extends Disposable { environmentService.extensionsPath ].map(path => path ? Promises.mkdir(path, { recursive: true }) : undefined)); + // Logger + const loggerService = new LoggerService(getLogLevel(environmentService), environmentService.logsHome); + services.set(ILoggerService, loggerService); + // Log - const logLevel = getLogLevel(environmentService); - const spdLogLogger = new SpdLogLogger('cli', join(environmentService.logsPath, 'cli.log'), true, false, logLevel); + const logger = this._register(loggerService.createLogger('cli', { name: localize('cli', "CLI") })); const otherLoggers: ILogger[] = []; - if (logLevel === LogLevel.Trace) { - otherLoggers.push(new ConsoleLogger(logLevel)); + if (loggerService.getLogLevel() === LogLevel.Trace) { + otherLoggers.push(new ConsoleLogger(loggerService.getLogLevel())); } - const logService = this._register(new LogService(spdLogLogger, otherLoggers)); + const logService = this._register(new LogService(logger, otherLoggers)); services.set(ILogService, logService); // Files diff --git a/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts b/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts index 7276fa3281d..ab5f8a1d87a 100644 --- a/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts +++ b/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts @@ -6,7 +6,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; -import { basename, dirname, join } from 'vs/base/common/path'; +import { basename, dirname, joinPath } from 'vs/base/common/resources'; import { Promises } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; @@ -29,10 +29,10 @@ export class LogsDataCleaner extends Disposable { this.logService.trace('[logs cleanup]: Starting to clean up old logs.'); try { - const currentLog = basename(this.environmentService.logsPath); - const logsRoot = dirname(this.environmentService.logsPath); + const currentLog = basename(this.environmentService.logsHome); + const logsRoot = dirname(this.environmentService.logsHome); - const logFiles = await Promises.readdir(logsRoot); + const logFiles = await Promises.readdir(logsRoot.fsPath); const allSessions = logFiles.filter(logFile => /^\d{8}T\d{6}$/.test(logFile)); const oldSessions = allSessions.sort().filter(session => session !== currentLog); @@ -41,7 +41,7 @@ export class LogsDataCleaner extends Disposable { if (sessionsToDelete.length > 0) { this.logService.trace(`[logs cleanup]: Removing log folders '${sessionsToDelete.join(', ')}'`); - await Promise.all(sessionsToDelete.map(sessionToDelete => Promises.rm(join(logsRoot, sessionToDelete)))); + await Promise.all(sessionsToDelete.map(sessionToDelete => Promises.rm(joinPath(logsRoot, sessionToDelete).fsPath))); } } catch (error) { onUnexpectedError(error); diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 99f4c92a644..eeed5383617 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -14,7 +14,6 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { combinedDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IPCServer, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; import { Server as UtilityProcessMessagePortServer, once } from 'vs/base/parts/ipc/node/ipc.mp'; @@ -233,11 +232,11 @@ class SharedProcessMain extends Disposable { services.set(INativeEnvironmentService, environmentService); // Logger - const loggerService = new LoggerChannelClient(undefined, this.configuration.logLevel, this.configuration.loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) })), mainProcessService.getChannel('logger')); + const loggerService = new LoggerChannelClient(undefined, this.configuration.logLevel, environmentService.logsHome, this.configuration.loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) })), mainProcessService.getChannel('logger')); services.set(ILoggerService, loggerService); // Log - const logger = this._register(loggerService.createLogger(joinPath(URI.file(environmentService.logsPath), 'sharedprocess.log'), { id: 'sharedLog', name: localize('sharedLog', "Shared") })); + const logger = this._register(loggerService.createLogger('sharedprocess', { name: localize('sharedLog', "Shared") })); const consoleLogger = this._register(new ConsoleLogger(logger.getLevel())); const logService = this._register(new LogService(logger, [consoleLogger])); services.set(ILogService, logService); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index f41968d9b0e..66e37b69e3a 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -61,13 +61,11 @@ export interface IEnvironmentService { // --- settings sync userDataSyncHome: URI; - userDataSyncLogResource: URI; sync: 'on' | 'off' | undefined; // --- continue edit session continueOn?: string; editSessionId?: string; - editSessionsLogResource: URI; // --- extension development debugExtensionHost: IExtensionHostDebugParams; @@ -79,7 +77,7 @@ export interface IEnvironmentService { extensionTestsLocationURI?: URI; // --- logging - logsPath: string; + logsHome: URI; logLevel?: string; extensionLogLevel?: [string, string][]; verbose: boolean; @@ -87,7 +85,6 @@ export interface IEnvironmentService { // --- telemetry disableTelemetry: boolean; - telemetryLogResource: URI; serviceMachineIdResource: URI; // --- Policy @@ -149,6 +146,7 @@ export interface INativeEnvironmentService extends IEnvironmentService { disableKeytar?: boolean; crossOriginIsolated?: boolean; + isRemoteServer?: boolean; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index bf6bec62ce9..cabac5aa087 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -70,21 +70,15 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron @memoize get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync'); } - get logsPath(): string { + get logsHome(): URI { if (!this.args.logsPath) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); this.args.logsPath = join(this.userDataPath, 'logs', key); } - return this.args.logsPath; + return URI.file(this.args.logsPath); } - @memoize - get userDataSyncLogResource(): URI { return URI.file(join(this.logsPath, 'userDataSync.log')); } - - @memoize - get editSessionsLogResource(): URI { return URI.file(join(this.logsPath, 'editSessions.log')); } - @memoize get sync(): 'on' | 'off' | undefined { return this.args.sync; } @@ -240,7 +234,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron get crashReporterDirectory(): string | undefined { return this.args['crash-reporter-directory']; } @memoize - get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); } get disableTelemetry(): boolean { return !!this.args['disable-telemetry']; } @memoize diff --git a/src/vs/platform/log/common/fileLog.ts b/src/vs/platform/log/common/fileLog.ts index ac62616b4d0..60ad7b2f10c 100644 --- a/src/vs/platform/log/common/fileLog.ts +++ b/src/vs/platform/log/common/fileLog.ts @@ -99,9 +99,10 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS constructor( logLevel: LogLevel, - @IFileService private readonly fileService: IFileService, + logsHome: URI, + private readonly fileService: IFileService, ) { - super(logLevel); + super(logLevel, logsHome); } protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger { diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index ff5e3c3570e..d450886d782 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -8,8 +8,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { isWindows } from 'vs/base/common/platform'; -import { Mutable, isNumber } from 'vs/base/common/types'; +import { joinPath } from 'vs/base/common/resources'; +import { Mutable, isNumber, isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -118,6 +120,11 @@ export interface ILoggerOptions { */ hidden?: boolean; + /** + * Condition which must be true to show this logger + */ + when?: string; + /** * Id of the extension that created this logger. */ @@ -130,6 +137,7 @@ export interface ILoggerResource { readonly name?: string; readonly logLevel?: LogLevel; readonly hidden?: boolean; + readonly when?: string; readonly extensionId?: string; } @@ -143,16 +151,23 @@ export interface ILoggerService { readonly _serviceBrand: undefined; /** - * Creates a logger, or gets one if it already exists. + * Creates a logger for the given resource, or gets one if it already exists. * * This will also register the logger with the logger service. */ createLogger(resource: URI, options?: ILoggerOptions): ILogger; + /** + * Creates a logger with the given id in the logs folder, or gets one if it already exists. + * + * This will also register the logger with the logger service. + */ + createLogger(id: string, options?: Omit): ILogger; + /** * Gets an existing logger, if any. */ - getLogger(resource: URI): ILogger | undefined; + getLogger(resourceOrId: URI | string): ILogger | undefined; /** * An event which fires when the log level of a logger has changed @@ -182,7 +197,7 @@ export interface ILoggerService { /** * Set the visibility of a logger. */ - setVisibility(resource: URI, visible: boolean): void; + setVisibility(resourceOrId: URI | string, visible: boolean): void; /** * An event which fires when the logger resources are changed @@ -527,13 +542,13 @@ export class MultiplexLogger extends AbstractLogger implements ILogger { } } +type LoggerEntry = { logger: ILogger | undefined; info: Mutable }; + export abstract class AbstractLoggerService extends Disposable implements ILoggerService { declare readonly _serviceBrand: undefined; - private readonly _loggers = new ResourceMap(); - - private readonly _loggerResources = new ResourceMap>(); + private readonly _loggers = new ResourceMap(); private _onDidChangeLoggers = this._register(new Emitter<{ added: ILoggerResource[]; removed: ILoggerResource[] }>); readonly onDidChangeLoggers = this._onDidChangeLoggers.event; @@ -546,109 +561,128 @@ export abstract class AbstractLoggerService extends Disposable implements ILogge constructor( protected logLevel: LogLevel, + private readonly logsHome: URI, loggerResources?: Iterable, ) { super(); if (loggerResources) { for (const loggerResource of loggerResources) { - this._loggerResources.set(loggerResource.resource, loggerResource); + this._loggers.set(loggerResource.resource, { logger: undefined, info: loggerResource }); } } } - getLogger(resource: URI): ILogger | undefined { - return this._loggers.get(resource); + private getLoggerEntry(resourceOrId: URI | string): LoggerEntry | undefined { + if (isString(resourceOrId)) { + return [...this._loggers.values()].find(logger => logger.info.id === resourceOrId); + } + return this._loggers.get(resourceOrId); } - createLogger(resource: URI, options?: ILoggerOptions): ILogger { - let logger = this._loggers.get(resource); + getLogger(resourceOrId: URI | string): ILogger | undefined { + return this.getLoggerEntry(resourceOrId)?.logger; + } + + createLogger(resource: URI, options?: ILoggerOptions): ILogger; + createLogger(id: string, options?: ILoggerOptions): ILogger; + createLogger(idOrResource: URI | string, options?: ILoggerOptions): ILogger { + const resource = this.toResource(idOrResource); + let logger = this._loggers.get(resource)?.logger; const logLevel = options?.logLevel === 'always' ? LogLevel.Trace : options?.logLevel; if (!logger) { logger = this.doCreateLogger(resource, logLevel ?? this.getLogLevel(resource) ?? this.logLevel, options); - this._loggers.set(resource, logger); } - this.registerLogger({ resource, id: options?.id ?? resource.toString(), logLevel, name: options?.name, hidden: options?.hidden, extensionId: options?.extensionId }); + const loggerEntry: LoggerEntry = { + logger, + info: { resource, id: options?.id ?? resource.toString(), logLevel, name: options?.name, hidden: options?.hidden, extensionId: options?.extensionId, when: options?.when } + }; + this.registerLogger(loggerEntry.info); + // TODO: @sandy081 Remove this once registerLogger can take ILogger + this._loggers.set(resource, loggerEntry); return logger; } + protected toResource(idOrResource: string | URI): URI { + return isString(idOrResource) ? joinPath(this.logsHome, `${idOrResource}.log`) : idOrResource; + } + setLogLevel(logLevel: LogLevel): void; setLogLevel(resource: URI, logLevel: LogLevel): void; setLogLevel(arg1: any, arg2?: any): void { if (URI.isUri(arg1)) { const resource = arg1; const logLevel = arg2; - const loggerResource = this._loggerResources.get(resource); - if (loggerResource && logLevel !== loggerResource.logLevel) { - loggerResource.logLevel = logLevel === this.logLevel ? undefined : logLevel; - this._loggers.get(resource)?.setLevel(logLevel); - this._loggerResources.set(loggerResource.resource, loggerResource); + const logger = this._loggers.get(resource); + if (logger && logLevel !== logger.info.logLevel) { + logger.info.logLevel = logLevel === this.logLevel ? undefined : logLevel; + logger.logger?.setLevel(logLevel); + this._loggers.set(logger.info.resource, logger); this._onDidChangeLogLevel.fire([resource, logLevel]); } } else { this.logLevel = arg1; for (const [resource, logger] of this._loggers.entries()) { - if (this._loggerResources.get(resource)?.logLevel === undefined) { - logger.setLevel(this.logLevel); + if (this._loggers.get(resource)?.info.logLevel === undefined) { + logger.logger?.setLevel(this.logLevel); } } this._onDidChangeLogLevel.fire(this.logLevel); } } - setVisibility(resource: URI, visibility: boolean): void { - const loggerResource = this._loggerResources.get(resource); - if (loggerResource && visibility !== !loggerResource.hidden) { - loggerResource.hidden = !visibility; - this._loggerResources.set(loggerResource.resource, loggerResource); - this._onDidChangeVisibility.fire([resource, visibility]); + setVisibility(resourceOrId: URI | string, visibility: boolean): void { + const logger = this.getLoggerEntry(resourceOrId); + if (logger && visibility !== !logger.info.hidden) { + logger.info.hidden = !visibility; + this._loggers.set(logger.info.resource, logger); + this._onDidChangeVisibility.fire([logger.info.resource, visibility]); } } getLogLevel(resource?: URI): LogLevel { let logLevel; if (resource) { - logLevel = this._loggerResources.get(resource)?.logLevel; + logLevel = this._loggers.get(resource)?.info.logLevel; } return logLevel ?? this.logLevel; } registerLogger(resource: ILoggerResource): void { - const existing = this._loggerResources.get(resource.resource); + const existing = this._loggers.get(resource.resource); if (existing) { - if (existing.hidden !== resource.hidden) { + if (existing.info.hidden !== resource.hidden) { this.setVisibility(resource.resource, !resource.hidden); } } else { - this._loggerResources.set(resource.resource, resource); + this._loggers.set(resource.resource, { info: resource, logger: undefined }); this._onDidChangeLoggers.fire({ added: [resource], removed: [] }); } } deregisterLogger(resource: URI): void { - const existing = this._loggerResources.get(resource); + const existing = this._loggers.get(resource); if (existing) { - this._loggerResources.delete(resource); - const logger = this._loggers.get(resource); - if (logger) { - this._loggers.delete(resource); - logger.dispose(); + if (existing.logger) { + existing.logger.dispose(); } - this._onDidChangeLoggers.fire({ added: [], removed: [existing] }); + this._loggers.delete(resource); + this._onDidChangeLoggers.fire({ added: [], removed: [existing.info] }); } } - getRegisteredLoggers(): Iterable { - return this._loggerResources.values(); + *getRegisteredLoggers(): Iterable { + for (const entry of this._loggers.values()) { + yield entry.info; + } } getRegisteredLogger(resource: URI): ILoggerResource | undefined { - return this._loggerResources.get(resource); + return this._loggers.get(resource)?.info; } override dispose(): void { - this._loggers.forEach(logger => logger.dispose()); + this._loggers.forEach(logger => logger.logger?.dispose()); this._loggers.clear(); - this._loggerResources.clear(); super.dispose(); } @@ -673,15 +707,6 @@ export class NullLogService extends NullLogger implements ILogService { declare readonly _serviceBrand: undefined; } -export class NullLoggerService extends AbstractLoggerService { - - constructor() { super(LogLevel.Info); } - - protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions | undefined): ILogger { - return new NullLogger(); - } -} - export function getLogLevel(environmentService: IEnvironmentService): LogLevel { if (environmentService.verbose) { return LogLevel.Trace; @@ -725,3 +750,6 @@ export function parseLogLevel(logLevel: string): LogLevel | undefined { } return undefined; } + +// Contexts +export const CONTEXT_LOG_LEVEL = new RawContextKey('logLevel', LogLevelToString(LogLevel.Info)); diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 10c7d7fd17d..baee389cef7 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/common/logIpc.ts @@ -12,8 +12,8 @@ import { IURITransformer } from 'vs/base/common/uriIpc'; export class LoggerChannelClient extends AbstractLoggerService implements ILoggerService { - constructor(private readonly windowId: number | undefined, logLevel: LogLevel, loggers: ILoggerResource[], private readonly channel: IChannel) { - super(logLevel, loggers); + constructor(private readonly windowId: number | undefined, logLevel: LogLevel, logsHome: URI, loggers: ILoggerResource[], private readonly channel: IChannel) { + super(logLevel, logsHome, loggers); this._register(channel.listen('onDidChangeLogLevel', windowId)(arg => { if (isLogLevel(arg)) { super.setLogLevel(arg); diff --git a/src/vs/platform/log/electron-main/loggerService.ts b/src/vs/platform/log/electron-main/loggerService.ts index ffbc7585e00..134fc35f4c4 100644 --- a/src/vs/platform/log/electron-main/loggerService.ts +++ b/src/vs/platform/log/electron-main/loggerService.ts @@ -22,6 +22,8 @@ export interface ILoggerMainService extends ILoggerService { createLogger(resource: URI, options?: ILoggerOptions, windowId?: number): ILogger; + createLogger(id: string, options?: Omit, windowId?: number): ILogger; + registerLogger(resource: ILoggerResource, windowId?: number): void; getRegisteredLoggers(windowId?: number): ILoggerResource[]; @@ -34,7 +36,10 @@ export class LoggerMainService extends LoggerService implements ILoggerMainServi private readonly loggerResourcesByWindow = new ResourceMap(); - override createLogger(resource: URI, options?: ILoggerOptions, windowId?: number): ILogger { + override createLogger(resource: URI, options?: ILoggerOptions, windowId?: number): ILogger; + override createLogger(id: string, options?: ILoggerOptions, windowId?: number): ILogger; + override createLogger(idOrResource: URI | string, options?: ILoggerOptions, windowId?: number): ILogger { + const resource = this.toResource(idOrResource); if (windowId !== undefined) { this.loggerResourcesByWindow.set(resource, windowId); } diff --git a/src/vs/platform/remoteTunnel/common/remoteTunnel.ts b/src/vs/platform/remoteTunnel/common/remoteTunnel.ts index 800df82ef41..4d4e1d9e4e9 100644 --- a/src/vs/platform/remoteTunnel/common/remoteTunnel.ts +++ b/src/vs/platform/remoteTunnel/common/remoteTunnel.ts @@ -65,6 +65,5 @@ export const CONFIGURATION_KEY_PREFIX = 'remote.tunnels.access'; export const CONFIGURATION_KEY_HOST_NAME = CONFIGURATION_KEY_PREFIX + '.hostNameOverride'; export const CONFIGURATION_KEY_PREVENT_SLEEP = CONFIGURATION_KEY_PREFIX + '.preventSleep'; -export const LOG_FILE_NAME = 'remoteTunnelService.log'; +export const LOG_ID = 'remoteTunnelService'; export const LOGGER_NAME = localize('remoteTunnelLog', "Remote Tunnel Service"); -export const LOG_CHANNEL_ID = 'remoteTunnelServiceLog'; diff --git a/src/vs/platform/remoteTunnel/node/remoteTunnelService.ts b/src/vs/platform/remoteTunnel/node/remoteTunnelService.ts index a4341af28fe..7e76730577d 100644 --- a/src/vs/platform/remoteTunnel/node/remoteTunnelService.ts +++ b/src/vs/platform/remoteTunnel/node/remoteTunnelService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CONFIGURATION_KEY_HOST_NAME, CONFIGURATION_KEY_PREVENT_SLEEP, ConnectionInfo, IRemoteTunnelAccount, IRemoteTunnelService, LOGGER_NAME, LOG_CHANNEL_ID, LOG_FILE_NAME, TunnelStates, TunnelStatus } from 'vs/platform/remoteTunnel/common/remoteTunnel'; +import { CONFIGURATION_KEY_HOST_NAME, CONFIGURATION_KEY_PREVENT_SLEEP, ConnectionInfo, IRemoteTunnelAccount, IRemoteTunnelService, LOGGER_NAME, LOG_ID, TunnelStates, TunnelStatus } from 'vs/platform/remoteTunnel/common/remoteTunnel'; import { Emitter } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -18,7 +18,6 @@ import { ISharedProcessLifecycleService } from 'vs/platform/lifecycle/node/share import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; import { hostname, homedir } from 'os'; -import { URI } from 'vs/base/common/uri'; type RemoteTunnelEnablementClassification = { owner: 'aeschli'; @@ -72,8 +71,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); - const remoteTunnelLogResource = URI.file(join(environmentService.logsPath, LOG_FILE_NAME)); - this._logger = this._register(loggerService.createLogger(remoteTunnelLogResource, { name: LOGGER_NAME, id: LOG_CHANNEL_ID })); + this._logger = this._register(loggerService.createLogger(LOG_ID, { name: LOGGER_NAME })); this._startTunnelProcessDelayer = new Delayer(100); this._register(this._logger.onDidChangeLogLevel(l => this._logger.info('Log level changed to ' + LogLevelToString(l)))); diff --git a/src/vs/platform/request/browser/requestService.ts b/src/vs/platform/request/browser/requestService.ts index 95b0c28df12..a55aca1a72d 100644 --- a/src/vs/platform/request/browser/requestService.ts +++ b/src/vs/platform/request/browser/requestService.ts @@ -7,41 +7,29 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { request } from 'vs/base/parts/request/browser/request'; import { IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IRequestService } from 'vs/platform/request/common/request'; +import { ILoggerService } from 'vs/platform/log/common/log'; +import { AbstractRequestService, IRequestService } from 'vs/platform/request/common/request'; /** * This service exposes the `request` API, while using the global * or configured proxy settings. */ -export class RequestService implements IRequestService { +export class RequestService extends AbstractRequestService implements IRequestService { declare readonly _serviceBrand: undefined; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, - @ILogService private readonly logService: ILogService + @ILoggerService loggerService: ILoggerService ) { + super(false, loggerService); } async request(options: IRequestOptions, token: CancellationToken): Promise { - this.logService.trace(`RequestService#request (browser) - begin: ${options.type} ${options.url}`); - if (!options.proxyAuthorization) { options.proxyAuthorization = this.configurationService.getValue('http.proxyAuthorization'); } - - try { - const result = await request(options, token); - - this.logService.trace(`RequestService#request (browser) - end: ${options.type} ${options.url} ${result.res.statusCode}`); - - return result; - } catch (error) { - this.logService.error(`RequestService#request (browser) - error: ${options.type} ${options.url} ${error}`); - - throw error; - } + return this.logAndRequest('browser', options, () => request(options, token)); } async resolveProxy(url: string): Promise { diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index af45363bdb1..feb0e08ac7b 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -5,10 +5,13 @@ import { streamToBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { getErrorMessage } from 'vs/base/common/errors'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; import { localize } from 'vs/nls'; import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { CONTEXT_LOG_LEVEL, ILogger, ILoggerService, LogLevel, LogLevelToString } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; export const IRequestService = createDecorator('requestService'); @@ -21,6 +24,41 @@ export interface IRequestService { resolveProxy(url: string): Promise; } +export abstract class AbstractRequestService extends Disposable implements IRequestService { + + declare readonly _serviceBrand: undefined; + + protected readonly logger: ILogger; + private counter = 0; + + constructor( + remote: boolean, + loggerService: ILoggerService + ) { + super(); + this.logger = loggerService.createLogger('request', { + name: remote ? localize('remote request', "Remote Network Requests") : localize('request', "Network Requests"), + when: CONTEXT_LOG_LEVEL.isEqualTo(LogLevelToString(LogLevel.Trace)).serialize() + }); + } + + protected async logAndRequest(stack: string, options: IRequestOptions, request: () => Promise): Promise { + const prefix = `${stack} #${++this.counter}: ${options.url}`; + this.logger.trace(`${prefix} - begin`, options.type, options.headers); + try { + const result = await request(); + this.logger.trace(`${prefix} - end`, options.type, result.res.statusCode, result.res.headers); + return result; + } catch (error) { + this.logger.error(`${prefix} - error`, options.type, getErrorMessage(error)); + throw error; + } + } + + abstract request(options: IRequestOptions, token: CancellationToken): Promise; + abstract resolveProxy(url: string): Promise; +} + export function isSuccess(context: IRequestContext): boolean { return (context.res.statusCode && context.res.statusCode >= 200 && context.res.statusCode < 300) || context.res.statusCode === 1223; } @@ -61,7 +99,6 @@ export async function asJson(context: IRequestContext): Promise { if (e.affectsConfiguration('http')) { @@ -75,8 +75,6 @@ export class RequestService extends Disposable implements IRequestService { } async request(options: NodeRequestOptions, token: CancellationToken): Promise { - this.logService.trace(`RequestService#request (${options.isChromiumNetwork ? 'electron' : 'nodejs'}) - begin: ${options.type} ${options.url}`); - const { proxyUrl, strictSSL } = this; let shellEnv: typeof process.env | undefined = undefined; @@ -85,7 +83,7 @@ export class RequestService extends Disposable implements IRequestService { } catch (error) { if (!this.shellEnvErrorLogged) { this.shellEnvErrorLogged = true; - this.logService.error(`RequestService#request (${options.isChromiumNetwork ? 'electron' : 'nodejs'}) - resolving shell environment failed: ${error}`); + this.logService.error(`resolving shell environment failed`, getErrorMessage(error)); } } @@ -105,17 +103,7 @@ export class RequestService extends Disposable implements IRequestService { }; } - try { - const result = await this.doRequest(options, token); - - this.logService.trace(`RequestService#request (${options.isChromiumNetwork ? 'electron' : 'nodejs'}) - end: ${options.type} ${options.url} ${result.res.statusCode}`); - - return result; - } catch (error) { - this.logService.error(`RequestService#request (${options.isChromiumNetwork ? 'electron' : 'nodejs'}) - error: ${options.type} ${options.url} ${error}`); - - throw error; - } + return this.doRequest(options, token); } private async getNodeRequest(options: IRequestOptions): Promise { @@ -126,78 +114,79 @@ export class RequestService extends Disposable implements IRequestService { } private doRequest(options: NodeRequestOptions, token: CancellationToken): Promise { - return Promises.withAsyncBody(async (resolve, reject) => { - const endpoint = parseUrl(options.url!); - const rawRequest = options.getRawRequest - ? options.getRawRequest(options) - : await this.getNodeRequest(options); + return this.logAndRequest(options.isChromiumNetwork ? 'electron' : 'node', options, () => + Promises.withAsyncBody(async (resolve, reject) => { + const endpoint = parseUrl(options.url!); + const rawRequest = options.getRawRequest + ? options.getRawRequest(options) + : await this.getNodeRequest(options); - const opts: https.RequestOptions = { - hostname: endpoint.hostname, - port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), - protocol: endpoint.protocol, - path: endpoint.path, - method: options.type || 'GET', - headers: options.headers, - agent: options.agent, - rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true - }; + const opts: https.RequestOptions = { + hostname: endpoint.hostname, + port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80), + protocol: endpoint.protocol, + path: endpoint.path, + method: options.type || 'GET', + headers: options.headers, + agent: options.agent, + rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true + }; - if (options.user && options.password) { - opts.auth = options.user + ':' + options.password; - } + if (options.user && options.password) { + opts.auth = options.user + ':' + options.password; + } - const req = rawRequest(opts, (res: http.IncomingMessage) => { - const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; - if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { - this.doRequest({ - ...options, - url: res.headers['location'], - followRedirects: followRedirects - 1 - }, token).then(resolve, reject); - } else { - let stream: streams.ReadableStreamEvents = res; + const req = rawRequest(opts, (res: http.IncomingMessage) => { + const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3; + if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) { + this.doRequest({ + ...options, + url: res.headers['location'], + followRedirects: followRedirects - 1 + }, token).then(resolve, reject); + } else { + let stream: streams.ReadableStreamEvents = res; - // Responses from Electron net module should be treated as response - // from browser, which will apply gzip filter and decompress the response - // using zlib before passing the result to us. Following step can be bypassed - // in this case and proceed further. - // Refs https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;l=1266-1318 - if (!options.isChromiumNetwork && res.headers['content-encoding'] === 'gzip') { - stream = res.pipe(createGunzip()); + // Responses from Electron net module should be treated as response + // from browser, which will apply gzip filter and decompress the response + // using zlib before passing the result to us. Following step can be bypassed + // in this case and proceed further. + // Refs https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;l=1266-1318 + if (!options.isChromiumNetwork && res.headers['content-encoding'] === 'gzip') { + stream = res.pipe(createGunzip()); + } + + resolve({ res, stream: streamToBufferReadableStream(stream) } as IRequestContext); } + }); - resolve({ res, stream: streamToBufferReadableStream(stream) } as IRequestContext); + req.on('error', reject); + + if (options.timeout) { + req.setTimeout(options.timeout); } - }); - req.on('error', reject); - - if (options.timeout) { - req.setTimeout(options.timeout); - } - - // Chromium will abort the request if forbidden headers are set. - // Ref https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc;l=14-48; - // for additional context. - if (options.isChromiumNetwork) { - req.removeHeader('Content-Length'); - } - - if (options.data) { - if (typeof options.data === 'string') { - req.write(options.data); + // Chromium will abort the request if forbidden headers are set. + // Ref https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc;l=14-48; + // for additional context. + if (options.isChromiumNetwork) { + req.removeHeader('Content-Length'); } - } - req.end(); + if (options.data) { + if (typeof options.data === 'string') { + req.write(options.data); + } + } - token.onCancellationRequested(() => { - req.abort(); + req.end(); - reject(new CancellationError()); - }); - }); + token.onCancellationRequested(() => { + req.abort(); + + reject(new CancellationError()); + }); + })); } async resolveProxy(url: string): Promise { diff --git a/src/vs/platform/telemetry/common/telemetryLogAppender.ts b/src/vs/platform/telemetry/common/telemetryLogAppender.ts index 3690bb0826f..ba49e5a64e4 100644 --- a/src/vs/platform/telemetry/common/telemetryLogAppender.ts +++ b/src/vs/platform/telemetry/common/telemetryLogAppender.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService, ILogger, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; -import { ITelemetryAppender, isLoggingOnly, supportsTelemetry, telemetryLogChannelId, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryAppender, isLoggingOnly, supportsTelemetry, telemetryLogId, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; export class TelemetryLogAppender extends Disposable implements ITelemetryAppender { @@ -23,18 +23,16 @@ export class TelemetryLogAppender extends Disposable implements ITelemetryAppend ) { super(); - const logger = loggerService.getLogger(environmentService.telemetryLogResource); + const logger = loggerService.getLogger(telemetryLogId); if (logger) { this.logger = this._register(logger); } else { // Not a perfect check, but a nice way to indicate if we only have logging enabled for debug purposes and nothing is actually being sent const justLoggingAndNotSending = isLoggingOnly(productService, environmentService); const logSuffix = justLoggingAndNotSending ? ' (Not Sent)' : ''; - const telemetryLogResource = environmentService.telemetryLogResource; const isVisible = () => supportsTelemetry(productService, environmentService) && logService.getLevel() === LogLevel.Trace; - this.logger = this._register(loggerService.createLogger(telemetryLogResource, { id: telemetryLogChannelId, name: localize('telemetryLog', "Telemetry{0}", logSuffix), hidden: !isVisible() })); - this._register(logService.onDidChangeLogLevel(() => loggerService.setVisibility(telemetryLogResource, isVisible()))); - + this.logger = this._register(loggerService.createLogger(telemetryLogId, { name: localize('telemetryLog', "Telemetry{0}", logSuffix), hidden: !isVisible() })); + this._register(logService.onDidChangeLogLevel(() => loggerService.setVisibility(telemetryLogId, isVisible()))); this.logger.info('Below are logs for every telemetry event sent from VS Code once the log level is set to trace.'); this.logger.info('==========================================================='); } diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index 3dd93b1bb3b..126bce63ddc 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -68,7 +68,7 @@ export class NullEndpointTelemetryService implements ICustomEndpointTelemetrySer } } -export const telemetryLogChannelId = 'telemetryLog'; +export const telemetryLogId = 'telemetry'; export const extensionTelemetryLogChannelId = 'extensionTelemetryLog'; export interface ITelemetryAppender { diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index a287ae3ccf4..a1627c3f854 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { join } from 'vs/base/common/path'; -import { URI } from 'vs/base/common/uri'; import { DefaultURITransformer } from 'vs/base/common/uriIpc'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; @@ -30,9 +28,9 @@ const productService: IProductService = { _serviceBrand: undefined, ...product } const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); // Logging -const loggerService = new LoggerService(LogLevel.Info); +const loggerService = new LoggerService(LogLevel.Info, environmentService.logsHome); server.registerChannel(TerminalIpcChannels.Logger, new LoggerChannel(loggerService, () => DefaultURITransformer)); -const logger = loggerService.createLogger(URI.file(join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`)), { name: process.env.VSCODE_PTY_LOG_NAME ?? localize('ptyHost', "Pty Host") }); +const logger = loggerService.createLogger(TerminalLogConstants.FileName, { name: process.env.VSCODE_PTY_LOG_NAME ?? localize('ptyHost', "Pty Host") }); delete process.env.VSCODE_PTY_LOG_NAME; const logService = new LogService(logger, [new ConsoleLogger()]); diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 3c019f0d160..1c946d0cfdb 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -136,7 +136,7 @@ export class PtyHostService extends Disposable implements IPtyService { private _startPtyHost(): [Client, IPtyService] { const opts: IIPCOptions = { serverName: 'Pty Host', - args: ['--type=ptyHost', '--logsPath', this._environmentService.logsPath], + args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], env: { VSCODE_LAST_PTY_ID: lastPtyId, VSCODE_PTY_LOG_NAME: this.loggerName, diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index eb52d29bd75..3dc28248782 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -573,6 +573,6 @@ export interface IConflictSetting { //#endregion -export const USER_DATA_SYNC_LOG_ID = 'userDataSyncLog'; +export const USER_DATA_SYNC_LOG_ID = 'userDataSync'; export const USER_DATA_SYNC_SCHEME = 'vscode-userdata-sync'; export const PREVIEW_DIR_NAME = 'preview'; diff --git a/src/vs/platform/userDataSync/common/userDataSyncLog.ts b/src/vs/platform/userDataSync/common/userDataSyncLog.ts index 0adfbacc9ac..d458fc3e8db 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncLog.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncLog.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { AbstractLogger, ILogger, ILoggerService } from 'vs/platform/log/common/log'; import { IUserDataSyncLogService, USER_DATA_SYNC_LOG_ID } from 'vs/platform/userDataSync/common/userDataSync'; @@ -15,10 +14,9 @@ export class UserDataSyncLogService extends AbstractLogger implements IUserDataS constructor( @ILoggerService loggerService: ILoggerService, - @IEnvironmentService environmentService: IEnvironmentService ) { super(); - this.logger = this._register(loggerService.createLogger(environmentService.userDataSyncLogResource, { id: USER_DATA_SYNC_LOG_ID, name: localize('userDataSyncLog', "Settings Sync") })); + this.logger = this._register(loggerService.createLogger(USER_DATA_SYNC_LOG_ID, { name: localize('userDataSyncLog', "Settings Sync") })); } trace(message: string, ...args: any[]): void { diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 14d03fb6e26..b9060a46f0c 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1383,7 +1383,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic window: [], global: this.loggerService.getRegisteredLoggers() }, - logsPath: this.environmentMainService.logsPath, + logsPath: this.environmentMainService.logsHome.fsPath, product, isInitialStartup: options.initialStartup, diff --git a/src/vs/server/node/remoteAgentEnvironmentImpl.ts b/src/vs/server/node/remoteAgentEnvironmentImpl.ts index a991de0deae..b5aa198663e 100644 --- a/src/vs/server/node/remoteAgentEnvironmentImpl.ts +++ b/src/vs/server/node/remoteAgentEnvironmentImpl.ts @@ -15,11 +15,12 @@ import { transformOutgoingURIs } from 'vs/base/common/uriIpc'; import { listProcesses } from 'vs/base/node/ps'; import { getMachineInfo, collectWorkspaceStats } from 'vs/platform/diagnostics/node/diagnosticsService'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; -import { basename, join } from 'vs/base/common/path'; +import { basename } from 'vs/base/common/path'; import { ProcessItem } from 'vs/base/common/processes'; import { ServerConnectionToken, ServerConnectionTokenType } from 'vs/server/node/serverConnectionToken'; import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { joinPath } from 'vs/base/common/resources'; export class RemoteAgentEnvironmentChannel implements IServerChannel { @@ -100,8 +101,8 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel { connectionToken: (this._connectionToken.type !== ServerConnectionTokenType.None ? this._connectionToken.value : ''), appRoot: URI.file(this._environmentService.appRoot), settingsPath: this._environmentService.machineSettingsResource, - logsPath: URI.file(this._environmentService.logsPath), - extensionHostLogsPath: URI.file(join(this._environmentService.logsPath, `exthost${RemoteAgentEnvironmentChannel._namePool++}`)), + logsPath: this._environmentService.logsHome, + extensionHostLogsPath: joinPath(this._environmentService.logsHome, `exthost${RemoteAgentEnvironmentChannel._namePool++}`), globalStorageHome: this._userDataProfilesService.defaultProfile.globalStorageHome, workspaceStorageHome: this._environmentService.workspaceStorageHome, localHistoryHome: this._environmentService.localHistoryHome, diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index c0a770ad3aa..2ea8b96116a 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { getLogLevel, ILogService } from 'vs/platform/log/common/log'; +import { getLogLevel, ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -25,8 +25,6 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { IProductService } from 'vs/platform/product/common/productService'; -import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; -import { RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IServerEnvironmentService, ServerEnvironmentService, ServerParsedArgs } from 'vs/server/node/serverEnvironmentService'; import { ExtensionManagementCLI } from 'vs/platform/extensionManagement/common/extensionManagementCLI'; import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks'; @@ -49,6 +47,8 @@ import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { ServerUserDataProfilesService } from 'vs/platform/userDataProfile/node/userDataProfile'; import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/node/extensionsProfileScannerService'; import { LogService } from 'vs/platform/log/common/logService'; +import { LoggerService } from 'vs/platform/log/node/loggerService'; +import { localize } from 'vs/nls'; class CliMain extends Disposable { @@ -86,12 +86,14 @@ class CliMain extends Disposable { const environmentService = new ServerEnvironmentService(this.args, productService); services.set(IServerEnvironmentService, environmentService); - const logService = new LogService(new SpdLogLogger(RemoteExtensionLogFileName, join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`), true, false, getLogLevel(environmentService))); + const loggerService = new LoggerService(getLogLevel(environmentService), environmentService.logsHome); + services.set(ILoggerService, loggerService); + + const logService = new LogService(this._register(loggerService.createLogger('remoteCLI', { name: localize('remotecli', "Remote CLI") }))); services.set(ILogService, logService); logService.trace(`Remote configuration data at ${this.remoteDataFolder}`); logService.trace('process arguments:', this.args); - // Files const fileService = this._register(new FileService(logService)); services.set(IFileService, fileService); diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index 432344f146a..38d70707b0d 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -209,5 +209,6 @@ export interface IServerEnvironmentService extends INativeEnvironmentService { } export class ServerEnvironmentService extends NativeEnvironmentService implements IServerEnvironmentService { + get isRemoteServer(): boolean { return true; } override get args(): ServerParsedArgs { return super.args as ServerParsedArgs; } } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 408b37adf23..7e5208f9dcc 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -73,7 +73,6 @@ import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/use import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; import { LoggerService } from 'vs/platform/log/node/loggerService'; -import { URI } from 'vs/base/common/uri'; import { ServerUserDataProfilesService } from 'vs/platform/userDataProfile/node/userDataProfile'; import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/node/extensionsProfileScannerService'; import { LogService } from 'vs/platform/log/common/logService'; @@ -96,14 +95,14 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IEnvironmentService, environmentService); services.set(INativeEnvironmentService, environmentService); - const loggerService = new LoggerService(getLogLevel(environmentService)); + const loggerService = new LoggerService(getLogLevel(environmentService), environmentService.logsHome); services.set(ILoggerService, loggerService); socketServer.registerChannel('logger', new LoggerChannel(loggerService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority))); - const logger = loggerService.createLogger(URI.file(path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`)), { id: 'remoteServerLog', name: localize('remoteExtensionLog', "Remote Server") }); + const logger = loggerService.createLogger(RemoteExtensionLogFileName, { name: localize('remoteExtensionLog', "Remote Server") }); const logService = new LogService(logger, [new ServerLogger(getLogLevel(environmentService))]); services.set(ILogService, logService); - setTimeout(() => cleanupOlderLogs(environmentService.logsPath).then(null, err => logService.error(err)), 10000); + setTimeout(() => cleanupOlderLogs(environmentService.logsHome.fsPath).then(null, err => logService.error(err)), 10000); logService.trace(`Remote configuration data at ${REMOTE_DATA_FOLDER}`); logService.trace('process arguments:', environmentService.args); diff --git a/src/vs/workbench/api/common/extHostLoggerService.ts b/src/vs/workbench/api/common/extHostLoggerService.ts index beb14d0ebae..8a9c7dbb072 100644 --- a/src/vs/workbench/api/common/extHostLoggerService.ts +++ b/src/vs/workbench/api/common/extHostLoggerService.ts @@ -19,7 +19,7 @@ export class ExtHostLoggerService extends AbstractLoggerService implements ExtHo @IExtHostRpcService rpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, ) { - super(initData.logLevel, initData.loggers.map(logger => revive(logger))); + super(initData.logLevel, initData.logsLocation, initData.loggers.map(logger => revive(logger))); this._proxy = rpc.getProxy(MainContext.MainThreadLogger); } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 7a13fa96855..33045c3133b 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -276,7 +276,7 @@ export class BrowserMain extends Disposable { serviceCollection.set(IWorkbenchFileService, fileService); // Logger - const loggerService = new FileLoggerService(logLevel, fileService); + const loggerService = new FileLoggerService(logLevel, logsPath, fileService); serviceCollection.set(ILoggerService, loggerService); // URI Identity @@ -341,7 +341,7 @@ export class BrowserMain extends Disposable { this._register(workspaceTrustManagementService.onDidChangeTrust(() => configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()))); // Request Service - const requestService = new BrowserRequestService(remoteAgentService, configurationService, logService); + const requestService = new BrowserRequestService(remoteAgentService, configurationService, loggerService); serviceCollection.set(IRequestService, requestService); // Userdata Sync Store Management Service diff --git a/src/vs/workbench/contrib/editSessions/common/editSessions.ts b/src/vs/workbench/contrib/editSessions/common/editSessions.ts index bf60962ca26..4c752035d81 100644 --- a/src/vs/workbench/contrib/editSessions/common/editSessions.ts +++ b/src/vs/workbench/contrib/editSessions/common/editSessions.ts @@ -108,4 +108,4 @@ export function hashedEditSessionId(editSessionId: string) { return sha1.digest(); } -export const editSessionsLogId = 'editSessionsSyncLog'; +export const editSessionsLogId = 'editSessions'; diff --git a/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts b/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts index 0eabf6ef990..937cbdac372 100644 --- a/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts +++ b/src/vs/workbench/contrib/editSessions/common/editSessionsLogService.ts @@ -18,7 +18,7 @@ export class EditSessionsLogService extends AbstractLogger implements IEditSessi @IEnvironmentService environmentService: IEnvironmentService ) { super(); - this.logger = this._register(loggerService.createLogger(environmentService.editSessionsLogResource, { id: editSessionsLogId, name: localize('cloudChangesLog', "Cloud Changes") })); + this.logger = this._register(loggerService.createLogger(editSessionsLogId, { name: localize('cloudChangesLog', "Cloud Changes") })); } trace(message: string, ...args: any[]): void { diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 9eb65941fa8..7bf43b3dc8b 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -12,15 +12,18 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; import { IOutputChannelRegistry, IOutputService, Extensions } from 'vs/workbench/services/output/common/output'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ILogService, ILoggerResource, ILoggerService, LogLevel } from 'vs/platform/log/common/log'; +import { CONTEXT_LOG_LEVEL, ILogService, ILoggerResource, ILoggerService, LogLevel, LogLevelToString, isLogLevel } from 'vs/platform/log/common/log'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import { rendererLogId, showWindowLogActionId } from 'vs/workbench/common/logConstants'; import { createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationError, getErrorMessage, isCancellationError } from 'vs/base/common/errors'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IDefaultLogLevelsService } from 'vs/workbench/contrib/logs/common/defaultLogLevels'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CounterSet } from 'vs/base/common/map'; registerAction2(class extends Action2 { constructor() { @@ -51,32 +54,56 @@ registerAction2(class extends Action2 { class LogOutputChannels extends Disposable implements IWorkbenchContribution { + private readonly contextKeys = new CounterSet(); + private readonly outputChannelRegistry = Registry.as(Extensions.OutputChannels); + constructor( @ILogService private readonly logService: ILogService, - @ILoggerService loggerService: ILoggerService, + @ILoggerService private readonly loggerService: ILoggerService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @IFileService private readonly fileService: IFileService, ) { super(); - this.registerLogOutputChannels(loggerService.getRegisteredLoggers()); + const contextKey = CONTEXT_LOG_LEVEL.bindTo(contextKeyService); + contextKey.set(LogLevelToString(loggerService.getLogLevel())); + loggerService.onDidChangeLogLevel(e => { + if (isLogLevel(e)) { + contextKey.set(LogLevelToString(loggerService.getLogLevel())); + } + }); + + this.onDidAddLoggers(loggerService.getRegisteredLoggers()); this._register(loggerService.onDidChangeLoggers(({ added, removed }) => { - this.registerLogOutputChannels(added); - this.deregisterLogOutputChannels(removed); + this.onDidAddLoggers(added); + this.onDidRemoveLoggers(removed); })); this._register(loggerService.onDidChangeVisibility(([resource, visibility]) => { const logger = loggerService.getRegisteredLogger(resource); if (logger) { if (visibility) { - this.registerLogOutputChannels([logger]); + this.registerLogChannel(logger); } else { - this.deregisterLogOutputChannels([logger]); + this.outputChannelRegistry.removeChannel(logger.id); } } })); this.registerShowWindowLogAction(); + this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(() => this.onDidChangeContext())); } - private registerLogOutputChannels(loggers: Iterable): void { + private onDidAddLoggers(loggers: Iterable): void { for (const logger of loggers) { + if (logger.when) { + const contextKeyExpr = ContextKeyExpr.deserialize(logger.when); + if (contextKeyExpr) { + for (const key of contextKeyExpr.keys()) { + this.contextKeys.add(key); + } + if (!this.contextKeyService.contextMatchesRules(contextKeyExpr)) { + continue; + } + } + } if (logger.hidden) { continue; } @@ -84,20 +111,41 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } } - private deregisterLogOutputChannels(loggers: Iterable): void { - const outputChannelRegistry = Registry.as(Extensions.OutputChannels); + private onDidChangeContext(): void { + for (const logger of this.loggerService.getRegisteredLoggers()) { + if (logger.when) { + if (this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(logger.when))) { + this.registerLogChannel(logger); + } else { + this.outputChannelRegistry.removeChannel(logger.id); + } + } + } + } + + private onDidRemoveLoggers(loggers: Iterable): void { for (const logger of loggers) { - outputChannelRegistry.removeChannel(logger.id); + if (logger.when) { + const contextKeyExpr = ContextKeyExpr.deserialize(logger.when); + if (contextKeyExpr) { + for (const key of contextKeyExpr.keys()) { + this.contextKeys.delete(key); + } + } + } + this.outputChannelRegistry.removeChannel(logger.id); } } private registerLogChannel(logger: ILoggerResource): void { + if (this.outputChannelRegistry.getChannel(logger.id)) { + return; + } const promise = createCancelablePromise(async token => { await whenProviderRegistered(logger.resource, this.fileService); - const outputChannelRegistry = Registry.as(Extensions.OutputChannels); try { await this.whenFileExists(logger.resource, 1, token); - outputChannelRegistry.registerChannel({ id: logger.id, label: logger.name ?? logger.id, file: logger.resource, log: true, extensionId: logger.extensionId }); + this.outputChannelRegistry.registerChannel({ id: logger.id, label: logger.name ?? logger.id, file: logger.resource, log: true, extensionId: logger.extensionId }); } catch (error) { if (!isCancellationError(error)) { this.logService.error('Error while registering log channel', logger.resource.toString(), getErrorMessage(error)); diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index 5451519eb35..fec96ccae1d 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -13,7 +13,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { dirname, basename, isEqual } from 'vs/base/common/resources'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IOutputService } from 'vs/workbench/services/output/common/output'; -import { extensionTelemetryLogChannelId, telemetryLogChannelId } from 'vs/platform/telemetry/common/telemetryUtils'; +import { extensionTelemetryLogChannelId, telemetryLogId } from 'vs/platform/telemetry/common/telemetryUtils'; import { IDefaultLogLevelsService } from 'vs/workbench/contrib/logs/common/defaultLogLevels'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -52,7 +52,7 @@ export class SetLogLevelAction extends Action { const extensionLogs: LogChannelQuickPickItem[] = [], logs: LogChannelQuickPickItem[] = []; const logLevel = this.loggerService.getLogLevel(); for (const channel of this.outputService.getChannelDescriptors()) { - if (!channel.log || !channel.file || channel.id === telemetryLogChannelId || channel.id === extensionTelemetryLogChannelId) { + if (!channel.log || !channel.file || channel.id === telemetryLogId || channel.id === extensionTelemetryLogChannelId) { continue; } const channelLogLevel = this.loggerService.getLogLevel(channel.file) ?? logLevel; @@ -201,7 +201,7 @@ export class OpenWindowSessionLogFileAction extends Action { } private async getSessions(): Promise { - const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme }); + const logsPath = this.environmentService.logsHome.with({ scheme: this.environmentService.logFile.scheme }); const result: URI[] = [logsPath]; const stat = await this.fileService.resolve(dirname(logsPath)); if (stat.children) { diff --git a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts index 489e6020db0..f4222a3d1c1 100644 --- a/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts +++ b/src/vs/workbench/contrib/logs/common/logsDataCleaner.ts @@ -7,7 +7,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; import { basename, dirname } from 'vs/base/common/resources'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { URI } from 'vs/base/common/uri'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Promises } from 'vs/base/common/async'; @@ -25,10 +24,9 @@ export class LogsDataCleaner extends Disposable { private cleanUpOldLogsSoon(): void { let handle: any = setTimeout(async () => { handle = undefined; - const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme }); - const stat = await this.fileService.resolve(dirname(logsPath)); + const stat = await this.fileService.resolve(dirname(this.environmentService.logsHome)); if (stat.children) { - const currentLog = basename(logsPath); + const currentLog = basename(this.environmentService.logsHome); const allSessions = stat.children.filter(stat => stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name)); const oldSessions = allSessions.sort().filter((d, i) => d.name !== currentLog); const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 49)); diff --git a/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts b/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts index beb436ec8e7..3a057d2b8b9 100644 --- a/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { join } from 'vs/base/common/path'; -import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { INativeHostService } from 'vs/platform/native/common/native'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; +import { joinPath } from 'vs/base/common/resources'; export class OpenLogsFolderAction extends Action { @@ -24,7 +23,7 @@ export class OpenLogsFolderAction extends Action { } override run(): Promise { - return this.nativeHostService.showItemInFolder(URI.file(join(this.environmentService.logsPath, 'main.log')).fsPath); + return this.nativeHostService.showItemInFolder(joinPath(this.environmentService.logsHome, 'main.log').fsPath); } } diff --git a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts index bd5adf2020c..67221c5ad4a 100644 --- a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts +++ b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts @@ -6,7 +6,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IProductService } from 'vs/platform/product/common/productService'; -import { CONFIGURATION_KEY_HOST_NAME, CONFIGURATION_KEY_PREFIX, CONFIGURATION_KEY_PREVENT_SLEEP, ConnectionInfo, IRemoteTunnelAccount, IRemoteTunnelService, LOGGER_NAME, LOG_CHANNEL_ID, LOG_FILE_NAME } from 'vs/platform/remoteTunnel/common/remoteTunnel'; +import { CONFIGURATION_KEY_HOST_NAME, CONFIGURATION_KEY_PREFIX, CONFIGURATION_KEY_PREVENT_SLEEP, ConnectionInfo, IRemoteTunnelAccount, IRemoteTunnelService, LOGGER_NAME, LOG_ID } from 'vs/platform/remoteTunnel/common/remoteTunnel'; import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; import { localize } from 'vs/nls'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -35,7 +35,6 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; -import { join } from 'vs/base/common/path'; import { ITunnelApplicationConfig } from 'vs/base/common/product'; import { isNumber, isObject, isString } from 'vs/base/common/types'; @@ -116,9 +115,7 @@ export class RemoteTunnelWorkbenchContribution extends Disposable implements IWo ) { super(); - const remoteTunnelServiceLogResource = URI.file(join(environmentService.logsPath, LOG_FILE_NAME)); - - this.logger = this._register(loggerService.createLogger(remoteTunnelServiceLogResource, { name: LOGGER_NAME, id: LOG_CHANNEL_ID })); + this.logger = this._register(loggerService.createLogger(LOG_ID, { name: LOGGER_NAME })); this.connectionStateContext = REMOTE_TUNNEL_CONNECTION_STATE.bindTo(this.contextKeyService); @@ -639,7 +636,7 @@ export class RemoteTunnelWorkbenchContribution extends Disposable implements IWo async run(accessor: ServicesAccessor) { const outputService = accessor.get(IOutputService); - outputService.showChannel(LOG_CHANNEL_ID); + outputService.showChannel(LOG_ID); } })); diff --git a/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts b/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts index e829c042440..36393808917 100644 --- a/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts +++ b/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts @@ -18,6 +18,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { mock } from 'vs/base/test/common/mock'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileAccess } from 'vs/base/common/network'; +import { TestLoggerService } from 'vs/workbench/test/common/workbenchTestServices'; interface ColorInfo { description: string; @@ -40,7 +41,7 @@ suite('Color Registry', function () { const docUrl = 'https://raw.githubusercontent.com/microsoft/vscode-docs/main/api/references/theme-color.md'; - const reqContext = await new RequestService(new TestConfigurationService(), environmentService, new NullLogService()).request({ url: docUrl }, CancellationToken.None); + const reqContext = await new RequestService(new TestConfigurationService(), environmentService, new NullLogService(), new TestLoggerService()).request({ url: docUrl }, CancellationToken.None); const content = (await asTextOrError(reqContext))!; const expression = /-\s*\`([\w\.]+)\`: (.*)/g; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts index a84c6a82e63..2c294a869c6 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncViews.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { TreeView, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ALL_SYNC_RESOURCES, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, getLastSyncResourceUri, SyncResource, ISyncUserDataProfile } from 'vs/platform/userDataSync/common/userDataSync'; +import { ALL_SYNC_RESOURCES, IUserDataSyncService, ISyncResourceHandle as IResourceHandle, SyncStatus, IUserDataSyncEnablementService, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, getLastSyncResourceUri, SyncResource, ISyncUserDataProfile, USER_DATA_SYNC_LOG_ID } from 'vs/platform/userDataSync/common/userDataSync'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI, UriDto } from 'vs/base/common/uri'; @@ -649,7 +649,7 @@ class UserDataSyncTroubleshootViewDataProvider implements ITreeViewDataProvider private async getSyncLogs(): Promise { const logsFolders: URI[] = []; - const stat = await this.fileService.resolve(this.uriIdentityService.extUri.dirname(this.uriIdentityService.extUri.dirname(this.environmentService.userDataSyncLogResource))); + const stat = await this.fileService.resolve(this.uriIdentityService.extUri.dirname(this.uriIdentityService.extUri.dirname(this.environmentService.logsHome))); if (stat.children) { logsFolders.push(...stat.children .filter(stat => stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name)) @@ -660,14 +660,16 @@ class UserDataSyncTroubleshootViewDataProvider implements ITreeViewDataProvider const result: ITreeItem[] = []; for (const logFolder of logsFolders) { - const syncLogResource = this.uriIdentityService.extUri.joinPath(logFolder, this.uriIdentityService.extUri.basename(this.environmentService.userDataSyncLogResource)); - if (await this.fileService.exists(syncLogResource)) { + const folderStat = await this.fileService.resolve(logFolder); + const childStat = folderStat.children?.find(stat => this.uriIdentityService.extUri.basename(stat.resource).startsWith(`${USER_DATA_SYNC_LOG_ID}.`)); + if (childStat) { + const syncLogResource = childStat.resource; result.push({ handle: syncLogResource.toString(), collapsibleState: TreeItemCollapsibleState.None, resourceUri: syncLogResource, label: { label: this.uriIdentityService.extUri.basename(logFolder) }, - description: this.uriIdentityService.extUri.isEqual(syncLogResource, this.environmentService.userDataSyncLogResource) ? localize({ key: 'current', comment: ['Represents current log file'] }, "Current") : undefined, + description: this.uriIdentityService.extUri.isEqual(logFolder, this.environmentService.logsHome) ? localize({ key: 'current', comment: ['Represents current log file'] }, "Current") : undefined, command: { id: API_OPEN_EDITOR_COMMAND_ID, title: '', arguments: [syncLogResource, undefined, undefined] }, }); } diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index e77d86ba257..2ef30b63d72 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -175,7 +175,7 @@ export class DesktopMain extends Disposable { ...this.configuration.loggers.global.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) })), ...this.configuration.loggers.window.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource), hidden: true })), ]; - const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, loggers, mainProcessService.getChannel('logger')); + const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, environmentService.logsHome, loggers, mainProcessService.getChannel('logger')); serviceCollection.set(ILoggerService, loggerService); // Log diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 7982196ca87..553cbdeb5a5 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -44,9 +44,6 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get isBuilt(): boolean { return !!this.productService.commit; } - @memoize - get logsPath(): string { return this.logsHome.path; } - @memoize get logLevel(): string | undefined { const logLevelFromPayload = this.payload?.get('logLevel'); @@ -122,12 +119,6 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get userDataSyncHome(): URI { return joinPath(this.userRoamingDataHome, 'sync', this.workspaceId); } - @memoize - get userDataSyncLogResource(): URI { return joinPath(this.logsHome, 'userDataSync.log'); } - - @memoize - get editSessionsLogResource(): URI { return joinPath(this.logsHome, 'editSessions.log'); } - @memoize get sync(): 'on' | 'off' | undefined { return undefined; } @@ -235,7 +226,6 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi } @memoize - get telemetryLogResource(): URI { return joinPath(this.logsHome, 'telemetry.log'); } get extensionTelemetryLogResource(): URI { return joinPath(this.logsHome, 'extensionTelemetry.log'); } @memoize @@ -265,7 +255,7 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi constructor( private readonly workspaceId: string, - private readonly logsHome: URI, + readonly logsHome: URI, readonly options: IWorkbenchConstructionOptions, private readonly productService: IProductService ) { diff --git a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts index cdc7a708ab2..77580ee035b 100644 --- a/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-sandbox/environmentService.ts @@ -12,7 +12,6 @@ import { AbstractNativeEnvironmentService } from 'vs/platform/environment/common import { memoize } from 'vs/base/common/decorators'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { join } from 'vs/base/common/path'; import { IProductService } from 'vs/platform/product/common/productService'; import { joinPath } from 'vs/base/common/resources'; @@ -86,7 +85,7 @@ export class NativeWorkbenchEnvironmentService extends AbstractNativeEnvironment override get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.vscodeUserData }); } @memoize - get windowLogsPath(): URI { return URI.file(join(this.logsPath, `window${this.configuration.windowId}`)); } + get windowLogsPath(): URI { return joinPath(this.logsHome, `window${this.configuration.windowId}`); } @memoize get logFile(): URI { return joinPath(this.windowLogsPath, `renderer.log`); } diff --git a/src/vs/workbench/services/request/browser/requestService.ts b/src/vs/workbench/services/request/browser/requestService.ts index 14dc5332868..d93c7cb6be4 100644 --- a/src/vs/workbench/services/request/browser/requestService.ts +++ b/src/vs/workbench/services/request/browser/requestService.ts @@ -6,7 +6,7 @@ import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILoggerService } from 'vs/platform/log/common/log'; import { RequestChannelClient } from 'vs/platform/request/common/requestIpc'; import { IRemoteAgentService, IRemoteAgentConnection } from 'vs/workbench/services/remote/common/remoteAgentService'; import { RequestService } from 'vs/platform/request/browser/requestService'; @@ -18,9 +18,9 @@ export class BrowserRequestService extends RequestService { constructor( @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @IConfigurationService configurationService: IConfigurationService, - @ILogService logService: ILogService + @ILoggerService loggerService: ILoggerService ) { - super(configurationService, logService); + super(configurationService, loggerService); } override async request(options: IRequestOptions, token: CancellationToken): Promise { diff --git a/src/vs/workbench/services/request/electron-sandbox/requestService.ts b/src/vs/workbench/services/request/electron-sandbox/requestService.ts index 70f94928ac3..3a673a25e64 100644 --- a/src/vs/workbench/services/request/electron-sandbox/requestService.ts +++ b/src/vs/workbench/services/request/electron-sandbox/requestService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService } from 'vs/platform/log/common/log'; +import { ILoggerService } from 'vs/platform/log/common/log'; import { RequestService } from 'vs/platform/request/browser/requestService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -14,10 +14,10 @@ export class NativeRequestService extends RequestService { constructor( @IConfigurationService configurationService: IConfigurationService, - @ILogService logService: ILogService, + @ILoggerService loggerService: ILoggerService, @INativeHostService private nativeHostService: INativeHostService ) { - super(configurationService, logService); + super(configurationService, loggerService); } override async resolveProxy(url: string): Promise { diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index dd0c4bc4be5..63fd3640e94 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ViewContainer, IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions, IViewContainerModel, IAddedViewDescriptorRef, IViewDescriptorRef, IAddedViewDescriptorState, defaultViewIcon } from 'vs/workbench/common/views'; -import { IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -22,6 +22,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { IOutputService } from 'vs/workbench/services/output/common/output'; +import { CounterSet } from 'vs/base/common/map'; const VIEWS_LOG_ID = 'viewsLog'; const VIEWS_LOG_NAME = localize('views log', "Views"); @@ -48,38 +49,6 @@ registerAction2(class extends Action2 { export function getViewsStateStorageId(viewContainerStorageId: string): string { return `${viewContainerStorageId}.hidden`; } -class CounterSet implements IReadableSet { - - private map = new Map(); - - add(value: T): CounterSet { - this.map.set(value, (this.map.get(value) || 0) + 1); - return this; - } - - delete(value: T): boolean { - let counter = this.map.get(value) || 0; - - if (counter === 0) { - return false; - } - - counter--; - - if (counter === 0) { - this.map.delete(value); - } else { - this.map.set(value, counter); - } - - return true; - } - - has(value: T): boolean { - return this.map.has(value); - } -} - interface IStoredWorkspaceViewState { collapsed: boolean; isHidden: boolean; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 5ed6bd8d3b9..55c6dee7f71 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -57,7 +57,7 @@ import { IEditorService, ISaveEditorsOptions, IRevertAllEditorsOptions, Preferre import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; import { Dimension, IDimension } from 'vs/base/browser/dom'; -import { ILoggerService, ILogService, NullLoggerService, NullLogService } from 'vs/platform/log/common/log'; +import { ILoggerService, ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ILabelService } from 'vs/platform/label/common/label'; import { timeout } from 'vs/base/common/async'; import { PaneComposite, PaneCompositeDescriptor } from 'vs/workbench/browser/panecomposite'; @@ -99,7 +99,7 @@ import { IInputBox, IInputOptions, IPickOptions, IQuickInputButton, IQuickInputS import { QuickInputService } from 'vs/workbench/services/quickinput/browser/quickInputService'; import { IListService } from 'vs/platform/list/browser/listService'; import { win32, posix } from 'vs/base/common/path'; -import { TestContextService, TestStorageService, TestTextResourcePropertiesService, TestExtensionService, TestProductService, createFileStat } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestStorageService, TestTextResourcePropertiesService, TestExtensionService, TestProductService, createFileStat, TestLoggerService } from 'vs/workbench/test/common/workbenchTestServices'; import { IViewsService, IView, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -304,7 +304,7 @@ export function workbenchInstantiationService( instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : disposables.add(instantiationService.createInstance(TestTextFileService))); instantiationService.stub(IHostService, instantiationService.createInstance(TestHostService)); instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); - instantiationService.stub(ILoggerService, new NullLoggerService()); + instantiationService.stub(ILoggerService, new TestLoggerService(TestEnvironmentService.logsHome)); instantiationService.stub(ILogService, new NullLogService()); const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]); instantiationService.stub(IEditorGroupsService, editorGroupService); diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 31124d0e8dd..e1398daa91d 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -23,6 +23,14 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import product from 'vs/platform/product/common/product'; import { IActivity, IActivityService } from 'vs/workbench/services/activity/common/activity'; import { IStoredFileWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/storedFileWorkingCopy'; +import { AbstractLoggerService, ILogger, LogLevel, NullLogger } from 'vs/platform/log/common/log'; + +export class TestLoggerService extends AbstractLoggerService { + constructor(logsHome?: URI) { + super(LogLevel.Info, logsHome ?? URI.file('tests').with({ scheme: 'vscode-tests' })); + } + protected doCreateLogger(): ILogger { return new NullLogger(); } +} export class TestTextResourcePropertiesService implements ITextResourcePropertiesService {