Path: blob/main/src/vs/workbench/contrib/logs/common/logs.contribution.ts
5284 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 { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';19import { CounterSet } from '../../../../base/common/map.js';20import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';21import { Schemas } from '../../../../base/common/network.js';22import { IDefaultLogLevelsService } from '../../../services/log/common/defaultLogLevels.js';2324registerAction2(class extends Action2 {25constructor() {26super({27id: SetLogLevelAction.ID,28title: SetLogLevelAction.TITLE,29category: Categories.Developer,30f1: true31});32}33run(servicesAccessor: ServicesAccessor): Promise<void> {34const action = servicesAccessor.get(IInstantiationService).createInstance(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.TITLE.value);35return action.run().finally(() => action.dispose());36}37});3839registerAction2(class extends Action2 {40constructor() {41super({42id: 'workbench.action.setDefaultLogLevel',43title: nls.localize2('setDefaultLogLevel', "Set Default Log Level"),44category: Categories.Developer,45});46}47run(servicesAccessor: ServicesAccessor, logLevel: LogLevel, extensionId?: string): Promise<void> {48return servicesAccessor.get(IDefaultLogLevelsService).setDefaultLogLevel(logLevel, extensionId);49}50});5152class LogOutputChannels extends Disposable implements IWorkbenchContribution {5354private readonly contextKeys = new CounterSet<string>();55private readonly outputChannelRegistry = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels);5657constructor(58@ILoggerService private readonly loggerService: ILoggerService,59@IContextKeyService private readonly contextKeyService: IContextKeyService,60@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,61) {62super();63const contextKey = CONTEXT_LOG_LEVEL.bindTo(contextKeyService);64contextKey.set(LogLevelToString(loggerService.getLogLevel()));65this._register(loggerService.onDidChangeLogLevel(e => {66if (isLogLevel(e)) {67contextKey.set(LogLevelToString(loggerService.getLogLevel()));68}69}));7071this.onDidAddLoggers(loggerService.getRegisteredLoggers());72this._register(loggerService.onDidChangeLoggers(({ added, removed }) => {73this.onDidAddLoggers(added);74this.onDidRemoveLoggers(removed);75}));76this._register(loggerService.onDidChangeVisibility(([resource, visibility]) => {77const logger = loggerService.getRegisteredLogger(resource);78if (logger) {79if (visibility) {80this.registerLogChannel(logger);81} else {82this.deregisterLogChannel(logger);83}84}85}));86this.registerShowWindowLogAction();87this._register(Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys))(() => this.onDidChangeContext()));88}8990private onDidAddLoggers(loggers: Iterable<ILoggerResource>): void {91for (const logger of loggers) {92if (logger.when) {93const contextKeyExpr = ContextKeyExpr.deserialize(logger.when);94if (contextKeyExpr) {95for (const key of contextKeyExpr.keys()) {96this.contextKeys.add(key);97}98if (!this.contextKeyService.contextMatchesRules(contextKeyExpr)) {99continue;100}101}102}103if (logger.hidden) {104continue;105}106this.registerLogChannel(logger);107}108}109110private onDidChangeContext(): void {111for (const logger of this.loggerService.getRegisteredLoggers()) {112if (logger.when) {113if (this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(logger.when))) {114this.registerLogChannel(logger);115} else {116this.deregisterLogChannel(logger);117}118}119}120}121122private onDidRemoveLoggers(loggers: Iterable<ILoggerResource>): void {123for (const logger of loggers) {124if (logger.when) {125const contextKeyExpr = ContextKeyExpr.deserialize(logger.when);126if (contextKeyExpr) {127for (const key of contextKeyExpr.keys()) {128this.contextKeys.delete(key);129}130}131}132this.deregisterLogChannel(logger);133}134}135136private registerLogChannel(logger: ILoggerResource): void {137if (logger.group) {138this.registerCompoundLogChannel(logger.group.id, logger.group.name, logger);139return;140}141142const channel = this.outputChannelRegistry.getChannel(logger.id);143if (channel && isSingleSourceOutputChannelDescriptor(channel) && this.uriIdentityService.extUri.isEqual(channel.source.resource, logger.resource)) {144return;145}146147const existingChannel = this.outputChannelRegistry.getChannel(logger.id);148const remoteLogger = existingChannel && isSingleSourceOutputChannelDescriptor(existingChannel) && existingChannel.source.resource.scheme === Schemas.vscodeRemote ? this.loggerService.getRegisteredLogger(existingChannel.source.resource) : undefined;149if (remoteLogger) {150this.deregisterLogChannel(remoteLogger);151}152const hasToAppendRemote = existingChannel && logger.resource.scheme === Schemas.vscodeRemote;153const id = hasToAppendRemote ? `${logger.id}.remote` : logger.id;154const label = hasToAppendRemote ? nls.localize('remote name', "{0} (Remote)", logger.name ?? logger.id) : logger.name ?? logger.id;155this.outputChannelRegistry.registerChannel({ id, label, source: { resource: logger.resource }, log: true, extensionId: logger.extensionId });156}157158private registerCompoundLogChannel(id: string, name: string, logger: ILoggerResource): void {159const channel = this.outputChannelRegistry.getChannel(id);160const source = { resource: logger.resource, name: logger.name ?? logger.id };161if (channel) {162if (isMultiSourceOutputChannelDescriptor(channel) && !channel.source.some(({ resource }) => this.uriIdentityService.extUri.isEqual(resource, logger.resource))) {163this.outputChannelRegistry.updateChannelSources(id, [...channel.source, source]);164}165} else {166this.outputChannelRegistry.registerChannel({ id, label: name, log: true, source: [source] });167}168}169170private deregisterLogChannel(logger: ILoggerResource): void {171if (logger.group) {172const channel = this.outputChannelRegistry.getChannel(logger.group.id);173if (channel && isMultiSourceOutputChannelDescriptor(channel)) {174this.outputChannelRegistry.updateChannelSources(logger.group.id, channel.source.filter(({ resource }) => !this.uriIdentityService.extUri.isEqual(resource, logger.resource)));175}176} else {177this.outputChannelRegistry.removeChannel(logger.id);178}179}180181private registerShowWindowLogAction(): void {182this._register(registerAction2(class ShowWindowLogAction extends Action2 {183constructor() {184super({185id: showWindowLogActionId,186title: nls.localize2('show window log', "Show Window Log"),187category: Categories.Developer,188f1: true189});190}191async run(servicesAccessor: ServicesAccessor): Promise<void> {192const outputService = servicesAccessor.get(IOutputService);193outputService.showChannel(windowLogId);194}195}));196}197}198199Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored);200201202