Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/logs/common/logsActions.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 * as nls from '../../../../nls.js';
7
import { Action } from '../../../../base/common/actions.js';
8
import { ILoggerService, LogLevel, LogLevelToLocalizedString, isLogLevel } from '../../../../platform/log/common/log.js';
9
import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js';
10
import { URI } from '../../../../base/common/uri.js';
11
import { IFileService } from '../../../../platform/files/common/files.js';
12
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
13
import { dirname, basename, isEqual } from '../../../../base/common/resources.js';
14
import { IEditorService } from '../../../services/editor/common/editorService.js';
15
import { IOutputChannelDescriptor, IOutputService, isMultiSourceOutputChannelDescriptor, isSingleSourceOutputChannelDescriptor } from '../../../services/output/common/output.js';
16
import { IDefaultLogLevelsService } from './defaultLogLevels.js';
17
import { Codicon } from '../../../../base/common/codicons.js';
18
import { ThemeIcon } from '../../../../base/common/themables.js';
19
import { DisposableStore } from '../../../../base/common/lifecycle.js';
20
21
type LogLevelQuickPickItem = IQuickPickItem & { level: LogLevel };
22
type LogChannelQuickPickItem = IQuickPickItem & { id: string; channel: IOutputChannelDescriptor };
23
24
export class SetLogLevelAction extends Action {
25
26
static readonly ID = 'workbench.action.setLogLevel';
27
static readonly TITLE = nls.localize2('setLogLevel', "Set Log Level...");
28
29
constructor(id: string, label: string,
30
@IQuickInputService private readonly quickInputService: IQuickInputService,
31
@ILoggerService private readonly loggerService: ILoggerService,
32
@IOutputService private readonly outputService: IOutputService,
33
@IDefaultLogLevelsService private readonly defaultLogLevelsService: IDefaultLogLevelsService,
34
) {
35
super(id, label);
36
}
37
38
override async run(): Promise<void> {
39
const logLevelOrChannel = await this.selectLogLevelOrChannel();
40
if (logLevelOrChannel !== null) {
41
if (isLogLevel(logLevelOrChannel)) {
42
this.loggerService.setLogLevel(logLevelOrChannel);
43
} else {
44
await this.setLogLevelForChannel(logLevelOrChannel);
45
}
46
}
47
}
48
49
private async selectLogLevelOrChannel(): Promise<LogChannelQuickPickItem | LogLevel | null> {
50
const defaultLogLevels = await this.defaultLogLevelsService.getDefaultLogLevels();
51
const extensionLogs: LogChannelQuickPickItem[] = [], logs: LogChannelQuickPickItem[] = [];
52
const logLevel = this.loggerService.getLogLevel();
53
for (const channel of this.outputService.getChannelDescriptors()) {
54
if (!this.outputService.canSetLogLevel(channel)) {
55
continue;
56
}
57
const sources = isSingleSourceOutputChannelDescriptor(channel) ? [channel.source] : isMultiSourceOutputChannelDescriptor(channel) ? channel.source : [];
58
if (!sources.length) {
59
continue;
60
}
61
const channelLogLevel = sources.reduce((prev, curr) => Math.min(prev, this.loggerService.getLogLevel(curr.resource) ?? logLevel), logLevel);
62
const item: LogChannelQuickPickItem = {
63
id: channel.id,
64
label: channel.label,
65
description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined,
66
channel
67
};
68
if (channel.extensionId) {
69
extensionLogs.push(item);
70
} else {
71
logs.push(item);
72
}
73
}
74
const entries: (LogLevelQuickPickItem | LogChannelQuickPickItem | IQuickPickSeparator)[] = [];
75
entries.push({ type: 'separator', label: nls.localize('all', "All") });
76
entries.push(...this.getLogLevelEntries(defaultLogLevels.default, this.loggerService.getLogLevel(), true));
77
if (extensionLogs.length) {
78
entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") });
79
entries.push(...extensionLogs.sort((a, b) => a.label.localeCompare(b.label)));
80
}
81
entries.push({ type: 'separator', label: nls.localize('loggers', "Logs") });
82
entries.push(...logs.sort((a, b) => a.label.localeCompare(b.label)));
83
84
return new Promise((resolve, reject) => {
85
const disposables = new DisposableStore();
86
const quickPick = disposables.add(this.quickInputService.createQuickPick({ useSeparators: true }));
87
quickPick.placeholder = nls.localize('selectlog', "Set Log Level");
88
quickPick.items = entries;
89
let selectedItem: IQuickPickItem | undefined;
90
disposables.add(quickPick.onDidTriggerItemButton(e => {
91
quickPick.hide();
92
this.defaultLogLevelsService.setDefaultLogLevel((<LogLevelQuickPickItem>e.item).level);
93
}));
94
disposables.add(quickPick.onDidAccept(e => {
95
selectedItem = quickPick.selectedItems[0];
96
quickPick.hide();
97
}));
98
disposables.add(quickPick.onDidHide(() => {
99
const result = selectedItem ? (<LogLevelQuickPickItem>selectedItem).level ?? <LogChannelQuickPickItem>selectedItem : null;
100
disposables.dispose();
101
resolve(result);
102
}));
103
quickPick.show();
104
});
105
}
106
107
private async setLogLevelForChannel(logChannel: LogChannelQuickPickItem): Promise<void> {
108
const defaultLogLevels = await this.defaultLogLevelsService.getDefaultLogLevels();
109
const defaultLogLevel = defaultLogLevels.extensions.find(e => e[0] === logChannel.channel.extensionId?.toLowerCase())?.[1] ?? defaultLogLevels.default;
110
const entries = this.getLogLevelEntries(defaultLogLevel, this.outputService.getLogLevel(logChannel.channel) ?? defaultLogLevel, !!logChannel.channel.extensionId);
111
112
return new Promise((resolve, reject) => {
113
const disposables = new DisposableStore();
114
const quickPick = disposables.add(this.quickInputService.createQuickPick());
115
quickPick.placeholder = logChannel ? nls.localize('selectLogLevelFor', " {0}: Select log level", logChannel?.label) : nls.localize('selectLogLevel', "Select log level");
116
quickPick.items = entries;
117
quickPick.activeItems = entries.filter((entry) => entry.level === this.loggerService.getLogLevel());
118
let selectedItem: LogLevelQuickPickItem | undefined;
119
disposables.add(quickPick.onDidTriggerItemButton(e => {
120
quickPick.hide();
121
this.defaultLogLevelsService.setDefaultLogLevel((<LogLevelQuickPickItem>e.item).level, logChannel.channel.extensionId);
122
}));
123
disposables.add(quickPick.onDidAccept(e => {
124
selectedItem = quickPick.selectedItems[0] as LogLevelQuickPickItem;
125
quickPick.hide();
126
}));
127
disposables.add(quickPick.onDidHide(() => {
128
if (selectedItem) {
129
this.outputService.setLogLevel(logChannel.channel, selectedItem.level);
130
}
131
disposables.dispose();
132
resolve();
133
}));
134
quickPick.show();
135
});
136
}
137
138
private getLogLevelEntries(defaultLogLevel: LogLevel, currentLogLevel: LogLevel, canSetDefaultLogLevel: boolean): LogLevelQuickPickItem[] {
139
const button: IQuickInputButton | undefined = canSetDefaultLogLevel ? { iconClass: ThemeIcon.asClassName(Codicon.checkAll), tooltip: nls.localize('resetLogLevel', "Set as Default Log Level") } : undefined;
140
return [
141
{ label: this.getLabel(LogLevel.Trace, currentLogLevel), level: LogLevel.Trace, description: this.getDescription(LogLevel.Trace, defaultLogLevel), buttons: button && defaultLogLevel !== LogLevel.Trace ? [button] : undefined },
142
{ label: this.getLabel(LogLevel.Debug, currentLogLevel), level: LogLevel.Debug, description: this.getDescription(LogLevel.Debug, defaultLogLevel), buttons: button && defaultLogLevel !== LogLevel.Debug ? [button] : undefined },
143
{ label: this.getLabel(LogLevel.Info, currentLogLevel), level: LogLevel.Info, description: this.getDescription(LogLevel.Info, defaultLogLevel), buttons: button && defaultLogLevel !== LogLevel.Info ? [button] : undefined },
144
{ label: this.getLabel(LogLevel.Warning, currentLogLevel), level: LogLevel.Warning, description: this.getDescription(LogLevel.Warning, defaultLogLevel), buttons: button && defaultLogLevel !== LogLevel.Warning ? [button] : undefined },
145
{ label: this.getLabel(LogLevel.Error, currentLogLevel), level: LogLevel.Error, description: this.getDescription(LogLevel.Error, defaultLogLevel), buttons: button && defaultLogLevel !== LogLevel.Error ? [button] : undefined },
146
{ label: this.getLabel(LogLevel.Off, currentLogLevel), level: LogLevel.Off, description: this.getDescription(LogLevel.Off, defaultLogLevel), buttons: button && defaultLogLevel !== LogLevel.Off ? [button] : undefined },
147
];
148
}
149
150
private getLabel(level: LogLevel, current?: LogLevel): string {
151
const label = LogLevelToLocalizedString(level).value;
152
return level === current ? `$(check) ${label}` : label;
153
}
154
155
private getDescription(level: LogLevel, defaultLogLevel: LogLevel): string | undefined {
156
return defaultLogLevel === level ? nls.localize('default', "Default") : undefined;
157
}
158
159
}
160
161
export class OpenWindowSessionLogFileAction extends Action {
162
163
static readonly ID = 'workbench.action.openSessionLogFile';
164
static readonly TITLE = nls.localize2('openSessionLogFile', "Open Window Log File (Session)...");
165
166
constructor(id: string, label: string,
167
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
168
@IFileService private readonly fileService: IFileService,
169
@IQuickInputService private readonly quickInputService: IQuickInputService,
170
@IEditorService private readonly editorService: IEditorService,
171
) {
172
super(id, label);
173
}
174
175
override async run(): Promise<void> {
176
const sessionResult = await this.quickInputService.pick(
177
this.getSessions().then(sessions => sessions.map((s, index): IQuickPickItem => ({
178
id: s.toString(),
179
label: basename(s),
180
description: index === 0 ? nls.localize('current', "Current") : undefined
181
}))),
182
{
183
canPickMany: false,
184
placeHolder: nls.localize('sessions placeholder', "Select Session")
185
});
186
if (sessionResult) {
187
const logFileResult = await this.quickInputService.pick(
188
this.getLogFiles(URI.parse(sessionResult.id!)).then(logFiles => logFiles.map((s): IQuickPickItem => ({
189
id: s.toString(),
190
label: basename(s)
191
}))),
192
{
193
canPickMany: false,
194
placeHolder: nls.localize('log placeholder', "Select Log file")
195
});
196
if (logFileResult) {
197
return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!), options: { pinned: true } }).then(() => undefined);
198
}
199
}
200
}
201
202
private async getSessions(): Promise<URI[]> {
203
const logsPath = this.environmentService.logsHome.with({ scheme: this.environmentService.logFile.scheme });
204
const result: URI[] = [logsPath];
205
const stat = await this.fileService.resolve(dirname(logsPath));
206
if (stat.children) {
207
result.push(...stat.children
208
.filter(stat => !isEqual(stat.resource, logsPath) && stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name))
209
.sort()
210
.reverse()
211
.map(d => d.resource));
212
}
213
return result;
214
}
215
216
private async getLogFiles(session: URI): Promise<URI[]> {
217
const stat = await this.fileService.resolve(session);
218
if (stat.children) {
219
return stat.children.filter(stat => !stat.isDirectory).map(stat => stat.resource);
220
}
221
return [];
222
}
223
}
224
225
226