Skip to content

Commit

Permalink
Implement #39574
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Jan 23, 2018
1 parent 018cc14 commit de24732
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import { ipcRenderer } from 'electron';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createSharedProcessContributions } from 'vs/code/electron-browser/sharedProcess/contrib/contributions';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { ILogService } from 'vs/platform/log/common/log';
import { ILogService, FollowerLogService } from 'vs/platform/log/common/log';
import { LogLevelChannelClient } from 'vs/platform/log/common/logIpc';

export interface ISharedProcessConfiguration {
readonly machineId: string;
Expand Down Expand Up @@ -81,7 +82,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
const services = new ServiceCollection();

const environmentService = new EnvironmentService(initData.args, process.execPath);
const logService = createSpdLogService('sharedprocess', environmentService);
const logLevelClient = new LogLevelChannelClient(server.getChannel('loglevel', { route: () => 'main' }));
const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', environmentService));
process.once('exit', () => logService.dispose());

logService.info('main', JSON.stringify(configuration));
Expand Down
6 changes: 6 additions & 0 deletions src/vs/code/electron-main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateServ
import { IIssueService } from 'vs/platform/issue/common/issue';
import { IssueChannel } from 'vs/platform/issue/common/issueIpc';
import { IssueService } from 'vs/platform/issue/electron-main/issueService';
import { LogLevelChannel } from 'vs/platform/log/common/logIpc';

export class CodeApplication {

Expand Down Expand Up @@ -378,6 +379,11 @@ export class CodeApplication {
this.electronIpcServer.registerChannel('windows', windowsChannel);
this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel));

// Log level management
const logLevelChannel = new LogLevelChannel(accessor.get(ILogService));
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);
this.sharedProcessClient.done(client => client.registerChannel('loglevel', logLevelChannel));

// Lifecycle
this.lifecycleService.ready();

Expand Down
19 changes: 3 additions & 16 deletions src/vs/platform/log/common/bufferLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

'use strict';

import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';

interface ILog {
level: LogLevel;
Expand All @@ -24,17 +24,12 @@ function getLogFunction(logger: ILogService, level: LogLevel): Function {
}
}

export class BufferLogService implements ILogService {
export class BufferLogService extends AbstractLogService implements ILogService {

_serviceBrand: any;
private buffer: ILog[] = [];
private _logger: ILogService | undefined = undefined;

constructor(
private level: LogLevel = LogLevel.Error
) {
}

set logger(logger: ILogService) {
this._logger = logger;

Expand All @@ -46,19 +41,11 @@ export class BufferLogService implements ILogService {
this.buffer = [];
}

setLevel(logLevel: LogLevel): void {
this.level = logLevel;
}

getLevel(): LogLevel {
return this.level;
}

private _log(level: LogLevel, args: IArguments): void {
if (this._logger) {
const fn = getLogFunction(this._logger, level);
fn.apply(this._logger, args);
} else if (this.level <= level) {
} else if (this.getLevel() <= level) {
this.buffer.push({ level, args });
}
}
Expand Down
43 changes: 43 additions & 0 deletions src/vs/platform/log/common/logIpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
import { TPromise } from 'vs/base/common/winjs.base';
import { LogLevel, ILogService } from 'vs/platform/log/common/log';
import Event, { buffer } from 'vs/base/common/event';

export interface ILogLevelManagementChannel extends IChannel {
call(command: 'event:onDidChangeLogLevel'): TPromise<LogLevel>;
call(command: 'setLogLevel', logLevel: LogLevel): TPromise<void>;
}

export class LogLevelChannel implements ILogLevelManagementChannel {

onDidChangeLogLevel: Event<LogLevel>;

constructor(private service: ILogService) {
this.onDidChangeLogLevel = buffer(service.onDidChangeLogLevel, true);
}

call(command: string, arg?: any): TPromise<any> {
switch (command) {
case 'event:onDidChangeLogLevel': return eventToCall(this.onDidChangeLogLevel);
case 'setLogLevel': this.service.setLevel(arg); return TPromise.as(null);
}
return undefined;
}
}

export class LogLevelChannelClient {

constructor(private channel: ILogLevelManagementChannel) { }

private _onDidChangeLogLevel = eventFromCall<LogLevel>(this.channel, 'event:onDidChangeLogLevel');
get onDidChangeLogLevel(): Event<LogLevel> { return this._onDidChangeLogLevel; }

setLogLevel(level: LogLevel): TPromise<void> {
return this.channel.call('setLogLevel', level);
}
}
28 changes: 11 additions & 17 deletions src/vs/platform/log/node/spdlogService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
'use strict';

import * as path from 'path';
import { ILogService, LogLevel, NullLogService } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, NullLogService, AbstractLogService } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { RotatingLogger, setAsyncMode } from 'spdlog';

Expand All @@ -25,50 +25,44 @@ export function createSpdLogService(processName: string, environmentService: IEn
return new NullLogService();
}

class SpdLogService implements ILogService {
class SpdLogService extends AbstractLogService implements ILogService {

_serviceBrand: any;

constructor(
private readonly logger: RotatingLogger,
private level: LogLevel = LogLevel.Error
level: LogLevel
) {
}

setLevel(logLevel: LogLevel): void {
this.level = logLevel;
}

getLevel(): LogLevel {
return this.level;
super();
this.setLevel(level);
}

trace(): void {
if (this.level <= LogLevel.Trace) {
if (this.getLevel() <= LogLevel.Trace) {
this.logger.trace(this.format(arguments));
}
}

debug(): void {
if (this.level <= LogLevel.Debug) {
if (this.getLevel() <= LogLevel.Debug) {
this.logger.debug(this.format(arguments));
}
}

info(): void {
if (this.level <= LogLevel.Info) {
if (this.getLevel() <= LogLevel.Info) {
this.logger.info(this.format(arguments));
}
}

warn(): void {
if (this.level <= LogLevel.Warning) {
if (this.getLevel() <= LogLevel.Warning) {
this.logger.warn(this.format(arguments));
}
}

error(): void {
if (this.level <= LogLevel.Error) {
if (this.getLevel() <= LogLevel.Error) {
const arg = arguments[0];

if (arg instanceof Error) {
Expand All @@ -82,7 +76,7 @@ class SpdLogService implements ILogService {
}

critical(): void {
if (this.level <= LogLevel.Critical) {
if (this.getLevel() <= LogLevel.Critical) {
this.logger.critical(this.format(arguments));
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/vs/workbench/api/electron-browser/mainThreadLogService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

'use strict';

import { ExtHostContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';

@extHostCustomer
export class MainThreadLogLevelManagementChannel extends Disposable {

constructor(
extHostContext: IExtHostContext,
@ILogService logService: ILogService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLogLevel(level)));
}

}
7 changes: 6 additions & 1 deletion src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
import { LogLevel } from 'vs/platform/log/common/log';

export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
Expand Down Expand Up @@ -744,6 +745,10 @@ export interface ExtHostWindowShape {
$onDidChangeWindowFocus(value: boolean): void;
}

export interface ExtHostLogServiceShape {
$setLogLevel(level: LogLevel);
}

// --- proxy identifiers

export const MainContext = {
Expand Down Expand Up @@ -794,7 +799,7 @@ export const ExtHostContext = {
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
// ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask', ProxyType.CustomMarshaller),
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/node/extHostExtensionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._activator = null;
this._extHostLogService = new ExtHostLogService(environmentService);
this._extHostLogService = new ExtHostLogService(environmentService, this._logService);

// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, logService);
Expand Down
44 changes: 28 additions & 16 deletions src/vs/workbench/api/node/extHostLogService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,59 @@ import * as path from 'path';
import * as vscode from 'vscode';
import { TPromise } from 'vs/base/common/winjs.base';
import { mkdirp, dirExists } from 'vs/base/node/pfs';
import Event, { Emitter } from 'vs/base/common/event';
import Event from 'vs/base/common/event';
import { LogLevel } from 'vs/workbench/api/node/extHostTypes';
import { ILogService } from 'vs/platform/log/common/log';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { memoize } from 'vs/base/common/decorators';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
import { Disposable } from 'vs/base/common/lifecycle';

export class ExtHostLogService {
export class ExtHostLogService extends Disposable implements ExtHostLogServiceShape {
private _loggers: Map<string, ExtHostLogger> = new Map();

constructor(private _environmentService: IEnvironmentService) {
constructor(
private _environmentService: IEnvironmentService,
private _logService: ILogService
) {
super();
}

$setLogLevel(level: LogLevel) {
this._logService.setLevel(level);
}

getExtLogger(extensionID: string): ExtHostLogger {
if (!this._loggers.has(extensionID)) {
const logService = createSpdLogService(extensionID, this._environmentService, extensionID);
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
this._loggers.set(extensionID, new ExtHostLogger(logService, logsDirPath));
let logger = this._loggers.get(extensionID);
if (!logger) {
logger = this.createLogger(extensionID);
this._loggers.set(extensionID, logger);
}
return logger;
}

return this._loggers.get(extensionID);
private createLogger(extensionID: string): ExtHostLogger {
const logService = createSpdLogService(extensionID, this._environmentService, extensionID);
const logsDirPath = path.join(this._environmentService.logsPath, extensionID);
this._register(this._logService.onDidChangeLogLevel(level => logService.setLevel(level)));
return new ExtHostLogger(logService, logsDirPath);
}
}

export class ExtHostLogger implements vscode.Logger {
private _currentLevel: LogLevel;
private _onDidChangeLogLevel: Emitter<LogLevel>;

constructor(
private readonly _logService: ILogService,
private readonly _logDirectory: string
) {
this._currentLevel = this._logService.getLevel();
this._onDidChangeLogLevel = new Emitter<LogLevel>();
this.onDidChangeLogLevel = this._onDidChangeLogLevel.event;
}

// TODO
readonly onDidChangeLogLevel: Event<LogLevel>;
get onDidChangeLogLevel(): Event<LogLevel> {
return this._logService.onDidChangeLogLevel;
}

get currentLevel(): LogLevel { return this._currentLevel; }
get currentLevel(): LogLevel { return this._logService.getLevel(); }

@memoize
get logDirectory(): TPromise<string> {
Expand Down
16 changes: 11 additions & 5 deletions src/vs/workbench/electron-browser/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';

import fs = require('fs');
import { ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { ConsoleLogService, MultiplexLogService, ILogService, FollowerLogService } from 'vs/platform/log/common/log';
import { IssueChannelClient } from 'vs/platform/issue/common/issueIpc';
import { IIssueService } from 'vs/platform/issue/common/issue';
import { LogLevelChannelClient } from 'vs/platform/log/common/logIpc';
gracefulFs.gracefulify(fs); // enable gracefulFs

export function startup(configuration: IWindowConfiguration): TPromise<void> {
Expand Down Expand Up @@ -75,10 +76,7 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
const mainServices = createMainProcessServices(mainProcessClient, configuration);

const environmentService = new EnvironmentService(configuration, configuration.execPath);
const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, environmentService);
const consoleLogService = new ConsoleLogService(environmentService);
const logService = new MultiplexLogService([consoleLogService, spdlogService]);

const logService = createLogService(mainProcessClient, configuration, environmentService);
logService.trace('openWorkbench configuration', JSON.stringify(configuration));

// Since the configuration service is one of the core services that is used in so many places, we initialize it
Expand Down Expand Up @@ -200,6 +198,14 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro
return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
}

function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService {
const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, environmentService);
const consoleLogService = new ConsoleLogService(environmentService);
const logService = new MultiplexLogService([consoleLogService, spdlogService]);
const logLevelClient = new LogLevelChannelClient(mainProcessClient.getChannel('loglevel'));
return new FollowerLogService(logLevelClient, logService);
}

function createMainProcessServices(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration): ServiceCollection {
const serviceCollection = new ServiceCollection();

Expand Down

0 comments on commit de24732

Please sign in to comment.