Path: blob/main/src/vs/workbench/services/log/common/defaultLogLevels.ts
4780 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { ILogService, ILoggerService, LogLevel, LogLevelToString, getLogLevel, parseLogLevel } from '../../../../platform/log/common/log.js';6import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';7import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';8import { FileOperationResult, IFileService, toFileOperationResult } from '../../../../platform/files/common/files.js';9import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js';10import { isString, isUndefined } from '../../../../base/common/types.js';11import { EXTENSION_IDENTIFIER_WITH_LOG_REGEX } from '../../../../platform/environment/common/environmentService.js';12import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';13import { parse } from '../../../../base/common/json.js';14import { Disposable } from '../../../../base/common/lifecycle.js';15import { Emitter, Event } from '../../../../base/common/event.js';16import { equals } from '../../../../base/common/objects.js';1718interface ParsedArgvLogLevels {19default?: LogLevel;20extensions?: [string, LogLevel][];21}2223export type DefaultLogLevels = Required<Readonly<ParsedArgvLogLevels>>;2425export const IDefaultLogLevelsService = createDecorator<IDefaultLogLevelsService>('IDefaultLogLevelsService');2627export interface IDefaultLogLevelsService {2829readonly _serviceBrand: undefined;3031readonly defaultLogLevels: DefaultLogLevels;32readonly onDidChangeDefaultLogLevels: Event<DefaultLogLevels>;3334getDefaultLogLevel(extensionId?: string): LogLevel;35setDefaultLogLevel(logLevel: LogLevel, extensionId?: string): Promise<void>;36}3738class DefaultLogLevelsService extends Disposable implements IDefaultLogLevelsService {3940_serviceBrand: undefined;4142private _onDidChangeDefaultLogLevels = this._register(new Emitter<DefaultLogLevels>);43readonly onDidChangeDefaultLogLevels = this._onDidChangeDefaultLogLevels.event;4445private _defaultLogLevels: DefaultLogLevels;4647constructor(48@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,49@IFileService private readonly fileService: IFileService,50@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,51@ILogService private readonly logService: ILogService,52@ILoggerService private readonly loggerService: ILoggerService,53) {54super();55this._defaultLogLevels = {56default: this._getDefaultLogLevelFromEnv(),57extensions: this._getExtensionsDefaultLogLevelsFromEnv()58};59this._register(this.fileService.onDidFilesChange(e => {60if (e.contains(this.environmentService.argvResource)) {61this.onDidChangeArgv();62}63}));64}6566private async onDidChangeArgv(): Promise<void> {67const defaultLogLevelsFromArgv = await this._parseLogLevelsFromArgv();68this.updateDefaultLogLevels(defaultLogLevelsFromArgv);69}7071get defaultLogLevels(): DefaultLogLevels {72return this._defaultLogLevels;73}7475private updateDefaultLogLevels(defaultLogLevelsFromArgv: ParsedArgvLogLevels | undefined): void {76const defaultLogLevels = {77default: defaultLogLevelsFromArgv?.default ?? this._getDefaultLogLevelFromEnv(),78extensions: defaultLogLevelsFromArgv?.extensions ?? this._getExtensionsDefaultLogLevelsFromEnv()79};80if (!equals(this._defaultLogLevels, defaultLogLevels)) {81this._defaultLogLevels = defaultLogLevels;82this._onDidChangeDefaultLogLevels.fire(this._defaultLogLevels);83}84}8586getDefaultLogLevel(extensionId?: string): LogLevel {87if (extensionId) {88extensionId = extensionId.toLowerCase();89return this._getDefaultLogLevel(this._defaultLogLevels, extensionId);90} else {91return this._getDefaultLogLevel(this._defaultLogLevels);92}93}9495async setDefaultLogLevel(defaultLogLevel: LogLevel, extensionId?: string): Promise<void> {96const defaultLogLevelsFromArgv = await this._parseLogLevelsFromArgv() ?? {};97if (extensionId) {98extensionId = extensionId.toLowerCase();99const currentDefaultLogLevel = this._getDefaultLogLevel(defaultLogLevelsFromArgv, extensionId);100defaultLogLevelsFromArgv.extensions = defaultLogLevelsFromArgv.extensions ?? [];101const extension = defaultLogLevelsFromArgv.extensions.find(([extension]) => extension === extensionId);102if (extension) {103extension[1] = defaultLogLevel;104} else {105defaultLogLevelsFromArgv.extensions.push([extensionId, defaultLogLevel]);106}107await this._writeLogLevelsToArgv(defaultLogLevelsFromArgv);108const extensionLoggers = [...this.loggerService.getRegisteredLoggers()].filter(logger => logger.extensionId && logger.extensionId.toLowerCase() === extensionId);109for (const { resource } of extensionLoggers) {110if (this.loggerService.getLogLevel(resource) === currentDefaultLogLevel) {111this.loggerService.setLogLevel(resource, defaultLogLevel);112}113}114} else {115const currentLogLevel = this._getDefaultLogLevel(defaultLogLevelsFromArgv);116defaultLogLevelsFromArgv.default = defaultLogLevel;117await this._writeLogLevelsToArgv(defaultLogLevelsFromArgv);118if (this.loggerService.getLogLevel() === currentLogLevel) {119this.loggerService.setLogLevel(defaultLogLevel);120}121}122this.updateDefaultLogLevels(defaultLogLevelsFromArgv);123}124125private _getDefaultLogLevel(argvLogLevels: ParsedArgvLogLevels, extension?: string): LogLevel {126if (extension) {127const extensionLogLevel = argvLogLevels.extensions?.find(([extensionId]) => extensionId === extension);128if (extensionLogLevel) {129return extensionLogLevel[1];130}131}132return argvLogLevels.default ?? getLogLevel(this.environmentService);133}134135private async _writeLogLevelsToArgv(logLevels: ParsedArgvLogLevels): Promise<void> {136const logLevelsValue: string[] = [];137if (!isUndefined(logLevels.default)) {138logLevelsValue.push(LogLevelToString(logLevels.default));139}140for (const [extension, logLevel] of logLevels.extensions ?? []) {141logLevelsValue.push(`${extension}=${LogLevelToString(logLevel)}`);142}143await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['log-level'], value: logLevelsValue.length ? logLevelsValue : undefined }], true);144}145146private async _parseLogLevelsFromArgv(): Promise<ParsedArgvLogLevels | undefined> {147const result: ParsedArgvLogLevels = { extensions: [] };148const logLevels = await this._readLogLevelsFromArgv();149for (const extensionLogLevel of logLevels) {150const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(extensionLogLevel);151if (matches && matches[1] && matches[2]) {152const logLevel = parseLogLevel(matches[2]);153if (!isUndefined(logLevel)) {154result.extensions?.push([matches[1].toLowerCase(), logLevel]);155}156} else {157const logLevel = parseLogLevel(extensionLogLevel);158if (!isUndefined(logLevel)) {159result.default = logLevel;160}161}162}163return !isUndefined(result.default) || result.extensions?.length ? result : undefined;164}165166private async _readLogLevelsFromArgv(): Promise<string[]> {167try {168const content = await this.fileService.readFile(this.environmentService.argvResource);169const argv: { 'log-level'?: string | string[] } = parse(content.value.toString());170return isString(argv['log-level']) ? [argv['log-level']] : Array.isArray(argv['log-level']) ? argv['log-level'] : [];171} catch (error) {172if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {173this.logService.error(error);174}175}176return [];177}178179private _getDefaultLogLevelFromEnv(): LogLevel {180return getLogLevel(this.environmentService);181}182183private _getExtensionsDefaultLogLevelsFromEnv(): [string, LogLevel][] {184const result: [string, LogLevel][] = [];185for (const [extension, logLevelValue] of this.environmentService.extensionLogLevel ?? []) {186const logLevel = parseLogLevel(logLevelValue);187if (!isUndefined(logLevel)) {188result.push([extension, logLevel]);189}190}191return result;192}193}194195registerSingleton(IDefaultLogLevelsService, DefaultLogLevelsService, InstantiationType.Delayed);196197198