Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log improvements #163532

Merged
merged 2 commits into from Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion extensions/vscode-api-tests/package.json
Expand Up @@ -33,7 +33,7 @@
"taskPresentationGroup",
"terminalDataWriteEvent",
"terminalDimensions",
"tunnels",
"tunnels",
"envShellEvent",
"testCoverage",
"testObserver",
Expand All @@ -44,6 +44,7 @@
"treeViewReveal",
"workspaceTrust",
"telemetry",
"extensionLog",
"localization"
],
"private": true,
Expand Down
Expand Up @@ -49,7 +49,7 @@ import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ILanguagePackService } from 'vs/platform/languagePacks/common/languagePacks';
import { NativeLanguagePackService } from 'vs/platform/languagePacks/node/languagePacks';
import { ConsoleLogger, ILoggerService, ILogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { FollowerLogService, LoggerChannelClient, LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
import { FollowerLogService, LoggerChannelClient, LogLevelChannel, LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
Expand Down Expand Up @@ -368,6 +368,10 @@ class SharedProcessMain extends Disposable {

private initChannels(accessor: ServicesAccessor): void {

// Log Level
const logLevelChannel = new LogLevelChannel(accessor.get(ILogService), accessor.get(ILoggerService));
this.server.registerChannel('logLevel', logLevelChannel);

// Extensions Management
const channel = new ExtensionManagementChannel(accessor.get(IExtensionManagementService), () => null);
this.server.registerChannel('extensions', channel);
Expand Down
2 changes: 1 addition & 1 deletion src/vs/code/electron-main/app.ts
Expand Up @@ -816,7 +816,7 @@ export class CodeApplication extends Disposable {
mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel);

// Log Level (main & shared process)
const logLevelChannel = new LogLevelChannel(accessor.get(ILogService));
const logLevelChannel = new LogLevelChannel(accessor.get(ILogService), accessor.get(ILoggerService));
mainProcessElectronServer.registerChannel('logLevel', logLevelChannel);
sharedProcessClient.then(client => client.registerChannel('logLevel', logLevelChannel));

Expand Down
6 changes: 3 additions & 3 deletions src/vs/code/electron-main/main.ts
Expand Up @@ -47,7 +47,6 @@ import { ILifecycleMainService, LifecycleMainService } from 'vs/platform/lifecyc
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 { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
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';
Expand Down Expand Up @@ -117,6 +116,7 @@ class CodeMain {
const logService = accessor.get(ILogService);
const lifecycleMainService = accessor.get(ILifecycleMainService);
const fileService = accessor.get(IFileService);
const loggerService = accessor.get(ILoggerService);

// Create the main IPC server by trying to be the server
// If this throws an error it means we are not the first
Expand All @@ -129,7 +129,7 @@ class CodeMain {
});

// Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906)
bufferLogService.logger = new SpdLogLogger('main', join(environmentMainService.logsPath, 'main.log'), true, false, bufferLogService.getLevel());
bufferLogService.logger = loggerService.createLogger(URI.file(join(environmentMainService.logsPath, 'main.log')), { name: 'main' });

// Lifecycle
once(lifecycleMainService.onWillShutdown)(evt => {
Expand Down Expand Up @@ -177,7 +177,7 @@ class CodeMain {
services.set(IUriIdentityService, uriIdentityService);

// Logger
services.set(ILoggerService, new LoggerService(logService, fileService));
services.set(ILoggerService, new LoggerService(logService));

// State
const stateMainService = new StateMainService(environmentMainService, logService, fileService);
Expand Down
6 changes: 2 additions & 4 deletions src/vs/platform/log/common/fileLog.ts
Expand Up @@ -8,7 +8,6 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { basename, dirname, joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ByteSize, FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { AbstractLogger, AbstractLoggerService, format, ILogger, ILoggerOptions, ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log';

Expand Down Expand Up @@ -146,15 +145,14 @@ export class FileLoggerService extends AbstractLoggerService implements ILoggerS

constructor(
@ILogService logService: ILogService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IFileService private readonly fileService: IFileService,
) {
super(logService.getLevel(), logService.onDidChangeLogLevel);
super(logService.getLevel(), logService.onDidChangeLogLevel, []);
}

protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
const logger = new BufferLogService(logLevel);
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogger, options?.name || basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters));
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = new FileLogger(options?.name || basename(resource), resource, logger.getLevel(), !!options?.donotUseFormatters, this.fileService));
return logger;
}
}
90 changes: 71 additions & 19 deletions src/vs/platform/log/common/log.ts
Expand Up @@ -6,6 +6,7 @@
import { toErrorMessage } from 'vs/base/common/errorMessage';
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 { URI } from 'vs/base/common/uri';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
Expand Down Expand Up @@ -111,12 +112,28 @@ export interface ILoggerService {
/**
* Creates a logger, or gets one if it already exists.
*/
createLogger(file: URI, options?: ILoggerOptions): ILogger;
createLogger(resource: URI, options?: ILoggerOptions): ILogger;

/**
* Gets an existing logger, if any.
*/
getLogger(file: URI): ILogger | undefined;
getLogger(resource: URI): ILogger | undefined;

/**
* Set log level for a logger.
*/
setLevel(resource: URI, level: LogLevel | undefined): void;

/**
* Get log level for a logger.
*/
getLogLevel(resource: URI): LogLevel | undefined;

/**
* Get default log level for a logger with given name.
* @param name logger name
*/
getDefaultLogLevel(name: string): LogLevel;
}

export abstract class AbstractLogger extends Disposable {
Expand Down Expand Up @@ -504,44 +521,79 @@ 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 loggers = new Map<string, ILogger>();
private readonly logLevelChangeableLoggers: ILogger[] = [];
private readonly loggerItems = new ResourceMap<ILoggerItem>();

constructor(
private logLevel: LogLevel,
onDidChangeLogLevel: Event<LogLevel>,
private readonly defaultLogLevels: [string, LogLevel][]
) {
super();
this._register(onDidChangeLogLevel(logLevel => {
this.logLevel = logLevel;
this.logLevelChangeableLoggers.forEach(logger => logger.setLevel(logLevel));
}));
this._register(onDidChangeLogLevel(logLevel => this.setLevel(logLevel)));
}

getLoggers(): ILogger[] {
return [...this.loggerItems.values()].map(({ logger }) => logger);
}

getLogger(resource: URI) {
return this.loggers.get(resource.toString());
getLogger(resource: URI): ILogger | undefined {
return this.loggerItems.get(resource)?.logger;
}

createLogger(resource: URI, options?: ILoggerOptions): ILogger {
let logger = this.loggers.get(resource.toString());
let logger = this.loggerItems.get(resource)?.logger;
if (!logger) {
logger = this.doCreateLogger(resource, options?.always ? LogLevel.Trace : this.logLevel, options);
this.loggers.set(resource.toString(), logger);
if (!options?.always) {
this.logLevelChangeableLoggers.push(logger);
}
const logLevel = options?.always ? LogLevel.Trace : undefined;
logger = this.doCreateLogger(resource, logLevel ?? (options?.name ? this.getDefaultLogLevel(options?.name) : this.logLevel), options);
this.loggerItems.set(resource, { logger, logLevel });
}
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);
}
});
}

}

getLogLevel(resource: URI): LogLevel | undefined {
const logger = this.loggerItems.get(resource);
return logger?.logLevel;
}

getDefaultLogLevel(name: string): LogLevel {
return this.defaultLogLevels.find(([loggerName]) => loggerName === name)?.[1] ?? this.logLevel;
}

override dispose(): void {
this.logLevelChangeableLoggers.splice(0, this.logLevelChangeableLoggers.length);
this.loggers.forEach(logger => logger.dispose());
this.loggers.clear();
this.loggerItems.forEach(({ logger }) => logger.dispose());
this.loggerItems.clear();
super.dispose();
}

Expand Down
19 changes: 11 additions & 8 deletions src/vs/platform/log/common/logIpc.ts
Expand Up @@ -12,8 +12,11 @@ export class LogLevelChannel implements IServerChannel {

onDidChangeLogLevel: Event<LogLevel>;

constructor(private service: ILogService) {
this.onDidChangeLogLevel = Event.buffer(service.onDidChangeLogLevel, true);
constructor(
private readonly logService: ILogService,
private readonly loggerService: ILoggerService
) {
this.onDidChangeLogLevel = Event.buffer(logService.onDidChangeLogLevel, true);
}

listen(_: unknown, event: string): Event<any> {
Expand All @@ -26,7 +29,7 @@ export class LogLevelChannel implements IServerChannel {

async call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'setLevel': return this.service.setLevel(arg);
case 'setLevel': return arg[1] ? this.loggerService.setLevel(URI.revive(arg[1]), arg[0]) : this.logService.setLevel(arg[0]);
}

throw new Error(`Call not found: ${command}`);
Expand All @@ -42,12 +45,12 @@ export class LogLevelChannelClient {
return this.channel.listen('onDidChangeLogLevel');
}

setLevel(level: LogLevel): void {
LogLevelChannelClient.setLevel(this.channel, level);
setLevel(level: LogLevel, resource?: URI): void {
LogLevelChannelClient.setLevel(this.channel, level, resource);
}

public static setLevel(channel: IChannel, level: LogLevel): Promise<void> {
return channel.call('setLevel', level);
public static setLevel(channel: IChannel, level: LogLevel, resource?: URI): Promise<void> {
return channel.call('setLevel', [level, resource]);
}

}
Expand Down Expand Up @@ -108,7 +111,7 @@ export class LoggerChannel implements IServerChannel {
export class LoggerChannelClient extends AbstractLoggerService implements ILoggerService {

constructor(logLevel: LogLevel, onDidChangeLogLevel: Event<LogLevel>, private readonly channel: IChannel) {
super(logLevel, onDidChangeLogLevel);
super(logLevel, onDidChangeLogLevel, []);
}

createConsoleMainLogger(): ILogger {
Expand Down
15 changes: 3 additions & 12 deletions src/vs/platform/log/node/loggerService.ts
Expand Up @@ -3,30 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Schemas } from 'vs/base/common/network';
import { basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { IFileService } from 'vs/platform/files/common/files';
import { FileLogger } from 'vs/platform/log/common/fileLog';
import { AbstractLoggerService, ILogger, ILoggerOptions, ILoggerService, ILogService, LogLevel } from 'vs/platform/log/common/log';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';

export class LoggerService extends AbstractLoggerService implements ILoggerService {

constructor(
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
@ILogService logService: ILogService
) {
super(logService.getLevel(), logService.onDidChangeLogLevel);
super(logService.getLevel(), logService.onDidChangeLogLevel, []);
}

protected doCreateLogger(resource: URI, logLevel: LogLevel, options?: ILoggerOptions): ILogger {
if (resource.scheme === Schemas.file) {
return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel);
} else {
return new FileLogger(options?.name ?? basename(resource), resource, logLevel, !!options?.donotUseFormatters, this.fileService);
}
return new SpdLogLogger(options?.name || generateUuid(), resource.fsPath, !options?.donotRotate, !!options?.donotUseFormatters, logLevel);
}
}

Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { AbstractLogger, DEFAULT_LOG_LEVEL, ILogger, ILoggerService, LogLevel } from 'vs/platform/log/common/log';
Expand Down Expand Up @@ -75,6 +76,11 @@ class TestTelemetryLoggerService implements ILoggerService {

return this.logger;
}

onDidChangeLogLevel = Event.None;
setLevel(): void { }
getLogLevel() { return undefined; }
getDefaultLogLevel() { return this.logLevel; }
}

suite('TelemetryLogAdapter', () => {
Expand Down
13 changes: 9 additions & 4 deletions src/vs/platform/terminal/node/ptyHostMain.ts
Expand Up @@ -4,13 +4,15 @@
*--------------------------------------------------------------------------------------------*/

import { join } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv';
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { ConsoleLogger, getLogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { ConsoleLogger, LogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { LogLevelChannel } from 'vs/platform/log/common/logIpc';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
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 { IReconnectConstants, TerminalIpcChannels, TerminalLogConstants } from 'vs/platform/terminal/common/terminal';
Expand All @@ -25,11 +27,14 @@ delete process.env.VSCODE_LAST_PTY_ID;
// Logging
const productService: IProductService = { _serviceBrand: undefined, ...product };
const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService);
const bufferLogService = new BufferLogService();
const logService = new LogService(new MultiplexLogService([
new ConsoleLogger(),
new SpdLogLogger(TerminalLogConstants.FileName, join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`), true, false, getLogLevel(environmentService))
bufferLogService
]));
const logLevelChannel = new LogLevelChannel(logService);
const loggerService = new LoggerService(logService);
bufferLogService.logger = loggerService.createLogger(URI.file(join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`)), { name: TerminalLogConstants.FileName });
const logLevelChannel = new LogLevelChannel(logService, loggerService);
server.registerChannel(TerminalIpcChannels.Log, logLevelChannel);

const heartbeatService = new HeartbeatService();
Expand Down