Path: blob/main/src/vs/workbench/contrib/logs/common/defaultLogLevels.ts
3296 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';1617interface ParsedArgvLogLevels {18default?: LogLevel;19extensions?: [string, LogLevel][];20}2122export type DefaultLogLevels = Required<Readonly<ParsedArgvLogLevels>>;2324export const IDefaultLogLevelsService = createDecorator<IDefaultLogLevelsService>('IDefaultLogLevelsService');2526export interface IDefaultLogLevelsService {2728readonly _serviceBrand: undefined;2930/**31* An event which fires when default log levels are changed32*/33readonly onDidChangeDefaultLogLevels: Event<void>;3435getDefaultLogLevels(): Promise<DefaultLogLevels>;3637getDefaultLogLevel(extensionId?: string): Promise<LogLevel>;3839setDefaultLogLevel(logLevel: LogLevel, extensionId?: string): Promise<void>;40}4142class DefaultLogLevelsService extends Disposable implements IDefaultLogLevelsService {4344_serviceBrand: undefined;4546private _onDidChangeDefaultLogLevels = this._register(new Emitter<void>);47readonly onDidChangeDefaultLogLevels = this._onDidChangeDefaultLogLevels.event;4849constructor(50@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,51@IFileService private readonly fileService: IFileService,52@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,53@ILogService private readonly logService: ILogService,54@ILoggerService private readonly loggerService: ILoggerService,55) {56super();57}5859async getDefaultLogLevels(): Promise<DefaultLogLevels> {60const argvLogLevel = await this._parseLogLevelsFromArgv();61return {62default: argvLogLevel?.default ?? this._getDefaultLogLevelFromEnv(),63extensions: argvLogLevel?.extensions ?? this._getExtensionsDefaultLogLevelsFromEnv()64};65}6667async getDefaultLogLevel(extensionId?: string): Promise<LogLevel> {68const argvLogLevel = await this._parseLogLevelsFromArgv() ?? {};69if (extensionId) {70extensionId = extensionId.toLowerCase();71return this._getDefaultLogLevel(argvLogLevel, extensionId);72} else {73return this._getDefaultLogLevel(argvLogLevel);74}75}7677async setDefaultLogLevel(defaultLogLevel: LogLevel, extensionId?: string): Promise<void> {78const argvLogLevel = await this._parseLogLevelsFromArgv() ?? {};79if (extensionId) {80extensionId = extensionId.toLowerCase();81const currentDefaultLogLevel = this._getDefaultLogLevel(argvLogLevel, extensionId);82argvLogLevel.extensions = argvLogLevel.extensions ?? [];83const extension = argvLogLevel.extensions.find(([extension]) => extension === extensionId);84if (extension) {85extension[1] = defaultLogLevel;86} else {87argvLogLevel.extensions.push([extensionId, defaultLogLevel]);88}89await this._writeLogLevelsToArgv(argvLogLevel);90const extensionLoggers = [...this.loggerService.getRegisteredLoggers()].filter(logger => logger.extensionId && logger.extensionId.toLowerCase() === extensionId);91for (const { resource } of extensionLoggers) {92if (this.loggerService.getLogLevel(resource) === currentDefaultLogLevel) {93this.loggerService.setLogLevel(resource, defaultLogLevel);94}95}96} else {97const currentLogLevel = this._getDefaultLogLevel(argvLogLevel);98argvLogLevel.default = defaultLogLevel;99await this._writeLogLevelsToArgv(argvLogLevel);100if (this.loggerService.getLogLevel() === currentLogLevel) {101this.loggerService.setLogLevel(defaultLogLevel);102}103}104this._onDidChangeDefaultLogLevels.fire();105}106107private _getDefaultLogLevel(argvLogLevels: ParsedArgvLogLevels, extension?: string): LogLevel {108if (extension) {109const extensionLogLevel = argvLogLevels.extensions?.find(([extensionId]) => extensionId === extension);110if (extensionLogLevel) {111return extensionLogLevel[1];112}113}114return argvLogLevels.default ?? getLogLevel(this.environmentService);115}116117private async _writeLogLevelsToArgv(logLevels: ParsedArgvLogLevels): Promise<void> {118const logLevelsValue: string[] = [];119if (!isUndefined(logLevels.default)) {120logLevelsValue.push(LogLevelToString(logLevels.default));121}122for (const [extension, logLevel] of logLevels.extensions ?? []) {123logLevelsValue.push(`${extension}=${LogLevelToString(logLevel)}`);124}125await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['log-level'], value: logLevelsValue.length ? logLevelsValue : undefined }], true);126}127128private async _parseLogLevelsFromArgv(): Promise<ParsedArgvLogLevels | undefined> {129const result: ParsedArgvLogLevels = { extensions: [] };130const logLevels = await this._readLogLevelsFromArgv();131for (const extensionLogLevel of logLevels) {132const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(extensionLogLevel);133if (matches && matches[1] && matches[2]) {134const logLevel = parseLogLevel(matches[2]);135if (!isUndefined(logLevel)) {136result.extensions?.push([matches[1].toLowerCase(), logLevel]);137}138} else {139const logLevel = parseLogLevel(extensionLogLevel);140if (!isUndefined(logLevel)) {141result.default = logLevel;142}143}144}145return !isUndefined(result.default) || result.extensions?.length ? result : undefined;146}147148private async _readLogLevelsFromArgv(): Promise<string[]> {149try {150const content = await this.fileService.readFile(this.environmentService.argvResource);151const argv: { 'log-level'?: string | string[] } = parse(content.value.toString());152return isString(argv['log-level']) ? [argv['log-level']] : Array.isArray(argv['log-level']) ? argv['log-level'] : [];153} catch (error) {154if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {155this.logService.error(error);156}157}158return [];159}160161private _getDefaultLogLevelFromEnv(): LogLevel {162return getLogLevel(this.environmentService);163}164165private _getExtensionsDefaultLogLevelsFromEnv(): [string, LogLevel][] {166const result: [string, LogLevel][] = [];167for (const [extension, logLevelValue] of this.environmentService.extensionLogLevel ?? []) {168const logLevel = parseLogLevel(logLevelValue);169if (!isUndefined(logLevel)) {170result.push([extension, logLevel]);171}172}173return result;174}175}176177registerSingleton(IDefaultLogLevelsService, DefaultLogLevelsService, InstantiationType.Delayed);178179180