Path: blob/main/src/vs/workbench/contrib/logs/common/logs.contribution.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 * as nls from '../../../../nls.js';6import { Registry } from '../../../../platform/registry/common/platform.js';7import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';8import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';9import { SetLogLevelAction } from './logsActions.js';10import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js';11import { IOutputChannelRegistry, IOutputService, Extensions, isMultiSourceOutputChannelDescriptor, isSingleSourceOutputChannelDescriptor } from '../../../services/output/common/output.js';12import { Disposable } from '../../../../base/common/lifecycle.js';13import { CONTEXT_LOG_LEVEL, ILoggerResource, ILoggerService, LogLevel, LogLevelToString, isLogLevel } from '../../../../platform/log/common/log.js';14import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';15import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';16import { Event } from '../../../../base/common/event.js';17import { windowLogId, showWindowLogActionId } from '../../../services/log/common/logConstants.js';18import { IDefaultLogLevelsService } from './defaultLogLevels.js';19import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';20import { CounterSet } from '../../../../base/common/map.js';21import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';22import { Schemas } from '../../../../base/common/network.js';2324registerAction2(class extends Action2 {25constructor() {26super({27id: SetLogLevelAction.ID,28title: SetLogLevelAction.TITLE,29category: Categories.Developer,30f1: true31});32}33run(servicesAccessor: ServicesAccessor): Promise<void> {34return servicesAccessor.get(IInstantiationService).createInstance(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.TITLE.value).run();35}36});3738registerAction2(class extends Action2 {39constructor() {40super({41id: 'workbench.action.setDefaultLogLevel',42title: nls.localize2('setDefaultLogLevel', "Set Default Log Level"),43category: Categories.Developer,44});45}46run(servicesAccessor: ServicesAccessor, logLevel: LogLevel, extensionId?: string): Promise<void> {47return servicesAccessor.get(IDefaultLogLevelsService).setDefaultLogLevel(logLevel, extensionId);48}49});5051class LogOutputChannels extends Disposable implements IWorkbenchContribution {5253private readonly contextKeys = new CounterSet<string>();54private readonly outputChannelRegistry = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels);5556constructor(57@ILoggerService private readonly loggerService: ILoggerService,58@IContextKeyService private readonly contextKeyService: IContextKeyService,59@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,60) {61super();62const contextKey = CONTEXT_LOG_LEVEL.bindTo(contextKeyService);63contextKey.set(LogLevelToString(loggerService.getLogLevel()));64this._register(loggerService.onDidChangeLogLevel(e => {65if (isLogLevel(e)) {66contextKey.set(LogLevelToString(loggerService.getLogLevel()));67}68}));6970this.onDidAddLoggers(loggerService.getRegisteredLoggers());71this._register(loggerService.onDidChangeLoggers(({ added, removed }) => {72this.onDidAddLoggers(added);73this.onDidRemoveLoggers(removed);74}));75this._register(loggerService.onDidChangeVisibility(([resource, visibility]) => {76const logger = loggerService.getRegisteredLogger(resource);77if (logger) {78if (visibility) {79this.registerLogChannel(logger);80} else {81this.deregisterLogChannel(logger);82}83}84}));85this.registerShowWindowLogAction();86this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(() => this.onDidChangeContext()));87}8889private onDidAddLoggers(loggers: Iterable<ILoggerResource>): void {90for (const logger of loggers) {91if (logger.when) {92const contextKeyExpr = ContextKeyExpr.deserialize(logger.when);93if (contextKeyExpr) {94for (const key of contextKeyExpr.keys()) {95this.contextKeys.add(key);96}97if (!this.contextKeyService.contextMatchesRules(contextKeyExpr)) {98continue;99}100}101}102if (logger.hidden) {103continue;104}105this.registerLogChannel(logger);106}107}108109private onDidChangeContext(): void {110for (const logger of this.loggerService.getRegisteredLoggers()) {111if (logger.when) {112if (this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(logger.when))) {113this.registerLogChannel(logger);114} else {115this.deregisterLogChannel(logger);116}117}118}119}120121private onDidRemoveLoggers(loggers: Iterable<ILoggerResource>): void {122for (const logger of loggers) {123if (logger.when) {124const contextKeyExpr = ContextKeyExpr.deserialize(logger.when);125if (contextKeyExpr) {126for (const key of contextKeyExpr.keys()) {127this.contextKeys.delete(key);128}129}130}131this.deregisterLogChannel(logger);132}133}134135private registerLogChannel(logger: ILoggerResource): void {136if (logger.group) {137this.registerCompoundLogChannel(logger.group.id, logger.group.name, logger);138return;139}140141const channel = this.outputChannelRegistry.getChannel(logger.id);142if (channel && isSingleSourceOutputChannelDescriptor(channel) && this.uriIdentityService.extUri.isEqual(channel.source.resource, logger.resource)) {143return;144}145146const existingChannel = this.outputChannelRegistry.getChannel(logger.id);147const remoteLogger = existingChannel && isSingleSourceOutputChannelDescriptor(existingChannel) && existingChannel.source.resource.scheme === Schemas.vscodeRemote ? this.loggerService.getRegisteredLogger(existingChannel.source.resource) : undefined;148if (remoteLogger) {149this.deregisterLogChannel(remoteLogger);150}151const hasToAppendRemote = existingChannel && logger.resource.scheme === Schemas.vscodeRemote;152const id = hasToAppendRemote ? `${logger.id}.remote` : logger.id;153const label = hasToAppendRemote ? nls.localize('remote name', "{0} (Remote)", logger.name ?? logger.id) : logger.name ?? logger.id;154this.outputChannelRegistry.registerChannel({ id, label, source: { resource: logger.resource }, log: true, extensionId: logger.extensionId });155}156157private registerCompoundLogChannel(id: string, name: string, logger: ILoggerResource): void {158const channel = this.outputChannelRegistry.getChannel(id);159const source = { resource: logger.resource, name: logger.name ?? logger.id };160if (channel) {161if (isMultiSourceOutputChannelDescriptor(channel) && !channel.source.some(({ resource }) => this.uriIdentityService.extUri.isEqual(resource, logger.resource))) {162this.outputChannelRegistry.updateChannelSources(id, [...channel.source, source]);163}164} else {165this.outputChannelRegistry.registerChannel({ id, label: name, log: true, source: [source] });166}167}168169private deregisterLogChannel(logger: ILoggerResource): void {170if (logger.group) {171const channel = this.outputChannelRegistry.getChannel(logger.group.id);172if (channel && isMultiSourceOutputChannelDescriptor(channel)) {173this.outputChannelRegistry.updateChannelSources(logger.group.id, channel.source.filter(({ resource }) => !this.uriIdentityService.extUri.isEqual(resource, logger.resource)));174}175} else {176this.outputChannelRegistry.removeChannel(logger.id);177}178}179180private registerShowWindowLogAction(): void {181this._register(registerAction2(class ShowWindowLogAction extends Action2 {182constructor() {183super({184id: showWindowLogActionId,185title: nls.localize2('show window log', "Show Window Log"),186category: Categories.Developer,187f1: true188});189}190async run(servicesAccessor: ServicesAccessor): Promise<void> {191const outputService = servicesAccessor.get(IOutputService);192outputService.showChannel(windowLogId);193}194}));195}196}197198Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored);199200201