Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/log/common/defaultLogLevels.ts
4780 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 { ILogService, ILoggerService, LogLevel, LogLevelToString, getLogLevel, parseLogLevel } from '../../../../platform/log/common/log.js';
7
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
8
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
9
import { FileOperationResult, IFileService, toFileOperationResult } from '../../../../platform/files/common/files.js';
10
import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js';
11
import { isString, isUndefined } from '../../../../base/common/types.js';
12
import { EXTENSION_IDENTIFIER_WITH_LOG_REGEX } from '../../../../platform/environment/common/environmentService.js';
13
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
14
import { parse } from '../../../../base/common/json.js';
15
import { Disposable } from '../../../../base/common/lifecycle.js';
16
import { Emitter, Event } from '../../../../base/common/event.js';
17
import { equals } from '../../../../base/common/objects.js';
18
19
interface ParsedArgvLogLevels {
20
default?: LogLevel;
21
extensions?: [string, LogLevel][];
22
}
23
24
export type DefaultLogLevels = Required<Readonly<ParsedArgvLogLevels>>;
25
26
export const IDefaultLogLevelsService = createDecorator<IDefaultLogLevelsService>('IDefaultLogLevelsService');
27
28
export interface IDefaultLogLevelsService {
29
30
readonly _serviceBrand: undefined;
31
32
readonly defaultLogLevels: DefaultLogLevels;
33
readonly onDidChangeDefaultLogLevels: Event<DefaultLogLevels>;
34
35
getDefaultLogLevel(extensionId?: string): LogLevel;
36
setDefaultLogLevel(logLevel: LogLevel, extensionId?: string): Promise<void>;
37
}
38
39
class DefaultLogLevelsService extends Disposable implements IDefaultLogLevelsService {
40
41
_serviceBrand: undefined;
42
43
private _onDidChangeDefaultLogLevels = this._register(new Emitter<DefaultLogLevels>);
44
readonly onDidChangeDefaultLogLevels = this._onDidChangeDefaultLogLevels.event;
45
46
private _defaultLogLevels: DefaultLogLevels;
47
48
constructor(
49
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
50
@IFileService private readonly fileService: IFileService,
51
@IJSONEditingService private readonly jsonEditingService: IJSONEditingService,
52
@ILogService private readonly logService: ILogService,
53
@ILoggerService private readonly loggerService: ILoggerService,
54
) {
55
super();
56
this._defaultLogLevels = {
57
default: this._getDefaultLogLevelFromEnv(),
58
extensions: this._getExtensionsDefaultLogLevelsFromEnv()
59
};
60
this._register(this.fileService.onDidFilesChange(e => {
61
if (e.contains(this.environmentService.argvResource)) {
62
this.onDidChangeArgv();
63
}
64
}));
65
}
66
67
private async onDidChangeArgv(): Promise<void> {
68
const defaultLogLevelsFromArgv = await this._parseLogLevelsFromArgv();
69
this.updateDefaultLogLevels(defaultLogLevelsFromArgv);
70
}
71
72
get defaultLogLevels(): DefaultLogLevels {
73
return this._defaultLogLevels;
74
}
75
76
private updateDefaultLogLevels(defaultLogLevelsFromArgv: ParsedArgvLogLevels | undefined): void {
77
const defaultLogLevels = {
78
default: defaultLogLevelsFromArgv?.default ?? this._getDefaultLogLevelFromEnv(),
79
extensions: defaultLogLevelsFromArgv?.extensions ?? this._getExtensionsDefaultLogLevelsFromEnv()
80
};
81
if (!equals(this._defaultLogLevels, defaultLogLevels)) {
82
this._defaultLogLevels = defaultLogLevels;
83
this._onDidChangeDefaultLogLevels.fire(this._defaultLogLevels);
84
}
85
}
86
87
getDefaultLogLevel(extensionId?: string): LogLevel {
88
if (extensionId) {
89
extensionId = extensionId.toLowerCase();
90
return this._getDefaultLogLevel(this._defaultLogLevels, extensionId);
91
} else {
92
return this._getDefaultLogLevel(this._defaultLogLevels);
93
}
94
}
95
96
async setDefaultLogLevel(defaultLogLevel: LogLevel, extensionId?: string): Promise<void> {
97
const defaultLogLevelsFromArgv = await this._parseLogLevelsFromArgv() ?? {};
98
if (extensionId) {
99
extensionId = extensionId.toLowerCase();
100
const currentDefaultLogLevel = this._getDefaultLogLevel(defaultLogLevelsFromArgv, extensionId);
101
defaultLogLevelsFromArgv.extensions = defaultLogLevelsFromArgv.extensions ?? [];
102
const extension = defaultLogLevelsFromArgv.extensions.find(([extension]) => extension === extensionId);
103
if (extension) {
104
extension[1] = defaultLogLevel;
105
} else {
106
defaultLogLevelsFromArgv.extensions.push([extensionId, defaultLogLevel]);
107
}
108
await this._writeLogLevelsToArgv(defaultLogLevelsFromArgv);
109
const extensionLoggers = [...this.loggerService.getRegisteredLoggers()].filter(logger => logger.extensionId && logger.extensionId.toLowerCase() === extensionId);
110
for (const { resource } of extensionLoggers) {
111
if (this.loggerService.getLogLevel(resource) === currentDefaultLogLevel) {
112
this.loggerService.setLogLevel(resource, defaultLogLevel);
113
}
114
}
115
} else {
116
const currentLogLevel = this._getDefaultLogLevel(defaultLogLevelsFromArgv);
117
defaultLogLevelsFromArgv.default = defaultLogLevel;
118
await this._writeLogLevelsToArgv(defaultLogLevelsFromArgv);
119
if (this.loggerService.getLogLevel() === currentLogLevel) {
120
this.loggerService.setLogLevel(defaultLogLevel);
121
}
122
}
123
this.updateDefaultLogLevels(defaultLogLevelsFromArgv);
124
}
125
126
private _getDefaultLogLevel(argvLogLevels: ParsedArgvLogLevels, extension?: string): LogLevel {
127
if (extension) {
128
const extensionLogLevel = argvLogLevels.extensions?.find(([extensionId]) => extensionId === extension);
129
if (extensionLogLevel) {
130
return extensionLogLevel[1];
131
}
132
}
133
return argvLogLevels.default ?? getLogLevel(this.environmentService);
134
}
135
136
private async _writeLogLevelsToArgv(logLevels: ParsedArgvLogLevels): Promise<void> {
137
const logLevelsValue: string[] = [];
138
if (!isUndefined(logLevels.default)) {
139
logLevelsValue.push(LogLevelToString(logLevels.default));
140
}
141
for (const [extension, logLevel] of logLevels.extensions ?? []) {
142
logLevelsValue.push(`${extension}=${LogLevelToString(logLevel)}`);
143
}
144
await this.jsonEditingService.write(this.environmentService.argvResource, [{ path: ['log-level'], value: logLevelsValue.length ? logLevelsValue : undefined }], true);
145
}
146
147
private async _parseLogLevelsFromArgv(): Promise<ParsedArgvLogLevels | undefined> {
148
const result: ParsedArgvLogLevels = { extensions: [] };
149
const logLevels = await this._readLogLevelsFromArgv();
150
for (const extensionLogLevel of logLevels) {
151
const matches = EXTENSION_IDENTIFIER_WITH_LOG_REGEX.exec(extensionLogLevel);
152
if (matches && matches[1] && matches[2]) {
153
const logLevel = parseLogLevel(matches[2]);
154
if (!isUndefined(logLevel)) {
155
result.extensions?.push([matches[1].toLowerCase(), logLevel]);
156
}
157
} else {
158
const logLevel = parseLogLevel(extensionLogLevel);
159
if (!isUndefined(logLevel)) {
160
result.default = logLevel;
161
}
162
}
163
}
164
return !isUndefined(result.default) || result.extensions?.length ? result : undefined;
165
}
166
167
private async _readLogLevelsFromArgv(): Promise<string[]> {
168
try {
169
const content = await this.fileService.readFile(this.environmentService.argvResource);
170
const argv: { 'log-level'?: string | string[] } = parse(content.value.toString());
171
return isString(argv['log-level']) ? [argv['log-level']] : Array.isArray(argv['log-level']) ? argv['log-level'] : [];
172
} catch (error) {
173
if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {
174
this.logService.error(error);
175
}
176
}
177
return [];
178
}
179
180
private _getDefaultLogLevelFromEnv(): LogLevel {
181
return getLogLevel(this.environmentService);
182
}
183
184
private _getExtensionsDefaultLogLevelsFromEnv(): [string, LogLevel][] {
185
const result: [string, LogLevel][] = [];
186
for (const [extension, logLevelValue] of this.environmentService.extensionLogLevel ?? []) {
187
const logLevel = parseLogLevel(logLevelValue);
188
if (!isUndefined(logLevel)) {
189
result.push([extension, logLevel]);
190
}
191
}
192
return result;
193
}
194
}
195
196
registerSingleton(IDefaultLogLevelsService, DefaultLogLevelsService, InstantiationType.Delayed);
197
198