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