diff --git a/src/vs/platform/log/node/spdlogService.ts b/src/vs/platform/log/node/spdlogService.ts index 726bd185dcb..d39af82182d 100644 --- a/src/vs/platform/log/node/spdlogService.ts +++ b/src/vs/platform/log/node/spdlogService.ts @@ -10,10 +10,11 @@ import { ILogService, LogLevel, NullLogService } from 'vs/platform/log/common/lo import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { RotatingLogger, setAsyncMode } from 'spdlog'; -export function createLogService(processName: string, environmentService: IEnvironmentService): ILogService { +export function createLogService(processName: string, environmentService: IEnvironmentService, logsSubfolder?: string): ILogService { try { setAsyncMode(8192, 2000); - const logfilePath = path.join(environmentService.logsPath, `${processName}.log`); + const logsDirPath = logsSubfolder ? path.join(environmentService.logsPath, logsSubfolder) : environmentService.logsPath; + const logfilePath = path.join(logsDirPath, `${processName}.log`); const logger = new RotatingLogger(processName, logfilePath, 1024 * 1024 * 5, 6); logger.setLevel(0); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 86982258339..65354174c44 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4325,6 +4325,29 @@ declare module 'vscode' { resolveTask(task: Task, token?: CancellationToken): ProviderResult; } + export enum LogLevel { + Trace = 1, + Debug = 2, + Info = 3, + Warning = 4, + Error = 5, + Critical = 6, + Off = 7 + } + + export interface ILogger { + onDidChangeLogLevel: Event; + getLevel(): LogLevel; + getLogDirectory(): Thenable; + + trace(message: string, ...args: any[]): void; + debug(message: string, ...args: any[]): void; + info(message: string, ...args: any[]): void; + warn(message: string, ...args: any[]): void; + error(message: string | Error, ...args: any[]): void; + critical(message: string | Error, ...args: any[]): void; + } + /** * Namespace describing the environment the editor runs in. */ @@ -4365,6 +4388,13 @@ declare module 'vscode' { * @readonly */ export let sessionId: string; + + /** + * Something + * + * @readonly + */ + export let logger: ILogger; } /** diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ed71e123c75..6c3cdb9f832 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -59,6 +59,8 @@ import { toGlobPattern, toLanguageSelector } from 'vs/workbench/api/node/extHost import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; +import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; @@ -83,7 +85,8 @@ export function createApiFactory( extHostWorkspace: ExtHostWorkspace, extHostConfiguration: ExtHostConfiguration, extensionService: ExtHostExtensionService, - logService: ILogService + logService: ILogService, + environmentService: IEnvironmentService ): IExtensionApiFactory { // Addressable instances @@ -121,6 +124,7 @@ export function createApiFactory( const extHostProgress = new ExtHostProgress(threadService.get(MainContext.MainThreadProgress)); const extHostOutputService = new ExtHostOutputService(threadService); const extHostLanguages = new ExtHostLanguages(threadService); + const extHostLogService = new ExtHostLogService(environmentService); // Register API-ish commands ExtHostApiCommands.register(extHostCommands); @@ -202,6 +206,7 @@ export function createApiFactory( get language() { return Platform.language; }, get appName() { return product.nameLong; }, get appRoot() { return initData.environment.appRoot; }, + get logger() { return extHostLogService.getExtLogger(extension.id); }, }); // namespace: extensions @@ -570,6 +575,7 @@ export function createApiFactory( Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, Location: extHostTypes.Location, + LogLevel: extHostTypes.LogLevel, MarkdownString: extHostTypes.MarkdownString, OverviewRulerLane: EditorCommon.OverviewRulerLane, ParameterInformation: extHostTypes.ParameterInformation, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index f9fd93dfa20..67091188abb 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -33,7 +33,7 @@ import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; -import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; +import { EndOfLine, TextEditorLineNumbersStyle, LogLevel } from 'vs/workbench/api/node/extHostTypes'; import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks'; @@ -673,6 +673,10 @@ export interface ExtHostWindowShape { $onDidChangeWindowFocus(value: boolean): void; } +export interface ExtHostLogServiceShape { + $acceptLogLevelChanged(logLevel: LogLevel): void; +} + // --- proxy identifiers export const MainContext = { @@ -723,6 +727,7 @@ export const ExtHostContext = { ExtHostLanguageFeatures: createExtId('ExtHostLanguageFeatures'), ExtHostQuickOpen: createExtId('ExtHostQuickOpen'), ExtHostExtensionService: createExtId('ExtHostExtensionService'), + // ExtHostLogService: createExtId('ExtHostLogService'), ExtHostTerminalService: createExtId('ExtHostTerminalService'), ExtHostSCM: createExtId('ExtHostSCM'), ExtHostTask: createExtId('ExtHostTask'), diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 71a56b06be4..321b24b8f2b 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -21,6 +21,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { TernarySearchTree } from 'vs/base/common/map'; import { Barrier } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; class ExtensionMemento implements IExtensionMemento { @@ -127,7 +128,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { threadService: ExtHostThreadService, extHostWorkspace: ExtHostWorkspace, extHostConfiguration: ExtHostConfiguration, - logService: ILogService + logService: ILogService, + environmentService: IEnvironmentService ) { this._barrier = new Barrier(); this._registry = new ExtensionDescriptionRegistry(initData.extensions); @@ -140,7 +142,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._activator = null; // initialize API first (i.e. do not release barrier until the API is initialized) - const apiFactory = createApiFactory(initData, threadService, extHostWorkspace, extHostConfiguration, this, logService); + const apiFactory = createApiFactory(initData, threadService, extHostWorkspace, extHostConfiguration, this, logService, environmentService); initializeExtensionApi(this, apiFactory).then(() => { diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts new file mode 100644 index 00000000000..347dc8c6be0 --- /dev/null +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * 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 { ExtHostLogServiceShape } from './extHost.protocol'; +import { LogLevel } from 'vs/workbench/api/node/extHostTypes'; +import { ILogService } from 'vs/platform/log/common/log'; +import { createLogService } from 'vs/platform/log/node/spdlogService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +export class ExtHostLogService implements ExtHostLogServiceShape { + private _loggers: Map = new Map(); + + private _onDidChangeLogLevel: Emitter; + get onDidChangeLogLevel(): Event { return this._onDidChangeLogLevel.event; } + + constructor(private _environmentService: IEnvironmentService) { + this._onDidChangeLogLevel = new Emitter(); + } + + $acceptLogLevelChanged(logLevel: LogLevel): void { + this._onDidChangeLogLevel.fire(logLevel); + } + + getExtLogger(extensionID: string): ExtHostLogger { + if (!this._loggers.has(extensionID)) { + const logService = createLogService(extensionID, this._environmentService, extensionID); + const logsDirPath = path.join(this._environmentService.logsPath, extensionID); + this._loggers.set(extensionID, new ExtHostLogger(this, logService, logsDirPath)); + } + + return this._loggers.get(extensionID); + } +} + +export class ExtHostLogger implements vscode.ILogger { + private _currentLevel: LogLevel; + + get onDidChangeLogLevel(): Event { return this._extHostLogService.onDidChangeLogLevel; } + + constructor( + private readonly _extHostLogService: ExtHostLogService, + private readonly _logService: ILogService, + private readonly _logDirectory: string + ) { + this._extHostLogService.onDidChangeLogLevel(logLevel => this._currentLevel = logLevel); + } + + getLogDirectory(): TPromise { + return dirExists(this._logDirectory).then(exists => { + if (exists) { + return TPromise.wrap(null); + } else { + return mkdirp(this._logDirectory); + } + }).then(() => { + return this._logDirectory; + }); + } + + getLevel(): vscode.LogLevel { + return this._currentLevel; + } + + trace(message: string, ...args: any[]): void { + return this._logService.trace(message, ...args); + } + + debug(message: string, ...args: any[]): void { + return this._logService.debug(message, ...args); + } + + info(message: string, ...args: any[]): void { + return this._logService.info(message, ...args); + } + + warn(message: string, ...args: any[]): void { + return this._logService.warn(message, ...args); + } + + error(message: string | Error, ...args: any[]): void { + return this._logService.error(message, ...args); + } + + critical(message: string | Error, ...args: any[]): void { + return this._logService.critical(message, ...args); + } +} diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index b0cc9b02062..a81d0c845eb 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1531,3 +1531,13 @@ export class FunctionBreakpoint extends Breakpoint { this.functionName = functionName; } } + +export enum LogLevel { + Trace = 1, + Debug = 2, + Info = 3, + Warning = 4, + Error = 5, + Critical = 6, + Off = 7 +} diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 952ef7e4f96..122a62f3277 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -98,7 +98,7 @@ export class ExtensionHostMain { this._logService.trace('initData', initData); this._extHostConfiguration = new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration); - this._extensionService = new ExtHostExtensionService(initData, threadService, extHostWorkspace, this._extHostConfiguration, this._logService); + this._extensionService = new ExtHostExtensionService(initData, threadService, extHostWorkspace, this._extHostConfiguration, this._logService, environmentService); // error forwarding and stack trace scanning const extensionErrors = new WeakMap();