Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/output/browser/outputServices.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Event, Emitter } from '../../../../base/common/event.js';
7
import { Schemas } from '../../../../base/common/network.js';
8
import { URI } from '../../../../base/common/uri.js';
9
import { Disposable, DisposableMap } from '../../../../base/common/lifecycle.js';
10
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
11
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
12
import { Registry } from '../../../../platform/registry/common/platform.js';
13
import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry, ACTIVE_OUTPUT_CHANNEL_CONTEXT, CONTEXT_ACTIVE_FILE_OUTPUT, CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE, CONTEXT_ACTIVE_OUTPUT_LEVEL, CONTEXT_ACTIVE_OUTPUT_LEVEL_IS_DEFAULT, IOutputViewFilters, SHOW_DEBUG_FILTER_CONTEXT, SHOW_ERROR_FILTER_CONTEXT, SHOW_INFO_FILTER_CONTEXT, SHOW_TRACE_FILTER_CONTEXT, SHOW_WARNING_FILTER_CONTEXT, CONTEXT_ACTIVE_LOG_FILE_OUTPUT, IMultiSourceOutputChannelDescriptor, isSingleSourceOutputChannelDescriptor, HIDE_CATEGORY_FILTER_CONTEXT, isMultiSourceOutputChannelDescriptor, ILogEntry } from '../../../services/output/common/output.js';
14
import { OutputLinkProvider } from './outputLinkProvider.js';
15
import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js';
16
import { ITextModel } from '../../../../editor/common/model.js';
17
import { ILogService, ILoggerService, LogLevel, LogLevelToString } from '../../../../platform/log/common/log.js';
18
import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js';
19
import { DelegatedOutputChannelModel, FileOutputChannelModel, IOutputChannelModel, MultiFileOutputChannelModel } from '../common/outputChannelModel.js';
20
import { IViewsService } from '../../../services/views/common/viewsService.js';
21
import { OutputViewPane } from './outputView.js';
22
import { ILanguageService } from '../../../../editor/common/languages/language.js';
23
import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
24
import { IDefaultLogLevelsService } from '../../logs/common/defaultLogLevels.js';
25
import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js';
26
import { IFileService } from '../../../../platform/files/common/files.js';
27
import { localize } from '../../../../nls.js';
28
import { joinPath } from '../../../../base/common/resources.js';
29
import { VSBuffer } from '../../../../base/common/buffer.js';
30
import { telemetryLogId } from '../../../../platform/telemetry/common/telemetryUtils.js';
31
import { toLocalISOString } from '../../../../base/common/date.js';
32
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
33
34
const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';
35
36
class OutputChannel extends Disposable implements IOutputChannel {
37
38
scrollLock: boolean = false;
39
readonly model: IOutputChannelModel;
40
readonly id: string;
41
readonly label: string;
42
readonly uri: URI;
43
44
constructor(
45
readonly outputChannelDescriptor: IOutputChannelDescriptor,
46
private readonly outputLocation: URI,
47
private readonly outputDirPromise: Promise<void>,
48
@ILanguageService private readonly languageService: ILanguageService,
49
@IInstantiationService private readonly instantiationService: IInstantiationService,
50
) {
51
super();
52
this.id = outputChannelDescriptor.id;
53
this.label = outputChannelDescriptor.label;
54
this.uri = URI.from({ scheme: Schemas.outputChannel, path: this.id });
55
this.model = this._register(this.createOutputChannelModel(this.uri, outputChannelDescriptor));
56
}
57
58
private createOutputChannelModel(uri: URI, outputChannelDescriptor: IOutputChannelDescriptor): IOutputChannelModel {
59
const language = outputChannelDescriptor.languageId ? this.languageService.createById(outputChannelDescriptor.languageId) : this.languageService.createByMimeType(outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME);
60
if (isMultiSourceOutputChannelDescriptor(outputChannelDescriptor)) {
61
return this.instantiationService.createInstance(MultiFileOutputChannelModel, uri, language, [...outputChannelDescriptor.source]);
62
}
63
if (isSingleSourceOutputChannelDescriptor(outputChannelDescriptor)) {
64
return this.instantiationService.createInstance(FileOutputChannelModel, uri, language, outputChannelDescriptor.source);
65
}
66
return this.instantiationService.createInstance(DelegatedOutputChannelModel, this.id, uri, language, this.outputLocation, this.outputDirPromise);
67
}
68
69
getLogEntries(): ReadonlyArray<ILogEntry> {
70
return this.model.getLogEntries();
71
}
72
73
append(output: string): void {
74
this.model.append(output);
75
}
76
77
update(mode: OutputChannelUpdateMode, till?: number): void {
78
this.model.update(mode, till, true);
79
}
80
81
clear(): void {
82
this.model.clear();
83
}
84
85
replace(value: string): void {
86
this.model.replace(value);
87
}
88
}
89
90
interface IOutputFilterOptions {
91
filterHistory: string[];
92
trace: boolean;
93
debug: boolean;
94
info: boolean;
95
warning: boolean;
96
error: boolean;
97
sources: string;
98
}
99
100
class OutputViewFilters extends Disposable implements IOutputViewFilters {
101
102
private readonly _onDidChange = this._register(new Emitter<void>());
103
readonly onDidChange = this._onDidChange.event;
104
105
constructor(
106
options: IOutputFilterOptions,
107
private readonly contextKeyService: IContextKeyService
108
) {
109
super();
110
111
this._trace = SHOW_TRACE_FILTER_CONTEXT.bindTo(this.contextKeyService);
112
this._trace.set(options.trace);
113
114
this._debug = SHOW_DEBUG_FILTER_CONTEXT.bindTo(this.contextKeyService);
115
this._debug.set(options.debug);
116
117
this._info = SHOW_INFO_FILTER_CONTEXT.bindTo(this.contextKeyService);
118
this._info.set(options.info);
119
120
this._warning = SHOW_WARNING_FILTER_CONTEXT.bindTo(this.contextKeyService);
121
this._warning.set(options.warning);
122
123
this._error = SHOW_ERROR_FILTER_CONTEXT.bindTo(this.contextKeyService);
124
this._error.set(options.error);
125
126
this._categories = HIDE_CATEGORY_FILTER_CONTEXT.bindTo(this.contextKeyService);
127
this._categories.set(options.sources);
128
129
this.filterHistory = options.filterHistory;
130
}
131
132
filterHistory: string[];
133
134
private _filterText = '';
135
get text(): string {
136
return this._filterText;
137
}
138
set text(filterText: string) {
139
if (this._filterText !== filterText) {
140
this._filterText = filterText;
141
this._onDidChange.fire();
142
}
143
}
144
145
private readonly _trace: IContextKey<boolean>;
146
get trace(): boolean {
147
return !!this._trace.get();
148
}
149
set trace(trace: boolean) {
150
if (this._trace.get() !== trace) {
151
this._trace.set(trace);
152
this._onDidChange.fire();
153
}
154
}
155
156
private readonly _debug: IContextKey<boolean>;
157
get debug(): boolean {
158
return !!this._debug.get();
159
}
160
set debug(debug: boolean) {
161
if (this._debug.get() !== debug) {
162
this._debug.set(debug);
163
this._onDidChange.fire();
164
}
165
}
166
167
private readonly _info: IContextKey<boolean>;
168
get info(): boolean {
169
return !!this._info.get();
170
}
171
set info(info: boolean) {
172
if (this._info.get() !== info) {
173
this._info.set(info);
174
this._onDidChange.fire();
175
}
176
}
177
178
private readonly _warning: IContextKey<boolean>;
179
get warning(): boolean {
180
return !!this._warning.get();
181
}
182
set warning(warning: boolean) {
183
if (this._warning.get() !== warning) {
184
this._warning.set(warning);
185
this._onDidChange.fire();
186
}
187
}
188
189
private readonly _error: IContextKey<boolean>;
190
get error(): boolean {
191
return !!this._error.get();
192
}
193
set error(error: boolean) {
194
if (this._error.get() !== error) {
195
this._error.set(error);
196
this._onDidChange.fire();
197
}
198
}
199
200
private readonly _categories: IContextKey<string>;
201
get categories(): string {
202
return this._categories.get() || ',';
203
}
204
set categories(categories: string) {
205
this._categories.set(categories);
206
this._onDidChange.fire();
207
}
208
209
toggleCategory(category: string): void {
210
const categories = this.categories;
211
if (this.hasCategory(category)) {
212
this.categories = categories.replace(`,${category},`, ',');
213
} else {
214
this.categories = `${categories}${category},`;
215
}
216
}
217
218
hasCategory(category: string): boolean {
219
if (category === ',') {
220
return false;
221
}
222
return this.categories.includes(`,${category},`);
223
}
224
}
225
226
export class OutputService extends Disposable implements IOutputService, ITextModelContentProvider {
227
228
declare readonly _serviceBrand: undefined;
229
230
private readonly channels = this._register(new DisposableMap<string, OutputChannel>());
231
private activeChannelIdInStorage: string;
232
private activeChannel?: OutputChannel;
233
234
private readonly _onActiveOutputChannel = this._register(new Emitter<string>());
235
readonly onActiveOutputChannel: Event<string> = this._onActiveOutputChannel.event;
236
237
private readonly activeOutputChannelContext: IContextKey<string>;
238
private readonly activeFileOutputChannelContext: IContextKey<boolean>;
239
private readonly activeLogOutputChannelContext: IContextKey<boolean>;
240
private readonly activeOutputChannelLevelSettableContext: IContextKey<boolean>;
241
private readonly activeOutputChannelLevelContext: IContextKey<string>;
242
private readonly activeOutputChannelLevelIsDefaultContext: IContextKey<boolean>;
243
244
private readonly outputLocation: URI;
245
246
readonly filters: OutputViewFilters;
247
248
constructor(
249
@IStorageService private readonly storageService: IStorageService,
250
@IInstantiationService private readonly instantiationService: IInstantiationService,
251
@ITextModelService private readonly textModelService: ITextModelService,
252
@ILogService private readonly logService: ILogService,
253
@ILoggerService private readonly loggerService: ILoggerService,
254
@ILifecycleService private readonly lifecycleService: ILifecycleService,
255
@IViewsService private readonly viewsService: IViewsService,
256
@IContextKeyService contextKeyService: IContextKeyService,
257
@IDefaultLogLevelsService private readonly defaultLogLevelsService: IDefaultLogLevelsService,
258
@IFileDialogService private readonly fileDialogService: IFileDialogService,
259
@IFileService private readonly fileService: IFileService,
260
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
261
) {
262
super();
263
this.activeChannelIdInStorage = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, '');
264
this.activeOutputChannelContext = ACTIVE_OUTPUT_CHANNEL_CONTEXT.bindTo(contextKeyService);
265
this.activeOutputChannelContext.set(this.activeChannelIdInStorage);
266
this._register(this.onActiveOutputChannel(channel => this.activeOutputChannelContext.set(channel)));
267
268
this.activeFileOutputChannelContext = CONTEXT_ACTIVE_FILE_OUTPUT.bindTo(contextKeyService);
269
this.activeLogOutputChannelContext = CONTEXT_ACTIVE_LOG_FILE_OUTPUT.bindTo(contextKeyService);
270
this.activeOutputChannelLevelSettableContext = CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE.bindTo(contextKeyService);
271
this.activeOutputChannelLevelContext = CONTEXT_ACTIVE_OUTPUT_LEVEL.bindTo(contextKeyService);
272
this.activeOutputChannelLevelIsDefaultContext = CONTEXT_ACTIVE_OUTPUT_LEVEL_IS_DEFAULT.bindTo(contextKeyService);
273
274
this.outputLocation = joinPath(environmentService.windowLogsPath, `output_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
275
276
// Register as text model content provider for output
277
this._register(textModelService.registerTextModelContentProvider(Schemas.outputChannel, this));
278
this._register(instantiationService.createInstance(OutputLinkProvider));
279
280
// Create output channels for already registered channels
281
const registry = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels);
282
for (const channelIdentifier of registry.getChannels()) {
283
this.onDidRegisterChannel(channelIdentifier.id);
284
}
285
this._register(registry.onDidRegisterChannel(id => this.onDidRegisterChannel(id)));
286
this._register(registry.onDidUpdateChannelSources(channel => this.onDidUpdateChannelSources(channel)));
287
this._register(registry.onDidRemoveChannel(channel => this.onDidRemoveChannel(channel)));
288
289
// Set active channel to first channel if not set
290
if (!this.activeChannel) {
291
const channels = this.getChannelDescriptors();
292
this.setActiveChannel(channels && channels.length > 0 ? this.getChannel(channels[0].id) : undefined);
293
}
294
295
this._register(Event.filter(this.viewsService.onDidChangeViewVisibility, e => e.id === OUTPUT_VIEW_ID && e.visible)(() => {
296
if (this.activeChannel) {
297
this.viewsService.getActiveViewWithId<OutputViewPane>(OUTPUT_VIEW_ID)?.showChannel(this.activeChannel, true);
298
}
299
}));
300
301
this._register(this.loggerService.onDidChangeLogLevel(() => {
302
this.resetLogLevelFilters();
303
this.setLevelContext();
304
this.setLevelIsDefaultContext();
305
}));
306
this._register(this.defaultLogLevelsService.onDidChangeDefaultLogLevels(() => {
307
this.setLevelIsDefaultContext();
308
}));
309
310
this._register(this.lifecycleService.onDidShutdown(() => this.dispose()));
311
312
this.filters = this._register(new OutputViewFilters({
313
filterHistory: [],
314
trace: true,
315
debug: true,
316
info: true,
317
warning: true,
318
error: true,
319
sources: '',
320
}, contextKeyService));
321
}
322
323
provideTextContent(resource: URI): Promise<ITextModel> | null {
324
const channel = <OutputChannel>this.getChannel(resource.path);
325
if (channel) {
326
return channel.model.loadModel();
327
}
328
return null;
329
}
330
331
async showChannel(id: string, preserveFocus?: boolean): Promise<void> {
332
const channel = this.getChannel(id);
333
if (this.activeChannel?.id !== channel?.id) {
334
this.setActiveChannel(channel);
335
this._onActiveOutputChannel.fire(id);
336
}
337
const outputView = await this.viewsService.openView<OutputViewPane>(OUTPUT_VIEW_ID, !preserveFocus);
338
if (outputView && channel) {
339
outputView.showChannel(channel, !!preserveFocus);
340
}
341
}
342
343
getChannel(id: string): OutputChannel | undefined {
344
return this.channels.get(id);
345
}
346
347
getChannelDescriptor(id: string): IOutputChannelDescriptor | undefined {
348
return Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannel(id);
349
}
350
351
getChannelDescriptors(): IOutputChannelDescriptor[] {
352
return Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannels();
353
}
354
355
getActiveChannel(): IOutputChannel | undefined {
356
return this.activeChannel;
357
}
358
359
canSetLogLevel(channel: IOutputChannelDescriptor): boolean {
360
return channel.log && channel.id !== telemetryLogId;
361
}
362
363
getLogLevel(channel: IOutputChannelDescriptor): LogLevel | undefined {
364
if (!channel.log) {
365
return undefined;
366
}
367
const sources = isSingleSourceOutputChannelDescriptor(channel) ? [channel.source] : isMultiSourceOutputChannelDescriptor(channel) ? channel.source : [];
368
if (sources.length === 0) {
369
return undefined;
370
}
371
372
const logLevel = this.loggerService.getLogLevel();
373
return sources.reduce((prev, curr) => Math.min(prev, this.loggerService.getLogLevel(curr.resource) ?? logLevel), LogLevel.Error);
374
}
375
376
setLogLevel(channel: IOutputChannelDescriptor, logLevel: LogLevel): void {
377
if (!channel.log) {
378
return;
379
}
380
const sources = isSingleSourceOutputChannelDescriptor(channel) ? [channel.source] : isMultiSourceOutputChannelDescriptor(channel) ? channel.source : [];
381
if (sources.length === 0) {
382
return;
383
}
384
for (const source of sources) {
385
this.loggerService.setLogLevel(source.resource, logLevel);
386
}
387
}
388
389
registerCompoundLogChannel(descriptors: IOutputChannelDescriptor[]): string {
390
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels);
391
descriptors.sort((a, b) => a.label.localeCompare(b.label));
392
const id = descriptors.map(r => r.id.toLowerCase()).join('-');
393
if (!outputChannelRegistry.getChannel(id)) {
394
outputChannelRegistry.registerChannel({
395
id,
396
label: descriptors.map(r => r.label).join(', '),
397
log: descriptors.some(r => r.log),
398
user: true,
399
source: descriptors.map(descriptor => {
400
if (isSingleSourceOutputChannelDescriptor(descriptor)) {
401
return [{ resource: descriptor.source.resource, name: descriptor.source.name ?? descriptor.label }];
402
}
403
if (isMultiSourceOutputChannelDescriptor(descriptor)) {
404
return descriptor.source;
405
}
406
const channel = this.getChannel(descriptor.id);
407
if (channel) {
408
return channel.model.source;
409
}
410
return [];
411
}).flat(),
412
});
413
}
414
return id;
415
}
416
417
async saveOutputAs(...channels: IOutputChannelDescriptor[]): Promise<void> {
418
let channel: IOutputChannel | undefined;
419
if (channels.length > 1) {
420
const compoundChannelId = this.registerCompoundLogChannel(channels);
421
channel = this.getChannel(compoundChannelId);
422
} else {
423
channel = this.getChannel(channels[0].id);
424
}
425
426
if (!channel) {
427
return;
428
}
429
430
try {
431
const name = channels.length > 1 ? 'output' : channels[0].label;
432
const uri = await this.fileDialogService.showSaveDialog({
433
title: localize('saveLog.dialogTitle', "Save Output As"),
434
availableFileSystems: [Schemas.file],
435
defaultUri: joinPath(await this.fileDialogService.defaultFilePath(), `${name}.log`),
436
filters: [{
437
name,
438
extensions: ['log']
439
}]
440
});
441
442
if (!uri) {
443
return;
444
}
445
446
const modelRef = await this.textModelService.createModelReference(channel.uri);
447
try {
448
await this.fileService.writeFile(uri, VSBuffer.fromString(modelRef.object.textEditorModel.getValue()));
449
} finally {
450
modelRef.dispose();
451
}
452
return;
453
}
454
finally {
455
if (channels.length > 1) {
456
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).removeChannel(channel.id);
457
}
458
}
459
}
460
461
private async onDidRegisterChannel(channelId: string): Promise<void> {
462
const channel = this.createChannel(channelId);
463
this.channels.set(channelId, channel);
464
if (!this.activeChannel || this.activeChannelIdInStorage === channelId) {
465
this.setActiveChannel(channel);
466
this._onActiveOutputChannel.fire(channelId);
467
const outputView = this.viewsService.getActiveViewWithId<OutputViewPane>(OUTPUT_VIEW_ID);
468
outputView?.showChannel(channel, true);
469
}
470
}
471
472
private onDidUpdateChannelSources(channel: IMultiSourceOutputChannelDescriptor): void {
473
const outputChannel = this.channels.get(channel.id);
474
if (outputChannel) {
475
outputChannel.model.updateChannelSources(channel.source);
476
}
477
}
478
479
private onDidRemoveChannel(channel: IOutputChannelDescriptor): void {
480
if (this.activeChannel?.id === channel.id) {
481
const channels = this.getChannelDescriptors();
482
if (channels[0]) {
483
this.showChannel(channels[0].id);
484
}
485
}
486
this.channels.deleteAndDispose(channel.id);
487
}
488
489
private createChannel(id: string): OutputChannel {
490
const channel = this.instantiateChannel(id);
491
this._register(Event.once(channel.model.onDispose)(() => {
492
if (this.activeChannel === channel) {
493
const channels = this.getChannelDescriptors();
494
const channel = channels.length ? this.getChannel(channels[0].id) : undefined;
495
if (channel && this.viewsService.isViewVisible(OUTPUT_VIEW_ID)) {
496
this.showChannel(channel.id);
497
} else {
498
this.setActiveChannel(undefined);
499
}
500
}
501
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).removeChannel(id);
502
}));
503
504
return channel;
505
}
506
507
private outputFolderCreationPromise: Promise<void> | null = null;
508
private instantiateChannel(id: string): OutputChannel {
509
const channelData = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannel(id);
510
if (!channelData) {
511
this.logService.error(`Channel '${id}' is not registered yet`);
512
throw new Error(`Channel '${id}' is not registered yet`);
513
}
514
if (!this.outputFolderCreationPromise) {
515
this.outputFolderCreationPromise = this.fileService.createFolder(this.outputLocation).then(() => undefined);
516
}
517
return this.instantiationService.createInstance(OutputChannel, channelData, this.outputLocation, this.outputFolderCreationPromise);
518
}
519
520
private resetLogLevelFilters(): void {
521
const descriptor = this.activeChannel?.outputChannelDescriptor;
522
const channelLogLevel = descriptor ? this.getLogLevel(descriptor) : undefined;
523
if (channelLogLevel !== undefined) {
524
this.filters.error = channelLogLevel <= LogLevel.Error;
525
this.filters.warning = channelLogLevel <= LogLevel.Warning;
526
this.filters.info = channelLogLevel <= LogLevel.Info;
527
this.filters.debug = channelLogLevel <= LogLevel.Debug;
528
this.filters.trace = channelLogLevel <= LogLevel.Trace;
529
}
530
}
531
532
private setLevelContext(): void {
533
const descriptor = this.activeChannel?.outputChannelDescriptor;
534
const channelLogLevel = descriptor ? this.getLogLevel(descriptor) : undefined;
535
this.activeOutputChannelLevelContext.set(channelLogLevel !== undefined ? LogLevelToString(channelLogLevel) : '');
536
}
537
538
private async setLevelIsDefaultContext(): Promise<void> {
539
const descriptor = this.activeChannel?.outputChannelDescriptor;
540
const channelLogLevel = descriptor ? this.getLogLevel(descriptor) : undefined;
541
if (channelLogLevel !== undefined) {
542
const channelDefaultLogLevel = await this.defaultLogLevelsService.getDefaultLogLevel(descriptor?.extensionId);
543
this.activeOutputChannelLevelIsDefaultContext.set(channelDefaultLogLevel === channelLogLevel);
544
} else {
545
this.activeOutputChannelLevelIsDefaultContext.set(false);
546
}
547
}
548
549
private setActiveChannel(channel: OutputChannel | undefined): void {
550
this.activeChannel = channel;
551
const descriptor = channel?.outputChannelDescriptor;
552
this.activeFileOutputChannelContext.set(!!descriptor && isSingleSourceOutputChannelDescriptor(descriptor));
553
this.activeLogOutputChannelContext.set(!!descriptor?.log);
554
this.activeOutputChannelLevelSettableContext.set(descriptor !== undefined && this.canSetLogLevel(descriptor));
555
this.setLevelIsDefaultContext();
556
this.setLevelContext();
557
558
if (this.activeChannel) {
559
this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE, StorageTarget.MACHINE);
560
} else {
561
this.storageService.remove(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE);
562
}
563
}
564
}
565
566