Merge pull request #171129 from microsoft/sandy081/logLevel

Persist log level on reload for extension logs
This commit is contained in:
Sandeep Somavarapu
2023-01-12 13:25:36 +01:00
committed by GitHub
31 changed files with 438 additions and 299 deletions

View File

@@ -216,7 +216,7 @@ class SharedProcessMain extends Disposable {
// Logger
const logLevelClient = new LogLevelChannelClient(this.server.getChannel('logLevel', mainRouter));
const loggerService = new LoggerChannelClient(this.configuration.logLevel, logLevelClient.onDidChangeLogLevel, mainProcessService.getChannel('logger'));
const loggerService = new LoggerChannelClient(undefined, this.configuration.logLevel, logLevelClient.onDidChangeLogLevel, this.configuration.loggers, mainProcessService.getChannel('logger'));
services.set(ILoggerService, loggerService);
// Log
@@ -275,7 +275,8 @@ class SharedProcessMain extends Disposable {
]);
// URI Identity
services.set(IUriIdentityService, new UriIdentityService(fileService));
const uriIdentityService = new UriIdentityService(fileService);
services.set(IUriIdentityService, uriIdentityService);
// Request
services.set(IRequestService, new SharedProcessRequestService(mainProcessService, configurationService, productService, logService));
@@ -374,7 +375,9 @@ class SharedProcessMain extends Disposable {
},
configurationService,
environmentService,
logService
logService,
loggerService,
uriIdentityService
);
ptyHostService.initialize();

View File

@@ -59,7 +59,7 @@ import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platfo
import { ILaunchMainService, LaunchMainService } from 'vs/platform/launch/electron-main/launchMainService';
import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { ILoggerService, ILogService } from 'vs/platform/log/common/log';
import { LoggerChannel, LogLevelChannel } from 'vs/platform/log/common/logIpc';
import { LogLevelChannel } from 'vs/platform/log/common/logIpc';
import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
import { INativeHostMainService, NativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
import { IProductService } from 'vs/platform/product/common/productService';
@@ -110,6 +110,8 @@ import { ProfileStorageChangesListenerChannel } from 'vs/platform/userDataProfil
import { Promises, RunOnceScheduler, runWhenIdle } from 'vs/base/common/async';
import { resolveMachineId } from 'vs/platform/telemetry/electron-main/telemetryUtils';
import { ExtensionsProfileScannerService } from 'vs/platform/extensionManagement/node/extensionsProfileScannerService';
import { LoggerChannel } from 'vs/platform/log/electron-main/logIpc';
import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService';
/**
* The main VS Code application. There will only ever be one instance,
@@ -804,7 +806,7 @@ export class CodeApplication extends Disposable {
sharedProcessClient.then(client => client.registerChannel('logLevel', logLevelChannel));
// Logger
const loggerChannel = new LoggerChannel(accessor.get(ILoggerService),);
const loggerChannel = new LoggerChannel(accessor.get(ILoggerMainService),);
mainProcessElectronServer.registerChannel('logger', loggerChannel);
sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel));

View File

@@ -46,7 +46,6 @@ import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainS
import { ILifecycleMainService, LifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { ConsoleMainLogger, getLogLevel, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
@@ -69,6 +68,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
import { PROFILES_ENABLEMENT_CONFIG } from 'vs/platform/userDataProfile/common/userDataProfile';
import { ILoggerMainService, LoggerMainService } from 'vs/platform/log/electron-main/loggerService';
/**
* The main VS Code entry point.
@@ -177,7 +177,7 @@ class CodeMain {
services.set(IUriIdentityService, uriIdentityService);
// Logger
services.set(ILoggerService, new LoggerService(logService));
services.set(ILoggerMainService, new LoggerMainService(logService));
// State
const stateMainService = new StateMainService(environmentMainService, logService, fileService);

View File

@@ -8,6 +8,7 @@ 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 } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@@ -104,7 +105,20 @@ export interface ILoggerOptions {
always?: boolean;
}
export interface ILoggerResource {
readonly resource: URI;
readonly name?: string;
readonly id?: string;
readonly logLevel?: LogLevel;
}
export type DidChangeLoggerResourceEvent = {
readonly added: Iterable<ILoggerResource>;
readonly removed: Iterable<ILoggerResource>;
};
export interface ILoggerService {
readonly _serviceBrand: undefined;
/**
@@ -120,12 +134,37 @@ export interface ILoggerService {
/**
* Set log level for a logger.
*/
setLevel(resource: URI, level: LogLevel | undefined): void;
setLogLevel(resource: URI, level: LogLevel): void;
/**
* Get log level for a logger.
*/
getLogLevel(resource: URI): LogLevel | undefined;
/**
* An event which fires when the log level of a logger has changed
*/
readonly onDidChangeLogLevel: Event<ILoggerResource>;
/**
* An event which fires when the logger resources are changed
*/
readonly onDidChangeLoggerResources: Event<DidChangeLoggerResourceEvent>;
/**
* Register the logger resoruce
*/
registerLoggerResource(resource: ILoggerResource): void;
/**
* Deregister the logger resoruce
*/
deregisterLoggerResource(resource: URI): void;
/**
* Get all registered logger resources
*/
getLoggerResources(): Iterable<ILoggerResource>;
}
export abstract class AbstractLogger extends Disposable implements ILogger {
@@ -485,74 +524,96 @@ export class LogService extends Disposable implements ILogService {
}
}
interface ILoggerItem {
readonly logger: ILogger;
logLevel: LogLevel | undefined;
}
export abstract class AbstractLoggerService extends Disposable implements ILoggerService {
declare readonly _serviceBrand: undefined;
private readonly loggerItems = new ResourceMap<ILoggerItem>();
private readonly _loggers = new ResourceMap<ILogger>();
private readonly _loggerResources = new ResourceMap<Mutable<ILoggerResource>>();
private _onDidChangeLoggerResources = this._register(new Emitter<{ added: ILoggerResource[]; removed: ILoggerResource[] }>);
readonly onDidChangeLoggerResources = this._onDidChangeLoggerResources.event;
private _onDidChangeLogLevel = this._register(new Emitter<ILoggerResource>);
readonly onDidChangeLogLevel = this._onDidChangeLogLevel.event;
constructor(
private logLevel: LogLevel,
protected logLevel: LogLevel,
onDidChangeLogLevel: Event<LogLevel>,
loggerResources?: Iterable<ILoggerResource>,
) {
super();
this._register(onDidChangeLogLevel(logLevel => this.setLevel(logLevel)));
}
getLoggers(): ILogger[] {
return [...this.loggerItems.values()].map(({ logger }) => logger);
this._register(onDidChangeLogLevel(logLevel => this.setGlobalLogLevel(logLevel)));
if (loggerResources) {
for (const loggerResource of loggerResources) {
this._loggerResources.set(loggerResource.resource, loggerResource);
}
}
}
getLogger(resource: URI): ILogger | undefined {
return this.loggerItems.get(resource)?.logger;
return this._loggers.get(resource);
}
createLogger(resource: URI, options?: ILoggerOptions, logLevel?: LogLevel): ILogger {
let logger = this.loggerItems.get(resource)?.logger;
let logger = this._loggers.get(resource);
if (!logger) {
logLevel = options?.always ? LogLevel.Trace : logLevel;
logger = this.doCreateLogger(resource, logLevel ?? this.logLevel, options);
this.loggerItems.set(resource, { logger, logLevel });
logger = this.doCreateLogger(resource, logLevel ?? this.getLogLevel(resource) ?? this.logLevel, options);
this._loggers.set(resource, logger);
this.registerLoggerResource({ resource, logLevel, name: options?.name });
}
return logger;
}
setLevel(logLevel: LogLevel): void;
setLevel(resource: URI, logLevel: LogLevel): void;
setLevel(arg1: any, arg2?: any): void {
const resource = URI.isUri(arg1) ? arg1 : undefined;
const logLevel = resource ? arg2 : arg1;
if (resource) {
const logger = this.loggerItems.get(resource);
if (logger && logger.logLevel !== logLevel) {
logger.logLevel = logLevel;
logger.logger.setLevel(logLevel);
}
} else {
this.logLevel = logLevel;
this.loggerItems.forEach(({ logLevel, logger }) => {
if (logLevel === undefined) {
logger.setLevel(this.logLevel);
}
});
setLogLevel(resource: URI, logLevel: LogLevel): void {
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);
this._onDidChangeLogLevel.fire(loggerResource);
}
}
protected setGlobalLogLevel(logLevel: LogLevel): void {
this.logLevel = logLevel;
for (const [resource, logger] of this._loggers.entries()) {
if (this._loggerResources.get(resource)?.logLevel === undefined) {
logger.setLevel(this.logLevel);
}
}
}
getLogLevel(resource: URI): LogLevel | undefined {
const logger = this.loggerItems.get(resource);
return logger?.logLevel;
return this._loggerResources.get(resource)?.logLevel;
}
registerLoggerResource(resource: ILoggerResource): void {
const existing = this._loggerResources.get(resource.resource);
if (!existing) {
this._loggerResources.set(resource.resource, resource);
this._onDidChangeLoggerResources.fire({ added: [resource], removed: [] });
}
}
deregisterLoggerResource(resource: URI): void {
const existing = this._loggerResources.get(resource);
if (existing) {
this._loggerResources.delete(resource);
this._onDidChangeLoggerResources.fire({ added: [], removed: [existing] });
}
}
getLoggerResources(): Iterable<ILoggerResource> {
return this._loggerResources.values();
}
override dispose(): void {
this.loggerItems.forEach(({ logger }) => logger.dispose());
this.loggerItems.clear();
this._loggers.forEach(logger => logger.dispose());
this._loggers.clear();
this._loggerResources.clear();
super.dispose();
}

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { URI, UriDto } from 'vs/base/common/uri';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { AbstractLoggerService, AbstractMessageLogger, AdapterLogger, ILogger, ILoggerOptions, ILoggerService, ILogService, log, LogLevel, LogService } from 'vs/platform/log/common/log';
import { AbstractLoggerService, AbstractMessageLogger, AdapterLogger, DidChangeLoggerResourceEvent, ILogger, ILoggerOptions, ILoggerResource, ILoggerService, ILogService, LogLevel, LogService } from 'vs/platform/log/common/log';
export class LogLevelChannel implements IServerChannel {
@@ -29,7 +29,7 @@ export class LogLevelChannel implements IServerChannel {
async call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'setLevel': return arg[1] ? this.loggerService.setLevel(URI.revive(arg[1]), arg[0]) : this.logService.setLevel(arg[0]);
case 'setLevel': return arg[1] ? this.loggerService.setLogLevel(URI.revive(arg[1]), arg[0]) : this.logService.setLevel(arg[0]);
}
throw new Error(`Call not found: ${command}`);
@@ -55,63 +55,19 @@ export class LogLevelChannelClient {
}
export class LoggerChannel implements IServerChannel {
private readonly loggers = new Map<string, ILogger>();
constructor(private readonly loggerService: ILoggerService) { }
listen(_: unknown, event: string): Event<any> {
throw new Error(`Event not found: ${event}`);
}
async call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'createLogger': this.createLogger(URI.revive(arg[0]), arg[1]); return;
case 'log': return this.log(URI.revive(arg[0]), arg[1]);
case 'consoleLog': return this.consoleLog(arg[0], arg[1]);
}
throw new Error(`Call not found: ${command}`);
}
private createLogger(file: URI, options: ILoggerOptions): void {
this.loggers.set(file.toString(), this.loggerService.createLogger(file, options));
}
private consoleLog(level: LogLevel, args: any[]): void {
let consoleFn = console.log;
switch (level) {
case LogLevel.Error:
consoleFn = console.error;
break;
case LogLevel.Warning:
consoleFn = console.warn;
break;
case LogLevel.Info:
consoleFn = console.info;
break;
}
consoleFn.call(console, ...args);
}
private log(file: URI, messages: [LogLevel, string][]): void {
const logger = this.loggers.get(file.toString());
if (!logger) {
throw new Error('Create the logger before logging');
}
for (const [level, message] of messages) {
log(logger, level, message);
}
}
}
export class LoggerChannelClient extends AbstractLoggerService implements ILoggerService {
constructor(logLevel: LogLevel, onDidChangeLogLevel: Event<LogLevel>, private readonly channel: IChannel) {
super(logLevel, onDidChangeLogLevel);
constructor(private readonly windowId: number | undefined, logLevel: LogLevel, onDidChangeLogLevel: Event<LogLevel>, loggers: UriDto<ILoggerResource>[], private readonly channel: IChannel) {
super(logLevel, onDidChangeLogLevel, loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) })));
this._register(channel.listen<ILoggerResource>('onDidChangeLogLevel', windowId)((loggerResource) => super.setLogLevel(URI.revive(loggerResource.resource), loggerResource.logLevel ?? this.logLevel)));
this._register(channel.listen<DidChangeLoggerResourceEvent>('onDidChangeLoggerResources', windowId)(({ added, removed }) => {
for (const loggerResource of added) {
super.registerLoggerResource({ ...loggerResource, resource: URI.revive(loggerResource.resource) });
}
for (const loggerResource of removed) {
super.deregisterLoggerResource(loggerResource.resource);
}
}));
}
createConsoleMainLogger(): ILogger {
@@ -122,6 +78,18 @@ export class LoggerChannelClient extends AbstractLoggerService implements ILogge
});
}
override registerLoggerResource(resource: ILoggerResource): void {
this.channel.call('registerLoggerResource', [resource, this.windowId]);
}
override deregisterLoggerResource(resource: URI): void {
this.channel.call('deregisterLoggerResource', [resource, this.windowId]);
}
override setLogLevel(resource: URI, logLevel: LogLevel): void {
this.channel.call('setLogLevel', [resource, logLevel]);
}
protected doCreateLogger(file: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
return new Logger(this.channel, file, logLevel, options);
}

View File

@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { ILogger, ILoggerOptions, log, LogLevel } from 'vs/platform/log/common/log';
import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService';
export class LoggerChannel implements IServerChannel {
private readonly loggers = new Map<string, ILogger>();
constructor(private readonly loggerService: ILoggerMainService) { }
listen(_: unknown, event: string, windowId?: number): Event<any> {
switch (event) {
case 'onDidChangeLoggerResources': return windowId ? this.loggerService.getOnDidChangeLoggerResourcesEvent(windowId) : this.loggerService.onDidChangeLoggerResources;
case 'onDidChangeLogLevel': return windowId ? this.loggerService.getOnDidChangeLogLevelEvent(windowId) : this.loggerService.onDidChangeLogLevel;
}
throw new Error(`Event not found: ${event}`);
}
async call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'createLogger': this.createLogger(URI.revive(arg[0]), arg[1]); return;
case 'log': return this.log(URI.revive(arg[0]), arg[1]);
case 'consoleLog': return this.consoleLog(arg[0], arg[1]);
case 'setLogLevel': return this.loggerService.setLogLevel(URI.revive(arg[0]), arg[1]);
case 'registerLoggerResource': return this.loggerService.registerLoggerResource({ ...arg[0], resource: URI.revive(arg[0].resource) }, arg[1]);
case 'deregisterLoggerResource': return this.loggerService.deregisterLoggerResource(URI.revive(arg[0]));
}
throw new Error(`Call not found: ${command}`);
}
private createLogger(file: URI, options: ILoggerOptions): void {
this.loggers.set(file.toString(), this.loggerService.createLogger(file, options));
}
private consoleLog(level: LogLevel, args: any[]): void {
let consoleFn = console.log;
switch (level) {
case LogLevel.Error:
consoleFn = console.error;
break;
case LogLevel.Warning:
consoleFn = console.warn;
break;
case LogLevel.Info:
consoleFn = console.info;
break;
}
consoleFn.call(console, ...args);
}
private log(file: URI, messages: [LogLevel, string][]): void {
const logger = this.loggers.get(file.toString());
if (!logger) {
throw new Error('Create the logger before logging');
}
for (const [level, message] of messages) {
log(logger, level, message);
}
}
}

View File

@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceMap } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
import { DidChangeLoggerResourceEvent, ILoggerResource, ILoggerService } from 'vs/platform/log/common/log';
import { LoggerService } from 'vs/platform/log/node/loggerService';
export const ILoggerMainService = refineServiceDecorator<ILoggerService, ILoggerMainService>(ILoggerService);
export interface ILoggerMainService extends ILoggerService {
getOnDidChangeLogLevelEvent(windowId: number): Event<ILoggerResource>;
getOnDidChangeLoggerResourcesEvent(windowId: number): Event<DidChangeLoggerResourceEvent>;
registerLoggerResource(resource: ILoggerResource, windowId?: number): void;
getLoggerResources(windowId?: number): ILoggerResource[];
deregisterLoggerResources(windowId: number): void;
}
export class LoggerMainService extends LoggerService implements ILoggerMainService {
private readonly loggerResourcesByWindow = new ResourceMap<number>();
override registerLoggerResource(resource: ILoggerResource, windowId?: number): void {
if (windowId !== undefined) {
this.loggerResourcesByWindow.set(resource.resource, windowId);
}
super.registerLoggerResource(resource);
}
override deregisterLoggerResource(resource: URI): void {
this.loggerResourcesByWindow.delete(resource);
super.deregisterLoggerResource(resource);
}
override getLoggerResources(windowId?: number): ILoggerResource[] {
const resources: ILoggerResource[] = [];
for (const resource of super.getLoggerResources()) {
if (this.isInterestedLoggerResource(resource.resource, windowId)) {
resources.push(resource);
}
}
return resources;
}
getOnDidChangeLogLevelEvent(windowId: number): Event<ILoggerResource> {
return Event.filter(this.onDidChangeLogLevel, e => this.isInterestedLoggerResource(e.resource, windowId));
}
getOnDidChangeLoggerResourcesEvent(windowId: number): Event<DidChangeLoggerResourceEvent> {
return Event.filter(
Event.map(this.onDidChangeLoggerResources, e => {
const r = {
added: [...e.added].filter(loggerResource => this.isInterestedLoggerResource(loggerResource.resource, windowId)),
removed: [...e.removed].filter(loggerResource => this.isInterestedLoggerResource(loggerResource.resource, windowId)),
};
return r;
}), e => e.added.length > 0 || e.removed.length > 0);
}
deregisterLoggerResources(windowId: number): void {
for (const [resource, resourceWindow] of this.loggerResourcesByWindow) {
if (resourceWindow === windowId) {
this.deregisterLoggerResource(resource);
}
}
}
private isInterestedLoggerResource(resource: URI, windowId: number | undefined): boolean {
const loggerWindowId = this.loggerResourcesByWindow.get(resource);
return loggerWindowId === undefined || loggerWindowId === windowId;
}
override dispose(): void {
super.dispose();
this.loggerResourcesByWindow.clear();
}
}

View File

@@ -24,6 +24,7 @@ import { WindowError } from 'vs/platform/window/electron-main/window';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IPolicyService } from 'vs/platform/policy/common/policy';
import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService';
export class SharedProcess extends Disposable implements ISharedProcess {
@@ -42,6 +43,7 @@ export class SharedProcess extends Disposable implements ISharedProcess {
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@ILogService private readonly logService: ILogService,
@ILoggerMainService private readonly loggerMainService: ILoggerMainService,
@IPolicyService private readonly policyService: IPolicyService,
@IThemeMainService private readonly themeMainService: IThemeMainService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService
@@ -245,6 +247,7 @@ export class SharedProcess extends Disposable implements ISharedProcess {
userEnv: this.userEnv,
args: this.environmentMainService.args,
logLevel: this.logService.getLevel(),
loggers: this.loggerMainService.getLoggerResources(),
product,
policiesData: this.policyService.serialize()
});

View File

@@ -6,7 +6,7 @@
import { IStringDictionary } from 'vs/base/common/collections';
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { LogLevel } from 'vs/platform/log/common/log';
import { ILoggerResource, LogLevel } from 'vs/platform/log/common/log';
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
import { UriDto } from 'vs/base/common/uri';
@@ -27,6 +27,8 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration {
readonly logLevel: LogLevel;
readonly loggers: UriDto<ILoggerResource>[];
readonly profiles: readonly UriDto<IUserDataProfile>[];
readonly policiesData?: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>;

View File

@@ -72,9 +72,13 @@ export class TestTelemetryLoggerService implements ILoggerService {
}
onDidChangeLogLevel = Event.None;
setLevel(): void { }
onDidChangeLoggerResources = Event.None;
setLogLevel(): void { }
getLogLevel() { return undefined; }
getDefaultLogLevel() { return this.logLevel; }
registerLoggerResource() { }
deregisterLoggerResource(): void { }
getLoggerResources() { return []; }
}
suite('TelemetryLogAdapter', () => {

View File

@@ -13,14 +13,18 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { parsePtyHostPort } from 'vs/platform/environment/common/environmentService';
import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, ILoggerService } from 'vs/platform/log/common/log';
import { LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
import { RequestStore } from 'vs/platform/terminal/common/requestStore';
import { HeartbeatConstants, IHeartbeatService, IProcessDataEvent, IPtyService, IReconnectConstants, IRequestResolveVariablesEvent, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, TerminalIcon, TerminalIpcChannels, IProcessProperty, TitleEventSource, ProcessPropertyType, IProcessPropertyMap, TerminalSettingId, ISerializedTerminalState, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal';
import { HeartbeatConstants, IHeartbeatService, IProcessDataEvent, IPtyService, IReconnectConstants, IRequestResolveVariablesEvent, IShellLaunchConfig, ITerminalLaunchError, ITerminalProfile, ITerminalsLayoutInfo, TerminalIcon, TerminalIpcChannels, IProcessProperty, TitleEventSource, ProcessPropertyType, IProcessPropertyMap, TerminalSettingId, ISerializedTerminalState, ITerminalProcessOptions, TerminalLogConstants } from 'vs/platform/terminal/common/terminal';
import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration';
import { IGetTerminalLayoutInfoArgs, IProcessDetails, ISetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
import { detectAvailableProfiles } from 'vs/platform/terminal/node/terminalProfiles';
import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabilities/capabilities';
import { localize } from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { join } from 'vs/base/common/path';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
enum Constants {
MaxRestarts = 5
@@ -82,6 +86,8 @@ export class PtyHostService extends Disposable implements IPtyService {
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IEnvironmentService private readonly _environmentService: INativeEnvironmentService,
@ILogService private readonly _logService: ILogService,
@ILoggerService private readonly _loggerService: ILoggerService,
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
) {
super();
@@ -180,9 +186,16 @@ export class PtyHostService extends Disposable implements IPtyService {
// Setup logging
const logChannel = client.getChannel(TerminalIpcChannels.Log);
LogLevelChannelClient.setLevel(logChannel, this._logService.getLevel());
const ptyHostLogResource = URI.file(join(this._environmentService.logsPath, `${TerminalLogConstants.FileName}.log`));
this._loggerService.registerLoggerResource({ id: 'ptyHostLog', name: localize('ptyHost', "Pty Host"), resource: ptyHostLogResource });
this._register(this._logService.onDidChangeLogLevel(() => {
LogLevelChannelClient.setLevel(logChannel, this._logService.getLevel());
}));
this._register(this._loggerService.onDidChangeLogLevel(({ resource, logLevel }) => {
if (this._uriIdentityService.extUri.isEqual(ptyHostLogResource, resource)) {
LogLevelChannelClient.setLevel(logChannel, logLevel ?? this._logService.getLevel());
}
}));
// Create proxy and forward events
const proxy = ProxyChannel.toService<IPtyService>(client.getChannel(TerminalIpcChannels.PtyHost));

View File

@@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { FileType } from 'vs/platform/files/common/files';
import { LogLevel } from 'vs/platform/log/common/log';
import { ILoggerResource, LogLevel } from 'vs/platform/log/common/log';
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
@@ -299,6 +299,7 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
isInitialStartup?: boolean;
logLevel: LogLevel;
loggers: UriDto<ILoggerResource>[];
fullscreen?: boolean;
maximized?: boolean;

View File

@@ -50,6 +50,7 @@ import { getPiiPathsFromEnvironment, isInternalTelemetry, ITelemetryAppender, su
import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties';
import { hostname, release } from 'os';
import { resolveMachineId } from 'vs/platform/telemetry/electron-main/telemetryUtils';
import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService';
export interface IWindowCreationOptions {
readonly state: IWindowState;
@@ -183,6 +184,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
constructor(
config: IWindowCreationOptions,
@ILogService private readonly logService: ILogService,
@ILoggerMainService private readonly loggerMainService: ILoggerMainService,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@IPolicyService private readonly policyService: IPolicyService,
@IUserDataProfilesMainService private readonly userDataProfilesService: IUserDataProfilesMainService,
@@ -1085,6 +1087,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
profile: this.profile || this.userDataProfilesService.defaultProfile
};
configuration.logLevel = this.logService.getLevel();
configuration.loggers = this.loggerMainService.getLoggerResources(this.id);
// Load config
this.load(configuration, { isReload: true, disableExtensions: cli?.['disable-extensions'] });
@@ -1657,6 +1660,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
override dispose(): void {
super.dispose();
// Deregister the loggers for this window
this.loggerMainService.deregisterLoggerResources(this.id);
this._win = null!; // Important to dereference the window object to allow for GC
}
}

View File

@@ -54,6 +54,7 @@ import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/ed
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IPolicyService } from 'vs/platform/policy/common/policy';
import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile';
import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService';
//#region Helper Interfaces
@@ -201,6 +202,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
private readonly machineId: string,
private readonly initialUserEnv: IProcessEnvironment,
@ILogService private readonly logService: ILogService,
@ILoggerMainService private readonly loggerService: ILoggerMainService,
@IStateMainService private readonly stateMainService: IStateMainService,
@IPolicyService private readonly policyService: IPolicyService,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@@ -1370,6 +1372,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
filesToWait: options.filesToOpen?.filesToWait,
logLevel: this.logService.getLevel(),
loggers: this.loggerService.getLoggerResources(),
logsPath: this.environmentMainService.logsPath,
product,
@@ -1446,6 +1449,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
configuration['extensions-dir'] = currentWindowConfig['extensions-dir'];
configuration['disable-extensions'] = currentWindowConfig['disable-extensions'];
}
configuration.loggers = currentWindowConfig?.loggers ?? configuration.loggers;
}
// Update window identifier and session now

View File

@@ -38,7 +38,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 { AbstractLogger, DEFAULT_LOG_LEVEL, getLogLevel, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log';
import { AbstractLogger, DEFAULT_LOG_LEVEL, getLogLevel, ILoggerService, ILogService, LogLevel, MultiplexLogService } from 'vs/platform/log/common/log';
import { LogLevelChannel } from 'vs/platform/log/common/logIpc';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
@@ -98,6 +98,8 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
setTimeout(() => cleanupOlderLogs(environmentService.logsPath).then(null, err => logService.error(err)), 10000);
const loggerService = new LoggerService(logService);
services.set(ILoggerService, loggerService);
bufferLogService.logger = loggerService.createLogger(URI.file(path.join(environmentService.logsPath, `${RemoteExtensionLogFileName}.log`)), { name: RemoteExtensionLogFileName });
logService.trace(`Remote configuration data at ${REMOTE_DATA_FOLDER}`);

View File

@@ -11,9 +11,7 @@ import { UriComponents, URI } from 'vs/base/common/uri';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService';
import { IOutputService } from 'vs/workbench/services/output/common/output';
import { localExtHostLog, remoteExtHostLog, webWorkerExtHostLog } from 'vs/workbench/services/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadLogger)
export class MainThreadLoggerService implements MainThreadLoggerShape {
@@ -24,18 +22,11 @@ export class MainThreadLoggerService implements MainThreadLoggerShape {
extHostContext: IExtHostContext,
@ILogService logService: ILogService,
@ILoggerService private readonly loggerService: ILoggerService,
@ILogLevelService extensionLoggerService: ILogLevelService,
@IOutputService outputService: IOutputService,
) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostLogLevelServiceShape);
this.disposables.add(logService.onDidChangeLogLevel(level => proxy.$setLevel(level)));
this.disposables.add(extensionLoggerService.onDidChangeLogLevel(({ id, logLevel }) => {
const channel = outputService.getChannelDescriptor(id);
const resource = channel?.log ? channel.file : undefined;
if (resource && (channel?.extensionId || id === localExtHostLog || id === remoteExtHostLog || id === webWorkerExtHostLog)) {
proxy.$setLevel(logLevel, resource);
}
}));
this.disposables.add(loggerService.onDidChangeLogLevel(loggerResource => proxy.$setLevel(loggerResource.logLevel ?? logService.getLevel(), loggerResource.resource)));
}
$log(file: UriComponents, messages: [LogLevel, string][]): void {

View File

@@ -12,6 +12,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { IViewsService } from 'vs/workbench/common/views';
import { isNumber } from 'vs/base/common/types';
import { ILoggerService } from 'vs/platform/log/common/log';
@extHostNamedCustomer(MainContext.MainThreadOutputService)
export class MainThreadOutputService extends Disposable implements MainThreadOutputServiceShape {
@@ -21,15 +22,18 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
private readonly _proxy: ExtHostOutputServiceShape;
private readonly _outputService: IOutputService;
private readonly _viewsService: IViewsService;
private readonly _loggerService: ILoggerService;
constructor(
extHostContext: IExtHostContext,
@IOutputService outputService: IOutputService,
@IViewsService viewsService: IViewsService
@IViewsService viewsService: IViewsService,
@ILoggerService loggerService: ILoggerService,
) {
super();
this._outputService = outputService;
this._viewsService = viewsService;
this._loggerService = loggerService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostOutputService);
@@ -45,8 +49,12 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
const idCounter = (MainThreadOutputService._extensionIdPool.get(extensionId) || 0) + 1;
MainThreadOutputService._extensionIdPool.set(extensionId, idCounter);
const id = `extension-output-${extensionId}-#${idCounter}-${label}`;
const resource = URI.revive(file);
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: URI.revive(file), log, languageId, extensionId });
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: resource, log, languageId, extensionId });
if (log) {
this._loggerService.registerLoggerResource({ resource, id, name: label });
}
this._register(toDisposable(() => this.$dispose(id)));
return id;
}

View File

@@ -10,6 +10,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { isUndefined } from 'vs/base/common/types';
import { revive } from 'vs/base/common/marshalling';
export class ExtHostLoggerService extends AbstractLoggerService implements ExtHostLogLevelServiceShape {
@@ -20,15 +21,15 @@ export class ExtHostLoggerService extends AbstractLoggerService implements ExtHo
@IExtHostRpcService rpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
super(initData.logLevel, Event.None);
super(initData.logLevel, Event.None, initData.loggers.map(logger => revive(logger)));
this._proxy = rpc.getProxy(MainContext.MainThreadLogger);
}
$setLevel(level: LogLevel, resource?: UriComponents): void {
if (resource) {
this.setLevel(URI.revive(resource), level);
this.setLogLevel(URI.revive(resource), level);
} else if (!isUndefined(level)) {
this.setLevel(level);
this.setGlobalLogLevel(level);
}
}

View File

@@ -246,12 +246,16 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
}
};
const onDidChangeLogLevel = disposables.add(new Emitter<LogLevel>());
function setLogLevel(newLogLevel: LogLevel): void {
logLevel = newLogLevel;
onDidChangeLogLevel.fire(newLogLevel);
}
channelPromise.then(channel => {
disposables.add(channel);
disposables.add(channel.onDidChangeLogLevel(e => {
logLevel = e;
onDidChangeLogLevel.fire(e);
}));
if (channel.logLevel !== logLevel) {
setLogLevel(channel.logLevel);
}
disposables.add(channel.onDidChangeLogLevel(e => setLogLevel(e)));
});
return {
...this.createExtHostOutputChannel(name, channelPromise),

View File

@@ -12,10 +12,6 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILogLevelService, LogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService';
registerSingleton(ILogLevelService, LogLevelService, InstantiationType.Delayed);
class WebLogOutputChannels extends Disposable implements IWorkbenchContribution {

View File

@@ -1,55 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILoggerService, LogLevel } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Disposable } from 'vs/base/common/lifecycle';
import { IOutputService } from 'vs/workbench/services/output/common/output';
export const ILogLevelService = createDecorator<ILogLevelService>('ILogLevelService');
export interface ILogLevelService {
readonly _serviceBrand: undefined;
readonly onDidChangeLogLevel: Event<{ readonly id: string; logLevel: LogLevel }>;
setLogLevel(id: string, logLevel: LogLevel): void;
getLogLevel(id: string): LogLevel | undefined;
}
export class LogLevelService extends Disposable implements ILogLevelService {
readonly _serviceBrand: undefined;
private readonly _onDidChangeLogLevel = this._register(new Emitter<{ readonly id: string; logLevel: LogLevel }>());
readonly onDidChangeLogLevel = this._onDidChangeLogLevel.event;
private readonly logLevels = new Map<string, LogLevel>();
constructor(
@IOutputService protected readonly outputService: IOutputService,
@ILoggerService private readonly loggerService: ILoggerService
) {
super();
}
getLogLevel(id: string): LogLevel | undefined {
return this.logLevels.get(id);
}
setLogLevel(id: string, logLevel: LogLevel): boolean {
if (this.getLogLevel(id) === logLevel) {
return false;
}
this.logLevels.set(id, logLevel);
const channel = this.outputService.getChannelDescriptor(id);
const resource = channel?.log ? channel.file : undefined;
if (resource) {
this.loggerService.setLevel(resource, logLevel);
}
this._onDidChangeLogLevel.fire({ id, logLevel });
return true;
}
}

View File

@@ -5,22 +5,21 @@
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { ILogService, LogLevel, getLogLevel, parseLogLevel } from 'vs/platform/log/common/log';
import { ILogService, ILoggerService, LogLevel, getLogLevel, parseLogLevel } from 'vs/platform/log/common/log';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { dirname, basename, isEqual } from 'vs/base/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IOutputChannelDescriptor, IOutputService } from 'vs/workbench/services/output/common/output';
import { IOutputService } from 'vs/workbench/services/output/common/output';
import { isNumber } from 'vs/base/common/types';
import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService';
import { extensionTelemetryLogChannelId, telemetryLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
type LogLevelQuickPickItem = IQuickPickItem & { level: LogLevel };
type LogChannelQuickPickItem = IQuickPickItem & { channel: IOutputChannelDescriptor };
type LogChannelQuickPickItem = IQuickPickItem & { id: string; resource: URI; extensionId?: string };
function isLogLevel(thing: unknown): thing is LogLevel {
return isNumber(thing);
@@ -34,7 +33,7 @@ export class SetLogLevelAction extends Action {
constructor(id: string, label: string,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@ILogService private readonly logService: ILogService,
@ILogLevelService private readonly logLevelService: ILogLevelService,
@ILoggerService private readonly loggerService: ILoggerService,
@IOutputService private readonly outputService: IOutputService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
) {
@@ -52,54 +51,44 @@ export class SetLogLevelAction extends Action {
}
}
private async selectLogLevelOrChannel(): Promise<IOutputChannelDescriptor | LogLevel | null> {
const extensionLogs = [], logs = [];
private async selectLogLevelOrChannel(): Promise<LogChannelQuickPickItem | LogLevel | null> {
const extensionLogs: LogChannelQuickPickItem[] = [], logs: LogChannelQuickPickItem[] = [];
const logLevel = this.logService.getLevel();
for (const channel of this.outputService.getChannelDescriptors()) {
if (!channel.log || channel.id === telemetryLogChannelId || channel.id === extensionTelemetryLogChannelId) {
if (!channel.log || !channel.file || channel.id === telemetryLogChannelId || channel.id === extensionTelemetryLogChannelId) {
continue;
}
const channelLogLevel = this.loggerService.getLogLevel(channel.file) ?? logLevel;
const item: LogChannelQuickPickItem = { id: channel.id, resource: channel.file, label: channel.label, description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined, extensionId: channel.extensionId };
if (channel.extensionId) {
extensionLogs.push(channel);
extensionLogs.push(item);
} else {
logs.push(channel);
logs.push(item);
}
}
const entries: (LogLevelQuickPickItem | LogChannelQuickPickItem | IQuickPickSeparator)[] = [];
entries.push({ type: 'separator', label: nls.localize('all', "All") });
entries.push(...this.getLogLevelEntries(this.getDefaultLogLevel(null), this.logService.getLevel()));
entries.push(...this.getLogLevelEntries(this.getDefaultLogLevel(), this.logService.getLevel()));
entries.push({ type: 'separator', label: nls.localize('loggers', "Logs") });
const logLevel = this.logService.getLevel();
for (const channel of logs.sort((a, b) => a.label.localeCompare(b.label))) {
const channelLogLevel = this.logLevelService.getLogLevel(channel.id) ?? logLevel;
entries.push({ label: channel.label, channel, description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined });
}
entries.push(...logs.sort((a, b) => a.label.localeCompare(b.label)));
if (extensionLogs.length && logs.length) {
entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") });
}
for (const channel of extensionLogs.sort((a, b) => a.label.localeCompare(b.label))) {
const channelLogLevel = this.logLevelService.getLogLevel(channel.id) ?? logLevel;
entries.push({ label: channel.label, channel, description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined });
}
entries.push(...extensionLogs.sort((a, b) => a.label.localeCompare(b.label)));
const entry = await this.quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Set Log Level") });
if (entry) {
if ((<LogLevelQuickPickItem>entry).level) {
return (<LogLevelQuickPickItem>entry).level;
}
if ((<LogChannelQuickPickItem>entry).channel) {
return (<LogChannelQuickPickItem>entry).channel;
}
}
return null;
return entry
? (<LogLevelQuickPickItem>entry).level ? (<LogLevelQuickPickItem>entry).level : <LogChannelQuickPickItem>entry
: null;
}
private async setLogLevelForChannel(logChannel: IOutputChannelDescriptor): Promise<void> {
const defaultLogLevel = this.getDefaultLogLevel(logChannel);
const currentLogLevel = this.logLevelService.getLogLevel(logChannel.id) ?? defaultLogLevel;
private async setLogLevelForChannel(logChannel: LogChannelQuickPickItem): Promise<void> {
const defaultLogLevel = this.getDefaultLogLevel(logChannel.extensionId);
const currentLogLevel = this.loggerService.getLogLevel(logChannel.resource) ?? defaultLogLevel;
const entries = this.getLogLevelEntries(defaultLogLevel, currentLogLevel);
const entry = await this.quickInputService.pick(entries, { placeHolder: logChannel ? nls.localize('selectLogLevelFor', " {0}: Select log level", logChannel?.label) : nls.localize('selectLogLevel', "Select log level"), activeItem: entries[this.logService.getLevel()] });
if (entry) {
this.logLevelService.setLogLevel(logChannel.id, entry.level);
this.loggerService.setLogLevel(logChannel.resource, entry.level);
}
}
@@ -132,10 +121,10 @@ export class SetLogLevelAction extends Action {
return defaultLogLevel === level ? nls.localize('default', "Default") : undefined;
}
private getDefaultLogLevel(outputChannel: IOutputChannelDescriptor | null): LogLevel {
private getDefaultLogLevel(extensionId?: string): LogLevel {
let logLevel: LogLevel | undefined;
if (outputChannel?.extensionId) {
const logLevelValue = this.environmentService.extensionLogLevel?.find(([id]) => areSameExtensions({ id }, { id: outputChannel.extensionId! }))?.[1];
if (extensionId) {
const logLevelValue = this.environmentService.extensionLogLevel?.find(([id]) => areSameExtensions({ id }, { id: extensionId }))?.[1];
if (logLevelValue) {
logLevel = parseLogLevel(logLevelValue);
}

View File

@@ -1,51 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILoggerService, LogLevel } from 'vs/platform/log/common/log';
import { IOutputService } from 'vs/workbench/services/output/common/output';
import { IMainProcessService, ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { LogLevelService as CommonLogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService';
import { remotePtyHostLog, remoteServerLog, sharedLogChannelId, userDataSyncLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants';
import { LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { LOG_CHANNEL_ID as remoteTunnelLogChannelId } from 'vs/platform/remoteTunnel/common/remoteTunnel';
export class LogLevelService extends CommonLogLevelService {
constructor(
@IOutputService outputService: IOutputService,
@ILoggerService loggerService: ILoggerService,
@ISharedProcessService private readonly sharedProcessService: ISharedProcessService,
@IMainProcessService private readonly mainProcessService: IMainProcessService,
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
) {
super(outputService, loggerService);
}
override setLogLevel(id: string, logLevel: LogLevel): boolean {
if (!super.setLogLevel(id, logLevel)) {
return false;
}
const channel = this.outputService.getChannelDescriptor(id);
const resource = channel?.log ? channel.file : undefined;
LogLevelChannelClient.setLevel(this.mainProcessService.getChannel('logLevel'), logLevel, resource);
if (id === sharedLogChannelId || id === userDataSyncLogChannelId || id === remoteTunnelLogChannelId) {
LogLevelChannelClient.setLevel(this.sharedProcessService.getChannel('logLevel'), logLevel, resource);
return true;
}
const connection = this.remoteAgentService.getConnection();
if ((id === remoteServerLog || id === remotePtyHostLog) && connection) {
connection.withChannel('logger', (channel) => LogLevelChannelClient.setLevel(channel, logLevel, resource));
return true;
}
return true;
}
}

View File

@@ -20,11 +20,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
import { registerLogChannel } from 'vs/workbench/services/output/common/output';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILogLevelService } from 'vs/workbench/contrib/logs/common/logLevelService';
import { LogLevelService } from 'vs/workbench/contrib/logs/electron-sandbox/logLevelService';
registerSingleton(ILogLevelService, LogLevelService, InstantiationType.Delayed);
class NativeLogOutputChannels extends Disposable implements IWorkbenchContribution {

View File

@@ -34,6 +34,7 @@ import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'
import { timeout } from 'vs/base/common/async';
import { TerminalLogConstants } from 'vs/platform/terminal/common/terminal';
import { remotePtyHostLog, remoteServerLog } from 'vs/workbench/contrib/logs/common/logConstants';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
export class LabelContribution implements IWorkbenchContribution {
constructor(
@@ -93,18 +94,36 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib
}
}
class RemoteLogOutputChannels implements IWorkbenchContribution {
class RemoteLogOutputChannels extends Disposable implements IWorkbenchContribution {
constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
@ILoggerService loggerService: ILoggerService,
@ILogService logService: ILogService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
) {
remoteAgentService.getEnvironment().then(remoteEnv => {
if (remoteEnv) {
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
outputChannelRegistry.registerChannel({ id: remoteServerLog, label: localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true });
outputChannelRegistry.registerChannel({ id: remotePtyHostLog, label: localize('remotePtyHostLog', "Remote Pty Host"), file: joinPath(remoteEnv.logsPath, `${TerminalLogConstants.FileName}.log`), log: true });
}
});
super();
const connection = remoteAgentService.getConnection();
if (connection) {
remoteAgentService.getEnvironment().then(remoteEnv => {
if (remoteEnv) {
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
const remoteExtensionHostLogFile = joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`);
const remotePtyLogFile = joinPath(remoteEnv.logsPath, `${TerminalLogConstants.FileName}.log`);
const remoteServerLoggerResource = { id: remoteServerLog, name: localize('remoteExtensionLog', "Remote Server"), resource: remoteExtensionHostLogFile };
loggerService.registerLoggerResource(remoteServerLoggerResource);
outputChannelRegistry.registerChannel({ id: remoteServerLoggerResource.id, label: remoteServerLoggerResource.name, file: remoteServerLoggerResource.resource, log: true });
const remotePtyHostLoggerResource = { id: remotePtyHostLog, name: localize('remotePtyHostLog', "Remote Pty Host"), resource: remotePtyLogFile };
loggerService.registerLoggerResource(remotePtyHostLoggerResource);
outputChannelRegistry.registerChannel({ id: remotePtyHostLoggerResource.id, label: remotePtyHostLoggerResource.name, file: remotePtyHostLoggerResource.resource, log: true });
this._register(loggerService.onDidChangeLogLevel(({ resource, logLevel }) => {
if (uriIdentityService.extUri.isEqual(resource, remoteExtensionHostLogFile) || uriIdentityService.extUri.isEqual(resource, remotePtyLogFile)) {
connection.withChannel('logger', (channel) => LogLevelChannelClient.setLevel(channel, logLevel ?? logService.getLevel(), resource));
}
}));
}
});
}
}
}

View File

@@ -171,7 +171,7 @@ export class DesktopMain extends Disposable {
// Logger
const logLevelChannelClient = new LogLevelChannelClient(mainProcessService.getChannel('logLevel'));
const loggerService = new LoggerChannelClient(this.configuration.logLevel, logLevelChannelClient.onDidChangeLogLevel, mainProcessService.getChannel('logger'));
const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, logLevelChannelClient.onDidChangeLogLevel, this.configuration.loggers, mainProcessService.getChannel('logger'));
serviceCollection.set(ILoggerService, loggerService);
// Log

View File

@@ -11,7 +11,7 @@ import { createMessageOfType, MessageType, isMessageOfType, ExtensionHostExitCod
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, ILoggerService } from 'vs/platform/log/common/log';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as platform from 'vs/base/common/platform';
import * as dom from 'vs/base/browser/dom';
@@ -66,6 +66,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ILabelService private readonly _labelService: ILabelService,
@ILogService private readonly _logService: ILogService,
@ILoggerService private readonly _loggerService: ILoggerService,
@IBrowserWorkbenchEnvironmentService private readonly _environmentService: IBrowserWorkbenchEnvironmentService,
@IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService,
@IProductService private readonly _productService: IProductService,
@@ -251,7 +252,9 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
}
// Register log channel for web worker exthost log
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id: webWorkerExtHostLog, label: localize('name', "Worker Extension Host"), file: this._extensionHostLogFile, log: true });
const webWorkerExtHostLoggerResource = { id: webWorkerExtHostLog, name: localize('name', "Worker Extension Host"), resource: this._extensionHostLogFile };
this._loggerService.registerLoggerResource(webWorkerExtHostLoggerResource);
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id: webWorkerExtHostLoggerResource.id, label: webWorkerExtHostLoggerResource.name, file: webWorkerExtHostLoggerResource.resource, log: true });
return protocol;
}
@@ -315,6 +318,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost
nlsBaseUrl: nlsUrlWithDetails,
telemetryInfo,
logLevel: this._logService.getLevel(),
loggers: [...this._loggerService.getLoggerResources()],
logsLocation: this._extensionHostLogsLocation,
logFile: this._extensionHostLogFile,
autoStart: initData.autoStart,

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from 'vs/base/common/buffer';
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents, UriDto } from 'vs/base/common/uri';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { LogLevel } from 'vs/platform/log/common/log';
import { ILoggerResource, LogLevel } from 'vs/platform/log/common/log';
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
@@ -31,6 +31,7 @@ export interface IExtensionHostInitData {
nlsBaseUrl?: URI;
telemetryInfo: ITelemetryInfo;
logLevel: LogLevel;
loggers: UriDto<ILoggerResource>[];
logsLocation: URI;
logFile: URI;
autoStart: boolean;

View File

@@ -16,7 +16,7 @@ import { localize } from 'vs/nls';
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, ILoggerService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { Registry } from 'vs/platform/registry/common/platform';
import { connectRemoteAgentExtensionHost, IConnectionOptions, IRemoteExtensionHostStartParams, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection';
@@ -68,6 +68,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILogService private readonly _logService: ILogService,
@ILoggerService protected readonly _loggerService: ILoggerService,
@ILabelService private readonly _labelService: ILabelService,
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
@@ -171,7 +172,9 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
disposable.dispose();
// Register log channel for remote exthost log
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id: remoteExtHostLog, label: localize('remote extension host Log', "Remote Extension Host"), file: logFile, log: true });
const remoteExtHostLoggerResource = { id: remoteExtHostLog, name: localize('remote extension host Log', "Remote Extension Host"), resource: logFile };
this._loggerService.registerLoggerResource(remoteExtHostLoggerResource);
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id: remoteExtHostLoggerResource.id, label: remoteExtHostLoggerResource.name, file: remoteExtHostLoggerResource.resource, log: true });
// release this promise
this._protocol = protocol;
@@ -248,6 +251,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
myExtensions: deltaExtensions.myToAdd,
telemetryInfo,
logLevel: this._logService.getLevel(),
loggers: [...this._loggerService.getLoggerResources()],
logsLocation: remoteInitData.extensionHostLogsPath,
logFile: joinPath(remoteInitData.extensionHostLogsPath, `${ExtensionHostLogFileName}.log`),
autoStart: true,

View File

@@ -16,7 +16,7 @@ import { BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, ILoggerService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -139,6 +139,7 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost {
@IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILogService protected readonly _logService: ILogService,
@ILoggerService protected readonly _loggerService: ILoggerService,
@ILabelService private readonly _labelService: ILabelService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
@IHostService private readonly _hostService: IHostService,
@@ -417,7 +418,9 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost {
disposable.dispose();
// Register log channel for exthost log
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id: localExtHostLog, label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true });
const localExtHostLoggerResource = { id: localExtHostLog, name: nls.localize('extension host Log', "Extension Host"), resource: this._extensionHostLogFile };
this._loggerService.registerLoggerResource(localExtHostLoggerResource);
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id: localExtHostLoggerResource.id, label: localExtHostLoggerResource.name, file: localExtHostLoggerResource.resource, log: true });
// release this promise
resolve();
@@ -472,6 +475,7 @@ export class NativeLocalProcessExtensionHost implements IExtensionHost {
myExtensions: deltaExtensions.myToAdd,
telemetryInfo,
logLevel: this._logService.getLevel(),
loggers: [...this._loggerService.getLoggerResources()],
logsLocation: this._environmentService.extHostLogsPath,
logFile: this._extensionHostLogFile,
autoStart: initData.autoStart,

View File

@@ -75,6 +75,7 @@ export const TestNativeWindowConfiguration: INativeWindowConfiguration = {
windowId: 0,
machineId: 'testMachineId',
logLevel: LogLevel.Error,
loggers: [],
mainPid: 0,
appRoot: '',
userEnv: {},